From b7321f7fcc27f0dd2079059ea157baffa8e068aa Mon Sep 17 00:00:00 2001 From: Aiden Cline <63023139+rekram1-node@users.noreply.github.com> Date: Sat, 24 Oct 2026 23:23:10 -0500 Subject: [PATCH] feat(ai): add Anthropic fast mode speed option (#58870) --- packages/ai/src/protocols/anthropic-messages.ts | 13 +++++++++++-- .../ai/test/provider/anthropic-messages.test.ts | 11 +++++++++++ 2 files changed, 22 insertions(+), 2 deletions(-) diff --git a/packages/ai/src/protocols/anthropic-messages.ts b/packages/ai/src/protocols/anthropic-messages.ts index 7ee20881571..3a461cd94b5 100644 --- a/packages/ai/src/protocols/anthropic-messages.ts +++ b/packages/ai/src/protocols/anthropic-messages.ts @@ -83,6 +83,7 @@ const AnthropicCacheControl = Schema.Struct({ ttl: Schema.optional(Schema.Literals(["5m", "1h"])), }) const AnthropicServiceTier = knownString<"auto" | "standard_only">() +const AnthropicSpeed = knownString<"fast" | "standard">() const AnthropicTextBlock = Schema.Struct({ type: Schema.tag("text"), @@ -359,6 +360,7 @@ const Options = Schema.Struct({ effort: Schema.optional(Schema.String), service_tier: Schema.optional(AnthropicServiceTier), serviceTier: Schema.optional(AnthropicServiceTier), + speed: Schema.optional(AnthropicSpeed), metadata: Schema.optional(AnthropicMetadata), container: Schema.optional(AnthropicContainer), inference_geo: optionalNull(Schema.String), @@ -402,6 +404,7 @@ const AnthropicBodyFields = { inference_geo: Schema.optional(Schema.NullOr(Schema.String)), metadata: Schema.optional(AnthropicMetadata), service_tier: Schema.optional(AnthropicServiceTier), + speed: Schema.optional(AnthropicSpeed), } export const AnthropicMessagesBody = Schema.Struct(AnthropicBodyFields) export type AnthropicMessagesBody = Schema.Schema.Type @@ -1118,6 +1121,7 @@ const fromRequest = Effect.fn("AnthropicMessages.fromRequest")(function* (reques inference_geo: options.inference_geo ?? options.inferenceGeo ?? undefined, metadata: options.metadata, service_tier: options.service_tier ?? options.serviceTier, + speed: options.speed, } if (!management) return body return { @@ -1644,7 +1648,7 @@ export const protocol = Protocol.make({ }) export const transport = < - Body extends Pick, + Body extends Pick, >() => { const http = HttpTransport.httpJson({ framing }) return { @@ -1671,7 +1675,9 @@ export const transport = < } } -function requiredBetaHeaders(body: Pick) { +function requiredBetaHeaders( + body: Pick, +) { // Always request interleaved thinking. The API accepts the header on any // model and ignores it where unsupported, while manual-thinking models need // it for thinking between tool calls. @@ -1685,6 +1691,9 @@ function requiredBetaHeaders(body: Pick message.role === "system" && message.output_config !== undefined)) betas.push("mid-conversation-output-config-2026-07-01") + // Opus 5.5 accepts `speed` without this header; earlier fast-mode models reject `speed` without it. + if (body.speed !== undefined) betas.push("fast-mode-2026-02-01") + const thinking = body.thinking if (thinking && !isThinkingOff(thinking) && thinking.block_binding) betas.push(THINKING_BINDING_BETA) return betas diff --git a/packages/ai/test/provider/anthropic-messages.test.ts b/packages/ai/test/provider/anthropic-messages.test.ts index 56ff73745d4..685c4fe1d84 100644 --- a/packages/ai/test/provider/anthropic-messages.test.ts +++ b/packages/ai/test/provider/anthropic-messages.test.ts @@ -243,6 +243,17 @@ describe("Anthropic Messages route", () => { }), ) + it.effect("sends fast mode speed with its beta header", () => + Effect.gen(function* () { + const fast = LLMRequest.update(request, { providerOptions: { speed: "fast" } }) + const compiled = yield* compileRequest(fast) + const prepared = yield* AnthropicMessages.route.prepareTransport(compiled.body, fast) + + expect(compiled.body.speed).toBe("fast") + expect(prepared.request.headers["anthropic-beta"]).toBe("interleaved-thinking-2025-05-14,fast-mode-2026-02-01") + }), + ) + it.effect("forwards unknown values for pass-through string enums", () => Effect.gen(function* () { const prepared = yield* compileRequest(