mirror of
https://github.com/BerriAI/litellm.git
synced 2026-08-28 05:25:59 +00:00
fix(anthropic): fall back when only some compaction iterations report thinking tokens
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
This commit is contained in:
parent
aadfa89ff6
commit
53ee9c8293
4 changed files with 60 additions and 17 deletions
|
|
@ -2134,9 +2134,10 @@ class AnthropicConfig(AnthropicModelInfo, BaseConfig):
|
|||
reasoning_content: str | None,
|
||||
completion_response: Mapping[str, object] | None,
|
||||
) -> CompletionTokensDetailsWrapper:
|
||||
iteration_thinking_tokens: Final = self._sum_iteration_thinking_tokens(iterations) if iterations else None
|
||||
reported_thinking_tokens: Final = (
|
||||
self._sum_iteration_thinking_tokens(iterations)
|
||||
if iterations
|
||||
iteration_thinking_tokens
|
||||
if iteration_thinking_tokens is not None
|
||||
else self._thinking_tokens_from_usage(usage_object)
|
||||
)
|
||||
if reported_thinking_tokens is not None:
|
||||
|
|
@ -2160,10 +2161,11 @@ class AnthropicConfig(AnthropicModelInfo, BaseConfig):
|
|||
|
||||
def _sum_iteration_thinking_tokens(self, iterations: Sequence[object]) -> int | None:
|
||||
per_iteration: Final = tuple(
|
||||
self._thinking_tokens_from_usage(iteration) for iteration in iterations if isinstance(iteration, Mapping)
|
||||
self._thinking_tokens_from_usage(iteration) if isinstance(iteration, Mapping) else None
|
||||
for iteration in iterations
|
||||
)
|
||||
reported: Final = tuple(tokens for tokens in per_iteration if tokens is not None)
|
||||
return sum(reported) if reported else None
|
||||
return sum(reported) if len(reported) == len(per_iteration) else None
|
||||
|
||||
@staticmethod
|
||||
def is_anthropic_usage_object(usage_object: dict) -> bool:
|
||||
|
|
|
|||
|
|
@ -1763,18 +1763,15 @@ class LiteLLMCompletionResponsesConfig:
|
|||
choice.finish_reason
|
||||
),
|
||||
role="assistant",
|
||||
content=(
|
||||
[
|
||||
OutputText(
|
||||
type="output_text",
|
||||
text=reasoning_content,
|
||||
annotations=[],
|
||||
)
|
||||
]
|
||||
if reasoning_content
|
||||
# mutable-ok: GenericResponseOutputItem.content is typed as a list
|
||||
else []
|
||||
),
|
||||
content=[
|
||||
OutputText(
|
||||
type="output_text",
|
||||
text=text,
|
||||
annotations=[],
|
||||
)
|
||||
for text in (reasoning_content,)
|
||||
if text
|
||||
],
|
||||
encrypted_content=encrypted_content,
|
||||
)
|
||||
]
|
||||
|
|
|
|||
|
|
@ -612,7 +612,7 @@ class AnthropicResponseUsageBlock(BaseModel):
|
|||
class AnthropicOutputTokensDetails(BaseModel):
|
||||
model_config = ConfigDict(extra="allow")
|
||||
|
||||
thinking_tokens: Optional[int] = None
|
||||
thinking_tokens: int | None = None
|
||||
|
||||
|
||||
AnthropicFinishReason = Literal["end_turn", "max_tokens", "stop_sequence", "tool_use"]
|
||||
|
|
|
|||
|
|
@ -180,6 +180,50 @@ def test_calculate_usage_sums_provider_thinking_tokens_across_iterations():
|
|||
assert usage.completion_tokens_details.text_tokens == 150
|
||||
|
||||
|
||||
def test_calculate_usage_falls_back_when_only_some_iterations_report_thinking_tokens():
|
||||
config = AnthropicConfig()
|
||||
|
||||
usage = config.calculate_usage(
|
||||
usage_object={
|
||||
"input_tokens": 10,
|
||||
"output_tokens": 300,
|
||||
"output_tokens_details": {"thinking_tokens": 240},
|
||||
"iterations": [
|
||||
{"input_tokens": 5, "output_tokens": 100, "output_tokens_details": {"thinking_tokens": 60}},
|
||||
{"input_tokens": 5, "output_tokens": 200},
|
||||
],
|
||||
},
|
||||
reasoning_content=None,
|
||||
)
|
||||
|
||||
assert usage.completion_tokens == 300
|
||||
assert usage.completion_tokens_details is not None
|
||||
assert usage.completion_tokens_details.reasoning_tokens == 240
|
||||
assert usage.completion_tokens_details.text_tokens == 60
|
||||
|
||||
|
||||
def test_calculate_usage_reports_unknown_split_when_only_some_iterations_report_thinking_tokens():
|
||||
config = AnthropicConfig()
|
||||
|
||||
usage = config.calculate_usage(
|
||||
usage_object={
|
||||
"input_tokens": 10,
|
||||
"output_tokens": 300,
|
||||
"iterations": [
|
||||
{"input_tokens": 5, "output_tokens": 100, "output_tokens_details": {"thinking_tokens": 60}},
|
||||
{"input_tokens": 5, "output_tokens": 200},
|
||||
],
|
||||
},
|
||||
reasoning_content="",
|
||||
completion_response={"content": [{"type": "thinking", "thinking": "", "signature": "sig"}]},
|
||||
)
|
||||
|
||||
assert usage.completion_tokens == 300
|
||||
assert usage.completion_tokens_details is not None
|
||||
assert usage.completion_tokens_details.reasoning_tokens is None
|
||||
assert usage.completion_tokens_details.text_tokens is None
|
||||
|
||||
|
||||
def test_calculate_usage_reports_unknown_split_when_thinking_ran_without_a_count():
|
||||
config = AnthropicConfig()
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue