@@ -25,6 +25,7 @@ import {
2525 BEDROCK_DEFAULT_TEMPERATURE ,
2626 AWS_INFERENCE_PROFILE_MAPPING ,
2727 BEDROCK_1M_CONTEXT_MODEL_IDS ,
28+ BEDROCK_ADAPTIVE_THINKING_MODEL_IDS ,
2829 BEDROCK_GLOBAL_INFERENCE_MODEL_IDS ,
2930 BEDROCK_NATIVE_1M_CONTEXT_MODEL_IDS ,
3031 BEDROCK_SERVICE_TIER_MODEL_IDS ,
@@ -62,11 +63,13 @@ interface BedrockInferenceConfig {
6263
6364// Define interface for Bedrock additional model request fields
6465// This includes thinking configuration, 1M context beta, and other model-specific parameters
66+ //
67+ // Two shapes are supported for the `thinking` field:
68+ // - Legacy (Claude Sonnet/Opus 4.x, Claude 3.7): { type: "enabled", budget_tokens: N }
69+ // - Adaptive (Claude Opus 4.7+): { type: "adaptive" } paired with a
70+ // top-level `output_config.effort` string on the payload itself.
6571interface BedrockAdditionalModelFields {
66- thinking ?: {
67- type : "enabled"
68- budget_tokens : number
69- }
72+ thinking ?: { type : "enabled" ; budget_tokens : number } | { type : "adaptive" }
7073 anthropic_beta ?: string [ ]
7174 [ key : string ] : any // Add index signature to be compatible with DocumentType
7275}
@@ -80,6 +83,39 @@ interface BedrockPayload {
8083 anthropic_version ?: string
8184 additionalModelRequestFields ?: BedrockAdditionalModelFields
8285 toolConfig ?: ToolConfiguration
86+ // Adaptive-thinking models (e.g. Claude Opus 4.7 on Bedrock) use this top-level
87+ // `output_config.effort` knob instead of the legacy `thinking.budget_tokens` number.
88+ output_config ?: { effort : "low" | "medium" | "high" }
89+ }
90+
91+ /**
92+ * Map a reasoning budget (in tokens) to a coarse effort bucket for the adaptive
93+ * thinking payload. Used only when invoking Bedrock models that require the newer
94+ * `thinking: { type: "adaptive" }` + `output_config.effort` shape.
95+ *
96+ * The thresholds mirror the historical budget ranges exposed by the reasoning UI:
97+ * <= 4096 tokens → "low"
98+ * <= 16384 tokens → "medium"
99+ * > 16384 tokens → "high"
100+ */
101+ function mapReasoningBudgetToBedrockEffort ( budget : number | undefined ) : "low" | "medium" | "high" {
102+ const b = typeof budget === "number" && Number . isFinite ( budget ) && budget > 0 ? budget : 0
103+ if ( b <= 4096 ) return "low"
104+ if ( b <= 16384 ) return "medium"
105+ return "high"
106+ }
107+
108+ /**
109+ * Normalize a freeform reasoning-effort setting string to the three buckets Bedrock
110+ * accepts on `output_config.effort`. Unknown or disabled values return undefined so
111+ * the caller can fall back to the budget-derived mapping.
112+ */
113+ function normalizeReasoningEffortForBedrock ( value : unknown ) : "low" | "medium" | "high" | undefined {
114+ if ( typeof value !== "string" ) return undefined
115+ const v = value . toLowerCase ( )
116+ if ( v === "low" || v === "medium" || v === "high" ) return v
117+ if ( v === "minimal" ) return "low"
118+ return undefined
83119}
84120
85121// Extended payload type that includes service_tier as a top-level parameter
@@ -322,6 +358,14 @@ export class AwsBedrockHandler extends BaseProvider implements SingleCompletionH
322358
323359 let additionalModelRequestFields : BedrockAdditionalModelFields | undefined
324360 let thinkingEnabled = false
361+ let adaptiveThinkingEffort : "low" | "medium" | "high" | undefined
362+
363+ // Resolve the base model id first so the thinking branch can decide between the
364+ // legacy budget_tokens payload and the newer adaptive + output_config.effort payload.
365+ // parseBaseModelId strips cross-region inference prefixes (e.g. `us.`, `eu.`) and the
366+ // synthetic `:1m` dropdown suffix.
367+ const baseModelId = this . parseBaseModelId ( modelConfig . id )
368+ const requiresAdaptiveThinking = BEDROCK_ADAPTIVE_THINKING_MODEL_IDS . includes ( baseModelId as any )
325369
326370 // Determine if thinking should be enabled
327371 // metadata?.thinking?.enabled: Explicitly enabled through API metadata (direct request)
@@ -334,17 +378,42 @@ export class AwsBedrockHandler extends BaseProvider implements SingleCompletionH
334378
335379 if ( ( isThinkingExplicitlyEnabled || isThinkingEnabledBySettings ) && modelConfig . info . supportsReasoningBudget ) {
336380 thinkingEnabled = true
337- additionalModelRequestFields = {
338- thinking : {
339- type : "enabled" ,
340- budget_tokens : metadata ?. thinking ?. maxThinkingTokens || modelConfig . reasoningBudget || 4096 ,
341- } ,
381+ const effectiveBudget = metadata ?. thinking ?. maxThinkingTokens || modelConfig . reasoningBudget || 4096
382+
383+ if ( requiresAdaptiveThinking ) {
384+ // Newer Claude models on Bedrock (e.g. Opus 4.7) reject the legacy
385+ // `thinking: { type: "enabled", budget_tokens: N }` shape with:
386+ // invalid_request_error: "thinking.type.enabled" is not supported for this model.
387+ // Use "thinking.type.adaptive" and "output_config.effort" to control thinking behavior.
388+ // Honor that by emitting `thinking: { type: "adaptive" }` plus a top-level
389+ // `output_config.effort`. Effort comes from the user's reasoningEffort setting
390+ // when present, otherwise we derive it from the token budget.
391+ adaptiveThinkingEffort =
392+ normalizeReasoningEffortForBedrock (
393+ ( this . options as ProviderSettings & { reasoningEffort ?: unknown } ) . reasoningEffort ,
394+ ) ?? mapReasoningBudgetToBedrockEffort ( effectiveBudget )
395+ additionalModelRequestFields = {
396+ thinking : { type : "adaptive" } ,
397+ }
398+ logger . info ( "Adaptive thinking enabled for Bedrock request" , {
399+ ctx : "bedrock" ,
400+ modelId : modelConfig . id ,
401+ thinking : additionalModelRequestFields . thinking ,
402+ effort : adaptiveThinkingEffort ,
403+ } )
404+ } else {
405+ additionalModelRequestFields = {
406+ thinking : {
407+ type : "enabled" ,
408+ budget_tokens : effectiveBudget ,
409+ } ,
410+ }
411+ logger . info ( "Extended thinking enabled for Bedrock request" , {
412+ ctx : "bedrock" ,
413+ modelId : modelConfig . id ,
414+ thinking : additionalModelRequestFields . thinking ,
415+ } )
342416 }
343- logger . info ( "Extended thinking enabled for Bedrock request" , {
344- ctx : "bedrock" ,
345- modelId : modelConfig . id ,
346- thinking : additionalModelRequestFields . thinking ,
347- } )
348417 }
349418
350419 const inferenceConfig : BedrockInferenceConfig = {
@@ -357,8 +426,6 @@ export class AwsBedrockHandler extends BaseProvider implements SingleCompletionH
357426 // - the configured target id contains the `:1m` / `[1m]` indicator (user picked the
358427 // 1M variant from the dropdown), OR
359428 // - the awsBedrock1MContext opt-in toggle is set by the user.
360- // Use parseBaseModelId to handle cross-region inference prefixes.
361- const baseModelId = this . parseBaseModelId ( modelConfig . id )
362429 const configuredTargetForIndicator =
363430 this . options . awsBedrockInvokeTarget || this . options . awsCustomArn || modelConfig . id
364431 const is1MContextEnabled =
@@ -429,6 +496,9 @@ export class AwsBedrockHandler extends BaseProvider implements SingleCompletionH
429496 ...( additionalModelRequestFields && { additionalModelRequestFields } ) ,
430497 // Add anthropic_version at top level when using thinking features
431498 ...( thinkingEnabled && { anthropic_version : "bedrock-2023-05-31" } ) ,
499+ // Adaptive-thinking models require the effort knob at the top level alongside
500+ // `thinking: { type: "adaptive" }` inside additionalModelRequestFields.
501+ ...( adaptiveThinkingEffort && { output_config : { effort : adaptiveThinkingEffort } } ) ,
432502 toolConfig,
433503 // Add service_tier as a top-level parameter (not inside additionalModelRequestFields)
434504 ...( useServiceTier && { service_tier : this . options . awsBedrockServiceTier } ) ,
0 commit comments