fix(cost): bill DeepSeek V4.1 Flash and V4 Pro at their off-peak rates outside peak hours

DeepSeek charges half the listed rate outside 01:00-04:00 and 06:00-10:00 UTC
Monday to Friday, so every deepseek-flash, deepseek-v4-flash,
deepseek-v4-flash-vision-exp, and deepseek-v4-pro entry now carries an
off_peak_pricing block with those windows and the halved input, output, and
cache-hit rates. The generated cost map schema picks up the block, and the
regression tests pin the peak and off-peak cost of one call at fixed moments.
This commit is contained in:
mateo-berri 2026-09-19 04:31:55 -07:00
parent 1f6e5b60b5
commit e0db862781
6 changed files with 699 additions and 0 deletions

View file

@ -59567,6 +59567,34 @@
"max_output_tokens": 393216,
"max_tokens": 393216,
"mode": "chat",
"off_peak_pricing": {
"cache_read_input_token_cost": 3e-09,
"input_cost_per_token": 1.5e-07,
"output_cost_per_token": 6e-07,
"windows": [
{
"hours_utc": [
"00:00-01:00",
"04:00-06:00",
"10:00-00:00"
],
"weekdays": [
1,
2,
3,
4,
5
]
},
{
"hours_utc": "00:00-00:00",
"weekdays": [
6,
7
]
}
]
},
"output_cost_per_token": 1.2e-06,
"source": "https://api-docs.deepseek.com/quick_start/pricing",
"supported_endpoints": [
@ -59593,6 +59621,34 @@
"max_output_tokens": 393216,
"max_tokens": 393216,
"mode": "chat",
"off_peak_pricing": {
"cache_read_input_token_cost": 3e-09,
"input_cost_per_token": 1.5e-07,
"output_cost_per_token": 6e-07,
"windows": [
{
"hours_utc": [
"00:00-01:00",
"04:00-06:00",
"10:00-00:00"
],
"weekdays": [
1,
2,
3,
4,
5
]
},
{
"hours_utc": "00:00-00:00",
"weekdays": [
6,
7
]
}
]
},
"output_cost_per_token": 1.2e-06,
"source": "https://api-docs.deepseek.com/quick_start/pricing",
"supported_endpoints": [
@ -59619,6 +59675,34 @@
"max_output_tokens": 393216,
"max_tokens": 393216,
"mode": "chat",
"off_peak_pricing": {
"cache_read_input_token_cost": 3e-09,
"input_cost_per_token": 1.5e-07,
"output_cost_per_token": 6e-07,
"windows": [
{
"hours_utc": [
"00:00-01:00",
"04:00-06:00",
"10:00-00:00"
],
"weekdays": [
1,
2,
3,
4,
5
]
},
{
"hours_utc": "00:00-00:00",
"weekdays": [
6,
7
]
}
]
},
"output_cost_per_token": 1.2e-06,
"source": "https://api-docs.deepseek.com/quick_start/pricing",
"supported_endpoints": [
@ -59645,6 +59729,34 @@
"max_output_tokens": 393216,
"max_tokens": 393216,
"mode": "chat",
"off_peak_pricing": {
"cache_read_input_token_cost": 2.2e-08,
"input_cost_per_token": 6.6e-07,
"output_cost_per_token": 1.98e-06,
"windows": [
{
"hours_utc": [
"00:00-01:00",
"04:00-06:00",
"10:00-00:00"
],
"weekdays": [
1,
2,
3,
4,
5
]
},
{
"hours_utc": "00:00-00:00",
"weekdays": [
6,
7
]
}
]
},
"output_cost_per_token": 3.96e-06,
"source": "https://api-docs.deepseek.com/quick_start/pricing",
"supported_endpoints": [
@ -59671,6 +59783,34 @@
"max_output_tokens": 393216,
"max_tokens": 393216,
"mode": "chat",
"off_peak_pricing": {
"cache_read_input_token_cost": 3e-09,
"input_cost_per_token": 1.5e-07,
"output_cost_per_token": 6e-07,
"windows": [
{
"hours_utc": [
"00:00-01:00",
"04:00-06:00",
"10:00-00:00"
],
"weekdays": [
1,
2,
3,
4,
5
]
},
{
"hours_utc": "00:00-00:00",
"weekdays": [
6,
7
]
}
]
},
"output_cost_per_token": 1.2e-06,
"source": "https://api-docs.deepseek.com/quick_start/pricing",
"supported_endpoints": [
@ -59697,6 +59837,34 @@
"max_output_tokens": 393216,
"max_tokens": 393216,
"mode": "chat",
"off_peak_pricing": {
"cache_read_input_token_cost": 3e-09,
"input_cost_per_token": 1.5e-07,
"output_cost_per_token": 6e-07,
"windows": [
{
"hours_utc": [
"00:00-01:00",
"04:00-06:00",
"10:00-00:00"
],
"weekdays": [
1,
2,
3,
4,
5
]
},
{
"hours_utc": "00:00-00:00",
"weekdays": [
6,
7
]
}
]
},
"output_cost_per_token": 1.2e-06,
"source": "https://api-docs.deepseek.com/quick_start/pricing",
"supported_endpoints": [
@ -59723,6 +59891,34 @@
"max_output_tokens": 393216,
"max_tokens": 393216,
"mode": "chat",
"off_peak_pricing": {
"cache_read_input_token_cost": 3e-09,
"input_cost_per_token": 1.5e-07,
"output_cost_per_token": 6e-07,
"windows": [
{
"hours_utc": [
"00:00-01:00",
"04:00-06:00",
"10:00-00:00"
],
"weekdays": [
1,
2,
3,
4,
5
]
},
{
"hours_utc": "00:00-00:00",
"weekdays": [
6,
7
]
}
]
},
"output_cost_per_token": 1.2e-06,
"source": "https://api-docs.deepseek.com/quick_start/pricing",
"supported_endpoints": [
@ -59749,6 +59945,34 @@
"max_output_tokens": 393216,
"max_tokens": 393216,
"mode": "chat",
"off_peak_pricing": {
"cache_read_input_token_cost": 2.2e-08,
"input_cost_per_token": 6.6e-07,
"output_cost_per_token": 1.98e-06,
"windows": [
{
"hours_utc": [
"00:00-01:00",
"04:00-06:00",
"10:00-00:00"
],
"weekdays": [
1,
2,
3,
4,
5
]
},
{
"hours_utc": "00:00-00:00",
"weekdays": [
6,
7
]
}
]
},
"output_cost_per_token": 3.96e-06,
"source": "https://api-docs.deepseek.com/quick_start/pricing",
"supported_endpoints": [

View file

@ -59567,6 +59567,34 @@
"max_output_tokens": 393216,
"max_tokens": 393216,
"mode": "chat",
"off_peak_pricing": {
"cache_read_input_token_cost": 3e-09,
"input_cost_per_token": 1.5e-07,
"output_cost_per_token": 6e-07,
"windows": [
{
"hours_utc": [
"00:00-01:00",
"04:00-06:00",
"10:00-00:00"
],
"weekdays": [
1,
2,
3,
4,
5
]
},
{
"hours_utc": "00:00-00:00",
"weekdays": [
6,
7
]
}
]
},
"output_cost_per_token": 1.2e-06,
"source": "https://api-docs.deepseek.com/quick_start/pricing",
"supported_endpoints": [
@ -59593,6 +59621,34 @@
"max_output_tokens": 393216,
"max_tokens": 393216,
"mode": "chat",
"off_peak_pricing": {
"cache_read_input_token_cost": 3e-09,
"input_cost_per_token": 1.5e-07,
"output_cost_per_token": 6e-07,
"windows": [
{
"hours_utc": [
"00:00-01:00",
"04:00-06:00",
"10:00-00:00"
],
"weekdays": [
1,
2,
3,
4,
5
]
},
{
"hours_utc": "00:00-00:00",
"weekdays": [
6,
7
]
}
]
},
"output_cost_per_token": 1.2e-06,
"source": "https://api-docs.deepseek.com/quick_start/pricing",
"supported_endpoints": [
@ -59619,6 +59675,34 @@
"max_output_tokens": 393216,
"max_tokens": 393216,
"mode": "chat",
"off_peak_pricing": {
"cache_read_input_token_cost": 3e-09,
"input_cost_per_token": 1.5e-07,
"output_cost_per_token": 6e-07,
"windows": [
{
"hours_utc": [
"00:00-01:00",
"04:00-06:00",
"10:00-00:00"
],
"weekdays": [
1,
2,
3,
4,
5
]
},
{
"hours_utc": "00:00-00:00",
"weekdays": [
6,
7
]
}
]
},
"output_cost_per_token": 1.2e-06,
"source": "https://api-docs.deepseek.com/quick_start/pricing",
"supported_endpoints": [
@ -59645,6 +59729,34 @@
"max_output_tokens": 393216,
"max_tokens": 393216,
"mode": "chat",
"off_peak_pricing": {
"cache_read_input_token_cost": 2.2e-08,
"input_cost_per_token": 6.6e-07,
"output_cost_per_token": 1.98e-06,
"windows": [
{
"hours_utc": [
"00:00-01:00",
"04:00-06:00",
"10:00-00:00"
],
"weekdays": [
1,
2,
3,
4,
5
]
},
{
"hours_utc": "00:00-00:00",
"weekdays": [
6,
7
]
}
]
},
"output_cost_per_token": 3.96e-06,
"source": "https://api-docs.deepseek.com/quick_start/pricing",
"supported_endpoints": [
@ -59671,6 +59783,34 @@
"max_output_tokens": 393216,
"max_tokens": 393216,
"mode": "chat",
"off_peak_pricing": {
"cache_read_input_token_cost": 3e-09,
"input_cost_per_token": 1.5e-07,
"output_cost_per_token": 6e-07,
"windows": [
{
"hours_utc": [
"00:00-01:00",
"04:00-06:00",
"10:00-00:00"
],
"weekdays": [
1,
2,
3,
4,
5
]
},
{
"hours_utc": "00:00-00:00",
"weekdays": [
6,
7
]
}
]
},
"output_cost_per_token": 1.2e-06,
"source": "https://api-docs.deepseek.com/quick_start/pricing",
"supported_endpoints": [
@ -59697,6 +59837,34 @@
"max_output_tokens": 393216,
"max_tokens": 393216,
"mode": "chat",
"off_peak_pricing": {
"cache_read_input_token_cost": 3e-09,
"input_cost_per_token": 1.5e-07,
"output_cost_per_token": 6e-07,
"windows": [
{
"hours_utc": [
"00:00-01:00",
"04:00-06:00",
"10:00-00:00"
],
"weekdays": [
1,
2,
3,
4,
5
]
},
{
"hours_utc": "00:00-00:00",
"weekdays": [
6,
7
]
}
]
},
"output_cost_per_token": 1.2e-06,
"source": "https://api-docs.deepseek.com/quick_start/pricing",
"supported_endpoints": [
@ -59723,6 +59891,34 @@
"max_output_tokens": 393216,
"max_tokens": 393216,
"mode": "chat",
"off_peak_pricing": {
"cache_read_input_token_cost": 3e-09,
"input_cost_per_token": 1.5e-07,
"output_cost_per_token": 6e-07,
"windows": [
{
"hours_utc": [
"00:00-01:00",
"04:00-06:00",
"10:00-00:00"
],
"weekdays": [
1,
2,
3,
4,
5
]
},
{
"hours_utc": "00:00-00:00",
"weekdays": [
6,
7
]
}
]
},
"output_cost_per_token": 1.2e-06,
"source": "https://api-docs.deepseek.com/quick_start/pricing",
"supported_endpoints": [
@ -59749,6 +59945,34 @@
"max_output_tokens": 393216,
"max_tokens": 393216,
"mode": "chat",
"off_peak_pricing": {
"cache_read_input_token_cost": 2.2e-08,
"input_cost_per_token": 6.6e-07,
"output_cost_per_token": 1.98e-06,
"windows": [
{
"hours_utc": [
"00:00-01:00",
"04:00-06:00",
"10:00-00:00"
],
"weekdays": [
1,
2,
3,
4,
5
]
},
{
"hours_utc": "00:00-00:00",
"weekdays": [
6,
7
]
}
]
},
"output_cost_per_token": 3.96e-06,
"source": "https://api-docs.deepseek.com/quick_start/pricing",
"supported_endpoints": [

View file

@ -457,6 +457,114 @@
"type": "number",
"minimum": 0
},
"off_peak_pricing": {
"type": "object",
"description": "Rates that replace the same-named base fields while the request falls inside the stated UTC windows.",
"properties": {
"hours_utc": {
"description": "UTC \"HH:MM-HH:MM\" window, or a list of them; a window may wrap past midnight.",
"oneOf": [
{
"type": "string",
"pattern": "^([01]\\d|2[0-3]):[0-5]\\d-([01]\\d|2[0-3]):[0-5]\\d$"
},
{
"type": "array",
"items": {
"type": "string",
"pattern": "^([01]\\d|2[0-3]):[0-5]\\d-([01]\\d|2[0-3]):[0-5]\\d$"
},
"minItems": 1
}
]
},
"windows": {
"type": "array",
"items": {
"type": "object",
"properties": {
"hours_utc": {
"description": "UTC \"HH:MM-HH:MM\" window, or a list of them; a window may wrap past midnight.",
"oneOf": [
{
"type": "string",
"pattern": "^([01]\\d|2[0-3]):[0-5]\\d-([01]\\d|2[0-3]):[0-5]\\d$"
},
{
"type": "array",
"items": {
"type": "string",
"pattern": "^([01]\\d|2[0-3]):[0-5]\\d-([01]\\d|2[0-3]):[0-5]\\d$"
},
"minItems": 1
}
]
},
"weekdays": {
"type": "array",
"description": "ISO-8601 weekday numbers (1 = Monday .. 7 = Sunday) or English day names the window applies on.",
"items": {
"oneOf": [
{
"type": "integer",
"minimum": 1,
"maximum": 7
},
{
"type": "string",
"pattern": "(?i)^(mon|monday|tue|tues|tuesday|wed|wednesday|thu|thur|thurs|thursday|fri|friday|sat|saturday|sun|sunday)$"
}
]
},
"minItems": 1
}
},
"required": [
"hours_utc"
],
"additionalProperties": false
},
"minItems": 1
},
"weekday_timezone": {
"type": "string",
"description": "IANA zone the weekdays of each window are read on; defaults to UTC."
},
"input_cost_per_token": {
"type": "number",
"minimum": 0
},
"output_cost_per_token": {
"type": "number",
"minimum": 0
},
"output_cost_per_reasoning_token": {
"type": "number",
"minimum": 0
},
"cache_read_input_token_cost": {
"type": "number",
"minimum": 0
},
"cache_creation_input_token_cost": {
"type": "number",
"minimum": 0
}
},
"anyOf": [
{
"required": [
"hours_utc"
]
},
{
"required": [
"windows"
]
}
],
"additionalProperties": false
},
"output_cost_per_audio_token": {
"type": "number",
"minimum": 0

View file

@ -0,0 +1,70 @@
from datetime import datetime, timezone
from typing import Final
import pytest
import litellm
from litellm._internal_context import pinned_billing_time
from litellm.types.utils import ModelResponse, PromptTokensDetailsWrapper, Usage
PEAK_MOMENTS: Final = (
pytest.param(datetime(2026, 9, 22, 8, 0, tzinfo=timezone.utc), id="tuesday-08:00"),
pytest.param(datetime(2026, 9, 25, 9, 59, tzinfo=timezone.utc), id="friday-09:59"),
pytest.param(datetime(2026, 9, 21, 1, 0, tzinfo=timezone.utc), id="monday-01:00"),
)
OFF_PEAK_MOMENTS: Final = (
pytest.param(datetime(2026, 9, 26, 2, 0, tzinfo=timezone.utc), id="saturday-02:00"),
pytest.param(datetime(2026, 9, 27, 8, 0, tzinfo=timezone.utc), id="sunday-08:00"),
pytest.param(datetime(2026, 9, 21, 0, 30, tzinfo=timezone.utc), id="monday-00:30"),
pytest.param(datetime(2026, 9, 23, 5, 0, tzinfo=timezone.utc), id="wednesday-05:00"),
pytest.param(datetime(2026, 9, 24, 10, 0, tzinfo=timezone.utc), id="thursday-10:00"),
pytest.param(datetime(2026, 9, 22, 12, 0, tzinfo=timezone.utc), id="tuesday-12:00"),
)
PEAK_COST_PER_MILLION_IN_AND_OUT_WITH_400K_CACHE_HITS: Final = {
"deepseek-flash": 1.3824,
"deepseek-v4-pro": 4.7696,
}
def one_million_in_and_out_with_400k_cache_hits(model: str) -> ModelResponse:
return ModelResponse(
model=model,
usage=Usage(
prompt_tokens=1_000_000,
completion_tokens=1_000_000,
total_tokens=2_000_000,
prompt_tokens_details=PromptTokensDetailsWrapper(cached_tokens=400_000),
),
)
def deepseek_cost_at(model: str, moment: datetime) -> float:
with pinned_billing_time(moment):
return litellm.completion_cost(
completion_response=one_million_in_and_out_with_400k_cache_hits(model),
model=model,
custom_llm_provider="deepseek",
)
@pytest.mark.usefixtures("local_model_cost_map")
@pytest.mark.parametrize(("model", "peak_cost"), PEAK_COST_PER_MILLION_IN_AND_OUT_WITH_400K_CACHE_HITS.items())
@pytest.mark.parametrize("moment", PEAK_MOMENTS)
def test_deepseek_bills_the_listed_rate_during_weekday_peak_hours(model: str, peak_cost: float, moment: datetime):
assert deepseek_cost_at(model, moment) == pytest.approx(peak_cost)
@pytest.mark.usefixtures("local_model_cost_map")
@pytest.mark.parametrize(("model", "peak_cost"), PEAK_COST_PER_MILLION_IN_AND_OUT_WITH_400K_CACHE_HITS.items())
@pytest.mark.parametrize("moment", OFF_PEAK_MOMENTS)
def test_deepseek_bills_half_the_listed_rate_off_peak(model: str, peak_cost: float, moment: datetime):
assert deepseek_cost_at(model, moment) == pytest.approx(peak_cost / 2)
@pytest.mark.usefixtures("local_model_cost_map")
@pytest.mark.parametrize("alias", ("deepseek-v4-flash", "deepseek-v4-flash-vision-exp", "deepseek/deepseek-flash"))
def test_deepseek_flash_aliases_follow_the_same_off_peak_schedule(alias: str):
saturday: Final = datetime(2026, 9, 26, 2, 0, tzinfo=timezone.utc)
assert deepseek_cost_at(alias, saturday) == pytest.approx(
PEAK_COST_PER_MILLION_IN_AND_OUT_WITH_400K_CACHE_HITS["deepseek-flash"] / 2
)

View file

@ -367,6 +367,47 @@ def test_active_mistral_chat_rows_price_cache_reads_below_input(path: Path):
assert drifted == []
DEEPSEEK_PRICED_ROWS: Final = tuple(
f"{prefix}{name}"
for name in ("deepseek-flash", "deepseek-v4-flash", "deepseek-v4-flash-vision-exp", "deepseek-v4-pro")
for prefix in ("", "deepseek/")
)
DEEPSEEK_OFF_PEAK_WINDOWS: Final = (
{"hours_utc": ["00:00-01:00", "04:00-06:00", "10:00-00:00"], "weekdays": [1, 2, 3, 4, 5]},
{"hours_utc": "00:00-00:00", "weekdays": [6, 7]},
)
DEEPSEEK_HALVED_RATES: Final = ("input_cost_per_token", "output_cost_per_token", "cache_read_input_token_cost")
def deepseek_off_peak_drift(entry: Mapping[str, object]) -> str | None:
block: Final = entry.get("off_peak_pricing")
if not isinstance(block, dict):
return "no off_peak_pricing block"
if tuple(block.get("windows", ())) != DEEPSEEK_OFF_PEAK_WINDOWS:
return f"windows={block.get('windows')}"
halved: Final = {rate: block.get(rate) for rate in DEEPSEEK_HALVED_RATES}
expected: Final = {rate: float(str(entry[rate])) / 2 for rate in DEEPSEEK_HALVED_RATES}
mismatched: Final = {
rate for rate in DEEPSEEK_HALVED_RATES if halved[rate] != pytest.approx(expected[rate], rel=1e-9)
}
return f"off-peak rates {halved} are not half of the listed rates" if mismatched else None
@pytest.mark.parametrize("path", (PRICES_PATH, BACKUP_PRICES_PATH), ids=("main", "backup"))
def test_deepseek_rows_bill_half_rate_outside_weekday_peak_hours(path: Path):
"""DeepSeek charges half its listed rate outside 01:00-04:00 and 06:00-10:00 UTC Monday to
Friday (api-docs.deepseek.com/quick_start/pricing, read 2026-09-19), so every row on that
pricing page carries an off_peak_pricing block with those windows and the halved rates."""
rows: Mapping[str, object] = json.loads(path.read_text())
drifted: Final = {
name: deepseek_off_peak_drift(entry)
for name in DEEPSEEK_PRICED_ROWS
if isinstance(entry := rows.get(name), dict) and deepseek_off_peak_drift(entry) is not None
}
assert drifted == {}
assert all(name in rows for name in DEEPSEEK_PRICED_ROWS)
PROVIDER_LABELS_WITHOUT_A_MODEL_SET: Final = frozenset({"sagemaker", "bedrock_converse"})
MODES_SERVED_OUTSIDE_THE_LLM_PROVIDER_REGISTRY: Final = frozenset({"search", "evaluation"})
VERTEX_FAMILIES_A_VERTEX_WILDCARD_GRANT_DOES_NOT_LIST: Final = frozenset(

View file

@ -952,6 +952,38 @@ def test_aaamodel_prices_and_context_window_json_is_valid():
"supports_image_size": {"type": "boolean"},
"supports_native_structured_output": {"type": "boolean"},
"use_openai_responses_path": {"type": "boolean"},
"off_peak_pricing": {
"type": "object",
"properties": {
"hours_utc": {
"oneOf": [{"type": "string"}, {"type": "array", "items": {"type": "string"}}],
},
"windows": {
"type": "array",
"items": {
"type": "object",
"properties": {
"hours_utc": {
"oneOf": [{"type": "string"}, {"type": "array", "items": {"type": "string"}}],
},
"weekdays": {
"type": "array",
"items": {"oneOf": [{"type": "integer"}, {"type": "string"}]},
},
},
"required": ["hours_utc"],
"additionalProperties": False,
},
},
"weekday_timezone": {"type": "string"},
"input_cost_per_token": {"type": "number"},
"output_cost_per_token": {"type": "number"},
"output_cost_per_reasoning_token": {"type": "number"},
"cache_read_input_token_cost": {"type": "number"},
"cache_creation_input_token_cost": {"type": "number"},
},
"additionalProperties": False,
},
"tiered_pricing": {
"type": "array",
"items": {