fix(llm): Anthropic 结构化输出记真实 usage——修计费记 0 成本(CR-H3)
This commit is contained in:
@@ -164,6 +164,51 @@ async def test_gemini_structured_output() -> None:
|
||||
assert "config" in models.last_kwargs
|
||||
|
||||
|
||||
async def test_anthropic_structured_output_threads_usage() -> None:
|
||||
# 结构化输出必须记真实 usage(CR-H3):instructor 的 create_with_completion 回
|
||||
# (parsed, raw),raw.usage 带真实 token 数;网关据此记账(§4.8)。
|
||||
class _RawUsage:
|
||||
input_tokens = 120
|
||||
output_tokens = 80
|
||||
cache_read_input_tokens = 30
|
||||
|
||||
class _Raw:
|
||||
usage = _RawUsage()
|
||||
|
||||
class _FakeStructured:
|
||||
def __init__(self) -> None:
|
||||
self.last_kwargs: dict[str, Any] = {}
|
||||
|
||||
async def create_with_completion(
|
||||
self, *, response_model: type[BaseModel], **kwargs: Any
|
||||
) -> tuple[BaseModel, Any]:
|
||||
self.last_kwargs = kwargs
|
||||
return response_model(score=7), _Raw()
|
||||
|
||||
# 修前路径调 `.create`(仅回 parsed)——保留它,令 RED 是值不符(0!=120)
|
||||
# 而非 AttributeError,坐实旧代码硬编码零 usage 的缺陷。
|
||||
async def create(self, *, response_model: type[BaseModel], **kwargs: Any) -> BaseModel:
|
||||
return response_model(score=7)
|
||||
|
||||
fake = _FakeStructured()
|
||||
client = _FakeAnthropicClient(_FakeAnthropicMessages("", 0, 0, 0))
|
||||
adapter = AnthropicAdapter("anthropic", client, structured_client=fake)
|
||||
req = LlmRequest(
|
||||
tier="analyst",
|
||||
input="打分",
|
||||
output_schema=Out,
|
||||
scope=Scope(user_id=uuid.UUID(int=1)),
|
||||
)
|
||||
|
||||
result = await adapter.complete(req, "claude-3-5-sonnet")
|
||||
|
||||
assert isinstance(result.parsed, Out)
|
||||
assert result.parsed.score == 7
|
||||
assert result.usage.input_tokens == 120
|
||||
assert result.usage.output_tokens == 80
|
||||
assert result.usage.cache_read_tokens == 30
|
||||
|
||||
|
||||
async def test_anthropic_stream_yields_text_then_usage() -> None:
|
||||
class _StreamEvent:
|
||||
def __init__(self, **kw: Any) -> None:
|
||||
|
||||
Reference in New Issue
Block a user