mirror of
https://github.com/BerriAI/litellm.git
synced 2026-09-10 22:41:41 +00:00
Merge pull request #19067 from BerriAI/litellm_caching_fix
Fix: anthropic token counter with thinking
This commit is contained in:
commit
49d873e894
2 changed files with 81 additions and 0 deletions
|
|
@ -719,6 +719,12 @@ def _count_content_list(
|
|||
use_default_image_token_count,
|
||||
default_token_count,
|
||||
)
|
||||
elif c["type"] == "thinking":
|
||||
# Claude extended thinking content block
|
||||
# Count the thinking text and skip signature (opaque signature blob)
|
||||
thinking_text = c.get("thinking", "")
|
||||
if thinking_text:
|
||||
num_tokens += count_function(thinking_text)
|
||||
else:
|
||||
raise ValueError(
|
||||
f"Invalid content item type: {type(c).__name__}. "
|
||||
|
|
|
|||
|
|
@ -897,3 +897,78 @@ def test_token_counter_with_image_url():
|
|||
except ValueError as e:
|
||||
assert "Invalid detail value" in str(e), f"Expected detail validation error, got: {e}"
|
||||
|
||||
|
||||
def test_token_counter_with_thinking_content():
|
||||
"""
|
||||
Test that _count_content_list() correctly handles Claude's extended thinking content blocks.
|
||||
|
||||
Validates that:
|
||||
- 'thinking' content type is recognized and counted
|
||||
- 'thinking' text field is counted
|
||||
- 'signature' field is skipped (opaque signature blob)
|
||||
- Full conversation with thinking blocks works
|
||||
"""
|
||||
messages = [
|
||||
{
|
||||
"role": "user",
|
||||
"content": [
|
||||
{
|
||||
"type": "text",
|
||||
"text": "Analyze this complex problem: who came first, chicken or egg"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"role": "assistant",
|
||||
"content": [
|
||||
{
|
||||
"type": "thinking",
|
||||
"thinking": "This is actually a fascinating question that touches on philosophy, biology, and semantics. Let me break this down: The egg came first from an evolutionary biology perspective.",
|
||||
"signature": "EqcLCkYICxgCKkCrqu6lP..." # Should be skipped
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"text": "# The Chicken-or-Egg Question: A Multi-Layered Answer\n\n## **The Short Answer: The Egg Came First**"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"role": "user",
|
||||
"content": [
|
||||
{
|
||||
"type": "text",
|
||||
"text": "Thanks"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
|
||||
tokens = token_counter(model="anthropic/claude-sonnet-4-5-20250929", messages=messages)
|
||||
assert tokens > 0, f"Expected positive token count, got {tokens}"
|
||||
# Should count: user message + thinking text + response text + "Thanks"
|
||||
# The thinking text alone is ~30 tokens, plus other content should be > 50 total
|
||||
assert tokens > 50, f"Expected substantial token count for message with thinking, got {tokens}"
|
||||
|
||||
# Test that thinking block without 'thinking' field doesn't crash (edge case)
|
||||
messages_no_thinking = [
|
||||
{
|
||||
"role": "assistant",
|
||||
"content": [
|
||||
{
|
||||
"type": "thinking",
|
||||
# No 'thinking' field - should count as 0 tokens
|
||||
"signature": "EqcLCkYICxgCKkCrqu6lP..."
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"text": "Response"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
|
||||
tokens_no_thinking = token_counter(model="anthropic/claude-sonnet-4-5-20250929", messages=messages_no_thinking)
|
||||
assert tokens_no_thinking > 0, f"Expected positive token count even with empty thinking, got {tokens_no_thinking}"
|
||||
# Should only count "Response" and message overhead
|
||||
assert tokens_no_thinking < 15, f"Expected minimal token count for empty thinking block, got {tokens_no_thinking}"
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue