diff --git a/extensions/amazon-bedrock/index.test.ts b/extensions/amazon-bedrock/index.test.ts index a4b541e17ce28..4653abf42e587 100644 --- a/extensions/amazon-bedrock/index.test.ts +++ b/extensions/amazon-bedrock/index.test.ts @@ -264,7 +264,7 @@ describe("amazon-bedrock provider plugin", () => { }); const result = callWrappedStream(provider, ANTHROPIC_MODEL, ANTHROPIC_MODEL_DESCRIPTOR); - // Anthropic models should get guardrailConfig + // Anthropic models get guardrailConfig + default anthropic_beta flags. expect(result._capturedPayload).toEqual({ guardrailConfig: { guardrailIdentifier: "guardrail-anthropic", @@ -272,11 +272,31 @@ describe("amazon-bedrock provider plugin", () => { streamProcessingMode: "async", trace: "disabled", }, + additionalModelRequestFields: { + anthropic_beta: [ + "fine-grained-tool-streaming-2025-05-14", + "interleaved-thinking-2025-05-14", + ], + }, }); // Anthropic models should NOT get cacheRetention: "none" expect(result).not.toHaveProperty("cacheRetention", "none"); }); + it("injects default anthropic_beta flags for Anthropic Bedrock models without guardrail", async () => { + const provider = await registerWithConfig(undefined); + const result = callWrappedStream(provider, ANTHROPIC_MODEL, ANTHROPIC_MODEL_DESCRIPTOR); + + expect(result._capturedPayload).toEqual({ + additionalModelRequestFields: { + anthropic_beta: [ + "fine-grained-tool-streaming-2025-05-14", + "interleaved-thinking-2025-05-14", + ], + }, + }); + }); + it("injects guardrailConfig for non-Anthropic models with cacheRetention: none", async () => { const provider = await registerWithConfig({ guardrail: { diff --git a/extensions/amazon-bedrock/register.sync.runtime.ts b/extensions/amazon-bedrock/register.sync.runtime.ts index a141ffd344478..70035cec80c88 100644 --- a/extensions/amazon-bedrock/register.sync.runtime.ts +++ b/extensions/amazon-bedrock/register.sync.runtime.ts @@ -16,6 +16,22 @@ import { } from "./api.js"; import { bedrockMemoryEmbeddingProviderAdapter } from "./memory-embedding-adapter.js"; +const PI_AI_DEFAULT_ANTHROPIC_BETAS = [ + "fine-grained-tool-streaming-2025-05-14", + "interleaved-thinking-2025-05-14", +] as const; + +function createBedrockAnthropicBetasWrapper(baseStreamFn: StreamFn | undefined): StreamFn | undefined { + if (!baseStreamFn) return baseStreamFn; + return (model, context, options) => + streamWithPayloadPatch(baseStreamFn, model, context, options, (payload) => { + payload.additionalModelRequestFields = { + ...(payload.additionalModelRequestFields as Record | undefined), + anthropic_beta: [...PI_AI_DEFAULT_ANTHROPIC_BETAS], + }; + }); +} + type GuardrailConfig = { guardrailIdentifier: string; guardrailVersion: string; @@ -82,7 +98,9 @@ export function registerAmazonBedrockPlugin(api: OpenClawPluginApi): void { api.registerMemoryEmbeddingProvider(bedrockMemoryEmbeddingProviderAdapter); const baseWrapStreamFn = ({ modelId, streamFn }: { modelId: string; streamFn?: StreamFn }) => - isAnthropicBedrockModel(modelId) ? streamFn : createBedrockNoCacheWrapper(streamFn); + isAnthropicBedrockModel(modelId) + ? createBedrockAnthropicBetasWrapper(streamFn) + : createBedrockNoCacheWrapper(streamFn); const cacheWrapStreamFn = guardrail?.guardrailIdentifier && guardrail?.guardrailVersion