fix(app): stop spawning providers per client by warming the snapshot store
The workspace screen was prefetching per-provider model lists via `useProviderModels`, which fanned out N parallel `list_provider_models_request`s that bypassed `ProviderSnapshotManager` and spawned each provider fresh on every call (e.g. repeated copilot ENOENT errors). The return value was never consumed. Replace it with `useProvidersSnapshot(serverId, workspaceDirectory)` so warmup goes through the shared, deduped, TTL-cached snapshot path that consumers already read from.
This commit is contained in:
@@ -1,49 +0,0 @@
|
|||||||
import { useMemo } from "react";
|
|
||||||
import { useQueries } from "@tanstack/react-query";
|
|
||||||
import {
|
|
||||||
AGENT_PROVIDER_DEFINITIONS,
|
|
||||||
type AgentProviderDefinition,
|
|
||||||
} from "@server/server/agent/provider-manifest";
|
|
||||||
import type { AgentModelDefinition, AgentProvider } from "@server/server/agent/agent-sdk-types";
|
|
||||||
import { useHostRuntimeClient, useHostRuntimeIsConnected } from "@/runtime/host-runtime";
|
|
||||||
|
|
||||||
const STALE_TIME = 5 * 60 * 1000;
|
|
||||||
|
|
||||||
export function useProviderModels(serverId: string) {
|
|
||||||
const client = useHostRuntimeClient(serverId);
|
|
||||||
const isConnected = useHostRuntimeIsConnected(serverId);
|
|
||||||
const enabled = Boolean(serverId && client && isConnected);
|
|
||||||
|
|
||||||
const queries = useQueries({
|
|
||||||
queries: AGENT_PROVIDER_DEFINITIONS.map((def) => ({
|
|
||||||
queryKey: ["providerModels", serverId, def.id] as const,
|
|
||||||
enabled,
|
|
||||||
staleTime: STALE_TIME,
|
|
||||||
queryFn: async () => {
|
|
||||||
if (!client) {
|
|
||||||
throw new Error("Host is not connected");
|
|
||||||
}
|
|
||||||
const payload = await client.listProviderModels(def.id as AgentProvider);
|
|
||||||
if (payload.error) {
|
|
||||||
throw new Error(payload.error);
|
|
||||||
}
|
|
||||||
return payload.models ?? [];
|
|
||||||
},
|
|
||||||
})),
|
|
||||||
});
|
|
||||||
|
|
||||||
const allProviderModels = useMemo(() => {
|
|
||||||
const map = new Map<string, AgentModelDefinition[]>();
|
|
||||||
for (let i = 0; i < AGENT_PROVIDER_DEFINITIONS.length; i++) {
|
|
||||||
const query = queries[i];
|
|
||||||
if (query?.data) {
|
|
||||||
map.set(AGENT_PROVIDER_DEFINITIONS[i]!.id, query.data);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return map;
|
|
||||||
}, [queries]);
|
|
||||||
|
|
||||||
const isLoading = queries.some((q) => q.isLoading);
|
|
||||||
|
|
||||||
return { allProviderModels, isLoading };
|
|
||||||
}
|
|
||||||
@@ -64,7 +64,7 @@ import {
|
|||||||
workspaceTabTargetsEqual,
|
workspaceTabTargetsEqual,
|
||||||
} from "@/utils/workspace-tab-identity";
|
} from "@/utils/workspace-tab-identity";
|
||||||
import { useHostRuntimeClient, useHostRuntimeIsConnected } from "@/runtime/host-runtime";
|
import { useHostRuntimeClient, useHostRuntimeIsConnected } from "@/runtime/host-runtime";
|
||||||
import { useProviderModels } from "@/hooks/use-provider-models";
|
import { useProvidersSnapshot } from "@/hooks/use-providers-snapshot";
|
||||||
import { useWorkspaceSetupStore } from "@/stores/workspace-setup-store";
|
import { useWorkspaceSetupStore } from "@/stores/workspace-setup-store";
|
||||||
import { useWorkspaceTerminalSessionRetention } from "@/terminal/hooks/use-workspace-terminal-session-retention";
|
import { useWorkspaceTerminalSessionRetention } from "@/terminal/hooks/use-workspace-terminal-session-retention";
|
||||||
import {
|
import {
|
||||||
@@ -612,9 +612,6 @@ function WorkspaceScreenContent({ serverId, workspaceId }: WorkspaceScreenProps)
|
|||||||
|
|
||||||
const normalizedServerId = trimNonEmpty(decodeSegment(serverId)) ?? "";
|
const normalizedServerId = trimNonEmpty(decodeSegment(serverId)) ?? "";
|
||||||
|
|
||||||
// Prefetch provider models early so the model picker is warm by the time it opens
|
|
||||||
useProviderModels(normalizedServerId);
|
|
||||||
|
|
||||||
const normalizedWorkspaceId =
|
const normalizedWorkspaceId =
|
||||||
resolveWorkspaceRouteId({
|
resolveWorkspaceRouteId({
|
||||||
routeWorkspaceId: workspaceId,
|
routeWorkspaceId: workspaceId,
|
||||||
@@ -645,6 +642,10 @@ function WorkspaceScreenContent({ serverId, workspaceId }: WorkspaceScreenProps)
|
|||||||
);
|
);
|
||||||
const workspaceDirectory = workspaceAuthority?.workspaceDirectory ?? null;
|
const workspaceDirectory = workspaceAuthority?.workspaceDirectory ?? null;
|
||||||
const isMissingWorkspaceExecutionAuthority = Boolean(workspaceDescriptor && !workspaceAuthority);
|
const isMissingWorkspaceExecutionAuthority = Boolean(workspaceDescriptor && !workspaceAuthority);
|
||||||
|
|
||||||
|
// Warm the server-side provider snapshot for this workspace cwd so the model
|
||||||
|
// picker is ready when opened. Consumers share the same query cache key.
|
||||||
|
useProvidersSnapshot(normalizedServerId, workspaceDirectory);
|
||||||
const [pendingTerminalCreateInput, setPendingTerminalCreateInput] = useState<{
|
const [pendingTerminalCreateInput, setPendingTerminalCreateInput] = useState<{
|
||||||
paneId?: string;
|
paneId?: string;
|
||||||
} | null>(null);
|
} | null>(null);
|
||||||
|
|||||||
Reference in New Issue
Block a user