feat(core): 添加 LLMResponse frozen dataclass
含 content/thinking/ttft_ms/max_inter_token_ms/call_id 等 11 个字段。 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1 +1,24 @@
|
|||||||
"""跨模块共享类型。"""
|
"""跨模块共享类型。"""
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from dataclasses import dataclass
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass(frozen=True)
|
||||||
|
class LLMResponse:
|
||||||
|
"""LLM/VLM 调用的统一返回值。
|
||||||
|
|
||||||
|
由 adapters 层生成,core 层消费。frozen=True 确保响应不可变。
|
||||||
|
"""
|
||||||
|
|
||||||
|
content: str
|
||||||
|
thinking: str
|
||||||
|
model: str
|
||||||
|
provider: str
|
||||||
|
prompt_tokens: int
|
||||||
|
completion_tokens: int
|
||||||
|
latency_ms: int
|
||||||
|
ttft_ms: float | None
|
||||||
|
max_inter_token_ms: float | None
|
||||||
|
cache_hit: bool
|
||||||
|
call_id: str
|
||||||
|
|||||||
@@ -0,0 +1,51 @@
|
|||||||
|
"""core/types.py 单元测试。"""
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
from core.types import LLMResponse
|
||||||
|
|
||||||
|
|
||||||
|
class TestLLMResponse:
|
||||||
|
@pytest.fixture()
|
||||||
|
def sample_response(self) -> LLMResponse:
|
||||||
|
return LLMResponse(
|
||||||
|
content="回答内容",
|
||||||
|
thinking="思考过程",
|
||||||
|
model="deepseek-v4-pro",
|
||||||
|
provider="deepseek",
|
||||||
|
prompt_tokens=100,
|
||||||
|
completion_tokens=50,
|
||||||
|
latency_ms=1200,
|
||||||
|
ttft_ms=350.0,
|
||||||
|
max_inter_token_ms=45.0,
|
||||||
|
cache_hit=False,
|
||||||
|
call_id="test-uuid-001",
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_frozen_prevents_mutation(self, sample_response: LLMResponse) -> None:
|
||||||
|
with pytest.raises(AttributeError):
|
||||||
|
sample_response.content = "篡改"
|
||||||
|
|
||||||
|
def test_all_fields_accessible(self, sample_response: LLMResponse) -> None:
|
||||||
|
assert sample_response.content == "回答内容"
|
||||||
|
assert sample_response.thinking == "思考过程"
|
||||||
|
assert sample_response.model == "deepseek-v4-pro"
|
||||||
|
assert sample_response.provider == "deepseek"
|
||||||
|
assert sample_response.prompt_tokens == 100
|
||||||
|
assert sample_response.completion_tokens == 50
|
||||||
|
assert sample_response.latency_ms == 1200
|
||||||
|
assert sample_response.ttft_ms == 350.0
|
||||||
|
assert sample_response.max_inter_token_ms == 45.0
|
||||||
|
assert sample_response.cache_hit is False
|
||||||
|
assert sample_response.call_id == "test-uuid-001"
|
||||||
|
|
||||||
|
def test_cache_hit_response_has_none_ttft(self) -> None:
|
||||||
|
resp = LLMResponse(
|
||||||
|
content="cached", thinking="", model="m", provider="p",
|
||||||
|
prompt_tokens=0, completion_tokens=0, latency_ms=1,
|
||||||
|
ttft_ms=None, max_inter_token_ms=None, cache_hit=True, call_id="c",
|
||||||
|
)
|
||||||
|
assert resp.ttft_ms is None
|
||||||
|
assert resp.max_inter_token_ms is None
|
||||||
|
assert resp.cache_hit is True
|
||||||
Reference in New Issue
Block a user