discover_bedrock_metadata_test.go RawHistoryFolder
1package provider
2
3import "testing"
4
5// discoveredWireMaxTokens runs a discovered ID through the same path as
6// production (discovery metadata -> MergeCatalog -> FindModel -> the
7// agent's MaxTokens -> Bedrock buildRequest) and returns the merged model
8// plus the maxTokens that would be sent on the wire.
9func discoveredWireMaxTokens(t *testing.T, id string) (Model, int) {
10 t.Helper()
11 withLiveModels(t, []Model{bedrockDiscoveredModel(id, "us-east-1")})
12 m, err := FindModel("amazon-bedrock", id)
13 if err != nil {
14 t.Fatalf("FindModel(%q): %v", id, err)
15 }
16 c := &bedrockClient{region: "us-east-1"}
17 req, err := c.buildRequest(Request{Model: id, MaxTokens: m.MaxOutput})
18 if err != nil {
19 t.Fatalf("buildRequest(%q): %v", id, err)
20 }
21 return m, req.InferenceConfig.MaxTokens
22}
23
24// Discovered inference-profile IDs with no exact catalog entry used to
25// carry MaxOutput 0, so every request fell back to maxTokens 4096 and
26// long responses stopped with max_tokens.
27func TestBedrockDiscoveredProfileInheritsBaseMetadata(t *testing.T) {
28 base, err := FindModel("amazon-bedrock", "anthropic.claude-sonnet-4-6")
29 if err != nil {
30 t.Fatal(err)
31 }
32 for _, id := range []string{"apac.anthropic.claude-sonnet-4-6", "us-gov.anthropic.claude-sonnet-4-6"} {
33 m, wire := discoveredWireMaxTokens(t, id)
34 if m.MaxOutput != base.MaxOutput || wire != base.MaxOutput {
35 t.Errorf("%s: MaxOutput=%d wire=%d, want %d", id, m.MaxOutput, wire, base.MaxOutput)
36 }
37 if m.ContextWindow != base.ContextWindow || m.Reasoning != base.Reasoning {
38 t.Errorf("%s: metadata not inherited: %+v", id, m)
39 }
40 if m.PriceOutput != base.PriceOutput {
41 t.Errorf("%s: PriceOutput=%v, want %v", id, m.PriceOutput, base.PriceOutput)
42 }
43 }
44
45 m, _ := discoveredWireMaxTokens(t, "apac.anthropic.claude-sonnet-4-6")
46 if m.DisplayName != "Claude Sonnet 4.6 (APAC)" {
47 t.Errorf("DisplayName = %q", m.DisplayName)
48 }
49}
50
51// A base model catalogued only under geo-prefixed IDs still donates its
52// metadata, and the donor's region label is not carried over.
53func TestBedrockDiscoveredProfileUsesPrefixedCatalogVariant(t *testing.T) {
54 const id = "apac.anthropic.claude-opus-5"
55 m, wire := discoveredWireMaxTokens(t, id)
56 if wire != 128000 || !m.AdaptiveThinking || m.PriceOutput != 25 {
57 t.Errorf("%s: wire=%d adaptive=%v price=%v, want 128000 adaptive and $25", id, wire, m.AdaptiveThinking, m.PriceOutput)
58 }
59 if m.DisplayName != "Claude Opus 5 (APAC)" {
60 t.Errorf("DisplayName = %q", m.DisplayName)
61 }
62}
63
64// An uncatalogued Claude ID must not assume a 64k output limit. Opus 4
65// supports only 32k, and an unknown future model has no verified limit.
66func TestBedrockDiscoveredUnknownClaudeKeepsFallback(t *testing.T) {
67 for _, id := range []string{
68 "anthropic.claude-opus-4-20250514-v1:0",
69 "us.anthropic.claude-opus-4-20250514-v1:0",
70 "anthropic.claude-future-9",
71 } {
72 m, wire := discoveredWireMaxTokens(t, id)
73 if m.MaxOutput != 0 || m.ContextWindow != 0 || wire != 4096 {
74 t.Errorf("%s: MaxOutput=%d ctx=%d wire=%d, want 0/0/4096", id, m.MaxOutput, m.ContextWindow, wire)
75 }
76 }
77}
78
79// Legacy Claude and non-Anthropic models cap output well below 64k and
80// would reject a larger request, so they keep the client fallback.
81func TestBedrockDiscoveredLegacyAndOtherModelsKeepFallback(t *testing.T) {
82 for _, id := range []string{
83 "anthropic.claude-v2:1",
84 "anthropic.claude-instant-v1",
85 "us.anthropic.claude-3-5-sonnet-20241022-v2:0",
86 "anthropic.claude-3-haiku-20240307-v1:0",
87 "mistral.unknown-model-v1:0",
88 } {
89 m, wire := discoveredWireMaxTokens(t, id)
90 if m.MaxOutput != 0 || wire != 4096 {
91 t.Errorf("%s: MaxOutput=%d wire=%d, want 0/4096", id, m.MaxOutput, wire)
92 }
93 }
94}
95
96// Exact catalog IDs keep their own catalog metadata through MergeCatalog.
97func TestBedrockDiscoveredExactCatalogIDUnchanged(t *testing.T) {
98 const id = "au.anthropic.claude-sonnet-4-6"
99 want, err := FindModel("amazon-bedrock", id)
100 if err != nil {
101 t.Fatal(err)
102 }
103 m, wire := discoveredWireMaxTokens(t, id)
104 if m.MaxOutput != want.MaxOutput || wire != want.MaxOutput || m.DisplayName != want.DisplayName {
105 t.Errorf("got MaxOutput=%d wire=%d name=%q, want %d %q", m.MaxOutput, wire, m.DisplayName, want.MaxOutput, want.DisplayName)
106 }
107}