mirror of
https://github.com/getpaseo/paseo.git
synced 2026-10-26 03:03:04 +00:00
fix(server): keep Claude model queries open
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
import { describe, expect, test, vi } from "vitest";
|
||||
import type { ModelInfo } from "@anthropic-ai/claude-agent-sdk";
|
||||
import type { ModelInfo, SDKUserMessage } from "@anthropic-ai/claude-agent-sdk";
|
||||
|
||||
import { createTestLogger } from "../../../test-utils/test-logger.js";
|
||||
import { ClaudeAgentClient, convertClaudeHistoryEntry } from "./claude-agent.js";
|
||||
@@ -357,4 +357,52 @@ describe("ClaudeAgentClient.listModels", () => {
|
||||
]);
|
||||
expect(queryMock.return).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
test("keeps the Claude control-plane query open until supportedModels resolves", async () => {
|
||||
const queryMock = createSupportedModelsQueryMock([
|
||||
{
|
||||
value: "default",
|
||||
displayName: "Default (recommended)",
|
||||
description: "Sonnet 4.6 · Best for everyday tasks",
|
||||
},
|
||||
] satisfies ModelInfo[]);
|
||||
let promptIterator: AsyncIterator<SDKUserMessage, void> | null = null;
|
||||
let promptNextPromise: Promise<IteratorResult<SDKUserMessage, void>> | null = null;
|
||||
let promptClosedBeforeModelsResolved = false;
|
||||
|
||||
queryMock.supportedModels = vi.fn(async () => {
|
||||
promptNextPromise = promptIterator?.next() ?? null;
|
||||
if (!promptNextPromise) {
|
||||
throw new Error("Prompt iterator not captured");
|
||||
}
|
||||
promptNextPromise.then(() => {
|
||||
promptClosedBeforeModelsResolved = true;
|
||||
});
|
||||
await Promise.resolve();
|
||||
expect(promptClosedBeforeModelsResolved).toBe(false);
|
||||
return [
|
||||
{
|
||||
value: "default",
|
||||
displayName: "Default (recommended)",
|
||||
description: "Sonnet 4.6 · Best for everyday tasks",
|
||||
},
|
||||
] satisfies ModelInfo[];
|
||||
});
|
||||
|
||||
const queryFactory = vi.fn(({ prompt }) => {
|
||||
promptIterator = prompt[Symbol.asyncIterator]();
|
||||
return queryMock;
|
||||
});
|
||||
const client = new ClaudeAgentClient({
|
||||
logger,
|
||||
queryFactory: queryFactory as never,
|
||||
});
|
||||
|
||||
const models = await client.listModels({ cwd: process.cwd() });
|
||||
|
||||
expect(models).toHaveLength(1);
|
||||
expect(promptNextPromise).not.toBeNull();
|
||||
await expect(promptNextPromise).resolves.toEqual({ done: true, value: undefined });
|
||||
expect(queryMock.return).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -225,10 +225,6 @@ function applyRuntimeSettingsToClaudeOptions(
|
||||
};
|
||||
}
|
||||
|
||||
function createEmptyClaudePrompt(): AsyncGenerator<SDKUserMessage, void, undefined> {
|
||||
return (async function* empty() {})();
|
||||
}
|
||||
|
||||
function isClaudeThinkingEffort(value: string | null | undefined): value is ClaudeThinkingEffort {
|
||||
return value === "low" || value === "medium" || value === "high" || value === "max";
|
||||
}
|
||||
@@ -1032,8 +1028,9 @@ export class ClaudeAgentClient implements AgentClient {
|
||||
}
|
||||
|
||||
async listModels(options?: ListModelsOptions): Promise<AgentModelDefinition[]> {
|
||||
const input = createAsyncMessageInput<SDKUserMessage>();
|
||||
const claudeQuery = this.queryFactory({
|
||||
prompt: createEmptyClaudePrompt(),
|
||||
prompt: input.iterable,
|
||||
options: applyRuntimeSettingsToClaudeOptions(
|
||||
{
|
||||
cwd: options?.cwd ?? process.cwd(),
|
||||
@@ -1052,13 +1049,13 @@ export class ClaudeAgentClient implements AgentClient {
|
||||
this.logger.warn({ err: error }, "Failed to query Claude supportedModels()");
|
||||
throw error;
|
||||
} finally {
|
||||
input.end();
|
||||
try {
|
||||
await claudeQuery.return?.();
|
||||
} catch {
|
||||
// ignore control-plane shutdown errors
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
async listPersistedAgents(
|
||||
|
||||
Reference in New Issue
Block a user