mirror of
https://github.com/headroomlabs-ai/headroom.git
synced 2026-08-27 14:17:10 -04:00
## Description The shared `test` job is currently failing on every open PR because of a wall-clock time-bomb in the DeepSeek pricing tests, not because of any code change. `tests/test_providers/test_deepseek.py::TestDeepSeekPricingModule::test_registry_staleness_and_source_url` asserted: ```python assert not registry.is_stale() ``` `PricingRegistry.is_stale()` returns `(date.today() - last_updated) > timedelta(days=30)`. The DeepSeek registry ships `LAST_UPDATED = date(2026, 6, 19)`, so this assertion holds only while the current date stays within 30 days of that constant. Once it lapses, the test fails on time alone, turning the `test` shard red for every unrelated PR in the repo. It is failing right now (31 days past `LAST_UPDATED`). This is not testing code behavior: it only checks that the machine's clock is within 30 days of a hardcoded date. The sibling Anthropic and OpenAI registries are 560 days old and make no such assertion, so DeepSeek is the odd one out here rather than a deliberate freshness gate. ## Fix Drop the freshness assertion and keep the meaningful `source_url` check, renaming the test to `test_registry_source_url` to match what it now verifies. The staleness mechanism stays fully and time-independently covered by `tests/test_pricing.py::test_registry_staleness_and_warning`, which builds registries with `date.today() - timedelta(days=30)` (asserts not stale) and `date.today() - timedelta(days=31)` (asserts stale) plus the warning text. So this removes a fragile environmental assertion without reducing real coverage, and aligns DeepSeek with the Anthropic/OpenAI registries. ## Type of Change - [x] Bug fix (non-breaking change that fixes an issue) - [ ] New feature (non-breaking change that adds functionality) - [ ] Breaking change (fix or feature that would cause existing functionality to change) - [ ] Documentation update - [ ] Performance improvement - [ ] Code refactoring (no functional changes) ## Changes Made - `tests/test_providers/test_deepseek.py`: remove the wall-clock-dependent `assert not registry.is_stale()`, keep the `source_url` assertion, rename the test to `test_registry_source_url`, and add a comment explaining why freshness is not asserted here. ## Testing - [x] Unit tests pass (`pytest`) - [x] Linting passes (`ruff check .`) - [x] Type checking passes (`mypy headroom`) - [ ] New tests added for new functionality - [ ] Manual testing performed ### Test Output ```text $ uvx ruff@0.15.17 check tests/test_providers/test_deepseek.py All checks passed! ``` ## Real Behavior Proof - Environment: Windows 11, Python 3.12, project venv (`uv sync --extra proxy`), `uvx ruff@0.15.17`. - Exact command / steps: with the current date at 31 days past `LAST_UPDATED`, ran the registry's `is_stale()` and the fixed test body against the real modules, plus the mechanism test from `tests/test_pricing.py`. - Observed result: `get_deepseek_registry().is_stale()` is `True` on the current date (which is exactly what broke the old assertion); the fixed `test_registry_source_url` body passes regardless of the date; and `test_registry_staleness_and_warning` still passes, so the staleness mechanism remains covered. - Not tested: a live DeepSeek pricing fetch (out of scope; pricing values are unchanged). ## Review Readiness - [x] I have performed a self-review - [x] This PR is ready for human review ## Checklist - [x] My code follows the project's style guidelines - [x] I have performed a self-review of my code - [x] I have commented my code, particularly in hard-to-understand areas - [ ] I have made corresponding changes to the documentation - [x] My changes generate no new warnings - [ ] I have added tests that prove my fix is effective or that my feature works - [x] New and existing unit tests pass locally with my changes - [ ] I have updated the CHANGELOG.md if applicable
230 lines
9.2 KiB
Python
230 lines
9.2 KiB
Python
"""Tests for DeepSeek model pricing and cost estimation."""
|
|
|
|
import pytest
|
|
|
|
from headroom.pricing.deepseek_prices import (
|
|
DEEPSEEK_PRICES,
|
|
get_deepseek_registry,
|
|
)
|
|
from headroom.pricing.registry import PricingRegistry
|
|
|
|
|
|
class TestDeepSeekPricingModule:
|
|
"""Tests for the DeepSeek pricing data module."""
|
|
|
|
def test_deepseek_pricing_contains_v4_models(self):
|
|
assert "deepseek-v4-flash" in DEEPSEEK_PRICES
|
|
assert "deepseek-v4-pro" in DEEPSEEK_PRICES
|
|
assert len(DEEPSEEK_PRICES) == 2
|
|
|
|
def test_deepseek_v4_flash_pricing(self):
|
|
pricing = DEEPSEEK_PRICES["deepseek-v4-flash"]
|
|
assert pricing.input_per_1m == 0.14
|
|
assert pricing.output_per_1m == 0.28
|
|
assert pricing.cached_input_per_1m == 0.0028
|
|
assert pricing.context_window == 1_000_000
|
|
assert pricing.provider == "deepseek"
|
|
assert pricing.notes is not None
|
|
|
|
def test_deepseek_v4_pro_pricing(self):
|
|
pricing = DEEPSEEK_PRICES["deepseek-v4-pro"]
|
|
assert pricing.input_per_1m == 0.435
|
|
assert pricing.output_per_1m == 0.87
|
|
assert pricing.cached_input_per_1m == 0.003625
|
|
assert pricing.context_window == 1_000_000
|
|
assert pricing.provider == "deepseek"
|
|
assert pricing.notes is not None
|
|
|
|
def test_get_deepseek_registry(self):
|
|
registry = get_deepseek_registry()
|
|
assert isinstance(registry, PricingRegistry)
|
|
assert registry.get_price("deepseek-v4-flash") is not None
|
|
assert registry.get_price("deepseek-v4-pro") is not None
|
|
assert registry.get_price("nonexistent") is None
|
|
|
|
def test_registry_source_url(self):
|
|
registry = get_deepseek_registry()
|
|
assert registry.source_url == "https://api-docs.deepseek.com/quick_start/pricing"
|
|
# Deliberately no `assert not registry.is_stale()` here: is_stale()
|
|
# compares the shipped LAST_UPDATED against date.today(), so asserting
|
|
# freshness makes this test fail on wall-clock time alone once the
|
|
# pricing date ages past STALENESS_THRESHOLD_DAYS (30) - which then
|
|
# breaks CI on every unrelated PR in the repo. The staleness mechanism
|
|
# is covered time-independently in tests/test_pricing.py, and the
|
|
# sibling Anthropic/OpenAI registries make no freshness assertion.
|
|
|
|
def test_deepseek_registry_estimate_cost(self):
|
|
registry = get_deepseek_registry()
|
|
cost = registry.estimate_cost("deepseek-v4-flash", input_tokens=1_000_000)
|
|
assert cost.cost_usd == 0.14
|
|
assert "input" in cost.breakdown
|
|
assert cost.pricing_date is not None
|
|
|
|
def test_deepseek_registry_estimate_cost_with_cached(self):
|
|
registry = get_deepseek_registry()
|
|
cost = registry.estimate_cost(
|
|
"deepseek-v4-flash",
|
|
input_tokens=1_000_000,
|
|
cached_input_tokens=1_000_000,
|
|
)
|
|
assert cost.cost_usd == 0.14 + 0.0028
|
|
|
|
|
|
class TestDeepSeekLiteLLMInjection:
|
|
"""Tests for DeepSeek V4 pricing injection into litellm."""
|
|
|
|
def test_deepseek_v4_models_in_litellm_model_cost(self):
|
|
from headroom.pricing.litellm_pricing import LITELLM_AVAILABLE, litellm
|
|
|
|
if not LITELLM_AVAILABLE:
|
|
pytest.skip("litellm not available")
|
|
assert "deepseek-v4-flash" in litellm.model_cost
|
|
assert "deepseek-v4-pro" in litellm.model_cost
|
|
|
|
def test_deepseek_v4_prefixed_models_in_litellm_model_cost(self):
|
|
from headroom.pricing.litellm_pricing import LITELLM_AVAILABLE, litellm
|
|
|
|
if not LITELLM_AVAILABLE:
|
|
pytest.skip("litellm not available")
|
|
assert "deepseek/deepseek-v4-flash" in litellm.model_cost
|
|
assert "deepseek/deepseek-v4-pro" in litellm.model_cost
|
|
|
|
def test_deepseek_v4_flash_litellm_pricing(self):
|
|
from headroom.pricing.litellm_pricing import LITELLM_AVAILABLE, litellm
|
|
|
|
if not LITELLM_AVAILABLE:
|
|
pytest.skip("litellm not available")
|
|
flash = litellm.model_cost["deepseek-v4-flash"]
|
|
assert flash["input_cost_per_token"] == 0.14 / 1_000_000
|
|
assert flash["output_cost_per_token"] == 0.28 / 1_000_000
|
|
assert flash["cache_read_input_token_cost"] == 0.0028 / 1_000_000
|
|
assert flash["litellm_provider"] == "deepseek"
|
|
|
|
def test_deepseek_v4_pro_litellm_pricing(self):
|
|
from headroom.pricing.litellm_pricing import LITELLM_AVAILABLE, litellm
|
|
|
|
if not LITELLM_AVAILABLE:
|
|
pytest.skip("litellm not available")
|
|
pro = litellm.model_cost["deepseek-v4-pro"]
|
|
assert pro["input_cost_per_token"] == 0.435 / 1_000_000
|
|
assert pro["output_cost_per_token"] == 0.87 / 1_000_000
|
|
assert pro["cache_read_input_token_cost"] == 0.003625 / 1_000_000
|
|
assert pro["litellm_provider"] == "deepseek"
|
|
|
|
def test_cost_per_token_resolves_deepseek_v4_flash(self):
|
|
from headroom.pricing.litellm_pricing import (
|
|
LITELLM_AVAILABLE,
|
|
litellm,
|
|
resolve_litellm_model,
|
|
)
|
|
|
|
if not LITELLM_AVAILABLE:
|
|
pytest.skip("litellm not available")
|
|
# resolve_litellm_model adds the deepseek/ prefix so that
|
|
# litellm.cost_per_token can determine the provider via
|
|
# get_llm_provider(). Bare model names without a provider prefix
|
|
# would fail with BadRequestError.
|
|
resolved = resolve_litellm_model("deepseek-v4-flash")
|
|
input_cost, output_cost = litellm.cost_per_token(
|
|
model=resolved,
|
|
prompt_tokens=1_000_000,
|
|
completion_tokens=1_000_000,
|
|
)
|
|
assert input_cost == pytest.approx(0.14, rel=0.01)
|
|
assert output_cost == pytest.approx(0.28, rel=0.01)
|
|
|
|
def test_resolve_litellm_model_prefixes_deepseek(self):
|
|
from headroom.pricing.litellm_pricing import resolve_litellm_model
|
|
|
|
resolved = resolve_litellm_model("deepseek-v4-flash")
|
|
assert resolved == "deepseek/deepseek-v4-flash"
|
|
|
|
def test_injection_does_not_overwrite_existing_upstream_entries(self):
|
|
"""If litellm upstream already has these, our injection is a no-op."""
|
|
from headroom.pricing.litellm_pricing import LITELLM_AVAILABLE, litellm
|
|
|
|
if not LITELLM_AVAILABLE:
|
|
pytest.skip("litellm not available")
|
|
# Force-inject with wrong value, then verify the injection guard
|
|
litellm.model_cost["deepseek-v4-flash"] = {"input_cost_per_token": 999}
|
|
# Reimport to trigger _inject_deepseek_pricing — but it should NOT overwrite
|
|
import importlib
|
|
|
|
import headroom.pricing.litellm_pricing as lp
|
|
|
|
importlib.reload(lp)
|
|
assert litellm.model_cost["deepseek-v4-flash"]["input_cost_per_token"] == 999
|
|
# Reset to correct value
|
|
litellm.model_cost["deepseek-v4-flash"] = {
|
|
"input_cost_per_token": 0.14 / 1_000_000,
|
|
"output_cost_per_token": 0.28 / 1_000_000,
|
|
"cache_read_input_token_cost": 0.0028 / 1_000_000,
|
|
"litellm_provider": "deepseek",
|
|
"max_tokens": 384_000,
|
|
"max_input_tokens": 1_000_000,
|
|
}
|
|
|
|
|
|
class TestDeepSeekAnthropicProviderFallback:
|
|
"""Tests that Anthropic provider's _get_pricing handles DeepSeek models."""
|
|
|
|
def test_deepseek_v4_flash_fallback(self):
|
|
from headroom.providers.anthropic import AnthropicProvider
|
|
|
|
provider = AnthropicProvider()
|
|
pricing = provider._get_pricing("deepseek-v4-flash")
|
|
assert pricing is not None
|
|
assert pricing["input"] == 0.14
|
|
assert pricing["output"] == 0.28
|
|
assert pricing["cached_input"] == 0.0028
|
|
|
|
def test_deepseek_v4_pro_fallback(self):
|
|
from headroom.providers.anthropic import AnthropicProvider
|
|
|
|
provider = AnthropicProvider()
|
|
pricing = provider._get_pricing("deepseek-v4-pro")
|
|
assert pricing is not None
|
|
assert pricing["input"] == 0.435
|
|
assert pricing["output"] == 0.87
|
|
assert pricing["cached_input"] == 0.003625
|
|
|
|
def test_deepseek_unknown_model_returns_none(self):
|
|
from headroom.providers.anthropic import AnthropicProvider
|
|
|
|
provider = AnthropicProvider()
|
|
pricing = provider._get_pricing("deepseek-unknown-model")
|
|
assert pricing is None
|
|
|
|
def test_deepseek_partial_match_v4_flash_alias(self):
|
|
from headroom.providers.anthropic import AnthropicProvider
|
|
|
|
provider = AnthropicProvider()
|
|
# Should match via partial match (flash in v4-flash)
|
|
pricing = provider._get_pricing("deepseek-v4-flash-v1")
|
|
assert pricing is not None
|
|
|
|
def test_estimate_cost_deepseek_v4_flash(self):
|
|
from headroom.providers.anthropic import AnthropicProvider
|
|
|
|
provider = AnthropicProvider()
|
|
cost = provider.estimate_cost(
|
|
input_tokens=1_000_000,
|
|
output_tokens=0,
|
|
model="deepseek-v4-flash",
|
|
)
|
|
assert cost is not None
|
|
assert cost == 0.14
|
|
|
|
def test_estimate_cost_deepseek_v4_flash_with_cache(self):
|
|
from headroom.providers.anthropic import AnthropicProvider
|
|
|
|
provider = AnthropicProvider()
|
|
cost = provider.estimate_cost(
|
|
input_tokens=1_000_000,
|
|
output_tokens=0,
|
|
model="deepseek-v4-flash",
|
|
cached_tokens=1_000_000,
|
|
)
|
|
assert cost is not None
|
|
# All 1M input tokens are cached, so cost = cached_input only
|
|
assert cost == 0.0028
|