2323from contextlib import contextmanager
2424from contextvars import ContextVar
2525from dataclasses import dataclass
26+ from datetime import datetime , timezone
2627from decimal import Decimal
2728from typing import Any , Iterator
2829from abc import ABC , abstractmethod
3435# ---------------------------------------------------------------------------
3536
3637PROVIDER_DEFAULT_MODELS : dict [str , tuple [str , str ]] = {
37- "deepseek" : ("deepseek-chat " , "deepseek-reasoner " ),
38+ "deepseek" : ("deepseek-v4-flash " , "deepseek-v4-pro " ),
3839 "openai" : ("gpt-4o" , "gpt-4o" ),
3940 "anthropic" : ("claude-sonnet-4-20250514" , "claude-sonnet-4-20250514" ),
4041 "google" : ("gemini-2.5-flash" , "gemini-2.5-pro" ),
8182
8283_PICOS_PER_DOLLAR = 10 ** 12
8384_TOKENS_PER_MILLION = 1_000_000
85+ _DEEPSEEK_V4_EFFECTIVE_AT = datetime (2026 , 8 , 16 , 16 , tzinfo = timezone .utc )
86+ _DEEPSEEK_V4_MODELS = {
87+ "deepseek-v4-flash" : ("0.014" , "0.44" , "1.32" ),
88+ "deepseek-v4-flash-vision-exp" : ("0.014" , "0.44" , "1.32" ),
89+ "deepseek-v4-pro" : ("0.044" , "1.32" , "3.96" ),
90+ }
91+ _DEEPSEEK_V4_ALIASES = {
92+ "deepseek-chat" : "deepseek-v4-flash" ,
93+ "deepseek-reasoner" : "deepseek-v4-flash" ,
94+ }
8495
8596
8697@dataclass (frozen = True )
@@ -129,6 +140,35 @@ def _legacy_pricing_snapshot(provider: str) -> tuple[dict[str, Any], int, int]:
129140 }, input_picos , output_picos
130141
131142
143+ def _deepseek_v4_pricing_snapshot (model : str , occurred_at : datetime ) -> tuple [dict [str , Any ] | None , bool ]:
144+ """Freeze the official DeepSeek V4 rate card selected at a UTC timestamp."""
145+ canonical_model = _DEEPSEEK_V4_ALIASES .get (model .strip ().lower (), model .strip ().lower ())
146+ rates = _DEEPSEEK_V4_MODELS .get (canonical_model )
147+ if rates is None :
148+ return None , False
149+ instant = occurred_at .astimezone (timezone .utc )
150+ peak = instant .weekday () < 5 and (1 <= instant .hour < 4 or 6 <= instant .hour < 10 )
151+ multiplier = 1 if peak else 0.5
152+ hit , miss , output = (
153+ int (Decimal (rate ) * Decimal (str (multiplier )) * _PICOS_PER_DOLLAR )
154+ for rate in rates
155+ )
156+ current_schedule = instant >= _DEEPSEEK_V4_EFFECTIVE_AT
157+ return {
158+ "version" : "deepseek-v4-pricing-2026-08-16" ,
159+ "source" : "https://api-docs.deepseek.com/quick_start/pricing/" ,
160+ "currency" : "USD" ,
161+ "model" : canonical_model ,
162+ "priced_at" : instant .isoformat (),
163+ "effective_at" : _DEEPSEEK_V4_EFFECTIVE_AT .isoformat (),
164+ "billing_window" : "peak" if peak else "off_peak" ,
165+ "cache_hit_picos_per_million" : hit ,
166+ "cache_miss_picos_per_million" : miss ,
167+ "output_picos_per_million" : output ,
168+ "pricing_status" : "authoritative" if current_schedule else "estimated_current_schedule" ,
169+ }, current_schedule
170+
171+
132172def _usage_integer (usage : dict | None , key : str ) -> int | None :
133173 if usage is None or usage .get (key ) is None :
134174 return None
@@ -139,30 +179,61 @@ def _usage_integer(usage: dict | None, key: str) -> int | None:
139179
140180
141181def _track_cost (provider : str , usage : dict | None , * , model : str = "unknown" ,
142- latency_ms : int | None = None , completion_state : str = "completed" ) -> str :
182+ latency_ms : int | None = None , completion_state : str = "completed" ,
183+ occurred_at : datetime | None = None ) -> str :
143184 """Write one immutable provider attempt; summaries always read this ledger."""
144185 scope = _current_usage_scope ()
145186 prompt_tokens = _usage_integer (usage , "prompt_tokens" )
146187 completion_tokens = _usage_integer (usage , "completion_tokens" )
147188 cache_hit_tokens = _usage_integer (usage , "cache_hit_tokens" )
148189 cache_miss_tokens = _usage_integer (usage , "cache_miss_tokens" )
190+ if cache_hit_tokens is None :
191+ cache_hit_tokens = _usage_integer (usage , "prompt_cache_hit_tokens" )
192+ if cache_miss_tokens is None :
193+ cache_miss_tokens = _usage_integer (usage , "prompt_cache_miss_tokens" )
149194 reasoning_tokens = _usage_integer (usage , "reasoning_tokens" )
150- pricing_snapshot , input_rate , output_rate = _legacy_pricing_snapshot (provider )
195+ pricing_snapshot = None
196+ current_schedule = True
197+ if provider == "deepseek" :
198+ pricing_snapshot , current_schedule = _deepseek_v4_pricing_snapshot (
199+ model , occurred_at or datetime .now (timezone .utc ),
200+ )
201+ if pricing_snapshot is None :
202+ pricing_snapshot , input_rate , output_rate = _legacy_pricing_snapshot (provider )
203+ else :
204+ input_rate = int (pricing_snapshot ["cache_miss_picos_per_million" ])
205+ output_rate = int (pricing_snapshot ["output_picos_per_million" ])
151206 cost_picos = None
152207 if prompt_tokens is not None or completion_tokens is not None :
153- cost_picos = (
154- ((prompt_tokens or 0 ) * input_rate + (completion_tokens or 0 ) * output_rate )
155- // _TOKENS_PER_MILLION
156- )
208+ prompt = prompt_tokens or 0
209+ if "cache_hit_picos_per_million" in pricing_snapshot :
210+ hit = min (prompt , cache_hit_tokens or 0 )
211+ input_cost = (hit * int (pricing_snapshot ["cache_hit_picos_per_million" ])
212+ + (prompt - hit ) * input_rate )
213+ else :
214+ input_cost = prompt * input_rate
215+ cost_picos = (input_cost + (completion_tokens or 0 ) * output_rate ) // _TOKENS_PER_MILLION
216+ if cache_hit_tokens is None and cache_miss_tokens is None :
217+ cache_status = "unknown"
218+ elif (cache_hit_tokens or 0 ) and (cache_miss_tokens or 0 ):
219+ cache_status = "mixed"
220+ elif cache_hit_tokens :
221+ cache_status = "hit"
222+ else :
223+ cache_status = "miss"
224+ usage_status = "unknown" if usage is None else (
225+ "authoritative" if current_schedule and (provider != "deepseek" or cache_status != "unknown" )
226+ else "estimated"
227+ )
157228 attempt_id = f"usage_{ uuid .uuid4 ().hex } "
158229 scope .store .record_usage_attempt (
159230 attempt_id = attempt_id , provider = provider , model = model , surface = scope .surface ,
160231 user_id = scope .user_id , workspace_id = scope .workspace_id , session_id = scope .session_id ,
161- run_id = scope .run_id , cache_status = "unknown" , prompt_tokens = prompt_tokens ,
232+ run_id = scope .run_id , cache_status = cache_status , prompt_tokens = prompt_tokens ,
162233 cache_hit_tokens = cache_hit_tokens , cache_miss_tokens = cache_miss_tokens ,
163234 completion_tokens = completion_tokens , reasoning_tokens = reasoning_tokens ,
164235 latency_ms = latency_ms , completion_state = completion_state ,
165- usage_status = "authoritative" if usage is not None else "unknown" ,
236+ usage_status = usage_status ,
166237 cost_picos = cost_picos , pricing_snapshot = pricing_snapshot ,
167238 )
168239 return attempt_id
@@ -349,9 +420,13 @@ def _call():
349420 usage = getattr (response , "usage" , None )
350421 usage_dict = None
351422 if usage is not None :
423+ completion_details = getattr (usage , "completion_tokens_details" , None )
352424 usage_dict = {
353425 "prompt_tokens" : usage .prompt_tokens or 0 ,
354426 "completion_tokens" : usage .completion_tokens or 0 ,
427+ "prompt_cache_hit_tokens" : getattr (usage , "prompt_cache_hit_tokens" , None ),
428+ "prompt_cache_miss_tokens" : getattr (usage , "prompt_cache_miss_tokens" , None ),
429+ "reasoning_tokens" : getattr (completion_details , "reasoning_tokens" , None ),
355430 }
356431 _track_cost (self .config .provider , usage_dict , model = model ,
357432 latency_ms = round ((time .monotonic () - started ) * 1000 ))
0 commit comments