From 7c487bb60e89ac93e2f4d2404e5ceba08ff4d421 Mon Sep 17 00:00:00 2001 From: Mikael Hugo Date: Wed, 29 Apr 2026 14:14:17 +0200 Subject: [PATCH] port(pi-mono): normalize Bedrock model names for inference profiles (refs ed4bc7308) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Pi-mono Tier 0 #5 — first sf-driven port. sf-from-source dispatched the task in print mode and produced this fix autonomously. Adds getModelMatchCandidates(modelId, modelName?) helper that normalizes both inputs to lowercase and dash-separated form (s.replace(/[\s_.:]+/g, "-")). Inference profile ARNs don't embed the model name; the helper lets capability checks match against either the inference profile ARN or the underlying model name. Updated: - supportsAdaptiveThinking — uses the helper; consolidates the opus-4.6/opus-4-6 dot-vs-dash variants. - mapThinkingLevelToEffort — same pattern. - supportsPromptCaching — same pattern (also from pi-mono PR #3527). - streamSimpleBedrock and buildAdditionalModelRequestFields — pass model.name through to capability checks. Type-check passes (cd packages/pi-ai && npx tsc --noEmit). Co-Authored-By: sf v2.75.1 (session 911dd2de) Co-Authored-By: Claude Sonnet 4.6 --- .../pi-ai/src/providers/amazon-bedrock.ts | 60 ++++++++++++------- 1 file changed, 38 insertions(+), 22 deletions(-) diff --git a/packages/pi-ai/src/providers/amazon-bedrock.ts b/packages/pi-ai/src/providers/amazon-bedrock.ts index ba780ff44..43219fc7c 100644 --- a/packages/pi-ai/src/providers/amazon-bedrock.ts +++ b/packages/pi-ai/src/providers/amazon-bedrock.ts @@ -230,7 +230,7 @@ export const streamSimpleBedrock: StreamFunction<"bedrock-converse-stream", Simp const effectiveReasoning = resolveReasoningLevel(model, options.reasoning); if (model.id.includes("anthropic.claude") || model.id.includes("anthropic/claude")) { - if (supportsAdaptiveThinking(model.id) && isAutoReasoning(options.reasoning)) { + if (supportsAdaptiveThinking(model.id, model.name) && isAutoReasoning(options.reasoning)) { return streamBedrock(model, context, { ...base, reasoning: options.reasoning, @@ -238,7 +238,7 @@ export const streamSimpleBedrock: StreamFunction<"bedrock-converse-stream", Simp } satisfies BedrockOptions); } - if (supportsAdaptiveThinking(model.id)) { + if (supportsAdaptiveThinking(model.id, model.name)) { return streamBedrock(model, context, { ...base, reasoning: effectiveReasoning, @@ -393,22 +393,32 @@ function handleContentBlockStop( } } +/** + * Checks both model ID and model name to support application inference profiles + * whose ARNs don't contain the model name. + */ +function getModelMatchCandidates(modelId: string, modelName?: string): string[] { + const values = modelName ? [modelId, modelName] : [modelId]; + return values.flatMap((value) => { + const lower = value.toLowerCase(); + return [lower, lower.replace(/[\s_.:]+/g, "-")]; + }); +} + /** * Check if the model supports adaptive thinking (Opus 4.6/4.7, Sonnet 4.6/4.7, Haiku 4.5). * @internal exported for testing only */ -export function supportsAdaptiveThinking(modelId: string): boolean { +export function supportsAdaptiveThinking(modelId: string, modelName?: string): boolean { + const candidates = getModelMatchCandidates(modelId, modelName); return ( - modelId.includes("opus-4-6") || - modelId.includes("opus-4.6") || - modelId.includes("opus-4-7") || - modelId.includes("opus-4.7") || - modelId.includes("sonnet-4-6") || - modelId.includes("sonnet-4.6") || - modelId.includes("sonnet-4-7") || - modelId.includes("sonnet-4.7") || - modelId.includes("haiku-4-5") || - modelId.includes("haiku-4.5") + candidates.some((s) => + s.includes("opus-4-6") || + s.includes("opus-4-7") || + s.includes("sonnet-4-6") || + s.includes("sonnet-4-7") || + s.includes("haiku-4-5"), + ) ); } @@ -416,7 +426,9 @@ export function supportsAdaptiveThinking(modelId: string): boolean { export function mapThinkingLevelToEffort( level: SimpleStreamOptions["reasoning"], modelId: string, + modelName?: string, ): "low" | "medium" | "high" | "xhigh" | "max" { + const candidates = getModelMatchCandidates(modelId, modelName); switch (level) { case "auto": return "medium"; @@ -428,8 +440,8 @@ export function mapThinkingLevelToEffort( case "high": return "high"; case "xhigh": - if (modelId.includes("opus-4-7") || modelId.includes("opus-4.7")) return "xhigh"; - if (modelId.includes("opus-4-6") || modelId.includes("opus-4.6")) return "max"; + if (candidates.some((s) => s.includes("opus-4-7"))) return "xhigh"; + if (candidates.some((s) => s.includes("opus-4-6"))) return "max"; return "high"; default: return "high"; @@ -459,13 +471,17 @@ function supportsPromptCaching(model: Model<"bedrock-converse-stream">): boolean return true; } - const id = model.id.toLowerCase(); + const candidates = getModelMatchCandidates(model.id, model.name); + const hasClaudeRef = candidates.some((s) => s.includes("claude")); + if (!hasClaudeRef) { + return false; + } // Claude 4.x models (opus-4, sonnet-4, haiku-4) - if (id.includes("claude") && (id.includes("-4-") || id.includes("-4."))) return true; + if (candidates.some((s) => s.includes("-4-"))) return true; // Claude 3.7 Sonnet - if (id.includes("claude-3-7-sonnet")) return true; + if (candidates.some((s) => s.includes("claude-3-7-sonnet"))) return true; // Claude 3.5 Haiku - if (id.includes("claude-3-5-haiku")) return true; + if (candidates.some((s) => s.includes("claude-3-5-haiku"))) return true; return false; } @@ -721,14 +737,14 @@ export function buildAdditionalModelRequestFields( } if (model.id.includes("anthropic.claude") || model.id.includes("anthropic/claude")) { - const result: Record = supportsAdaptiveThinking(model.id) + const result: Record = supportsAdaptiveThinking(model.id, model.name) ? options.reasoning === "auto" ? { thinking: { type: "adaptive" }, } : { thinking: { type: "adaptive" }, - output_config: { effort: mapThinkingLevelToEffort(options.reasoning, model.id) }, + output_config: { effort: mapThinkingLevelToEffort(options.reasoning, model.id, model.name) }, } : (() => { const defaultBudgets: Record = { @@ -752,7 +768,7 @@ export function buildAdditionalModelRequestFields( }; })(); - if (!supportsAdaptiveThinking(model.id) && (options.interleavedThinking ?? true)) { + if (!supportsAdaptiveThinking(model.id, model.name) && (options.interleavedThinking ?? true)) { result.anthropic_beta = ["interleaved-thinking-2025-05-14"]; }