feat: collect provider cache tokens and reported model in transport
This commit is contained in:
@@ -45,6 +45,9 @@ def _sse_delta(chunk: dict[str, Any], usage_sink: dict[str, Any]) -> tuple[bool,
|
||||
"""从 chunk 提取增量: (True, content) 或 (False, reasoning);usage 帧旁路进 sink。"""
|
||||
if chunk.get("usage"):
|
||||
usage_sink["usage"] = chunk["usage"]
|
||||
if "model" not in usage_sink and chunk.get("model") is not None:
|
||||
# 首次写入即固定: 末帧的异常值不得覆盖首帧报的真实版本(issue #3)
|
||||
usage_sink["model"] = chunk["model"]
|
||||
choices = chunk.get("choices") or []
|
||||
if not choices:
|
||||
return None
|
||||
@@ -152,6 +155,32 @@ def _resolve_usage(usage: dict[str, Any]) -> tuple[int, int, str]:
|
||||
return 0, 0, "unavailable"
|
||||
|
||||
|
||||
def _coerce_cached_tokens(usage: Any) -> int | None:
|
||||
"""取 usage.prompt_tokens_details.cached_tokens(issue #3);形态异常一律 None。
|
||||
|
||||
`0` 与 `None` 必须可区分: 前者是"该源上报了一次真实零命中",后者是"该源
|
||||
不报这个数",下游对两者的处置不同(后者不可做缓存成本校正)。故只把
|
||||
**负数与非整数**归 None,`0` 如实保留。`bool` 显式排除——isinstance(True, int)
|
||||
在 Python 里为真,放行会把 `True` 记成 1 个命中 token。
|
||||
"""
|
||||
if not isinstance(usage, dict):
|
||||
return None
|
||||
details = usage.get("prompt_tokens_details")
|
||||
if not isinstance(details, dict):
|
||||
return None
|
||||
cached = details.get("cached_tokens")
|
||||
if isinstance(cached, bool) or not isinstance(cached, int) or cached < 0:
|
||||
return None
|
||||
return cached
|
||||
|
||||
|
||||
def _coerce_model_reported(value: Any) -> str | None:
|
||||
"""取响应体的 model 字段(issue #3);非 str 或空白串一律 None。"""
|
||||
if not isinstance(value, str) or not value.strip():
|
||||
return None
|
||||
return value
|
||||
|
||||
|
||||
def _resolve_stream_usage(sink: dict[str, Any], salvaged: bool) -> tuple[int, int, str]:
|
||||
"""流式用量口径: 打捞路径把 measured 降级为 estimated,unavailable 原样保留。
|
||||
|
||||
@@ -360,6 +389,8 @@ class OpenAICompatTransport:
|
||||
ttft_ms=ttft_ms,
|
||||
max_inter_token_ms=(max_gap if ttft_ms is not None else None),
|
||||
raw={"usage": sink.get("usage")},
|
||||
cached_prompt_tokens=_coerce_cached_tokens(sink.get("usage")),
|
||||
model_reported=_coerce_model_reported(sink.get("model")),
|
||||
)
|
||||
|
||||
def _check_done(
|
||||
@@ -442,6 +473,8 @@ class OpenAICompatTransport:
|
||||
ttft_ms=None,
|
||||
max_inter_token_ms=None,
|
||||
raw={"usage": body.get("usage")},
|
||||
cached_prompt_tokens=_coerce_cached_tokens(body.get("usage")),
|
||||
model_reported=_coerce_model_reported(body.get("model")),
|
||||
)
|
||||
|
||||
async def aclose(self) -> None:
|
||||
|
||||
Reference in New Issue
Block a user