@@ -220,7 +220,7 @@ def lookup_downgrade(provider: str, model: str) -> str | None:
220220
221221def _alt_unit_cost (provider : str , original_model : str , alt_model : str ,
222222 input_tokens : int , output_tokens : int , cache_tokens : int ,
223- cache_write_tokens : int = 0 , * ,
223+ cache_write_tokens : int , * ,
224224 at : datetime ) -> float | None :
225225 """Cost of the given token mix priced at ``alt_model``, or ``None`` when the
226226 alternative has no pricing data at ``at``.
@@ -233,10 +233,9 @@ def _alt_unit_cost(provider: str, original_model: str, alt_model: str,
233233 cache-write from the alternative side while the actual side (the
234234 ``cost_usd`` column) already includes it; that asymmetry inflated every
235235 savings figure derived from this function by the full cache-write cost of
236- the candidate. ``cache_write_tokens`` defaults to 0 for the one call site
237- (the per-turn quota-audit counterfactual) whose upstream aggregation does
238- not carry a cache-write figure at all — that is unchanged behaviour, not a
239- new omission.
236+ the candidate. Both callers provide cache-write tokens from
237+ their respective aggregates so the alternative side cannot silently omit a
238+ billed token class.
240239
241240 ``at`` is the instant the ORIGINAL traffic ran, and is required. A
242241 counterfactual priced at a different date than the traffic it replaces
@@ -998,25 +997,38 @@ def _segment_counts(
998997 )
999998
1000999
1000+ class _ModelCostAgg :
1001+ """Per-model token/cost aggregate for a mixed-model counterfactual."""
1002+
1003+ __slots__ = ("new_input" , "output" , "reread" , "cache_write" , "cost" ,
1004+ "first_turn_at" )
1005+
1006+ def __init__ (self ) -> None :
1007+ self .new_input = 0
1008+ self .output = 0
1009+ self .reread = 0
1010+ self .cache_write = 0
1011+ self .cost = 0.0
1012+ self .first_turn_at : datetime | None = None
1013+
1014+
10011015class _SessionAgg :
10021016 """Per-session aggregate of the flagged cheap-segment PREMIUM turns, for the
10031017 spot-check example list (largest-quota first)."""
10041018
10051019 __slots__ = ("session_id" , "quota_by_model" , "quota" , "new_input" ,
1006- "output" , "reread" , "tool_calls" , "cost" , "first_turn_at " )
1020+ "output" , "reread" , "tool_calls" , "cost" , "pricing_by_model " )
10071021
10081022 def __init__ (self , session_id : str ) -> None :
10091023 self .session_id = session_id
1010- # Earliest flagged turn in this session — the instant the counterfactual
1011- # below is priced at. Carried on the aggregate because the aggregate is
1012- # what gets priced: without it the dollar figure would fall back to
1013- # today's rate for traffic that ran weeks ago.
1014- self .first_turn_at : datetime | None = None
10151024 # (provider, model) -> flagged premium quota, so the example reports the
10161025 # model that actually carried the most misallocated quota in this session
10171026 # rather than freezing on whichever premium turn happened to appear first
10181027 # (a mixed-model session could otherwise mislabel the routing suggestion).
10191028 self .quota_by_model : dict [tuple [str | None , str ], float ] = {}
1029+ # Keep token classes separated by original model. A mixed-model session
1030+ # cannot be priced by applying one model's downgrade to every turn.
1031+ self .pricing_by_model : dict [tuple [str | None , str ], _ModelCostAgg ] = {}
10201032 self .quota = 0.0
10211033 self .new_input = 0
10221034 self .output = 0
@@ -1121,11 +1133,20 @@ def audit_opus_quota(
11211133 agg .reread += t .reread_tokens
11221134 agg .tool_calls += t .tool_fanout
11231135 agg .cost += t .cost_usd
1136+ key = (t .provider , t .model )
1137+ pricing_agg = agg .pricing_by_model .setdefault (
1138+ key , _ModelCostAgg (),
1139+ )
1140+ pricing_agg .new_input += t .new_input_tokens
1141+ pricing_agg .output += t .output_tokens
1142+ pricing_agg .reread += t .reread_tokens
1143+ pricing_agg .cache_write += t .cache_write_tokens
1144+ pricing_agg .cost += t .cost_usd
11241145 if t .start_time is not None and (
1125- agg .first_turn_at is None or t .start_time < agg .first_turn_at
1146+ pricing_agg .first_turn_at is None
1147+ or t .start_time < pricing_agg .first_turn_at
11261148 ):
1127- agg .first_turn_at = t .start_time
1128- key = (t .provider , t .model )
1149+ pricing_agg .first_turn_at = t .start_time
11291150 agg .quota_by_model [key ] = (
11301151 agg .quota_by_model .get (key , 0.0 ) + t .quota_weighted_tokens
11311152 )
@@ -1139,26 +1160,27 @@ def audit_opus_quota(
11391160 if session_flagged :
11401161 audit .candidate_sessions += 1
11411162
1142- # Secondary API-only implied-dollar counterfactual, aggregated over the
1143- # flagged premium turns and priced at each session's dominant premium model's
1144- # cheaper alternative (never the headline). Deliberately never folded into
1145- # `analyze_model_downgrade`'s `DowngradeFinding.past_overspend_usd`
1146- # (the `tj optimize` downsize card) — see the comment on that assignment
1147- # for why the two stay separate.
1163+ # Secondary API-only implied-dollar counterfactual, priced per original
1164+ # premium model at that model's cheaper alternative (never the headline).
1165+ # Deliberately never folded into `analyze_model_downgrade`'s
1166+ # `DowngradeFinding.past_overspend_usd` (the `tj optimize` downsize card) —
1167+ # see the comment on that assignment for why the two stay separate.
11481168 for agg in aggs .values ():
1149- provider , model = agg .dominant_model ()
1150- alt = lookup_downgrade (provider , model ) if provider else None
1151- if not alt :
1152- continue
1153- # ``alt`` is only set when ``provider`` was truthy above.
1154- assert provider is not None
1155- alt_unit = _alt_unit_cost (
1156- provider , model , alt , agg .new_input , agg .output , agg .reread ,
1157- at = span_instant (agg .first_turn_at , window_start = since ),
1158- )
1159- if alt_unit is not None :
1160- actual_cost += agg .cost
1161- alt_cost += alt_unit
1169+ for (provider , model ), pricing_agg in agg .pricing_by_model .items ():
1170+ alt = lookup_downgrade (provider , model ) if provider else None
1171+ if not alt :
1172+ continue
1173+ # ``alt`` is only set when ``provider`` was truthy above.
1174+ assert provider is not None
1175+ alt_unit = _alt_unit_cost (
1176+ provider , model , alt ,
1177+ pricing_agg .new_input , pricing_agg .output , pricing_agg .reread ,
1178+ cache_write_tokens = pricing_agg .cache_write ,
1179+ at = span_instant (pricing_agg .first_turn_at , window_start = since ),
1180+ )
1181+ if alt_unit is not None :
1182+ actual_cost += pricing_agg .cost
1183+ alt_cost += alt_unit
11621184
11631185 audit .opus_tokens = int (round (premium_quota ))
11641186 audit .candidate_tokens = int (round (misallocated_quota ))
0 commit comments