fix(server): route list_provider_models through the snapshot manager
The legacy list_provider_models_request handler was calling providerRegistry[p].fetchModels() directly, so every client call spawned the provider fresh (e.g. repeated copilot ENOENT errors). Old clients still send this RPC. Route it through ProviderSnapshotManager so simultaneous calls dedupe through the in-flight warmUps map, cached entries (ready / error / unavailable) are served without spawning, and at most one spawn occurs per cwd per TTL window regardless of client count. Direct-fetch path kept only as a fallback for the null-manager case (tests).
This commit is contained in:
@@ -3068,36 +3068,93 @@ export class Session {
|
|||||||
private async handleListProviderModelsRequest(
|
private async handleListProviderModelsRequest(
|
||||||
msg: Extract<SessionInboundMessage, { type: "list_provider_models_request" }>,
|
msg: Extract<SessionInboundMessage, { type: "list_provider_models_request" }>,
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
|
const cwd = msg.cwd ? expandTilde(msg.cwd) : undefined;
|
||||||
const fetchedAt = new Date().toISOString();
|
const fetchedAt = new Date().toISOString();
|
||||||
try {
|
const manager = this.providerSnapshotManager;
|
||||||
const models = await this.providerRegistry[msg.provider].fetchModels({
|
|
||||||
cwd: msg.cwd ? expandTilde(msg.cwd) : undefined,
|
if (!manager) {
|
||||||
});
|
try {
|
||||||
this.emit({
|
const models = await this.providerRegistry[msg.provider].fetchModels({ cwd });
|
||||||
type: "list_provider_models_response",
|
this.emit({
|
||||||
payload: {
|
type: "list_provider_models_response",
|
||||||
provider: msg.provider,
|
payload: {
|
||||||
models,
|
provider: msg.provider,
|
||||||
error: null,
|
models,
|
||||||
fetchedAt,
|
error: null,
|
||||||
requestId: msg.requestId,
|
fetchedAt,
|
||||||
},
|
requestId: msg.requestId,
|
||||||
});
|
},
|
||||||
} catch (error) {
|
});
|
||||||
this.sessionLogger.error(
|
} catch (error) {
|
||||||
{ err: error, provider: msg.provider },
|
this.sessionLogger.error(
|
||||||
`Failed to list models for ${msg.provider}`,
|
{ err: error, provider: msg.provider },
|
||||||
);
|
`Failed to list models for ${msg.provider}`,
|
||||||
this.emit({
|
);
|
||||||
type: "list_provider_models_response",
|
this.emit({
|
||||||
payload: {
|
type: "list_provider_models_response",
|
||||||
provider: msg.provider,
|
payload: {
|
||||||
error: (error as Error)?.message ?? String(error),
|
provider: msg.provider,
|
||||||
fetchedAt,
|
error: (error as Error)?.message ?? String(error),
|
||||||
requestId: msg.requestId,
|
fetchedAt,
|
||||||
},
|
requestId: msg.requestId,
|
||||||
});
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const findEntry = () =>
|
||||||
|
manager.getSnapshot(cwd).find((candidate) => candidate.provider === msg.provider);
|
||||||
|
|
||||||
|
let entry = findEntry();
|
||||||
|
if (!entry || entry.status === "loading") {
|
||||||
|
// Awaits the in-flight warmup (deduped per-cwd) so old clients still get
|
||||||
|
// a resolved answer rather than a loading placeholder.
|
||||||
|
await manager.refresh({ cwd, providers: [msg.provider] });
|
||||||
|
entry = findEntry();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!entry) {
|
||||||
|
this.emit({
|
||||||
|
type: "list_provider_models_response",
|
||||||
|
payload: {
|
||||||
|
provider: msg.provider,
|
||||||
|
error: `Unknown provider: ${msg.provider}`,
|
||||||
|
fetchedAt,
|
||||||
|
requestId: msg.requestId,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (entry.status === "ready") {
|
||||||
|
this.emit({
|
||||||
|
type: "list_provider_models_response",
|
||||||
|
payload: {
|
||||||
|
provider: msg.provider,
|
||||||
|
models: entry.models ?? [],
|
||||||
|
error: null,
|
||||||
|
fetchedAt: entry.fetchedAt ?? fetchedAt,
|
||||||
|
requestId: msg.requestId,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const errorMessage =
|
||||||
|
entry.status === "error"
|
||||||
|
? (entry.error ?? `Failed to list models for ${msg.provider}`)
|
||||||
|
: `Provider ${msg.provider} is not available`;
|
||||||
|
|
||||||
|
this.emit({
|
||||||
|
type: "list_provider_models_response",
|
||||||
|
payload: {
|
||||||
|
provider: msg.provider,
|
||||||
|
error: errorMessage,
|
||||||
|
fetchedAt,
|
||||||
|
requestId: msg.requestId,
|
||||||
|
},
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private async handleListProviderModesRequest(
|
private async handleListProviderModesRequest(
|
||||||
@@ -4771,6 +4828,7 @@ export class Session {
|
|||||||
isPathWithinRoot: (rootPath, candidatePath) =>
|
isPathWithinRoot: (rootPath, candidatePath) =>
|
||||||
this.isPathWithinRoot(rootPath, candidatePath),
|
this.isPathWithinRoot(rootPath, candidatePath),
|
||||||
killTerminalsUnderPath: (rootPath) => this.killTerminalsUnderPath(rootPath),
|
killTerminalsUnderPath: (rootPath) => this.killTerminalsUnderPath(rootPath),
|
||||||
|
sessionLogger: this.sessionLogger,
|
||||||
},
|
},
|
||||||
msg,
|
msg,
|
||||||
);
|
);
|
||||||
@@ -8467,6 +8525,8 @@ export class Session {
|
|||||||
isPathWithinRoot: (pathRoot, candidatePath) =>
|
isPathWithinRoot: (pathRoot, candidatePath) =>
|
||||||
this.isPathWithinRoot(pathRoot, candidatePath),
|
this.isPathWithinRoot(pathRoot, candidatePath),
|
||||||
killTrackedTerminal: (terminalId, options) => this.killTrackedTerminal(terminalId, options),
|
killTrackedTerminal: (terminalId, options) => this.killTrackedTerminal(terminalId, options),
|
||||||
|
detachTerminalStream: (terminalId, options) =>
|
||||||
|
void this.detachTerminalStream(terminalId, options),
|
||||||
sessionLogger: this.sessionLogger,
|
sessionLogger: this.sessionLogger,
|
||||||
terminalManager: this.terminalManager,
|
terminalManager: this.terminalManager,
|
||||||
},
|
},
|
||||||
|
|||||||
Reference in New Issue
Block a user