From b4543caf55d87b479be9fe326eb4262db802c483 Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Sat, 18 Apr 2026 22:08:16 +0100 Subject: [PATCH] test: dedupe anthropic transport fixtures --- src/agents/anthropic-transport-stream.test.ts | 294 +++++++----------- 1 file changed, 109 insertions(+), 185 deletions(-) diff --git a/src/agents/anthropic-transport-stream.test.ts b/src/agents/anthropic-transport-stream.test.ts index 99fdf28497e..e0d1fc9ab90 100644 --- a/src/agents/anthropic-transport-stream.test.ts +++ b/src/agents/anthropic-transport-stream.test.ts @@ -13,6 +13,12 @@ vi.mock("./provider-transport-fetch.js", () => ({ let createAnthropicMessagesTransportStreamFn: typeof import("./anthropic-transport-stream.js").createAnthropicMessagesTransportStreamFn; +type AnthropicMessagesModel = Model<"anthropic-messages">; +type AnthropicStreamFn = ReturnType; +type AnthropicStreamContext = Parameters[1]; +type AnthropicStreamOptions = Parameters[2]; +type RequestTransportConfig = Parameters[1]; + function createSseResponse(events: Record[] = []): Response { const body = events.map((event) => `data: ${JSON.stringify(event)}\n\n`).join(""); return new Response(body, { @@ -30,6 +36,48 @@ function latestAnthropicRequest() { }; } +function makeAnthropicTransportModel( + params: { + id?: string; + name?: string; + reasoning?: boolean; + maxTokens?: number; + headers?: Record; + requestTransport?: RequestTransportConfig; + } = {}, +): AnthropicMessagesModel { + return attachModelProviderRequestTransport( + { + id: params.id ?? "claude-sonnet-4-6", + name: params.name ?? "Claude Sonnet 4.6", + api: "anthropic-messages", + provider: "anthropic", + baseUrl: "https://api.anthropic.com", + reasoning: params.reasoning ?? true, + input: ["text"], + cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 }, + contextWindow: 200000, + maxTokens: params.maxTokens ?? 8192, + ...(params.headers ? { headers: params.headers } : {}), + } satisfies AnthropicMessagesModel, + params.requestTransport ?? { + proxy: { + mode: "env-proxy", + }, + }, + ); +} + +async function runTransportStream( + model: AnthropicMessagesModel, + context: AnthropicStreamContext, + options: AnthropicStreamOptions, +) { + const streamFn = createAnthropicMessagesTransportStreamFn(); + const stream = await Promise.resolve(streamFn(model, context, options)); + return stream.result(); +} + describe("anthropic transport stream", () => { beforeAll(async () => { ({ createAnthropicMessagesTransportStreamFn } = @@ -44,42 +92,26 @@ describe("anthropic transport stream", () => { }); it("uses the guarded fetch transport for api-key Anthropic requests", async () => { - const model = attachModelProviderRequestTransport( - { - id: "claude-sonnet-4-6", - name: "Claude Sonnet 4.6", - api: "anthropic-messages", - provider: "anthropic", - baseUrl: "https://api.anthropic.com", - reasoning: true, - input: ["text"], - cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 }, - contextWindow: 200000, - maxTokens: 8192, - headers: { "X-Provider": "anthropic" }, - } satisfies Model<"anthropic-messages">, - { + const model = makeAnthropicTransportModel({ + headers: { "X-Provider": "anthropic" }, + requestTransport: { proxy: { mode: "explicit-proxy", url: "http://proxy.internal:8443", }, }, - ); - const streamFn = createAnthropicMessagesTransportStreamFn(); + }); - const stream = await Promise.resolve( - streamFn( - model, - { - messages: [{ role: "user", content: "hello" }], - } as Parameters[1], - { - apiKey: "sk-ant-api", - headers: { "X-Call": "1" }, - } as Parameters[2], - ), + await runTransportStream( + model, + { + messages: [{ role: "user", content: "hello" }], + } as AnthropicStreamContext, + { + apiKey: "sk-ant-api", + headers: { "X-Call": "1" }, + } as AnthropicStreamOptions, ); - await stream.result(); expect(buildGuardedModelFetchMock).toHaveBeenCalledWith(model); expect(guardedFetchMock).toHaveBeenCalledWith( @@ -104,40 +136,16 @@ describe("anthropic transport stream", () => { }); it("ignores non-positive runtime maxTokens overrides and falls back to the model limit", async () => { - const model = attachModelProviderRequestTransport( + await runTransportStream( + makeAnthropicTransportModel(), { - id: "claude-sonnet-4-6", - name: "Claude Sonnet 4.6", - api: "anthropic-messages", - provider: "anthropic", - baseUrl: "https://api.anthropic.com", - reasoning: true, - input: ["text"], - cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 }, - contextWindow: 200000, - maxTokens: 8192, - } satisfies Model<"anthropic-messages">, + messages: [{ role: "user", content: "hello" }], + } as AnthropicStreamContext, { - proxy: { - mode: "env-proxy", - }, - }, + apiKey: "sk-ant-api", + maxTokens: 0, + } as AnthropicStreamOptions, ); - const streamFn = createAnthropicMessagesTransportStreamFn(); - - const stream = await Promise.resolve( - streamFn( - model, - { - messages: [{ role: "user", content: "hello" }], - } as Parameters[1], - { - apiKey: "sk-ant-api", - maxTokens: 0, - } as Parameters[2], - ), - ); - await stream.result(); expect(latestAnthropicRequest().payload).toMatchObject({ model: "claude-sonnet-4-6", @@ -147,40 +155,16 @@ describe("anthropic transport stream", () => { }); it("ignores fractional runtime maxTokens overrides that floor to zero", async () => { - const model = attachModelProviderRequestTransport( + await runTransportStream( + makeAnthropicTransportModel(), { - id: "claude-sonnet-4-6", - name: "Claude Sonnet 4.6", - api: "anthropic-messages", - provider: "anthropic", - baseUrl: "https://api.anthropic.com", - reasoning: true, - input: ["text"], - cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 }, - contextWindow: 200000, - maxTokens: 8192, - } satisfies Model<"anthropic-messages">, + messages: [{ role: "user", content: "hello" }], + } as AnthropicStreamContext, { - proxy: { - mode: "env-proxy", - }, - }, + apiKey: "sk-ant-api", + maxTokens: 0.5, + } as AnthropicStreamOptions, ); - const streamFn = createAnthropicMessagesTransportStreamFn(); - - const stream = await Promise.resolve( - streamFn( - model, - { - messages: [{ role: "user", content: "hello" }], - } as Parameters[1], - { - apiKey: "sk-ant-api", - maxTokens: 0.5, - } as Parameters[2], - ), - ); - await stream.result(); expect(latestAnthropicRequest().payload).toMatchObject({ model: "claude-sonnet-4-6", @@ -260,25 +244,13 @@ describe("anthropic transport stream", () => { }, ]), ); - const model = attachModelProviderRequestTransport( - { - id: "claude-sonnet-4-6", - name: "Claude Sonnet 4.6", - api: "anthropic-messages", - provider: "anthropic", - baseUrl: "https://api.anthropic.com", - reasoning: true, - input: ["text"], - cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 }, - contextWindow: 200000, - maxTokens: 8192, - } satisfies Model<"anthropic-messages">, - { + const model = makeAnthropicTransportModel({ + requestTransport: { tls: { ca: "ca-pem", }, }, - ); + }); const streamFn = createAnthropicMessagesTransportStreamFn(); const stream = await Promise.resolve( streamFn( @@ -338,25 +310,13 @@ describe("anthropic transport stream", () => { }); it("coerces replayed malformed tool-call args to an object for Anthropic payloads", async () => { - const model = attachModelProviderRequestTransport( - { - id: "claude-sonnet-4-6", - name: "Claude Sonnet 4.6", - api: "anthropic-messages", - provider: "anthropic", - baseUrl: "https://api.anthropic.com", - reasoning: true, - input: ["text"], - cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 }, - contextWindow: 200000, - maxTokens: 8192, - } satisfies Model<"anthropic-messages">, - { + const model = makeAnthropicTransportModel({ + requestTransport: { tls: { ca: "ca-pem", }, }, - ); + }); const streamFn = createAnthropicMessagesTransportStreamFn(); const stream = await Promise.resolve( @@ -407,40 +367,22 @@ describe("anthropic transport stream", () => { }); it("maps adaptive thinking effort for Claude 4.6 transport runs", async () => { - const model = attachModelProviderRequestTransport( - { - id: "claude-opus-4-6", - name: "Claude Opus 4.6", - api: "anthropic-messages", - provider: "anthropic", - baseUrl: "https://api.anthropic.com", - reasoning: true, - input: ["text"], - cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 }, - contextWindow: 200000, - maxTokens: 8192, - } satisfies Model<"anthropic-messages">, - { - proxy: { - mode: "env-proxy", - }, - }, - ); - const streamFn = createAnthropicMessagesTransportStreamFn(); + const model = makeAnthropicTransportModel({ + id: "claude-opus-4-6", + name: "Claude Opus 4.6", + maxTokens: 8192, + }); - const stream = await Promise.resolve( - streamFn( - model, - { - messages: [{ role: "user", content: "Think deeply." }], - } as Parameters[1], - { - apiKey: "sk-ant-api", - reasoning: "xhigh", - } as Parameters[2], - ), + await runTransportStream( + model, + { + messages: [{ role: "user", content: "Think deeply." }], + } as AnthropicStreamContext, + { + apiKey: "sk-ant-api", + reasoning: "xhigh", + } as AnthropicStreamOptions, ); - await stream.result(); expect(latestAnthropicRequest().payload).toMatchObject({ thinking: { type: "adaptive" }, @@ -449,40 +391,22 @@ describe("anthropic transport stream", () => { }); it("maps xhigh thinking effort for Claude Opus 4.7 transport runs", async () => { - const model = attachModelProviderRequestTransport( - { - id: "claude-opus-4-7", - name: "Claude Opus 4.7", - api: "anthropic-messages", - provider: "anthropic", - baseUrl: "https://api.anthropic.com", - reasoning: true, - input: ["text"], - cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 }, - contextWindow: 200000, - maxTokens: 8192, - } satisfies Model<"anthropic-messages">, - { - proxy: { - mode: "env-proxy", - }, - }, - ); - const streamFn = createAnthropicMessagesTransportStreamFn(); + const model = makeAnthropicTransportModel({ + id: "claude-opus-4-7", + name: "Claude Opus 4.7", + maxTokens: 8192, + }); - const stream = await Promise.resolve( - streamFn( - model, - { - messages: [{ role: "user", content: "Think extra hard." }], - } as Parameters[1], - { - apiKey: "sk-ant-api", - reasoning: "xhigh", - } as Parameters[2], - ), + await runTransportStream( + model, + { + messages: [{ role: "user", content: "Think extra hard." }], + } as AnthropicStreamContext, + { + apiKey: "sk-ant-api", + reasoning: "xhigh", + } as AnthropicStreamOptions, ); - await stream.result(); expect(latestAnthropicRequest().payload).toMatchObject({ thinking: { type: "adaptive" },