feat: add cached prompt tokens and reported model to response types
This commit is contained in:
@@ -30,12 +30,20 @@ class LLMResponse:
|
|||||||
ttft_ms: float | None
|
ttft_ms: float | None
|
||||||
max_inter_token_ms: float | None
|
max_inter_token_ms: float | None
|
||||||
cache_hit: bool
|
cache_hit: bool
|
||||||
|
"""**PolyGateway 自身响应缓存**命中(未产生网关调用);与供应商侧 prompt
|
||||||
|
cache 无关,后者见 `cached_prompt_tokens`。"""
|
||||||
call_id: str
|
call_id: str
|
||||||
# —— 库新增(只增不删,必带默认值;迁移兼容硬约束)——
|
# —— 库新增(只增不删,必带默认值;迁移兼容硬约束)——
|
||||||
source_name: str = ""
|
source_name: str = ""
|
||||||
cost: float | None = None
|
cost: float | None = None
|
||||||
usage_source: str = "measured"
|
usage_source: str = "measured"
|
||||||
structured_data: Any | None = None
|
structured_data: Any | None = None
|
||||||
|
cached_prompt_tokens: int | None = None
|
||||||
|
"""供应商 prompt cache 命中的输入 token 数(issue #3);None = 该源未上报,
|
||||||
|
与"上报了但是 0"(真实零命中)区分——两者对下游的处置不同。"""
|
||||||
|
model_reported: str | None = None
|
||||||
|
"""API 响应体里的 model 字段;None = 未上报。与 `model`(配置别名)可能
|
||||||
|
分叉——供应商把别名指向新权重时,实验复现必须认这个串。"""
|
||||||
|
|
||||||
|
|
||||||
@dataclass(frozen=True)
|
@dataclass(frozen=True)
|
||||||
@@ -82,6 +90,9 @@ class TransportResult:
|
|||||||
ttft_ms: float | None
|
ttft_ms: float | None
|
||||||
max_inter_token_ms: float | None
|
max_inter_token_ms: float | None
|
||||||
raw: dict[str, Any]
|
raw: dict[str, Any]
|
||||||
|
# —— 可观测字段(issue #3;带默认值,非 OpenAI 兼容的 transport 可不填)——
|
||||||
|
cached_prompt_tokens: int | None = None
|
||||||
|
model_reported: str | None = None
|
||||||
|
|
||||||
|
|
||||||
@dataclass(frozen=True)
|
@dataclass(frozen=True)
|
||||||
|
|||||||
@@ -49,6 +49,29 @@ class TestLLMResponse:
|
|||||||
assert resp.usage_source == "measured"
|
assert resp.usage_source == "measured"
|
||||||
assert resp.structured_data is None
|
assert resp.structured_data is None
|
||||||
|
|
||||||
|
def test_observability_fields_default_to_none(self):
|
||||||
|
"""issue #3: None = 该源未上报,与"上报了但是 0"区分(0 是真实零命中)。"""
|
||||||
|
resp = LLMResponse("c", "t", "m", "p", 1, 2, 3, None, None, False, "cid")
|
||||||
|
assert resp.cached_prompt_tokens is None
|
||||||
|
assert resp.model_reported is None
|
||||||
|
filled = LLMResponse(
|
||||||
|
"c",
|
||||||
|
"t",
|
||||||
|
"m",
|
||||||
|
"p",
|
||||||
|
1,
|
||||||
|
2,
|
||||||
|
3,
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
False,
|
||||||
|
"cid",
|
||||||
|
cached_prompt_tokens=0,
|
||||||
|
model_reported="MiniMax-Text-01-250321",
|
||||||
|
)
|
||||||
|
assert filled.cached_prompt_tokens == 0 # 真实零命中,不得与 None 混同
|
||||||
|
assert filled.model_reported == "MiniMax-Text-01-250321"
|
||||||
|
|
||||||
def test_frozen(self):
|
def test_frozen(self):
|
||||||
resp = LLMResponse("c", "t", "m", "p", 1, 2, 3, None, None, False, "cid")
|
resp = LLMResponse("c", "t", "m", "p", 1, 2, 3, None, None, False, "cid")
|
||||||
with pytest.raises(dataclasses.FrozenInstanceError):
|
with pytest.raises(dataclasses.FrozenInstanceError):
|
||||||
@@ -222,6 +245,8 @@ class TestAuxTypes:
|
|||||||
raw={"id": "x"},
|
raw={"id": "x"},
|
||||||
)
|
)
|
||||||
assert s.raw["id"] == "x"
|
assert s.raw["id"] == "x"
|
||||||
|
# issue #3: 新字段带默认值,不填也能构造(OCR 等其他 transport 零改动)
|
||||||
|
assert s.cached_prompt_tokens is None and s.model_reported is None
|
||||||
|
|
||||||
|
|
||||||
class TestOcrTypes:
|
class TestOcrTypes:
|
||||||
|
|||||||
Reference in New Issue
Block a user