fix(copilot): disable eager tool streaming for Claude 4.5 (#75393)

Disable Anthropic eager tool streaming only for GitHub Copilot Claude 4.5 proxy routes, preserving newer Claude behavior.

Fixes #75348

Co-authored-by: Kailigithub <Kailigithub@users.noreply.github.com>
This commit is contained in:
Kailigithub
2026-06-13 19:52:02 +08:00
committed by GitHub
parent 7404b2b5b4
commit 9f522ee7df
2 changed files with 30 additions and 1 deletions

View File

@@ -56,6 +56,10 @@ function isCopilotGeminiModelId(modelId: string): boolean {
return /(?:^|[-_.])gemini(?:$|[-_.])/.test(modelId);
}
function isCopilotClaude45ModelId(modelId: string): boolean {
return /^claude-(?:haiku|opus|sonnet)-4[.-]5(?:$|[-.])/.test(modelId);
}
export function resolveCopilotTransportApi(modelId: string): CopilotRuntimeApi {
const normalized = normalizeOptionalLowercaseString(modelId) ?? "";
if (normalized.includes("claude")) {
@@ -71,7 +75,15 @@ export function resolveCopilotModelCompat(
modelId: string,
): ModelDefinitionConfig["compat"] | undefined {
const normalized = normalizeOptionalLowercaseString(modelId) ?? "";
return isCopilotGeminiModelId(normalized) ? { ...COPILOT_CHAT_COMPLETIONS_COMPAT } : undefined;
if (isCopilotGeminiModelId(normalized)) {
return { ...COPILOT_CHAT_COMPLETIONS_COMPAT };
}
// Copilot's Claude 4.5 endpoints reject Anthropic's eager tool extension,
// while current Claude 4.6+ endpoints accept it.
if (isCopilotClaude45ModelId(normalized)) {
return { supportsEagerToolInputStreaming: false };
}
return undefined;
}
function compatSupportsEffort(

View File

@@ -90,8 +90,18 @@ describe("github-copilot model defaults", () => {
const def = buildCopilotModelDefinition("claude-sonnet-4.6");
expect(def.id).toBe("claude-sonnet-4.6");
expect(def.api).toBe("anthropic-messages");
expect(def.compat).toBeUndefined();
});
it.each(["claude-haiku-4.5", "claude-sonnet-4-5"])(
"disables eager tool streaming for Copilot Claude 4.5 model %s",
(modelId) => {
expect(buildCopilotModelDefinition(modelId).compat).toEqual({
supportsEagerToolInputStreaming: false,
});
},
);
it("uses static metadata overrides for gpt-5.5 fallback rows", () => {
const def = buildCopilotModelDefinition("gpt-5.5");
expect(def).toEqual({
@@ -243,6 +253,12 @@ describe("resolveCopilotForwardCompatModel", () => {
expect((result as unknown as Record<string, unknown>).input).toEqual(["text", "image"]);
});
it("disables eager tool streaming for synthetic Copilot Claude 4.5 models", () => {
const result = requireResolvedModel(createMockCtx("claude-haiku-4.5"));
expect(result.api).toBe("anthropic-messages");
expect(result.compat).toEqual({ supportsEagerToolInputStreaming: false });
});
it("creates synthetic Gemini models with Chat Completions compatibility", () => {
const result = requireResolvedModel(createMockCtx("gemini-3.1-pro-preview"));
expect((result as unknown as Record<string, unknown>).api).toBe("openai-completions");
@@ -620,6 +636,7 @@ describe("fetchCopilotModelCatalog", () => {
const opus45 = out.find((m) => m.id === "claude-opus-4-5");
expect(opus45?.thinkingLevelMap).toEqual({ xhigh: null, max: null });
expect(opus45?.compat).toEqual({
supportsEagerToolInputStreaming: false,
supportedReasoningEfforts: ["low", "medium", "high", "max"],
});
});