port(pi-mono): normalize Bedrock model names for inference profiles (refs ed4bc7308)
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 <noreply@anthropic.com>
This commit is contained in:
parent
a3c487c918
commit
7c487bb60e
1 changed files with 38 additions and 22 deletions
|
|
@ -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<string, any> = supportsAdaptiveThinking(model.id)
|
||||
const result: Record<string, any> = 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<ThinkingLevel, number> = {
|
||||
|
|
@ -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"];
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue