mirror of
https://github.com/rcourtman/Pulse.git
synced 2026-09-10 02:25:56 +00:00
Correct current Anthropic cost estimates
Price Anthropic Opus 4.5 through 5 at the current first-party rates while preserving legacy Opus pricing. Correct Haiku 4.5 in the same version-sensitive table and add regression coverage for current and legacy aliases.\n\nFixes #1789
This commit is contained in:
@@ -6627,6 +6627,16 @@ uses the provider resolved by Patrol readiness, even when the configured
|
||||
OpenRouter model is an unprefixed slash route such as
|
||||
`anthropic/claude-sonnet-5`; scorer replay persists and reuses that resolved
|
||||
provider so the same captured report prices deterministically.
|
||||
First-party Anthropic cost estimation is version-sensitive even within one
|
||||
model family. Standard API calls for Opus 4.5, 4.6, 4.7, 4.8, and 5 use the
|
||||
reviewed $5 input / $25 output per-million-token rates, while Opus 4, 4.1, and
|
||||
3 retain the legacy $15 / $75 rates. Haiku 4.5 similarly uses $1 / $5 rather
|
||||
than the Haiku 3 fallback. These version-specific rows must precede broad
|
||||
family fallbacks because pricing lookup is first-match prefix-based, and
|
||||
dated model aliases must resolve to the same row as their base version. The
|
||||
30-day runtime budget recalculates retained usage through this table, so a
|
||||
stale higher fallback must not disable Assistant or Patrol before estimated
|
||||
first-party spend reaches the configured limit.
|
||||
After a non-interactive Patrol finding lifecycle write succeeds, the structured
|
||||
tool result is authoritative and investigation remains closed. Watch first
|
||||
offers a bounded lifecycle-completion request containing only report and
|
||||
|
||||
@@ -51,8 +51,18 @@ var providerPrices = map[string][]modelPrice{
|
||||
flatPrice("gpt-4o*", 5.00, 15.00),
|
||||
},
|
||||
"anthropic": {
|
||||
// Anthropic first-party standard API prices, checked from
|
||||
// https://platform.claude.com/docs/en/about-claude/pricing on 2026-08-28.
|
||||
// Keep version-specific rows before the legacy family fallbacks because
|
||||
// lookupPrice returns the first matching prefix.
|
||||
flatPriceAsOf("claude-opus-5*", 5.00, 25.00, "2026-08-28"),
|
||||
flatPriceAsOf("claude-opus-4-8*", 5.00, 25.00, "2026-08-28"),
|
||||
flatPriceAsOf("claude-opus-4-7*", 5.00, 25.00, "2026-08-28"),
|
||||
flatPriceAsOf("claude-opus-4-6*", 5.00, 25.00, "2026-08-28"),
|
||||
flatPriceAsOf("claude-opus-4-5*", 5.00, 25.00, "2026-08-28"),
|
||||
flatPrice("claude-opus*", 15.00, 75.00),
|
||||
flatPrice("claude-sonnet*", 3.00, 15.00),
|
||||
flatPriceAsOf("claude-haiku-4-5*", 1.00, 5.00, "2026-08-28"),
|
||||
flatPrice("claude-haiku*", 0.25, 1.25),
|
||||
},
|
||||
"deepseek": {
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
package cost
|
||||
|
||||
import "testing"
|
||||
|
||||
func TestLookupPriceUsesCurrentAnthropicOpusPricing(t *testing.T) {
|
||||
tests := []struct {
|
||||
model string
|
||||
input float64
|
||||
output float64
|
||||
asOf string
|
||||
}{
|
||||
{model: "claude-opus-5", input: 5.00, output: 25.00, asOf: "2026-08-28"},
|
||||
{model: "claude-opus-5-20260801", input: 5.00, output: 25.00, asOf: "2026-08-28"},
|
||||
{model: "claude-opus-4-8", input: 5.00, output: 25.00, asOf: "2026-08-28"},
|
||||
{model: "claude-opus-4-7", input: 5.00, output: 25.00, asOf: "2026-08-28"},
|
||||
{model: "claude-opus-4-6", input: 5.00, output: 25.00, asOf: "2026-08-28"},
|
||||
{model: "claude-opus-4-5-20251101", input: 5.00, output: 25.00, asOf: "2026-08-28"},
|
||||
{model: "claude-opus-4-1-20250805", input: 15.00, output: 75.00, asOf: pricingAsOf},
|
||||
{model: "claude-opus-4-20250514", input: 15.00, output: 75.00, asOf: pricingAsOf},
|
||||
{model: "claude-opus-20240229", input: 15.00, output: 75.00, asOf: pricingAsOf},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
t.Run(test.model, func(t *testing.T) {
|
||||
price, ok := lookupPrice("anthropic", test.model, 50_000)
|
||||
if !ok {
|
||||
t.Fatal("expected Anthropic Opus pricing to resolve")
|
||||
}
|
||||
if price.InputUSDPerMTok != test.input || price.OutputUSDPerMTok != test.output {
|
||||
t.Fatalf("unexpected Anthropic Opus price: %+v", price)
|
||||
}
|
||||
if price.AsOf != test.asOf {
|
||||
t.Fatalf("Anthropic Opus pricing date = %q, want %q", price.AsOf, test.asOf)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestLookupPriceUsesCurrentAnthropicHaiku45Pricing(t *testing.T) {
|
||||
price, ok := lookupPrice("anthropic", "claude-haiku-4-5-20251001", 50_000)
|
||||
if !ok {
|
||||
t.Fatal("expected Anthropic Haiku 4.5 pricing to resolve")
|
||||
}
|
||||
if price.InputUSDPerMTok != 1.00 || price.OutputUSDPerMTok != 5.00 {
|
||||
t.Fatalf("unexpected Anthropic Haiku 4.5 price: %+v", price)
|
||||
}
|
||||
if price.AsOf != "2026-08-28" {
|
||||
t.Fatalf("Anthropic Haiku 4.5 pricing date = %q, want 2026-08-28", price.AsOf)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user