fix(providers): preserved bedrock profile thinking suffixes

Handled Bedrock inference profile ARNs through the normal thinking selector parser so suffixes such as :off do not become part of the ARN.

Fixes #3004
This commit is contained in:
roboomp
2026-06-18 21:26:35 +00:00
parent 3a736ff7c1
commit b67d07e829
2 changed files with 42 additions and 12 deletions
@@ -200,15 +200,19 @@ const AMAZON_BEDROCK_PROVIDER = "amazon-bedrock";
const BEDROCK_INFERENCE_PROFILE_ARN =
/^arn:aws(?:-[a-z]+)*:bedrock:[a-z0-9-]+:[0-9]*:(?:application-inference-profile|inference-profile)\/[a-z0-9][a-z0-9._:-]*$/i;
function resolveBedrockInferenceProfileReference(
provider: string,
function hasBedrockInferenceProfileThinkingSuffix(modelId: string): boolean {
const { base, level } = splitThinkingSuffix(modelId);
return level !== undefined && BEDROCK_INFERENCE_PROFILE_ARN.test(base.trim());
}
function resolveBedrockInferenceProfileModelId(
modelId: string,
availableModels: readonly Model<Api>[],
): Model<Api> | undefined {
if (provider.toLowerCase() !== AMAZON_BEDROCK_PROVIDER) return undefined;
const requestedId = modelId.trim();
if (!BEDROCK_INFERENCE_PROFILE_ARN.test(requestedId)) return undefined;
if (hasBedrockInferenceProfileThinkingSuffix(requestedId) || !BEDROCK_INFERENCE_PROFILE_ARN.test(requestedId)) {
return undefined;
}
const providerModels = availableModels.filter(model => model.provider.toLowerCase() === AMAZON_BEDROCK_PROVIDER);
const defaultModelId = DEFAULT_MODEL_PER_PROVIDER[AMAZON_BEDROCK_PROVIDER];
@@ -223,6 +227,15 @@ function resolveBedrockInferenceProfileReference(
};
}
function resolveBedrockInferenceProfileReference(
provider: string,
modelId: string,
availableModels: readonly Model<Api>[],
): Model<Api> | undefined {
if (provider.toLowerCase() !== AMAZON_BEDROCK_PROVIDER) return undefined;
return resolveBedrockInferenceProfileModelId(modelId, availableModels);
}
const UPSTREAM_ROUTING_SLUG = /^[a-z0-9](?:[a-z0-9-]*[a-z0-9])?$/i;
/**
@@ -502,6 +515,7 @@ function findExactCanonicalModelMatch(
* The single model-matching engine. Tries, in order:
* 1. exact `provider/id` reference (variant-alias and OpenRouter routed/date
* fallbacks included),
* 2. exact canonical id (coalesces provider variants),
* 3. exact bare id (preference-ranked),
* 4. retired effort-tier variant alias (collapsed catalog entries),
@@ -534,6 +548,11 @@ function matchModel(
return pickPreferredModel(exactMatches, context);
}
const bedrockInferenceProfile = resolveBedrockInferenceProfileModelId(modelPattern, availableModels);
if (bedrockInferenceProfile) {
return bedrockInferenceProfile;
}
// Retired effort-tier variant ids (bare, no provider prefix) resolve to
// their collapsed logical model; models from the providers whose table
// declared the alias win ties. Auto-derived `X-thinking` pairs resolve
@@ -883,7 +883,7 @@ describe("resolveCliModel", () => {
expect(result.model?.id).toBe("z-ai/glm-4.7-20251222:nitro");
});
test("accepts Bedrock inference profile ARNs as explicit provider model ids", () => {
test("accepts Bedrock inference profile ARNs and preserves thinking suffixes", () => {
const defaultBedrockModel = buildModel({
id: "us.anthropic.claude-opus-4-8",
name: "Claude Opus 4.8 (US)",
@@ -898,19 +898,30 @@ describe("resolveCliModel", () => {
});
const profileArn = "arn:aws:bedrock:us-east-2:1234567890:application-inference-profile/company-opus-48";
const result = resolveCliModel({
const baseResult = resolveCliModel({
cliProvider: "amazon-bedrock",
cliModel: profileArn,
modelRegistry: {
getAll: () => [defaultBedrockModel],
},
});
const offResult = resolveCliModel({
cliProvider: "amazon-bedrock",
cliModel: `${profileArn}:off`,
modelRegistry: {
getAll: () => [defaultBedrockModel],
},
});
expect(result.error).toBeUndefined();
expect(result.model?.provider).toBe("amazon-bedrock");
expect(result.model?.api).toBe("bedrock-converse-stream");
expect(result.model?.id).toBe(profileArn);
expect(result.model?.name).toBe("Bedrock inference profile");
expect(baseResult.error).toBeUndefined();
expect(baseResult.model?.provider).toBe("amazon-bedrock");
expect(baseResult.model?.api).toBe("bedrock-converse-stream");
expect(baseResult.model?.id).toBe(profileArn);
expect(baseResult.model?.name).toBe("Bedrock inference profile");
expect(baseResult.thinkingLevel).toBeUndefined();
expect(offResult.error).toBeUndefined();
expect(offResult.model?.id).toBe(profileArn);
expect(offResult.thinkingLevel).toBe("off");
});
test("returns a clear error when there are no models", () => {