refactor(anthropic): validate output_tokens_details with a pydantic model

This commit is contained in:
devin-ai-integration[bot] 2026-08-08 20:09:16 +00:00
parent bed5ab45c8
commit 0809e226df
3 changed files with 13 additions and 4 deletions

View file

@ -5,6 +5,7 @@ from collections.abc import Mapping
from typing import TYPE_CHECKING, Any, Final, NoReturn, cast
import httpx
from pydantic import ValidationError
import litellm
from litellm.constants import (
@ -39,6 +40,7 @@ from litellm.types.llms.anthropic import (
AnthropicMessagesTool,
AnthropicMessagesToolChoice,
AnthropicOutputSchema,
AnthropicOutputTokensDetails,
AnthropicSystemMessageContent,
AnthropicThinkingParam,
AnthropicWebSearchTool,
@ -2129,10 +2131,11 @@ class AnthropicConfig(AnthropicModelInfo, BaseConfig):
details: Final = usage_object.get("output_tokens_details")
if not isinstance(details, Mapping):
return None
thinking_tokens: Final = cast(Mapping[str, object], details).get("thinking_tokens")
if isinstance(thinking_tokens, bool) or not isinstance(thinking_tokens, (int, float)):
try:
parsed_details: Final = AnthropicOutputTokensDetails.model_validate(details)
except ValidationError:
return None
return int(thinking_tokens)
return parsed_details.thinking_tokens
def calculate_usage(
self,

View file

@ -609,6 +609,12 @@ class AnthropicResponseUsageBlock(BaseModel):
output_tokens: int
class AnthropicOutputTokensDetails(BaseModel):
model_config = ConfigDict(extra="allow", strict=True)
thinking_tokens: int | None = None
AnthropicFinishReason = Literal["end_turn", "max_tokens", "stop_sequence", "tool_use"]

View file

@ -2477,7 +2477,7 @@ def test_calculate_usage_reported_thinking_tokens_override_text_estimate():
@pytest.mark.parametrize(
"output_tokens_details",
[None, {}, {"thinking_tokens": None}],
[None, {}, {"thinking_tokens": None}, {"thinking_tokens": "180"}, {"thinking_tokens": True}],
)
def test_calculate_usage_falls_back_to_estimate_without_reported_thinking_tokens(output_tokens_details):
"""Older responses have no thinking_tokens, so the visible-text estimate must still apply."""