From 956571cabecf541699fb6f628d6339fa6fbbda21 Mon Sep 17 00:00:00 2001 From: Matt Rubens Date: Wed, 26 Nov 2025 17:54:10 -0500 Subject: [PATCH] Add fine grained tool streaming for OpenRouter Anthropic --- .../providers/__tests__/openrouter.spec.ts | 24 ++++++++++++------- src/api/providers/openrouter.ts | 14 +++++++++-- 2 files changed, 27 insertions(+), 11 deletions(-) diff --git a/src/api/providers/__tests__/openrouter.spec.ts b/src/api/providers/__tests__/openrouter.spec.ts index 118be755d70..37b839f3030 100644 --- a/src/api/providers/__tests__/openrouter.spec.ts +++ b/src/api/providers/__tests__/openrouter.spec.ts @@ -194,6 +194,7 @@ describe("OpenRouterHandler", () => { top_p: undefined, transforms: ["middle-out"], }), + { headers: { "x-anthropic-beta": "fine-grained-tool-streaming-2025-05-14" } }, ) }) @@ -218,7 +219,9 @@ describe("OpenRouterHandler", () => { await handler.createMessage("test", []).next() - expect(mockCreate).toHaveBeenCalledWith(expect.objectContaining({ transforms: ["middle-out"] })) + expect(mockCreate).toHaveBeenCalledWith(expect.objectContaining({ transforms: ["middle-out"] }), { + headers: { "x-anthropic-beta": "fine-grained-tool-streaming-2025-05-14" }, + }) }) it("adds cache control for supported models", async () => { @@ -260,6 +263,7 @@ describe("OpenRouterHandler", () => { }), ]), }), + { headers: { "x-anthropic-beta": "fine-grained-tool-streaming-2025-05-14" } }, ) }) @@ -295,14 +299,16 @@ describe("OpenRouterHandler", () => { expect(result).toBe("test completion") - expect(mockCreate).toHaveBeenCalledWith({ - model: mockOptions.openRouterModelId, - max_tokens: 8192, - thinking: undefined, - temperature: 0, - messages: [{ role: "user", content: "test prompt" }], - stream: false, - }) + expect(mockCreate).toHaveBeenCalledWith( + { + model: mockOptions.openRouterModelId, + max_tokens: 8192, + temperature: 0, + messages: [{ role: "user", content: "test prompt" }], + stream: false, + }, + { headers: { "x-anthropic-beta": "fine-grained-tool-streaming-2025-05-14" } }, + ) }) it("handles API errors", async () => { diff --git a/src/api/providers/openrouter.ts b/src/api/providers/openrouter.ts index 3db61087efd..1ffbe28f2d9 100644 --- a/src/api/providers/openrouter.ts +++ b/src/api/providers/openrouter.ts @@ -213,9 +213,14 @@ export class OpenRouterHandler extends BaseProvider implements SingleCompletionH ...(metadata?.tool_choice && { tool_choice: metadata.tool_choice }), } + // Add Anthropic beta header for fine-grained tool streaming when using Anthropic models + const requestOptions = modelId.startsWith("anthropic/") + ? { headers: { "x-anthropic-beta": "fine-grained-tool-streaming-2025-05-14" } } + : undefined + let stream try { - stream = await this.client.chat.completions.create(completionParams) + stream = await this.client.chat.completions.create(completionParams, requestOptions) } catch (error) { throw handleOpenAIError(error, this.providerName) } @@ -417,9 +422,14 @@ export class OpenRouterHandler extends BaseProvider implements SingleCompletionH ...(reasoning && { reasoning }), } + // Add Anthropic beta header for fine-grained tool streaming when using Anthropic models + const requestOptions = modelId.startsWith("anthropic/") + ? { headers: { "x-anthropic-beta": "fine-grained-tool-streaming-2025-05-14" } } + : undefined + let response try { - response = await this.client.chat.completions.create(completionParams) + response = await this.client.chat.completions.create(completionParams, requestOptions) } catch (error) { throw handleOpenAIError(error, this.providerName) }