mirror of
https://github.com/BerriAI/litellm.git
synced 2026-10-03 02:22:24 +00:00
fix(anthropic/stream): tolerate missing 'text' on content_block_start (#28067)
Anthropic Messages streaming spec: the initial text on a content_block_start
text block is conventionally an empty string and the real content arrives via
subsequent content_block_delta chunks. Some Anthropic-compatible upstreams
omit the field entirely rather than sending "", which previously caused
chunk_parser to raise KeyError and abort the stream with
MidStreamFallbackError. Use .get('text', '') so the parser keeps the existing
text='' default and lets the deltas accumulate normally.
Adds a regression test in tests/llm_translation/test_anthropic_completion.py.
This commit is contained in:
parent
e182a5e0ba
commit
1eb1d2388b
2 changed files with 26 additions and 1 deletions
|
|
@ -820,7 +820,13 @@ class ModelResponseIterator:
|
|||
"type"
|
||||
]
|
||||
if content_block_start["content_block"]["type"] == "text":
|
||||
text = content_block_start["content_block"]["text"]
|
||||
# Anthropic Messages streaming spec: the initial text on a
|
||||
# content_block_start text block is conventionally "" and the
|
||||
# real content arrives via subsequent content_block_delta
|
||||
# chunks. Some Anthropic-compatible upstreams omit the field
|
||||
# entirely rather than sending "", so default to "" to avoid
|
||||
# aborting the stream with KeyError.
|
||||
text = content_block_start["content_block"].get("text", "")
|
||||
elif (
|
||||
content_block_start["content_block"]["type"] == "tool_use"
|
||||
or content_block_start["content_block"]["type"] == "server_tool_use"
|
||||
|
|
|
|||
|
|
@ -262,6 +262,25 @@ def test_anthropic_tool_streaming():
|
|||
assert tool_use["index"] == correct_tool_index
|
||||
|
||||
|
||||
def test_anthropic_content_block_start_text_missing_field():
|
||||
"""
|
||||
Regression for #28067: some Anthropic-compatible upstreams omit the
|
||||
optional `text` field on a content_block_start text block. The actual
|
||||
content arrives via subsequent content_block_delta chunks, so the parser
|
||||
must tolerate the missing field instead of raising KeyError and aborting
|
||||
the stream.
|
||||
"""
|
||||
response_iter = ModelResponseIterator([], False)
|
||||
chunk = {
|
||||
"type": "content_block_start",
|
||||
"index": 0,
|
||||
# 'text' field intentionally omitted (observed in the wild)
|
||||
"content_block": {"type": "text"},
|
||||
}
|
||||
# Should not raise.
|
||||
response_iter.chunk_parser(chunk=chunk)
|
||||
|
||||
|
||||
def test_process_anthropic_headers_empty():
|
||||
result = process_anthropic_headers({})
|
||||
assert result == {}, "Expected empty dictionary for no input"
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue