fix: stop passing est_tokens off as measured usage

usage 帧缺失/非法时不再拿 est_tokens(最坏情形上界)当实测值,chat 与
embedding 两处兜底改记 0 并标 unavailable;打捞覆盖加 measured 前置条件,
避免 0/0 被洗成 estimated 而算出假的 0.0。embedding 全批合并扩三态(任一批
不可得 → 整体不可得),_total_cost 遇不可得批整体记 NULL。
This commit is contained in:
2026-07-30 10:39:32 -04:00
parent 42e429eb58
commit 195454d2e3
6 changed files with 252 additions and 25 deletions
+16 -2
View File
@@ -385,14 +385,21 @@ class EmbeddingClient:
vectors = [_l2_normalize(v) for v in vectors]
first = outcomes[0]
prompt_tokens = sum(o.result.prompt_tokens for o in outcomes)
estimated = any(o.result.usage_source == "estimated" for o in outcomes)
# 三态合并优先级(解耦设计 §3.2 #10): 任一批不可得 → 整体不可得
sources = {o.result.usage_source for o in outcomes}
if "unavailable" in sources:
merged_source = "unavailable"
elif "estimated" in sources:
merged_source = "estimated"
else:
merged_source = "measured"
return EmbeddingResponse(
vectors=vectors,
dim=first.result.dim,
model=first.source.model,
provider=first.source.provider,
prompt_tokens=prompt_tokens,
usage_source="estimated" if estimated else "measured",
usage_source=merged_source,
latency_ms=sum(o.latency_ms for o in outcomes),
call_id=first.call_id,
source_name=first.source.name,
@@ -400,8 +407,15 @@ class EmbeddingClient:
)
def _total_cost(self, outcomes: list[_BatchOutcome]) -> float | None:
"""全批成本;任一批用量不可得则整体记 NULL(解耦设计 §3.2 #11)。
逐批求和会把不可得的批当 0 计入,给出一个偏低却看似有效的金额——
"宁可算不出成本,也不算错成本"的不变式相悖。
"""
if self._pricing is None:
return None
if any(o.result.usage_source == "unavailable" for o in outcomes):
return None
costs = [self._pricing.cost(o.source.model, o.result.prompt_tokens, 0) for o in outcomes]
known = [c for c in costs if c is not None]
return sum(known) if known else None