Skip to content

fix(pricing): update DeepSeek V4 card and add a peak/off-peak axis - #905

Merged
agentforce314 merged 2 commits into
mainfrom
fix/deepseek-peak-pricing-904
Aug 26, 2026
Merged

fix(pricing): update DeepSeek V4 card and add a peak/off-peak axis#905
agentforce314 merged 2 commits into
mainfrom
fix/deepseek-peak-pricing-904

Conversation

@agentforce314

Copy link
Copy Markdown
Owner

Fixes #904.

The problem

_TIER_DEEPSEEK_PRO and _TIER_DEEPSEEK_FLASH held DeepSeek's pre-2026-08-16 rate card — which, as @xyzs996 points out, was itself a time-limited promotion. The card that replaced it is published as peak / off-peak, and get_pricing had no axis that could reach it:

  • input_tokens is prompt size. DeepSeek's tier does not depend on prompt size.
  • service_tier comes from the response. DeepSeek declares none, so it always resolved to "standard" — and peak/off-peak is not something a caller or provider declares per request anyway.

The modelling around it was right and survives unchanged: a cache miss is still just input, so cache_creation still mirrors input.

The fix: the third axis, not a documented gap

The issue offered a contained alternative — put the off-peak card in the constants and say so in a comment. I went with the timestamp axis instead. Off-peak is 133 of every 168 hours, not 143 as the issue estimates (the windows are 3h + 4h on each of 5 weekdays = 35 peak hours), so the single-card version would still be wrong ~21% of the time, by exactly 2x, on the axis that is cheapest to just model.

get_pricing and compute_cost now take request_time (POSIX epoch seconds, None = now), and a new public is_deepseek_peak owns the schedule: 01:00–04:00 and 06:00–10:00 UTC, Monday through Friday, windows half-open so 03:59:59 is peak and 04:00:00 is not.

Defaulting to "now" is what keeps this from touching any call site: the live path prices a response the moment it arrives, so record_api_usage is already correct without a change. Callers holding a real timestamp should pass it — one now does (below).

Rates

USD per 1M tokens, checked 2026-08-25 against https://api-docs.deepseek.com/quick_start/pricing/. Peak is exactly 2x off-peak.

input / cache_creation output cache_read
deepseek-v4-flash off-peak 0.22 0.66 0.007
deepseek-v4-flash peak 0.44 1.32 0.014
deepseek-v4-pro off-peak 0.66 1.98 0.022
deepseek-v4-pro peak 1.32 3.96 0.044

cache_read drives the size of the error, as the issue says: at ~96% of agentic input tokens, 0.003625 against a real 0.022 understated a deepseek-v4-pro session 2.3x off-peak and 4.5x peak — $0.0237 per 1M tokens reported against a real $0.0536, or $0.1073 inside a peak window.

Worth noting for anyone touching these rows later: pro is exactly 3x flash on input and output, but not on cache_read (0.022 against 0.007 is 22/7). That near-ratio invites deriving one row from the other; there is a test pinning that it does not hold.

Also in here

  • CostTracker.get_cache_savings prices each event at event.timestamp instead of at display time. It runs when /cost is opened, arbitrarily later than the requests it sums, so at "now" it would restate an off-peak session's savings at peak rates the moment the clock crossed 01:00 UTC.
  • A # checked 2026-08-25 line on the DeepSeek block, per the issue's suggestion. It is the row most likely to move again, and a promo card rotting into silent under-reporting is the same failure the gpt-5.6-luna row above it was written about.
  • README correction. The DeepSeek headline advertised ~$0.0435 / 1M cache-hit input — derived from the same stale card, and never matching the pricing table's own 0.003625 either. Now $0.022 off-peak, with the peak window and its ~227x stated underneath.
  • deepseek-v4-flash-vision-exp is deliberately left unregistered. It shares the flash card on the vendor's page, but it has no row in models/configs.py and is unreachable through the provider (supports_vision=False), so pricing it would be a row nothing can exercise.

Aggregate-replay paths, stated rather than fixed

cost_restore.build_cost_block and eval/harbor/advisor_cost.py recompute from an already-aggregated model_usage block, so they have no per-request time to pass. They inherit exactly the inexactness they already carry for the context tier (gpt-5.6-luna doubling above 272K), and both are commented to that effect. A session spanning a peak boundary gets priced entirely on one side of it; fixing that needs per-request cost records, not a different default in compute_cost. In practice advisor_cost.py never reaches it for DeepSeek — that path prefers the billed figure wherever cost_usd > 0, which for a metered provider is always.

Tests

tests/test_deepseek_peak_pricing.py, 52 new tests in three groups:

  • The schedule — each peak and off-peak hour, both window boundaries to the second, the Mon/Fri fencepost (the one place a weekday() >= 5 test and a 1 <= weekday() <= 5 test differ), weekends inside the windows, UTC-not-local, and a 168-hour sweep pinning 35 peak hours so a window cannot silently widen.
  • The card — published rates as absolutes on both sides, cache_creation == input on both, peak exactly 2x. Absolutes rather than ratios on purpose: the values this PR replaces were internally consistent (right ratios, mirrored cache_creation) and still 3x low, so only an external number catches that class of bug.
  • The wiringcompute_cost doubling in a peak window on the issue's measured agent token mix, its $0.0536 / $0.1073 figures pinned as dollars, the vendor-prefix strip carrying the timestamp, get_cache_savings honouring event timestamps, and a scope gate that every other model returns an identical card at all 168 hours of the week.

The existing DeepSeek and status-bar pricing assertions now pin an explicit instant rather than depending on when CI happens to run.

Full suite: 10,402 passed, 17 skipped. The one failure locally is TestBuildAppSmoke::test_pip_install_editable, a known dirty-working-tree timeout unrelated to pricing.

🤖 Generated with Claude Code

agentforce314 and others added 2 commits August 25, 2026 23:07
`_TIER_DEEPSEEK_PRO` and `_TIER_DEEPSEEK_FLASH` held DeepSeek's
pre-2026-08-16 rate card, which was itself a time-limited promotion. The
card that replaced it is published as peak / off-peak, and `get_pricing`
had no axis that could reach it: `input_tokens` is prompt size, and
`service_tier` comes from the response (DeepSeek declares none, so it
always resolved to "standard"). Peak/off-peak is a function of the
wall-clock time the request was sent, not of anything in it.

So this adds the third axis rather than picking one side of the schedule
and documenting the gap. `get_pricing` and `compute_cost` take
`request_time` (POSIX epoch seconds, `None` = now), and a new public
`is_deepseek_peak` owns the schedule: 01:00-04:00 and 06:00-10:00 UTC,
Monday through Friday, windows half-open so 03:59:59 is peak and 04:00:00
is not. Defaulting to "now" is correct for the live path without touching
it — `cost_tracker.record_api_usage` prices a response the moment it
arrives.

Rates, USD per 1M tokens, checked 2026-08-25 against
api-docs.deepseek.com/quick_start/pricing/ (peak is exactly 2x):

  deepseek-v4-flash  0.22 in / 0.66 out / 0.007 cache read
  deepseek-v4-pro    0.66 in / 1.98 out / 0.022 cache read

`cache_read` is what drives the size of the error: at ~96% of agentic
input tokens, 0.003625 against a real 0.022 understated a
deepseek-v4-pro session 2.3x off-peak and 4.5x peak — $0.0237 per 1M
tokens reported against a real $0.0536, and $0.1073 inside a peak window.

Also here:

- `CostTracker.get_cache_savings` prices each event at `event.timestamp`
  rather than at display time, so opening /cost after 01:00 UTC no longer
  restates an off-peak session's savings at peak rates.
- A `# checked YYYY-MM-DD` line on the DeepSeek block, per the issue's
  suggestion — it is the row most likely to move again.
- The README's DeepSeek headline quoted `~$0.0435` / 1M cache-hit input,
  derived from the same stale card (and never matching the table's own
  0.003625). Now $0.022 off-peak, with the peak window stated.
- `deepseek-v4-flash-vision-exp` is deliberately left unregistered: it
  shares the flash card on the vendor's page but has no row in
  models/configs.py and is unreachable through the provider.

Tests: 52 new in tests/test_deepseek_peak_pricing.py covering the
schedule (window boundaries, the Mon/Fri fencepost, weekends inside the
windows, a 168-hour sweep pinning 35 peak hours), the published card as
absolutes on both sides, and the scope gate that no other model is
time-tiered at any hour of the week. The existing DeepSeek and status-bar
pricing assertions now pin an explicit instant instead of depending on
when CI runs.

Closes #904

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The default-to-now test only asserted `is_deepseek_peak()` agreed with
`is_deepseek_peak(time.time())` — self-consistent, and equally true of a
default that had stopped reaching the clock entirely. Since every
production call site relies on that default, a regression there would
park all of them on one card with nothing failing.

Now fakes the clock in the pricing module's own namespace (not
`time.time` itself, which pytest also reads) and pins the default through
`is_deepseek_peak`, `get_pricing` and `compute_cost` on both sides of the
schedule.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@github-actions

Copy link
Copy Markdown

Test Results

     5 files     997 suites   29m 20s ⏱️
15 374 tests 15 349 ✅ 19 💤 6 ❌
31 022 runs  30 956 ✅ 58 💤 8 ❌

For more details on these failures, see this check.

Results for commit a1c01ac.

@agentforce314
agentforce314 merged commit d8b194e into main Aug 26, 2026
4 of 8 checks passed
@agentforce314
agentforce314 deleted the fix/deepseek-peak-pricing-904 branch August 26, 2026 06:53
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

DeepSeek V4 tiers hold the pre-2026-08-16 card, and get_pricing has no axis for the new peak/off-peak schedule

1 participant