mirror of
https://github.com/BerriAI/litellm.git
synced 2026-09-16 23:41:43 +00:00
fix: tokenize text-source documents instead of using flat fallback
This commit is contained in:
parent
3576093641
commit
fafd46161d
2 changed files with 54 additions and 5 deletions
|
|
@ -681,17 +681,32 @@ def _count_anthropic_document(
|
|||
Anthropic shape:
|
||||
{"type": "document", "source": {...}, "title": ..., "context": ..., "citations": ...}
|
||||
|
||||
We count the text-bearing fields ("title", "context") with the model
|
||||
tokenizer and add DEFAULT_IMAGE_TOKEN_COUNT for the opaque source payload
|
||||
(typically a PDF — we cannot dimension it). Skipped: "type",
|
||||
"cache_control", "citations" (configuration metadata, not prompt content).
|
||||
Source variants (per the Anthropic Citations API):
|
||||
- {"type": "text", "media_type": "text/plain", "data": "<inline text>"}
|
||||
- {"type": "base64", "media_type": ..., "data": ...}
|
||||
- {"type": "url", "url": ...}
|
||||
- {"type": "file", "file_id": ...}
|
||||
|
||||
For text sources we tokenize source["data"] with the model tokenizer
|
||||
(it's plain prompt content). For base64 / url / file sources the
|
||||
payload is opaque (typically a PDF — we cannot dimension it) and we
|
||||
add DEFAULT_IMAGE_TOKEN_COUNT as the fallback.
|
||||
|
||||
Title and context are always counted. Skipped: "type", "cache_control",
|
||||
"citations" (configuration metadata, not prompt content).
|
||||
"""
|
||||
tokens = 0
|
||||
for field in ("title", "context"):
|
||||
value = content.get(field)
|
||||
if isinstance(value, str) and value:
|
||||
tokens += count_function(value)
|
||||
if content.get("source") is not None:
|
||||
source = content.get("source")
|
||||
if isinstance(source, dict) and source.get("type") == "text":
|
||||
data = source.get("data")
|
||||
if isinstance(data, str) and data:
|
||||
tokens += count_function(data)
|
||||
elif source is not None:
|
||||
# base64 / url / file / unknown — opaque payload, use the fallback.
|
||||
tokens += DEFAULT_IMAGE_TOKEN_COUNT
|
||||
return tokens
|
||||
|
||||
|
|
|
|||
|
|
@ -1130,6 +1130,40 @@ def test_token_counter_with_anthropic_document_file():
|
|||
assert tokens > 0, f"Expected positive token count, got {tokens}"
|
||||
|
||||
|
||||
def test_token_counter_with_anthropic_document_text():
|
||||
"""
|
||||
Anthropic-native document block with source.type='text' tokenizes the
|
||||
inline text in source['data'], not the flat DEFAULT_IMAGE_TOKEN_COUNT
|
||||
fallback used for opaque (base64/url/file) payloads.
|
||||
"""
|
||||
inline_text = "Lorem ipsum dolor sit amet, consectetur adipiscing elit. " * 200
|
||||
messages = [
|
||||
{
|
||||
"role": "user",
|
||||
"content": [
|
||||
{
|
||||
"type": "document",
|
||||
"source": {
|
||||
"type": "text",
|
||||
"media_type": "text/plain",
|
||||
"data": inline_text,
|
||||
},
|
||||
"title": "Long inline doc",
|
||||
}
|
||||
],
|
||||
}
|
||||
]
|
||||
|
||||
tokens = token_counter(
|
||||
model="anthropic/claude-sonnet-4-5-20250929", messages=messages
|
||||
)
|
||||
# The inline text alone is well over a thousand tokens — substantially
|
||||
# more than the 250-token fallback used for opaque sources.
|
||||
assert (
|
||||
tokens > 500
|
||||
), f"Expected text-source document to tokenize source['data'], got {tokens}"
|
||||
|
||||
|
||||
def test_token_counter_with_image_inside_tool_result():
|
||||
"""
|
||||
Image block nested inside tool_result.content is counted via recursion.
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue