anthropics / anthropics/claude-agent-sdk-python
claude-sonnet-5 overreporting cost
- 主要語言
- Python
- 星號
- 8.1k
- 分支
- 1.3k
- 平均合併
- 2 天 31 分鐘
- 30 天內合併 PR
- 1
描述
# `total_cost_usd` reports Opus rates for `claude-sonnet-5`
## Summary
`ResultMessage.total_cost_usd` prices `claude-sonnet-5` at **$5 / $25 per Mtok** (Opus 5's published
rate) instead of Sonnet 5's published **$3 / $15**. `claude-sonnet-4-6` in the same test run prices
correctly, so this looks specific to the `claude-sonnet-5` model ID rather than to the Sonnet family.
The response identifies itself as `claude-sonnet-5`, so this appears to be the reported cost figure,
not the model being served. I have no way to check from a subscription whether plan accounting uses
the same figure — that part is a question, not a claim.
## Environment
- Claude Code 2.1.195
- claude-agent-sdk 0.2.108
- Python 3.12.9, Windows 11 (10.0.26200)
- Max subscription auth (no `ANTHROPIC_API_KEY`)
## Reproduction
```python
import asyncio, claude_agent_sdk as sdk
async def one(model_id):
opts = sdk.ClaudeAgentOptions(model=model_id, max_turns=1, allowed_tools=[],
system_prompt="Be terse.", setting_sources=[])
cost, u = None, {}
async for msg in sdk.query(prompt="Reply with exactly: OK", options=opts):
if isinstance(msg, sdk.ResultMessage):
cost, u = msg.total_cost_usd, (msg.usage or {})
i, o = u.get("input_tokens", 0), u.get("output_tokens", 0)
cr, cw = u.get("cache_read_input_tokens", 0), u.get("cache_creation_input_tokens", 0)
den = i + 5 * o + 0.1 * cr + 2.0 * cw # published 1:5 in:out, 0.1x read, 2.0x write
print(f"{model_id:20s} in={i} out={o} cr={cr} cw={cw} "
f"cost={cost} -> implied ${cost * 1e6 / den:.2f}/Mtok input")
async def main():
for m in ("claude-haiku-4-5", "claude-sonnet-4-6", "claude-sonnet-5", "claude-opus-5"):
await one(m)
asyncio.run(main())
```
## Observed
| requested | served (`msg.model`) | implied input $/Mtok | published |
|---|---|---|---|
| `claude-haiku-4-5` | `claude-haiku-4-5-20251001` | $1.04 | $1.00 ✓ |
| `claude-sonnet-4-6` | `claude-sonnet-4-6` | $3.01 | $3.00 ✓ |
| `claude-sonnet-5` | `claude-sonnet-5` | **$5.08** | $3.00 ✗ |
| `claude-opus-5` | `claude-opus-5` | $5.09 | $5.00 ✓ |
The cleanest evidence is a **zero-cache** `claude-sonnet-5` call, where no cache multiplier
assumptions enter the arithmetic at all:
```
in=682 out=6 cache_creation=0 cache_read=0 total_cost_usd=0.003560
682 x $5/M + 6 x $25/M = $0.003560 <- exact match, Opus rates
682 x $3/M + 6 x $15/M = $0.002136 <- Sonnet 5 published
682 x $2/M + 6 x $10/M = $0.001424 <- Sonnet 5 introductory
```
Reproduced across three independent calls with different token mixes: implied $5.08, $5.085, $5.00.
## Expected
`total_cost_usd` for `claude-sonnet-5` computed at Sonnet 5 rates ($3/$15, or $2/$10 during the
introductory period).
## Ruled out
- **Not the wrong model being served.** `msg.model` reports `claude-sonnet-5`, and `claude-sonnet-4-6`
in the same run prices correctly — so the model field does vary meaningfully.
- **Not "undiscounted Sonnet."** Sonnet 5 without the introductory discount is $3/$15, which predicts
$0.002136 on the call above. The measured figure is $0.003560.
- **Not a cache-multiplier assumption.** The decisive call has zero cache tokens in both directions.
## Not established
Whether an unrecognised model ID falls back to Opus rates — I could not construct the control,
because the API rejects IDs it cannot serve, so there is no "known-unknown" model to probe with.
## Impact
Any tool that trusts `total_cost_usd` over-books Sonnet 5 usage by ~2.5x. In our case that inflates
per-window utilisation figures and erases the measured cost gap between Sonnet and Opus, which is
the gap a cost-aware model-routing policy exists to exploit — so the reported number actively
inverts the routing decision it is used to make.
貢獻指南
這個儲存庫沒有索引到貢獻指南
研究方向
Start by tracing ResultMessage.total_cost_usd through the claude_agent_sdk implementation and inspect how model IDs are mapped to pricing. Run the zero-cache reproduction for claude-sonnet-5, then compare the reported value with the published $3/$15 Sonnet rates. Done means the reported cost uses the expected Sonnet pricing rather than Opus rates.
由索引模型根據 Issue 內容生成。
評估
- 技術堆疊
- python
- 領域
- api
- Issue 類型
- 缺陷
- 難度
- 3/5
- 預估耗時
- 1-2 天
- 活躍度
- 冷清
- 描述清晰度
- 基本清楚
- 新手友好度
- 64/100