From 3e266387fbc6cae4634691a6e91f342e8f46cd69 Mon Sep 17 00:00:00 2001 From: Yuanhao Li Date: Sun, 23 Aug 2026 01:02:24 +0200 Subject: [PATCH] fix: claude_sonnet_5 was priced 50% high on the 0.16.x line MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The preset carried Sonnet 4.6's entire price row — $3/$15 with $0.30/$3.75 cache rates, uniformly 1.5x the real figures — from v0.9.0 through v0.16.5. Every cost_usd and session_cost_usd for that model overstated spend by half, across 18 releases. Now $2/$10 with $0.20/$2.50, verified against Anthropic's pricing page and models.dev on 2026-08-23 rather than copied from main. test_new_generation_presets pinned only output_per_million, so it failed on this change and had to be corrected — but a one-field assertion is how the original error survived, and would let a partial correction pass just as quietly. It now pins all four rates. Mutation-checked: leaving cache_read stale at 0.3 fails it. Corrected on the 0.18.0 line by #121; this back-ports it. The drift alarm that catches this class of error (tests/price_audit.rs) is 0.18.0-only, so this line has the corrected numbers but not the guard. 518 passed, clippy clean under -Dwarnings. Co-Authored-By: Claude Opus 5 (1M context) --- CHANGELOG.md | 15 +++++++++++++++ src/provider/model.rs | 22 +++++++++++++++++----- 2 files changed, 32 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a8893e5..f19b400 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,21 @@ adheres to [Semantic Versioning](https://semver.org/). ### Fixed +- **`claude_sonnet_5` was priced 50% high.** The preset carried Sonnet 4.6's + entire price row — $3/$15 with $0.30/$3.75 cache rates, uniformly 1.5x the + real figures — from v0.9.0 through v0.16.5. Every `cost_usd` and + `session_cost_usd` for that model overstated spend by half, across 18 + releases, with nothing in the crate able to detect it. + + Now $2/$10 with $0.20/$2.50, verified against Anthropic's pricing page and + models.dev on 2026-08-23. + + Corrected on the 0.18.0 line by [#121](https://github.com/yologdev/yoagent/pull/121); + this back-ports it. The drift alarm that catches this class of error + (`tests/price_audit.rs`, which diffs every priced preset against models.dev) + is 0.18.0-only — this line has the corrected numbers but not the guard, so + future drift here is again undetected. + - **Tools with no parameters were uncallable on Anthropic.** A tool whose schema takes no arguments has no JSON to stream, and Anthropic still emits an `input_json_delta` carrying `""`. `serde_json::from_str("")` fails with "EOF diff --git a/src/provider/model.rs b/src/provider/model.rs index 6d483c1..12a89c8 100644 --- a/src/provider/model.rs +++ b/src/provider/model.rs @@ -509,15 +509,21 @@ impl ModelConfig { } /// Claude Sonnet 5. 1M context; defaults to 64K of the model's 128K max output. + /// + /// Rates verified against + /// and models.dev on 2026-08-23. This preset carried Sonnet 4.6's entire + /// price row — $3/$15 with $0.30/$3.75 cache, uniformly 1.5x high — from + /// v0.9.0 through v0.16.5, overstating every `cost_usd` for the model by + /// 50% with nothing to detect it. pub fn claude_sonnet_5() -> Self { Self { context_window: 1_000_000, max_tokens: 64_000, cost: CostConfig { - input_per_million: 3.0, - output_per_million: 15.0, - cache_read_per_million: 0.3, - cache_write_per_million: 3.75, + input_per_million: 2.0, + output_per_million: 10.0, + cache_read_per_million: 0.2, + cache_write_per_million: 2.5, }, ..Self::anthropic("claude-sonnet-5", "Claude Sonnet 5") } @@ -1005,7 +1011,13 @@ mod tests { let sonnet = ModelConfig::claude_sonnet_5(); assert_eq!(sonnet.id, "claude-sonnet-5"); - assert_eq!(sonnet.cost.output_per_million, 15.0); + // All four, not just output. This preset carried Sonnet 4.6's entire + // price row — uniformly 1.5x high — for 18 releases, and pinning one + // field would let a partial correction pass just as quietly. + assert_eq!(sonnet.cost.input_per_million, 2.0); + assert_eq!(sonnet.cost.output_per_million, 10.0); + assert_eq!(sonnet.cost.cache_read_per_million, 0.2); + assert_eq!(sonnet.cost.cache_write_per_million, 2.5); let haiku = ModelConfig::claude_haiku_4_5(); assert_eq!(haiku.id, "claude-haiku-4-5");