From 13123461994185b3d9fad1acc67e5cc01cc7947f Mon Sep 17 00:00:00 2001 From: Mario Zechner Date: Thu, 23 Apr 2026 23:43:00 +0200 Subject: [PATCH] fix(ai): expand Copilot eager streaming compat closes #3575 --- packages/ai/scripts/generate-models.ts | 6 +++++- packages/ai/src/models.generated.ts | 2 ++ .../test/anthropic-eager-tool-input-e2e.test.ts | 17 +++++++++++++++-- 3 files changed, 22 insertions(+), 3 deletions(-) diff --git a/packages/ai/scripts/generate-models.ts b/packages/ai/scripts/generate-models.ts index c3a43f26..13c9f5db 100644 --- a/packages/ai/scripts/generate-models.ts +++ b/packages/ai/scripts/generate-models.ts @@ -66,7 +66,11 @@ const KIMI_STATIC_HEADERS = { const AI_GATEWAY_MODELS_URL = "https://ai-gateway.vercel.sh/v1"; const AI_GATEWAY_BASE_URL = "https://ai-gateway.vercel.sh"; const ZAI_TOOL_STREAM_UNSUPPORTED_MODELS = new Set(["glm-4.5", "glm-4.5-air", "glm-4.5-flash", "glm-4.5v"]); -const EAGER_TOOL_INPUT_STREAMING_UNSUPPORTED_ANTHROPIC_MODELS = new Set(["github-copilot:claude-haiku-4.5"]); +const EAGER_TOOL_INPUT_STREAMING_UNSUPPORTED_ANTHROPIC_MODELS = new Set([ + "github-copilot:claude-haiku-4.5", + "github-copilot:claude-sonnet-4", + "github-copilot:claude-sonnet-4.5", +]); function getAnthropicMessagesCompat(provider: string, modelId: string): AnthropicMessagesCompat | undefined { return EAGER_TOOL_INPUT_STREAMING_UNSUPPORTED_ANTHROPIC_MODELS.has(`${provider}:${modelId}`) diff --git a/packages/ai/src/models.generated.ts b/packages/ai/src/models.generated.ts index 0f8aa6f5..711408da 100644 --- a/packages/ai/src/models.generated.ts +++ b/packages/ai/src/models.generated.ts @@ -3087,6 +3087,7 @@ export const MODELS = { provider: "github-copilot", baseUrl: "https://api.individual.githubcopilot.com", headers: {"User-Agent":"GitHubCopilotChat/0.35.0","Editor-Version":"vscode/1.107.0","Editor-Plugin-Version":"copilot-chat/0.35.0","Copilot-Integration-Id":"vscode-chat"}, + compat: {"supportsEagerToolInputStreaming":false}, reasoning: true, input: ["text", "image"], cost: { @@ -3105,6 +3106,7 @@ export const MODELS = { provider: "github-copilot", baseUrl: "https://api.individual.githubcopilot.com", headers: {"User-Agent":"GitHubCopilotChat/0.35.0","Editor-Version":"vscode/1.107.0","Editor-Plugin-Version":"copilot-chat/0.35.0","Copilot-Integration-Id":"vscode-chat"}, + compat: {"supportsEagerToolInputStreaming":false}, reasoning: true, input: ["text", "image"], cost: { diff --git a/packages/ai/test/anthropic-eager-tool-input-e2e.test.ts b/packages/ai/test/anthropic-eager-tool-input-e2e.test.ts index 6e5fe68c..3fed7ce2 100644 --- a/packages/ai/test/anthropic-eager-tool-input-e2e.test.ts +++ b/packages/ai/test/anthropic-eager-tool-input-e2e.test.ts @@ -55,6 +55,8 @@ function getProbePriority(model: Model<"anthropic-messages">): number { // stale Claude 3.x aliases that can remain in catalogs after upstream removal. if (modelId.includes("haiku") && (modelId.includes("4-5") || modelId.includes("4.5"))) { priority -= 1000; + } else if (modelId.includes("sonnet") && (modelId.includes("4-") || modelId.includes("4."))) { + priority -= 750; } else if (modelId.includes("claude") && (modelId.includes("4-") || modelId.includes("4."))) { priority -= 500; } @@ -78,7 +80,10 @@ function selectOneCasePerProvider(cases: AnthropicEagerE2ECase[]): AnthropicEage ); } -const probeCases = selectOneCasePerProvider(anthropicMessagesCases); +const generatedCompatCases = selectOneCasePerProvider(anthropicMessagesCases); +const forcedEagerProbeCases = selectOneCasePerProvider( + anthropicMessagesCases.filter((testCase) => testCase.model.compat?.supportsEagerToolInputStreaming !== false), +); function withEagerToolInputStreaming(model: Model<"anthropic-messages">): Model<"anthropic-messages"> { return { @@ -127,8 +132,16 @@ describe("Anthropic Messages eager tool input streaming E2E", () => { expect(anthropicMessagesCases.map((testCase) => testCase.name).sort()).toEqual(expectedModels.sort()); }); + describe("generated compatibility settings", () => { + for (const testCase of generatedCompatCases) { + it.skipIf(!testCase.apiKey)(`${testCase.name} accepts configured tool streaming`, { retry: 2 }, async () => { + await expectToolEnabledRequestAccepted(testCase.model, testCase.apiKey); + }); + } + }); + describe("forced eager_input_streaming probe", () => { - for (const testCase of probeCases) { + for (const testCase of forcedEagerProbeCases) { const model = withEagerToolInputStreaming(testCase.model); it.skipIf(!testCase.apiKey)(