Add Claude Opus 5 support
- commit
- 6f37bbb501f6f29a8055d5347570425da4351825
- author
- patriceckhart · · 2026-07-25 07:41 UTC
- parent
- fc39bd9
- signature
- unsigned
- refs
- tag: v0.3.5
7 files · +99 -13
M docs/providers.md +4 -2
| @@ -209,8 +209,10 @@ AWS_BEARER_TOKEN_BEDROCK=bedrock-api-key-... AWS_REGION=us-east-1 \ | |||
| 209 | 209 | ||
| 210 | 210 | Some Bedrock models require regional inference-profile IDs for on-demand | |
| 211 | 211 | throughput, such as `us.` or `eu.` prefixed model IDs. zot rewrites known | |
| 212 | - | families automatically where possible. Explicit profile IDs and ARNs are left | |
| 213 | - | unchanged. | |
| 212 | + | families automatically where possible. Claude Opus 5 uses | |
| 213 | + | `global.anthropic.claude-opus-5`; zot maps the bare foundation-model ID to that | |
| 214 | + | global profile and supports its adaptive thinking levels. Explicit profile IDs | |
| 215 | + | and ARNs are left unchanged. | |
| 214 | 216 | ||
| 215 | 217 | ### Google Vertex AI | |
| 216 | 218 | ||
| @@ -209,8 +209,10 @@ AWS_BEARER_TOKEN_BEDROCK=bedrock-api-key-... AWS_REGION=us-east-1 \ | |||||
| 209 | 209 | ||||
| 210 | Some Bedrock models require regional inference-profile IDs for on-demand | 210 | Some Bedrock models require regional inference-profile IDs for on-demand | ||
| 211 | throughput, such as `us.` or `eu.` prefixed model IDs. zot rewrites known | 211 | throughput, such as `us.` or `eu.` prefixed model IDs. zot rewrites known | ||
| 212 | - | families automatically where possible. Explicit profile IDs and ARNs are left | 212 | + | families automatically where possible. Claude Opus 5 uses |
| 213 | - | unchanged. | 213 | + | `global.anthropic.claude-opus-5`; zot maps the bare foundation-model ID to that |
| 214 | + | global profile and supports its adaptive thinking levels. Explicit profile IDs | |||
| 215 | + | and ARNs are left unchanged. | |||
| 214 | 216 | ||||
| 215 | ### Google Vertex AI | 217 | ### Google Vertex AI | ||
| 216 | 218 | ||||
M packages/provider/amazon_bedrock.go +23 -4
| @@ -213,9 +213,10 @@ type bedrockToolSpec struct { | |||
| 213 | 213 | } | |
| 214 | 214 | ||
| 215 | 215 | type bedrockRequest struct { | |
| 216 | - | Messages []bedrockMessage `json:"messages"` | |
| 217 | - | System []map[string]interface{} `json:"system,omitempty"` | |
| 218 | - | InferenceConfig struct { | |
| 216 | + | Messages []bedrockMessage `json:"messages"` | |
| 217 | + | System []map[string]interface{} `json:"system,omitempty"` | |
| 218 | + | AdditionalModelRequestFields map[string]interface{} `json:"additionalModelRequestFields,omitempty"` | |
| 219 | + | InferenceConfig struct { | |
| 219 | 220 | MaxTokens int `json:"maxTokens,omitempty"` | |
| 220 | 221 | Temperature *float32 `json:"temperature,omitempty"` | |
| 221 | 222 | } `json:"inferenceConfig,omitempty"` | |
| @@ -325,6 +326,11 @@ func (c *bedrockClient) buildRequest(req Request) (*bedrockRequest, error) { | |||
| 325 | 326 | // check operates on the same ID used for FindModel. | |
| 326 | 327 | resolvedModel := resolveBedrockInferenceProfileID(req.Model, c.region) | |
| 327 | 328 | caching := bedrockModelSupportsCaching(resolvedModel) | |
| 329 | + | model := Model{ID: resolvedModel} | |
| 330 | + | if catalogModel, err := FindModel("amazon-bedrock", resolvedModel); err == nil { | |
| 331 | + | model = catalogModel | |
| 332 | + | } | |
| 333 | + | adaptive := usesAdaptiveThinking(model) | |
| 328 | 334 | ||
| 329 | 335 | if req.System != "" { | |
| 330 | 336 | sysBlock := map[string]interface{}{"text": req.System} | |
| @@ -336,7 +342,15 @@ func (c *bedrockClient) buildRequest(req Request) (*bedrockRequest, error) { | |||
| 336 | 342 | out.System = []map[string]interface{}{sysBlock} | |
| 337 | 343 | } | |
| 338 | 344 | } | |
| 339 | - | out.InferenceConfig.Temperature = req.Temperature | |
| 345 | + | if !adaptive { | |
| 346 | + | out.InferenceConfig.Temperature = req.Temperature | |
| 347 | + | } | |
| 348 | + | if adaptive && req.Reasoning != "" { | |
| 349 | + | out.AdditionalModelRequestFields = map[string]interface{}{ | |
| 350 | + | "thinking": map[string]interface{}{"type": "adaptive"}, | |
| 351 | + | "output_config": map[string]interface{}{"effort": AnthropicAdaptiveEffort(req.Reasoning)}, | |
| 352 | + | } | |
| 353 | + | } | |
| 340 | 354 | out.InferenceConfig.MaxTokens = req.MaxTokens | |
| 341 | 355 | if out.InferenceConfig.MaxTokens == 0 { | |
| 342 | 356 | out.InferenceConfig.MaxTokens = 4096 | |
| @@ -489,6 +503,11 @@ func resolveBedrockInferenceProfileID(modelID, region string) string { | |||
| 489 | 503 | if bedrockHasGeoPrefix(modelID) { | |
| 490 | 504 | return modelID | |
| 491 | 505 | } | |
| 506 | + | // Opus 5 is exposed for on-demand throughput through the global | |
| 507 | + | // inference profile rather than a region-specific profile. | |
| 508 | + | if modelID == "anthropic.claude-opus-5" { | |
| 509 | + | return "global." + modelID | |
| 510 | + | } | |
| 492 | 511 | if !bedrockRequiresInferenceProfile(modelID) { | |
| 493 | 512 | return modelID | |
| 494 | 513 | } | |
| @@ -213,9 +213,10 @@ type bedrockToolSpec struct { | |||||
| 213 | } | 213 | } | ||
| 214 | 214 | ||||
| 215 | type bedrockRequest struct { | 215 | type bedrockRequest struct { | ||
| 216 | - | Messages []bedrockMessage `json:"messages"` | 216 | + | Messages []bedrockMessage `json:"messages"` |
| 217 | - | System []map[string]interface{} `json:"system,omitempty"` | 217 | + | System []map[string]interface{} `json:"system,omitempty"` |
| 218 | - | InferenceConfig struct { | 218 | + | AdditionalModelRequestFields map[string]interface{} `json:"additionalModelRequestFields,omitempty"` |
| 219 | + | InferenceConfig struct { | |||
| 219 | MaxTokens int `json:"maxTokens,omitempty"` | 220 | MaxTokens int `json:"maxTokens,omitempty"` | ||
| 220 | Temperature *float32 `json:"temperature,omitempty"` | 221 | Temperature *float32 `json:"temperature,omitempty"` | ||
| 221 | } `json:"inferenceConfig,omitempty"` | 222 | } `json:"inferenceConfig,omitempty"` | ||
| @@ -325,6 +326,11 @@ func (c *bedrockClient) buildRequest(req Request) (*bedrockRequest, error) { | |||||
| 325 | // check operates on the same ID used for FindModel. | 326 | // check operates on the same ID used for FindModel. | ||
| 326 | resolvedModel := resolveBedrockInferenceProfileID(req.Model, c.region) | 327 | resolvedModel := resolveBedrockInferenceProfileID(req.Model, c.region) | ||
| 327 | caching := bedrockModelSupportsCaching(resolvedModel) | 328 | caching := bedrockModelSupportsCaching(resolvedModel) | ||
| 329 | + | model := Model{ID: resolvedModel} | |||
| 330 | + | if catalogModel, err := FindModel("amazon-bedrock", resolvedModel); err == nil { | |||
| 331 | + | model = catalogModel | |||
| 332 | + | } | |||
| 333 | + | adaptive := usesAdaptiveThinking(model) | |||
| 328 | 334 | ||||
| 329 | if req.System != "" { | 335 | if req.System != "" { | ||
| 330 | sysBlock := map[string]interface{}{"text": req.System} | 336 | sysBlock := map[string]interface{}{"text": req.System} | ||
| @@ -336,7 +342,15 @@ func (c *bedrockClient) buildRequest(req Request) (*bedrockRequest, error) { | |||||
| 336 | out.System = []map[string]interface{}{sysBlock} | 342 | out.System = []map[string]interface{}{sysBlock} | ||
| 337 | } | 343 | } | ||
| 338 | } | 344 | } | ||
| 339 | - | out.InferenceConfig.Temperature = req.Temperature | 345 | + | if !adaptive { |
| 346 | + | out.InferenceConfig.Temperature = req.Temperature | |||
| 347 | + | } | |||
| 348 | + | if adaptive && req.Reasoning != "" { | |||
| 349 | + | out.AdditionalModelRequestFields = map[string]interface{}{ | |||
| 350 | + | "thinking": map[string]interface{}{"type": "adaptive"}, | |||
| 351 | + | "output_config": map[string]interface{}{"effort": AnthropicAdaptiveEffort(req.Reasoning)}, | |||
| 352 | + | } | |||
| 353 | + | } | |||
| 340 | out.InferenceConfig.MaxTokens = req.MaxTokens | 354 | out.InferenceConfig.MaxTokens = req.MaxTokens | ||
| 341 | if out.InferenceConfig.MaxTokens == 0 { | 355 | if out.InferenceConfig.MaxTokens == 0 { | ||
| 342 | out.InferenceConfig.MaxTokens = 4096 | 356 | out.InferenceConfig.MaxTokens = 4096 | ||
| @@ -489,6 +503,11 @@ func resolveBedrockInferenceProfileID(modelID, region string) string { | |||||
| 489 | if bedrockHasGeoPrefix(modelID) { | 503 | if bedrockHasGeoPrefix(modelID) { | ||
| 490 | return modelID | 504 | return modelID | ||
| 491 | } | 505 | } | ||
| 506 | + | // Opus 5 is exposed for on-demand throughput through the global | |||
| 507 | + | // inference profile rather than a region-specific profile. | |||
| 508 | + | if modelID == "anthropic.claude-opus-5" { | |||
| 509 | + | return "global." + modelID | |||
| 510 | + | } | |||
| 492 | if !bedrockRequiresInferenceProfile(modelID) { | 511 | if !bedrockRequiresInferenceProfile(modelID) { | ||
| 493 | return modelID | 512 | return modelID | ||
| 494 | } | 513 | } | ||
M packages/provider/amazon_bedrock_test.go +47 -1
| @@ -163,6 +163,36 @@ func TestBedrockModelSupportsCaching(t *testing.T) { | |||
| 163 | 163 | } | |
| 164 | 164 | } | |
| 165 | 165 | ||
| 166 | + | func TestBedrockBuildRequestClaudeOpus5AdaptiveThinking(t *testing.T) { | |
| 167 | + | client := &bedrockClient{region: "us-east-1"} | |
| 168 | + | temperature := float32(0.7) | |
| 169 | + | req, err := client.buildRequest(Request{ | |
| 170 | + | Model: "global.anthropic.claude-opus-5", | |
| 171 | + | Reasoning: "max", | |
| 172 | + | Temperature: &temperature, | |
| 173 | + | Messages: []Message{ | |
| 174 | + | {Role: RoleUser, Content: []Content{TextBlock{Text: "hello"}}}, | |
| 175 | + | }, | |
| 176 | + | }) | |
| 177 | + | if err != nil { | |
| 178 | + | t.Fatal(err) | |
| 179 | + | } | |
| 180 | + | if req.InferenceConfig.Temperature != nil { | |
| 181 | + | t.Fatalf("adaptive thinking must omit temperature, got %v", *req.InferenceConfig.Temperature) | |
| 182 | + | } | |
| 183 | + | if req.AdditionalModelRequestFields == nil { | |
| 184 | + | t.Fatal("missing additionalModelRequestFields") | |
| 185 | + | } | |
| 186 | + | thinking, ok := req.AdditionalModelRequestFields["thinking"].(map[string]interface{}) | |
| 187 | + | if !ok || thinking["type"] != "adaptive" { | |
| 188 | + | t.Fatalf("unexpected thinking config: %#v", req.AdditionalModelRequestFields["thinking"]) | |
| 189 | + | } | |
| 190 | + | output, ok := req.AdditionalModelRequestFields["output_config"].(map[string]interface{}) | |
| 191 | + | if !ok || output["effort"] != "max" { | |
| 192 | + | t.Fatalf("unexpected output config: %#v", req.AdditionalModelRequestFields["output_config"]) | |
| 193 | + | } | |
| 194 | + | } | |
| 195 | + | ||
| 166 | 196 | func TestBedrockBuildRequestMaxTokens(t *testing.T) { | |
| 167 | 197 | client := &bedrockClient{region: "us-east-1"} | |
| 168 | 198 | ||
| @@ -642,7 +672,10 @@ func TestResolveBedrockInferenceProfileID(t *testing.T) { | |||
| 642 | 672 | region string | |
| 643 | 673 | want string | |
| 644 | 674 | }{ | |
| 645 | - | // Bare Anthropic foundation IDs get the region-matched prefix. | |
| 675 | + | // Opus 5 is available through its global inference profile only. | |
| 676 | + | {"anthropic.claude-opus-5", "us-east-1", "global.anthropic.claude-opus-5"}, | |
| 677 | + | {"anthropic.claude-opus-5", "eu-central-1", "global.anthropic.claude-opus-5"}, | |
| 678 | + | // Other bare Anthropic foundation IDs get the region-matched prefix. | |
| 646 | 679 | {"anthropic.claude-sonnet-4-5-20250929-v1:0", "us-east-1", "us.anthropic.claude-sonnet-4-5-20250929-v1:0"}, | |
| 647 | 680 | {"anthropic.claude-sonnet-4-5-20250929-v1:0", "eu-central-1", "eu.anthropic.claude-sonnet-4-5-20250929-v1:0"}, | |
| 648 | 681 | {"anthropic.claude-opus-4-6-v1", "ap-southeast-2", "apac.anthropic.claude-opus-4-6-v1"}, | |
| @@ -666,3 +699,16 @@ func TestResolveBedrockInferenceProfileID(t *testing.T) { | |||
| 666 | 699 | } | |
| 667 | 700 | } | |
| 668 | 701 | } | |
| 702 | + | ||
| 703 | + | func TestClaudeOpus5BedrockCatalog(t *testing.T) { | |
| 704 | + | m, err := FindModel("amazon-bedrock", "global.anthropic.claude-opus-5") | |
| 705 | + | if err != nil { | |
| 706 | + | t.Fatal(err) | |
| 707 | + | } | |
| 708 | + | if m.ContextWindow != 1000000 || m.MaxOutput != 128000 || !m.Reasoning || !m.AdaptiveThinking { | |
| 709 | + | t.Fatalf("unexpected Bedrock Opus 5 model: %+v", m) | |
| 710 | + | } | |
| 711 | + | if _, err := FindModel("amazon-bedrock", "anthropic.claude-opus-5"); err == nil { | |
| 712 | + | t.Fatal("bare Bedrock Opus 5 ID must not be exposed in the catalog") | |
| 713 | + | } | |
| 714 | + | } | |
| @@ -163,6 +163,36 @@ func TestBedrockModelSupportsCaching(t *testing.T) { | |||||
| 163 | } | 163 | } | ||
| 164 | } | 164 | } | ||
| 165 | 165 | ||||
| 166 | + | func TestBedrockBuildRequestClaudeOpus5AdaptiveThinking(t *testing.T) { | |||
| 167 | + | client := &bedrockClient{region: "us-east-1"} | |||
| 168 | + | temperature := float32(0.7) | |||
| 169 | + | req, err := client.buildRequest(Request{ | |||
| 170 | + | Model: "global.anthropic.claude-opus-5", | |||
| 171 | + | Reasoning: "max", | |||
| 172 | + | Temperature: &temperature, | |||
| 173 | + | Messages: []Message{ | |||
| 174 | + | {Role: RoleUser, Content: []Content{TextBlock{Text: "hello"}}}, | |||
| 175 | + | }, | |||
| 176 | + | }) | |||
| 177 | + | if err != nil { | |||
| 178 | + | t.Fatal(err) | |||
| 179 | + | } | |||
| 180 | + | if req.InferenceConfig.Temperature != nil { | |||
| 181 | + | t.Fatalf("adaptive thinking must omit temperature, got %v", *req.InferenceConfig.Temperature) | |||
| 182 | + | } | |||
| 183 | + | if req.AdditionalModelRequestFields == nil { | |||
| 184 | + | t.Fatal("missing additionalModelRequestFields") | |||
| 185 | + | } | |||
| 186 | + | thinking, ok := req.AdditionalModelRequestFields["thinking"].(map[string]interface{}) | |||
| 187 | + | if !ok || thinking["type"] != "adaptive" { | |||
| 188 | + | t.Fatalf("unexpected thinking config: %#v", req.AdditionalModelRequestFields["thinking"]) | |||
| 189 | + | } | |||
| 190 | + | output, ok := req.AdditionalModelRequestFields["output_config"].(map[string]interface{}) | |||
| 191 | + | if !ok || output["effort"] != "max" { | |||
| 192 | + | t.Fatalf("unexpected output config: %#v", req.AdditionalModelRequestFields["output_config"]) | |||
| 193 | + | } | |||
| 194 | + | } | |||
| 195 | + | ||||
| 166 | func TestBedrockBuildRequestMaxTokens(t *testing.T) { | 196 | func TestBedrockBuildRequestMaxTokens(t *testing.T) { | ||
| 167 | client := &bedrockClient{region: "us-east-1"} | 197 | client := &bedrockClient{region: "us-east-1"} | ||
| 168 | 198 | ||||
| @@ -642,7 +672,10 @@ func TestResolveBedrockInferenceProfileID(t *testing.T) { | |||||
| 642 | region string | 672 | region string | ||
| 643 | want string | 673 | want string | ||
| 644 | }{ | 674 | }{ | ||
| 645 | - | // Bare Anthropic foundation IDs get the region-matched prefix. | 675 | + | // Opus 5 is available through its global inference profile only. |
| 676 | + | {"anthropic.claude-opus-5", "us-east-1", "global.anthropic.claude-opus-5"}, | |||
| 677 | + | {"anthropic.claude-opus-5", "eu-central-1", "global.anthropic.claude-opus-5"}, | |||
| 678 | + | // Other bare Anthropic foundation IDs get the region-matched prefix. | |||
| 646 | {"anthropic.claude-sonnet-4-5-20250929-v1:0", "us-east-1", "us.anthropic.claude-sonnet-4-5-20250929-v1:0"}, | 679 | {"anthropic.claude-sonnet-4-5-20250929-v1:0", "us-east-1", "us.anthropic.claude-sonnet-4-5-20250929-v1:0"}, | ||
| 647 | {"anthropic.claude-sonnet-4-5-20250929-v1:0", "eu-central-1", "eu.anthropic.claude-sonnet-4-5-20250929-v1:0"}, | 680 | {"anthropic.claude-sonnet-4-5-20250929-v1:0", "eu-central-1", "eu.anthropic.claude-sonnet-4-5-20250929-v1:0"}, | ||
| 648 | {"anthropic.claude-opus-4-6-v1", "ap-southeast-2", "apac.anthropic.claude-opus-4-6-v1"}, | 681 | {"anthropic.claude-opus-4-6-v1", "ap-southeast-2", "apac.anthropic.claude-opus-4-6-v1"}, | ||
| @@ -666,3 +699,16 @@ func TestResolveBedrockInferenceProfileID(t *testing.T) { | |||||
| 666 | } | 699 | } | ||
| 667 | } | 700 | } | ||
| 668 | } | 701 | } | ||
| 702 | + | ||||
| 703 | + | func TestClaudeOpus5BedrockCatalog(t *testing.T) { | |||
| 704 | + | m, err := FindModel("amazon-bedrock", "global.anthropic.claude-opus-5") | |||
| 705 | + | if err != nil { | |||
| 706 | + | t.Fatal(err) | |||
| 707 | + | } | |||
| 708 | + | if m.ContextWindow != 1000000 || m.MaxOutput != 128000 || !m.Reasoning || !m.AdaptiveThinking { | |||
| 709 | + | t.Fatalf("unexpected Bedrock Opus 5 model: %+v", m) | |||
| 710 | + | } | |||
| 711 | + | if _, err := FindModel("amazon-bedrock", "anthropic.claude-opus-5"); err == nil { | |||
| 712 | + | t.Fatal("bare Bedrock Opus 5 ID must not be exposed in the catalog") | |||
| 713 | + | } | |||
| 714 | + | } | |||
M packages/provider/anthropic.go +2 -2
| @@ -189,7 +189,7 @@ type anthRequest struct { | |||
| 189 | 189 | } | |
| 190 | 190 | ||
| 191 | 191 | // usesAdaptiveThinking reports whether a model only supports the | |
| 192 | - | // adaptive thinking mode (Opus 4.7 and later). These models reject | |
| 192 | + | // adaptive thinking mode (Opus 4.7 and later, including Opus 5). These models reject | |
| 193 | 193 | // explicit thinking budgets and non-default sampling parameters. The | |
| 194 | 194 | // catalog flag is authoritative; the id-substring fallback catches | |
| 195 | 195 | // the same family when reached through an Anthropic-Messages- | |
| @@ -199,7 +199,7 @@ func usesAdaptiveThinking(m Model) bool { | |||
| 199 | 199 | return true | |
| 200 | 200 | } | |
| 201 | 201 | id := strings.ToLower(m.ID) | |
| 202 | - | for _, marker := range []string{"opus-4-7", "opus-4.7", "opus-4-8", "opus-4.8", "sonnet-5", "fable-5"} { | |
| 202 | + | for _, marker := range []string{"opus-4-7", "opus-4.7", "opus-4-8", "opus-4.8", "opus-5", "opus.5", "sonnet-5", "fable-5"} { | |
| 203 | 203 | if strings.Contains(id, marker) { | |
| 204 | 204 | return true | |
| 205 | 205 | } | |
| @@ -189,7 +189,7 @@ type anthRequest struct { | |||||
| 189 | } | 189 | } | ||
| 190 | 190 | ||||
| 191 | // usesAdaptiveThinking reports whether a model only supports the | 191 | // usesAdaptiveThinking reports whether a model only supports the | ||
| 192 | - | // adaptive thinking mode (Opus 4.7 and later). These models reject | 192 | + | // adaptive thinking mode (Opus 4.7 and later, including Opus 5). These models reject |
| 193 | // explicit thinking budgets and non-default sampling parameters. The | 193 | // explicit thinking budgets and non-default sampling parameters. The | ||
| 194 | // catalog flag is authoritative; the id-substring fallback catches | 194 | // catalog flag is authoritative; the id-substring fallback catches | ||
| 195 | // the same family when reached through an Anthropic-Messages- | 195 | // the same family when reached through an Anthropic-Messages- | ||
| @@ -199,7 +199,7 @@ func usesAdaptiveThinking(m Model) bool { | |||||
| 199 | return true | 199 | return true | ||
| 200 | } | 200 | } | ||
| 201 | id := strings.ToLower(m.ID) | 201 | id := strings.ToLower(m.ID) | ||
| 202 | - | for _, marker := range []string{"opus-4-7", "opus-4.7", "opus-4-8", "opus-4.8", "sonnet-5", "fable-5"} { | 202 | + | for _, marker := range []string{"opus-4-7", "opus-4.7", "opus-4-8", "opus-4.8", "opus-5", "opus.5", "sonnet-5", "fable-5"} { |
| 203 | if strings.Contains(id, marker) { | 203 | if strings.Contains(id, marker) { | ||
| 204 | return true | 204 | return true | ||
| 205 | } | 205 | } | ||
M packages/provider/catalog_builtin.go +1 -0
| @@ -54,6 +54,7 @@ var builtinCatalog = []Model{ | |||
| 54 | 54 | {Provider: "amazon-bedrock", ID: "global.anthropic.claude-opus-4-6-v1", DisplayName: "Claude Opus 4.6 (Global)", ContextWindow: 1000000, MaxOutput: 128000, Reasoning: true, PriceInput: 5, PriceOutput: 25, PriceCacheRead: 0.5, PriceCacheWrite: 6.25, BaseURL: "https://bedrock-runtime.us-east-1.amazonaws.com"}, | |
| 55 | 55 | {Provider: "amazon-bedrock", ID: "global.anthropic.claude-opus-4-7", DisplayName: "Claude Opus 4.7 (Global)", ContextWindow: 1000000, MaxOutput: 128000, Reasoning: true, PriceInput: 5, PriceOutput: 25, PriceCacheRead: 0.5, PriceCacheWrite: 6.25, BaseURL: "https://bedrock-runtime.us-east-1.amazonaws.com"}, | |
| 56 | 56 | {Provider: "amazon-bedrock", ID: "global.anthropic.claude-opus-4-8", DisplayName: "Claude Opus 4.8 (Global)", ContextWindow: 1000000, MaxOutput: 128000, Reasoning: true, PriceInput: 5, PriceOutput: 25, PriceCacheRead: 0.5, PriceCacheWrite: 6.25, BaseURL: "https://bedrock-runtime.us-east-1.amazonaws.com"}, | |
| 57 | + | {Provider: "amazon-bedrock", ID: "global.anthropic.claude-opus-5", DisplayName: "Claude Opus 5 (Global)", ContextWindow: 1000000, MaxOutput: 128000, Reasoning: true, AdaptiveThinking: true, PriceInput: 5, PriceOutput: 25, PriceCacheRead: 0.5, PriceCacheWrite: 6.25, BaseURL: "https://bedrock-runtime.us-east-1.amazonaws.com"}, | |
| 57 | 58 | {Provider: "amazon-bedrock", ID: "global.anthropic.claude-sonnet-4-5-20250929-v1:0", DisplayName: "Claude Sonnet 4.5 (Global)", ContextWindow: 200000, MaxOutput: 64000, Reasoning: true, PriceInput: 3, PriceOutput: 15, PriceCacheRead: 0.3, PriceCacheWrite: 3.75, BaseURL: "https://bedrock-runtime.us-east-1.amazonaws.com"}, | |
| 58 | 59 | {Provider: "amazon-bedrock", ID: "global.anthropic.claude-sonnet-4-6", DisplayName: "Claude Sonnet 4.6 (Global)", ContextWindow: 1000000, MaxOutput: 64000, Reasoning: true, PriceInput: 3, PriceOutput: 15, PriceCacheRead: 0.3, PriceCacheWrite: 3.75, BaseURL: "https://bedrock-runtime.us-east-1.amazonaws.com"}, | |
| 59 | 60 | {Provider: "amazon-bedrock", ID: "global.anthropic.claude-sonnet-5", DisplayName: "Claude Sonnet 5 (Global)", ContextWindow: 1000000, MaxOutput: 128000, Reasoning: true, AdaptiveThinking: true, PriceInput: 2, PriceOutput: 10, PriceCacheRead: 0.2, PriceCacheWrite: 2.5, BaseURL: "https://bedrock-runtime.us-east-1.amazonaws.com"}, | |
| @@ -54,6 +54,7 @@ var builtinCatalog = []Model{ | |||||
| 54 | {Provider: "amazon-bedrock", ID: "global.anthropic.claude-opus-4-6-v1", DisplayName: "Claude Opus 4.6 (Global)", ContextWindow: 1000000, MaxOutput: 128000, Reasoning: true, PriceInput: 5, PriceOutput: 25, PriceCacheRead: 0.5, PriceCacheWrite: 6.25, BaseURL: "https://bedrock-runtime.us-east-1.amazonaws.com"}, | 54 | {Provider: "amazon-bedrock", ID: "global.anthropic.claude-opus-4-6-v1", DisplayName: "Claude Opus 4.6 (Global)", ContextWindow: 1000000, MaxOutput: 128000, Reasoning: true, PriceInput: 5, PriceOutput: 25, PriceCacheRead: 0.5, PriceCacheWrite: 6.25, BaseURL: "https://bedrock-runtime.us-east-1.amazonaws.com"}, | ||
| 55 | {Provider: "amazon-bedrock", ID: "global.anthropic.claude-opus-4-7", DisplayName: "Claude Opus 4.7 (Global)", ContextWindow: 1000000, MaxOutput: 128000, Reasoning: true, PriceInput: 5, PriceOutput: 25, PriceCacheRead: 0.5, PriceCacheWrite: 6.25, BaseURL: "https://bedrock-runtime.us-east-1.amazonaws.com"}, | 55 | {Provider: "amazon-bedrock", ID: "global.anthropic.claude-opus-4-7", DisplayName: "Claude Opus 4.7 (Global)", ContextWindow: 1000000, MaxOutput: 128000, Reasoning: true, PriceInput: 5, PriceOutput: 25, PriceCacheRead: 0.5, PriceCacheWrite: 6.25, BaseURL: "https://bedrock-runtime.us-east-1.amazonaws.com"}, | ||
| 56 | {Provider: "amazon-bedrock", ID: "global.anthropic.claude-opus-4-8", DisplayName: "Claude Opus 4.8 (Global)", ContextWindow: 1000000, MaxOutput: 128000, Reasoning: true, PriceInput: 5, PriceOutput: 25, PriceCacheRead: 0.5, PriceCacheWrite: 6.25, BaseURL: "https://bedrock-runtime.us-east-1.amazonaws.com"}, | 56 | {Provider: "amazon-bedrock", ID: "global.anthropic.claude-opus-4-8", DisplayName: "Claude Opus 4.8 (Global)", ContextWindow: 1000000, MaxOutput: 128000, Reasoning: true, PriceInput: 5, PriceOutput: 25, PriceCacheRead: 0.5, PriceCacheWrite: 6.25, BaseURL: "https://bedrock-runtime.us-east-1.amazonaws.com"}, | ||
| 57 | + | {Provider: "amazon-bedrock", ID: "global.anthropic.claude-opus-5", DisplayName: "Claude Opus 5 (Global)", ContextWindow: 1000000, MaxOutput: 128000, Reasoning: true, AdaptiveThinking: true, PriceInput: 5, PriceOutput: 25, PriceCacheRead: 0.5, PriceCacheWrite: 6.25, BaseURL: "https://bedrock-runtime.us-east-1.amazonaws.com"}, | |||
| 57 | {Provider: "amazon-bedrock", ID: "global.anthropic.claude-sonnet-4-5-20250929-v1:0", DisplayName: "Claude Sonnet 4.5 (Global)", ContextWindow: 200000, MaxOutput: 64000, Reasoning: true, PriceInput: 3, PriceOutput: 15, PriceCacheRead: 0.3, PriceCacheWrite: 3.75, BaseURL: "https://bedrock-runtime.us-east-1.amazonaws.com"}, | 58 | {Provider: "amazon-bedrock", ID: "global.anthropic.claude-sonnet-4-5-20250929-v1:0", DisplayName: "Claude Sonnet 4.5 (Global)", ContextWindow: 200000, MaxOutput: 64000, Reasoning: true, PriceInput: 3, PriceOutput: 15, PriceCacheRead: 0.3, PriceCacheWrite: 3.75, BaseURL: "https://bedrock-runtime.us-east-1.amazonaws.com"}, | ||
| 58 | {Provider: "amazon-bedrock", ID: "global.anthropic.claude-sonnet-4-6", DisplayName: "Claude Sonnet 4.6 (Global)", ContextWindow: 1000000, MaxOutput: 64000, Reasoning: true, PriceInput: 3, PriceOutput: 15, PriceCacheRead: 0.3, PriceCacheWrite: 3.75, BaseURL: "https://bedrock-runtime.us-east-1.amazonaws.com"}, | 59 | {Provider: "amazon-bedrock", ID: "global.anthropic.claude-sonnet-4-6", DisplayName: "Claude Sonnet 4.6 (Global)", ContextWindow: 1000000, MaxOutput: 64000, Reasoning: true, PriceInput: 3, PriceOutput: 15, PriceCacheRead: 0.3, PriceCacheWrite: 3.75, BaseURL: "https://bedrock-runtime.us-east-1.amazonaws.com"}, | ||
| 59 | {Provider: "amazon-bedrock", ID: "global.anthropic.claude-sonnet-5", DisplayName: "Claude Sonnet 5 (Global)", ContextWindow: 1000000, MaxOutput: 128000, Reasoning: true, AdaptiveThinking: true, PriceInput: 2, PriceOutput: 10, PriceCacheRead: 0.2, PriceCacheWrite: 2.5, BaseURL: "https://bedrock-runtime.us-east-1.amazonaws.com"}, | 60 | {Provider: "amazon-bedrock", ID: "global.anthropic.claude-sonnet-5", DisplayName: "Claude Sonnet 5 (Global)", ContextWindow: 1000000, MaxOutput: 128000, Reasoning: true, AdaptiveThinking: true, PriceInput: 2, PriceOutput: 10, PriceCacheRead: 0.2, PriceCacheWrite: 2.5, BaseURL: "https://bedrock-runtime.us-east-1.amazonaws.com"}, | ||
M packages/provider/models.go +5 -0
| @@ -266,6 +266,11 @@ var Catalog = []Model{ | |||
| 266 | 266 | PriceInput: 5, PriceOutput: 25, PriceCacheRead: 0.5, PriceCacheWrite: 6.25, | |
| 267 | 267 | Speculative: true, | |
| 268 | 268 | }, | |
| 269 | + | { | |
| 270 | + | Provider: "anthropic", ID: "claude-opus-5", DisplayName: "Claude Opus 5", | |
| 271 | + | ContextWindow: 1000000, MaxOutput: 128000, Reasoning: true, AdaptiveThinking: true, | |
| 272 | + | PriceInput: 5, PriceOutput: 25, PriceCacheRead: 0.5, PriceCacheWrite: 6.25, | |
| 273 | + | }, | |
| 269 | 274 | { | |
| 270 | 275 | Provider: "anthropic", ID: "claude-sonnet-4-6", DisplayName: "Claude Sonnet 4.6", | |
| 271 | 276 | ContextWindow: 1000000, MaxOutput: 64000, Reasoning: true, | |
| @@ -266,6 +266,11 @@ var Catalog = []Model{ | |||||
| 266 | PriceInput: 5, PriceOutput: 25, PriceCacheRead: 0.5, PriceCacheWrite: 6.25, | 266 | PriceInput: 5, PriceOutput: 25, PriceCacheRead: 0.5, PriceCacheWrite: 6.25, | ||
| 267 | Speculative: true, | 267 | Speculative: true, | ||
| 268 | }, | 268 | }, | ||
| 269 | + | { | |||
| 270 | + | Provider: "anthropic", ID: "claude-opus-5", DisplayName: "Claude Opus 5", | |||
| 271 | + | ContextWindow: 1000000, MaxOutput: 128000, Reasoning: true, AdaptiveThinking: true, | |||
| 272 | + | PriceInput: 5, PriceOutput: 25, PriceCacheRead: 0.5, PriceCacheWrite: 6.25, | |||
| 273 | + | }, | |||
| 269 | { | 274 | { | ||
| 270 | Provider: "anthropic", ID: "claude-sonnet-4-6", DisplayName: "Claude Sonnet 4.6", | 275 | Provider: "anthropic", ID: "claude-sonnet-4-6", DisplayName: "Claude Sonnet 4.6", | ||
| 271 | ContextWindow: 1000000, MaxOutput: 64000, Reasoning: true, | 276 | ContextWindow: 1000000, MaxOutput: 64000, Reasoning: true, | ||
M packages/provider/provider_test.go +17 -4
| @@ -94,9 +94,9 @@ func TestAnthropicAdaptiveThinking(t *testing.T) { | |||
| 94 | 94 | c := NewAnthropic("x", "").(*anthropicClient) | |
| 95 | 95 | temp := float32(0.7) | |
| 96 | 96 | ||
| 97 | - | // Opus 4.8 -> adaptive thinking, effort set, no budget, no temperature. | |
| 97 | + | // Opus 5 -> adaptive thinking, effort set, no budget, no temperature. | |
| 98 | 98 | wire, err := c.buildRequest(Request{ | |
| 99 | - | Model: "claude-opus-4-8", | |
| 99 | + | Model: "claude-opus-5", | |
| 100 | 100 | Reasoning: "high", | |
| 101 | 101 | Temperature: &temp, | |
| 102 | 102 | Messages: []Message{{Role: RoleUser, Content: []Content{TextBlock{Text: "hi"}}}}, | |
| @@ -119,7 +119,7 @@ func TestAnthropicAdaptiveThinking(t *testing.T) { | |||
| 119 | 119 | ||
| 120 | 120 | // maximum -> xhigh effort. | |
| 121 | 121 | wire, err = c.buildRequest(Request{ | |
| 122 | - | Model: "claude-opus-4-8", | |
| 122 | + | Model: "claude-opus-5", | |
| 123 | 123 | Reasoning: "maximum", | |
| 124 | 124 | Messages: []Message{{Role: RoleUser, Content: []Content{TextBlock{Text: "hi"}}}}, | |
| 125 | 125 | }) | |
| @@ -132,7 +132,7 @@ func TestAnthropicAdaptiveThinking(t *testing.T) { | |||
| 132 | 132 | ||
| 133 | 133 | // max is a separate native tier above xhigh on adaptive models. | |
| 134 | 134 | wire, err = c.buildRequest(Request{ | |
| 135 | - | Model: "claude-opus-4-8", | |
| 135 | + | Model: "claude-opus-5", | |
| 136 | 136 | Reasoning: "max", | |
| 137 | 137 | Messages: []Message{{Role: RoleUser, Content: []Content{TextBlock{Text: "hi"}}}}, | |
| 138 | 138 | }) | |
| @@ -264,6 +264,19 @@ func TestAnthropicStreamHappyPath(t *testing.T) { | |||
| 264 | 264 | } | |
| 265 | 265 | } | |
| 266 | 266 | ||
| 267 | + | func TestClaudeOpus5Catalog(t *testing.T) { | |
| 268 | + | m, err := FindModel("anthropic", "claude-opus-5") | |
| 269 | + | if err != nil { | |
| 270 | + | t.Fatal(err) | |
| 271 | + | } | |
| 272 | + | if m.DisplayName != "Claude Opus 5" || m.ContextWindow != 1000000 || m.MaxOutput != 128000 || !m.Reasoning || !m.AdaptiveThinking || m.Speculative { | |
| 273 | + | t.Fatalf("unexpected Opus 5 model: %+v", m) | |
| 274 | + | } | |
| 275 | + | if m.PriceInput != 5 || m.PriceOutput != 25 || m.PriceCacheRead != 0.5 || m.PriceCacheWrite != 6.25 { | |
| 276 | + | t.Fatalf("unexpected Opus 5 pricing: %+v", m) | |
| 277 | + | } | |
| 278 | + | } | |
| 279 | + | ||
| 267 | 280 | func TestClaudeSonnet5Catalog(t *testing.T) { | |
| 268 | 281 | m, err := FindModel("anthropic", "claude-sonnet-5") | |
| 269 | 282 | if err != nil { | |
| @@ -94,9 +94,9 @@ func TestAnthropicAdaptiveThinking(t *testing.T) { | |||||
| 94 | c := NewAnthropic("x", "").(*anthropicClient) | 94 | c := NewAnthropic("x", "").(*anthropicClient) | ||
| 95 | temp := float32(0.7) | 95 | temp := float32(0.7) | ||
| 96 | 96 | ||||
| 97 | - | // Opus 4.8 -> adaptive thinking, effort set, no budget, no temperature. | 97 | + | // Opus 5 -> adaptive thinking, effort set, no budget, no temperature. |
| 98 | wire, err := c.buildRequest(Request{ | 98 | wire, err := c.buildRequest(Request{ | ||
| 99 | - | Model: "claude-opus-4-8", | 99 | + | Model: "claude-opus-5", |
| 100 | Reasoning: "high", | 100 | Reasoning: "high", | ||
| 101 | Temperature: &temp, | 101 | Temperature: &temp, | ||
| 102 | Messages: []Message{{Role: RoleUser, Content: []Content{TextBlock{Text: "hi"}}}}, | 102 | Messages: []Message{{Role: RoleUser, Content: []Content{TextBlock{Text: "hi"}}}}, | ||
| @@ -119,7 +119,7 @@ func TestAnthropicAdaptiveThinking(t *testing.T) { | |||||
| 119 | 119 | ||||
| 120 | // maximum -> xhigh effort. | 120 | // maximum -> xhigh effort. | ||
| 121 | wire, err = c.buildRequest(Request{ | 121 | wire, err = c.buildRequest(Request{ | ||
| 122 | - | Model: "claude-opus-4-8", | 122 | + | Model: "claude-opus-5", |
| 123 | Reasoning: "maximum", | 123 | Reasoning: "maximum", | ||
| 124 | Messages: []Message{{Role: RoleUser, Content: []Content{TextBlock{Text: "hi"}}}}, | 124 | Messages: []Message{{Role: RoleUser, Content: []Content{TextBlock{Text: "hi"}}}}, | ||
| 125 | }) | 125 | }) | ||
| @@ -132,7 +132,7 @@ func TestAnthropicAdaptiveThinking(t *testing.T) { | |||||
| 132 | 132 | ||||
| 133 | // max is a separate native tier above xhigh on adaptive models. | 133 | // max is a separate native tier above xhigh on adaptive models. | ||
| 134 | wire, err = c.buildRequest(Request{ | 134 | wire, err = c.buildRequest(Request{ | ||
| 135 | - | Model: "claude-opus-4-8", | 135 | + | Model: "claude-opus-5", |
| 136 | Reasoning: "max", | 136 | Reasoning: "max", | ||
| 137 | Messages: []Message{{Role: RoleUser, Content: []Content{TextBlock{Text: "hi"}}}}, | 137 | Messages: []Message{{Role: RoleUser, Content: []Content{TextBlock{Text: "hi"}}}}, | ||
| 138 | }) | 138 | }) | ||
| @@ -264,6 +264,19 @@ func TestAnthropicStreamHappyPath(t *testing.T) { | |||||
| 264 | } | 264 | } | ||
| 265 | } | 265 | } | ||
| 266 | 266 | ||||
| 267 | + | func TestClaudeOpus5Catalog(t *testing.T) { | |||
| 268 | + | m, err := FindModel("anthropic", "claude-opus-5") | |||
| 269 | + | if err != nil { | |||
| 270 | + | t.Fatal(err) | |||
| 271 | + | } | |||
| 272 | + | if m.DisplayName != "Claude Opus 5" || m.ContextWindow != 1000000 || m.MaxOutput != 128000 || !m.Reasoning || !m.AdaptiveThinking || m.Speculative { | |||
| 273 | + | t.Fatalf("unexpected Opus 5 model: %+v", m) | |||
| 274 | + | } | |||
| 275 | + | if m.PriceInput != 5 || m.PriceOutput != 25 || m.PriceCacheRead != 0.5 || m.PriceCacheWrite != 6.25 { | |||
| 276 | + | t.Fatalf("unexpected Opus 5 pricing: %+v", m) | |||
| 277 | + | } | |||
| 278 | + | } | |||
| 279 | + | ||||
| 267 | func TestClaudeSonnet5Catalog(t *testing.T) { | 280 | func TestClaudeSonnet5Catalog(t *testing.T) { | ||
| 268 | m, err := FindModel("anthropic", "claude-sonnet-5") | 281 | m, err := FindModel("anthropic", "claude-sonnet-5") | ||
| 269 | if err != nil { | 282 | if err != nil { | ||