feat(catalog): add Claude Opus 5 entries for Amazon Bedrock

Changes:
- Add Amazon Bedrock catalog entries for Claude Opus 5
- Exclude unsupported jp. inference profiles during generation
- Add Bedrock prompt cache compatibility info, AWS model card sources, and regression tests

Reason / background:
- Incorporate the new model info from models.dev and align with the official AWS spec

Scope of impact:
- Model catalog generation, Bedrock compatibility info, catalog tests
This commit is contained in:
k1riiiii
2026-07-25 09:54:26 +00:00
parent 59619623e1
commit a481a89e62
6 changed files with 267 additions and 1 deletions
+4
View File
@@ -2,6 +2,10 @@
## [Unreleased]
### Added
- Added Claude Opus 5 model entries for Amazon Bedrock: `anthropic.claude-opus-5` plus its `us.`, `eu.`, `au.`, and `global.` regional/geo IDs.
## [17.1.3] - 2026-07-24
### Fixed
@@ -52,6 +52,7 @@ import {
applyCanonicalLimitFallback,
applyGeneratedModelPolicies,
CLOUDFLARE_FALLBACK_MODEL,
dropUnsupportedBedrockGeoIds,
linkOpenAIPromotionTargets,
} from "./generated-policies";
@@ -634,6 +635,7 @@ async function generateModels() {
allModels = dropFireworksWireIds(allModels);
allModels = dropUnusableZaiContextTierIds(allModels);
allModels = dropXiaomiAudioOnlyIds(allModels);
allModels = dropUnsupportedBedrockGeoIds(allModels);
allModels = normalizeAntigravityEndpoint(allModels);
// Normalize display names: gateway author prefixes ("OpenAI: …"), alias
// markers ("(latest)"), provider attribution ("(Antigravity)"), and
@@ -51,6 +51,20 @@ export const CLOUDFLARE_FALLBACK_MODEL: ModelSpec<"anthropic-messages"> = {
maxTokens: 64000,
};
/**
* `models.dev` currently lists `jp.anthropic.claude-opus-5`, but AWS's own
* Bedrock model card documents only `anthropic.claude-opus-5` plus the `us.`,
* `eu.`, `au.`, and `global.` Geo/Global inference-profile IDs under
* Programmatic Access; Japan regions are marked unsupported for Geo inference
* in the same card's regional-availability table. Bedrock rejects an
* undocumented inference-profile ID outright, so drop this specific upstream
* row rather than ship a selector that 4xxs on first use (PR #6591 review).
* https://docs.aws.amazon.com/bedrock/latest/userguide/model-card-anthropic-claude-opus-5.html
*/
export function dropUnsupportedBedrockGeoIds(models: readonly ModelSpec[]): ModelSpec[] {
return models.filter(model => !(model.provider === "amazon-bedrock" && model.id === "jp.anthropic.claude-opus-5"));
}
const CODEX_GPT_5_4_PRIORITY_BY_VARIANT: Partial<Record<OpenAIVariant, number>> = {
base: 0,
mini: 1,
+12
View File
@@ -42,6 +42,15 @@ const EXPLICIT_CHECKPOINTS_4096_1H: ResolvedBedrockCompat = {
promptCacheMaximumCheckpoints: 4,
};
// AWS モデルカード: 512 トークン、最大 4 個のキャッシュチェックポイント、5 分と 1 時間の TTL。
// https://docs.aws.amazon.com/bedrock/latest/userguide/model-card-anthropic-claude-opus-5.html
const EXPLICIT_CHECKPOINTS_512_1H: ResolvedBedrockCompat = {
promptCacheMode: "explicit",
supportsLongPromptCacheRetention: true,
promptCacheMinimumTokens: 512,
promptCacheMaximumCheckpoints: 4,
};
/**
* Explicit Nova cache points complement Bedrock's automatic prefix caching:
* AWS recommends them for consistent cache hits and input-cost savings. Keep
@@ -82,6 +91,9 @@ function detectedBedrockCompat(modelId: string): ResolvedBedrockCompat {
) {
return EXPLICIT_CHECKPOINTS_4096_1H;
}
if (id.includes("anthropic.claude-opus-5")) {
return EXPLICIT_CHECKPOINTS_512_1H;
}
if (id.includes("anthropic.claude-opus-4-6")) {
return EXPLICIT_CHECKPOINTS_4096_5M;
}
+151 -1
View File
@@ -7966,6 +7966,36 @@
"supportsDisplay": true
}
},
"anthropic.claude-opus-5": {
"id": "anthropic.claude-opus-5",
"name": "Claude Opus 5",
"api": "bedrock-converse-stream",
"provider": "amazon-bedrock",
"baseUrl": "https://bedrock-runtime.us-east-1.amazonaws.com",
"reasoning": true,
"input": [
"text",
"image"
],
"cost": {
"input": 5,
"output": 25,
"cacheRead": 0.5,
"cacheWrite": 6.25
},
"contextWindow": 1000000,
"maxTokens": 128000,
"thinking": {
"mode": "anthropic-adaptive",
"efforts": [
"low",
"medium",
"high",
"max"
],
"supportsDisplay": true
}
},
"anthropic.claude-sonnet-5": {
"id": "anthropic.claude-sonnet-5",
"name": "Claude Sonnet 5",
@@ -8084,6 +8114,36 @@
"supportsDisplay": true
}
},
"au.anthropic.claude-opus-5": {
"id": "au.anthropic.claude-opus-5",
"name": "Claude Opus 5 (AU)",
"api": "bedrock-converse-stream",
"provider": "amazon-bedrock",
"baseUrl": "https://bedrock-runtime.us-east-1.amazonaws.com",
"reasoning": true,
"input": [
"text",
"image"
],
"cost": {
"input": 5,
"output": 25,
"cacheRead": 0.5,
"cacheWrite": 6.25
},
"contextWindow": 1000000,
"maxTokens": 128000,
"thinking": {
"mode": "anthropic-adaptive",
"efforts": [
"low",
"medium",
"high",
"max"
],
"supportsDisplay": true
}
},
"au.anthropic.claude-sonnet-4-5-20250929-v1:0": {
"id": "au.anthropic.claude-sonnet-4-5-20250929-v1:0",
"name": "Claude Sonnet 4.5 (AU)",
@@ -8669,6 +8729,36 @@
"supportsDisplay": true
}
},
"eu.anthropic.claude-opus-5": {
"id": "eu.anthropic.claude-opus-5",
"name": "Claude Opus 5 (EU)",
"api": "bedrock-converse-stream",
"provider": "amazon-bedrock",
"baseUrl": "https://bedrock-runtime.us-east-1.amazonaws.com",
"reasoning": true,
"input": [
"text",
"image"
],
"cost": {
"input": 5.5,
"output": 27.5,
"cacheRead": 0.55,
"cacheWrite": 6.875
},
"contextWindow": 1000000,
"maxTokens": 128000,
"thinking": {
"mode": "anthropic-adaptive",
"efforts": [
"low",
"medium",
"high",
"max"
],
"supportsDisplay": true
}
},
"eu.anthropic.claude-sonnet-4-20250514-v1:0": {
"id": "eu.anthropic.claude-sonnet-4-20250514-v1:0",
"name": "Claude Sonnet 4 (EU)",
@@ -8992,6 +9082,36 @@
"supportsDisplay": true
}
},
"global.anthropic.claude-opus-5": {
"id": "global.anthropic.claude-opus-5",
"name": "Claude Opus 5 (Global)",
"api": "bedrock-converse-stream",
"provider": "amazon-bedrock",
"baseUrl": "https://bedrock-runtime.us-east-1.amazonaws.com",
"reasoning": true,
"input": [
"text",
"image"
],
"cost": {
"input": 5,
"output": 25,
"cacheRead": 0.5,
"cacheWrite": 6.25
},
"contextWindow": 1000000,
"maxTokens": 128000,
"thinking": {
"mode": "anthropic-adaptive",
"efforts": [
"low",
"medium",
"high",
"max"
],
"supportsDisplay": true
}
},
"global.anthropic.claude-sonnet-4-20250514-v1:0": {
"id": "global.anthropic.claude-sonnet-4-20250514-v1:0",
"name": "Claude Sonnet 4 (Global)",
@@ -10620,6 +10740,36 @@
"supportsDisplay": true
}
},
"us.anthropic.claude-opus-5": {
"id": "us.anthropic.claude-opus-5",
"name": "Claude Opus 5 (US)",
"api": "bedrock-converse-stream",
"provider": "amazon-bedrock",
"baseUrl": "https://bedrock-runtime.us-east-1.amazonaws.com",
"reasoning": true,
"input": [
"text",
"image"
],
"cost": {
"input": 5,
"output": 25,
"cacheRead": 0.5,
"cacheWrite": 6.25
},
"contextWindow": 1000000,
"maxTokens": 128000,
"thinking": {
"mode": "anthropic-adaptive",
"efforts": [
"low",
"medium",
"high",
"max"
],
"supportsDisplay": true
}
},
"us.anthropic.claude-sonnet-4-20250514-v1:0": {
"id": "us.anthropic.claude-sonnet-4-20250514-v1:0",
"name": "Claude Sonnet 4 (US)",
@@ -94800,4 +94950,4 @@
}
}
}
}
}
@@ -0,0 +1,84 @@
import { describe, expect, test } from "bun:test";
import { buildBedrockCompat } from "@oh-my-pi/pi-catalog/compat/bedrock";
import { getBundledModels } from "@oh-my-pi/pi-catalog/models";
import type { ModelSpec } from "@oh-my-pi/pi-catalog/types";
import { dropUnsupportedBedrockGeoIds } from "../scripts/generated-policies";
// AWS's Bedrock model card for Claude Opus 5 lists exactly these Programmatic
// Access IDs — the bare model ID plus the us./eu./au. Geo and global.
// inference profiles. Japan is explicitly marked unsupported for Geo
// inference in the same card's regional-availability table, so no `jp.`
// profile exists for this model (unlike several Opus 4.x generations).
// https://docs.aws.amazon.com/bedrock/latest/userguide/model-card-anthropic-claude-opus-5.html
const AWS_DOCUMENTED_OPUS_5_IDS = [
"anthropic.claude-opus-5",
"us.anthropic.claude-opus-5",
"eu.anthropic.claude-opus-5",
"au.anthropic.claude-opus-5",
"global.anthropic.claude-opus-5",
];
describe("Amazon Bedrock Claude Opus 5", () => {
test("bundles exactly the AWS-documented inference-profile IDs", () => {
const bedrockOpus5Ids = getBundledModels("amazon-bedrock")
.map(model => model.id)
.filter(id => id.endsWith("anthropic.claude-opus-5"));
expect(new Set(bedrockOpus5Ids)).toEqual(new Set(AWS_DOCUMENTED_OPUS_5_IDS));
// `models.dev` currently also lists `jp.anthropic.claude-opus-5`; Bedrock
// has no such inference profile for this model and would reject it.
expect(bedrockOpus5Ids).not.toContain("jp.anthropic.claude-opus-5");
});
test("dropUnsupportedBedrockGeoIds filters the undocumented jp. profile without touching other providers/ids", () => {
const bareSpec = (provider: string, id: string): ModelSpec<"bedrock-converse-stream"> => ({
id,
name: id,
api: "bedrock-converse-stream",
provider,
baseUrl: "https://bedrock-runtime.us-east-1.amazonaws.com",
reasoning: true,
input: ["text"],
cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
contextWindow: 200000,
maxTokens: 64000,
});
const input = [
bareSpec("amazon-bedrock", "jp.anthropic.claude-opus-5"),
bareSpec("amazon-bedrock", "us.anthropic.claude-opus-5"),
// A `jp.` id on a different Bedrock model, or on a different provider,
// must survive — only this exact (provider, id) pair is undocumented.
bareSpec("amazon-bedrock", "jp.anthropic.claude-opus-4-8"),
bareSpec("some-other-provider", "jp.anthropic.claude-opus-5"),
];
expect(dropUnsupportedBedrockGeoIds(input).map(model => model.id)).toEqual([
"us.anthropic.claude-opus-5",
"jp.anthropic.claude-opus-4-8",
"jp.anthropic.claude-opus-5",
]);
});
test("resolves the AWS-documented 512-token/four-checkpoint/1h prompt-cache capability", () => {
for (const id of AWS_DOCUMENTED_OPUS_5_IDS) {
const spec: ModelSpec<"bedrock-converse-stream"> = {
id,
name: id,
api: "bedrock-converse-stream",
provider: "amazon-bedrock",
baseUrl: "https://bedrock-runtime.us-east-1.amazonaws.com",
reasoning: true,
input: ["text", "image"],
cost: { input: 5, output: 25, cacheRead: 0.5, cacheWrite: 6.25 },
contextWindow: 1_000_000,
maxTokens: 128_000,
};
expect(buildBedrockCompat(spec)).toEqual({
promptCacheMode: "explicit",
supportsLongPromptCacheRetention: true,
promptCacheMinimumTokens: 512,
promptCacheMaximumCheckpoints: 4,
});
}
});
});