From e57f6158c90ff6de21bdea255fbc9b96023c9c3a Mon Sep 17 00:00:00 2001 From: mlsmaycon Date: Tue, 11 Aug 2026 02:44:02 +0000 Subject: [PATCH] [management] Add the Claude 5 lineup to the Agent Network catalog Claude Code resolves to Opus 5 and Sonnet 5 by default, and neither was selectable on a provider record. An operator building a record from the catalog could not authorise the client's own default, so llm_router denied those requests as model_not_routable. Opus 5 carried a supplemental pricing row that priced gateway traffic but never reached the dashboard; Sonnet 5 was absent everywhere, so a request that did route through a catch-all gateway recorded zero cost and under-counted every budget it should have charged. Add both to the Anthropic, Bedrock and Vertex lineups at the published rates, and drop the supplemental rows now that the catalog carries them. --- .../modules/agentnetwork/catalog/catalog.go | 6 ++++ .../agentnetwork/catalog/catalog_test.go | 36 +++++++++++++++++++ .../modules/agentnetwork/pricing/defaults.go | 6 ---- .../pricing/defaults_llm_pricing.example.yaml | 10 ++++++ .../agentnetwork/pricing/defaults_test.go | 8 +++-- 5 files changed, 57 insertions(+), 9 deletions(-) create mode 100644 management/internals/modules/agentnetwork/catalog/catalog_test.go diff --git a/management/internals/modules/agentnetwork/catalog/catalog.go b/management/internals/modules/agentnetwork/catalog/catalog.go index 2c4efd0b4..c534f9a85 100644 --- a/management/internals/modules/agentnetwork/catalog/catalog.go +++ b/management/internals/modules/agentnetwork/catalog/catalog.go @@ -296,6 +296,8 @@ var providers = []Provider{ // account to be on >= 30-day data retention or all requests // 400. Models: []Model{ + {ID: "claude-opus-5", Label: "Claude Opus 5", InputPer1k: 0.005, OutputPer1k: 0.025, CacheReadPer1k: 0.0005, CacheCreationPer1k: 0.00625, ContextWindow: 1000000}, + {ID: "claude-sonnet-5", Label: "Claude Sonnet 5", InputPer1k: 0.003, OutputPer1k: 0.015, CacheReadPer1k: 0.0003, CacheCreationPer1k: 0.00375, ContextWindow: 1000000}, {ID: "claude-fable-5", Label: "Claude Fable 5", InputPer1k: 0.010, OutputPer1k: 0.050, CacheReadPer1k: 0.001, CacheCreationPer1k: 0.0125, ContextWindow: 1000000}, {ID: "claude-opus-4-8", Label: "Claude Opus 4.8", InputPer1k: 0.005, OutputPer1k: 0.025, CacheReadPer1k: 0.0005, CacheCreationPer1k: 0.00625, ContextWindow: 1000000}, {ID: "claude-opus-4-7", Label: "Claude Opus 4.7", InputPer1k: 0.005, OutputPer1k: 0.025, CacheReadPer1k: 0.0005, CacheCreationPer1k: 0.00625, ContextWindow: 1000000}, @@ -355,6 +357,8 @@ var providers = []Provider{ // Llama 3.3 70B entry kept unchanged — LiteLLM tracks only // per-region Llama 3 entries; standalone 3.3 not yet listed. Models: []Model{ + {ID: "anthropic.claude-opus-5", Label: "Claude Opus 5 (Bedrock)", InputPer1k: 0.005, OutputPer1k: 0.025, CacheReadPer1k: 0.0005, CacheCreationPer1k: 0.00625, ContextWindow: 1000000}, + {ID: "anthropic.claude-sonnet-5", Label: "Claude Sonnet 5 (Bedrock)", InputPer1k: 0.003, OutputPer1k: 0.015, CacheReadPer1k: 0.0003, CacheCreationPer1k: 0.00375, ContextWindow: 1000000}, {ID: "anthropic.claude-opus-4-8", Label: "Claude Opus 4.8 (Bedrock)", InputPer1k: 0.005, OutputPer1k: 0.025, CacheReadPer1k: 0.0005, CacheCreationPer1k: 0.00625, ContextWindow: 1000000}, {ID: "anthropic.claude-opus-4-7", Label: "Claude Opus 4.7 (Bedrock)", InputPer1k: 0.005, OutputPer1k: 0.025, CacheReadPer1k: 0.0005, CacheCreationPer1k: 0.00625, ContextWindow: 1000000}, {ID: "anthropic.claude-opus-4-6", Label: "Claude Opus 4.6 (Bedrock)", InputPer1k: 0.005, OutputPer1k: 0.025, CacheReadPer1k: 0.0005, CacheCreationPer1k: 0.00625, ContextWindow: 1000000}, @@ -406,6 +410,8 @@ var providers = []Provider{ // exists — the router denies unmeterable publishers rather than forward // them uncounted. Models: []Model{ + {ID: "claude-opus-5", Label: "Claude Opus 5 (Vertex)", InputPer1k: 0.005, OutputPer1k: 0.025, CacheReadPer1k: 0.0005, CacheCreationPer1k: 0.00625, ContextWindow: 1000000}, + {ID: "claude-sonnet-5", Label: "Claude Sonnet 5 (Vertex)", InputPer1k: 0.003, OutputPer1k: 0.015, CacheReadPer1k: 0.0003, CacheCreationPer1k: 0.00375, ContextWindow: 1000000}, {ID: "claude-fable-5", Label: "Claude Fable 5 (Vertex)", InputPer1k: 0.010, OutputPer1k: 0.050, CacheReadPer1k: 0.001, CacheCreationPer1k: 0.0125, ContextWindow: 1000000}, {ID: "claude-opus-4-8", Label: "Claude Opus 4.8 (Vertex)", InputPer1k: 0.005, OutputPer1k: 0.025, CacheReadPer1k: 0.0005, CacheCreationPer1k: 0.00625, ContextWindow: 1000000}, {ID: "claude-opus-4-7", Label: "Claude Opus 4.7 (Vertex)", InputPer1k: 0.005, OutputPer1k: 0.025, CacheReadPer1k: 0.0005, CacheCreationPer1k: 0.00625, ContextWindow: 1000000}, diff --git a/management/internals/modules/agentnetwork/catalog/catalog_test.go b/management/internals/modules/agentnetwork/catalog/catalog_test.go new file mode 100644 index 000000000..e4e887e6f --- /dev/null +++ b/management/internals/modules/agentnetwork/catalog/catalog_test.go @@ -0,0 +1,36 @@ +package catalog + +import ( + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +// TestClaudeLineupSelectable pins the models Claude Code resolves to by +// default. A model absent from the lineup can't be ticked on a provider +// record, so llm_router denies it as not-routable and the operator has no +// way to authorise the client's own default. +func TestClaudeLineupSelectable(t *testing.T) { + for providerID, wanted := range map[string][]string{ + "anthropic_api": {"claude-opus-5", "claude-sonnet-5", "claude-haiku-4-5"}, + "bedrock_api": {"anthropic.claude-opus-5", "anthropic.claude-sonnet-5", "anthropic.claude-haiku-4-5"}, + "vertex_ai_api": {"claude-opus-5", "claude-sonnet-5", "claude-haiku-4-5"}, + } { + provider, ok := Lookup(providerID) + require.True(t, ok, "catalog must define %s", providerID) + + selectable := make(map[string]Model, len(provider.Models)) + for _, m := range provider.Models { + selectable[m.ID] = m + } + for _, id := range wanted { + model, found := selectable[id] + require.True(t, found, "%s must offer %s", providerID, id) + assert.NotEmpty(t, model.Label, "%s/%s needs a label for the picker", providerID, id) + assert.Positive(t, model.InputPer1k, "%s/%s needs an input rate", providerID, id) + assert.Positive(t, model.OutputPer1k, "%s/%s needs an output rate", providerID, id) + assert.Positive(t, model.ContextWindow, "%s/%s needs a context window", providerID, id) + } + } +} diff --git a/management/internals/modules/agentnetwork/pricing/defaults.go b/management/internals/modules/agentnetwork/pricing/defaults.go index c690313bc..315cfe208 100644 --- a/management/internals/modules/agentnetwork/pricing/defaults.go +++ b/management/internals/modules/agentnetwork/pricing/defaults.go @@ -47,17 +47,11 @@ var supplementalDefaults = map[string]map[string]Entry{ "gpt-5-nano": {InputPer1k: 0.00005, OutputPer1k: 0.0004, CachedInputPer1k: 0.000005}, }, "anthropic": { - // claude-opus-5 is not yet in the catalog lineup but gateway / - // grandfathered traffic uses it; priced so it isn't skipped. - "claude-opus-5": {InputPer1k: 0.005, OutputPer1k: 0.025, CacheReadPer1k: 0.0005, CacheCreationPer1k: 0.00625}, // "kimi-k3[1m]" is the 1M-context alias some Claude Code guides // configure against Moonshot's Anthropic-compatible endpoint; // priced identically to kimi-k3 so those requests aren't skipped. "kimi-k3[1m]": {InputPer1k: 0.003, OutputPer1k: 0.015, CacheReadPer1k: 0.0003}, }, - "bedrock": { - "anthropic.claude-opus-5": {InputPer1k: 0.005, OutputPer1k: 0.025, CacheReadPer1k: 0.0005, CacheCreationPer1k: 0.00625}, - }, } var ( diff --git a/management/internals/modules/agentnetwork/pricing/defaults_llm_pricing.example.yaml b/management/internals/modules/agentnetwork/pricing/defaults_llm_pricing.example.yaml index bb1cb09a8..78830ae3c 100644 --- a/management/internals/modules/agentnetwork/pricing/defaults_llm_pricing.example.yaml +++ b/management/internals/modules/agentnetwork/pricing/defaults_llm_pricing.example.yaml @@ -82,6 +82,11 @@ anthropic: output_per_1k: 0.015 cache_read_per_1k: 0.0003 cache_creation_per_1k: 0.00375 + claude-sonnet-5: + input_per_1k: 0.003 + output_per_1k: 0.015 + cache_read_per_1k: 0.0003 + cache_creation_per_1k: 0.00375 kimi-k3: input_per_1k: 0.003 output_per_1k: 0.015 @@ -145,6 +150,11 @@ bedrock: output_per_1k: 0.015 cache_read_per_1k: 0.0003 cache_creation_per_1k: 0.00375 + anthropic.claude-sonnet-5: + input_per_1k: 0.003 + output_per_1k: 0.015 + cache_read_per_1k: 0.0003 + cache_creation_per_1k: 0.00375 meta.llama3-3-70b-instruct: input_per_1k: 0.00072 output_per_1k: 0.00072 diff --git a/management/internals/modules/agentnetwork/pricing/defaults_test.go b/management/internals/modules/agentnetwork/pricing/defaults_test.go index 99c965687..04b6de550 100644 --- a/management/internals/modules/agentnetwork/pricing/defaults_test.go +++ b/management/internals/modules/agentnetwork/pricing/defaults_test.go @@ -116,11 +116,13 @@ func TestDefaultTable_PinnedRates(t *testing.T) { assert.InDelta(t, 0.010, fable.InputPer1k, 1e-9, "claude-fable-5 input") assert.InDelta(t, 0.0125, fable.CacheCreationPer1k, 1e-9, "claude-fable-5 cache creation") - // Supplementals present on their surfaces. + // Every id below must stay priced whichever source provides it: the + // catalog lineup for the current Claude 5 family, supplementalDefaults + // for the ids the dashboard deliberately doesn't offer. for surface, ids := range map[string][]string{ "openai": {"gpt-5", "gpt-5-mini", "gpt-5-nano"}, - "anthropic": {"claude-opus-5", "kimi-k3[1m]", "kimi-k3"}, - "bedrock": {"anthropic.claude-opus-5"}, + "anthropic": {"claude-opus-5", "claude-sonnet-5", "kimi-k3[1m]", "kimi-k3"}, + "bedrock": {"anthropic.claude-opus-5", "anthropic.claude-sonnet-5"}, } { for _, id := range ids { _, ok := table[surface][id]