anthropics / anthropics/claude-agent-sdk-python

claude-sonnet-5 overreporting cost

Đang mở
#1,182 0 bình luận 0 reaction 0 người được giao Xem trên GitHub
Ngôn ngữ chính
Python
Star
8.1k
Fork
1.3k
Merge trung bình
2 ngày 31 phút
Pull request đã merge (30 ngày)
1

Mô tả

# `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.

Hướng dẫn đóng góp

Chưa lập chỉ mục được hướng dẫn đóng góp cho kho mã nguồn này

Hướng nghiên cứu

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.

Do mô hình lập chỉ mục viết ra từ nội dung của issue.

Đánh giá

Công nghệ
python
Lĩnh vực
api
Loại issue
Lỗi
Độ khó
3/5
Thời gian dự kiến
1-2 ngày
Mức độ hoạt động
Ít trao đổi
Độ rõ ràng
Khá rõ ràng
Mức phù hợp với người mới
64/100

Nhận issue mới trong hộp thư của bạn

Bản tóm tắt ngắn những issue GitHub phù hợp với người mới.