mirror of
https://github.com/BerriAI/litellm.git
synced 2026-09-25 01:02:15 +00:00
Fix KeyError 'text' on content_block_start in Anthropic streaming
Some Anthropic-compatible upstream providers (e.g. api-cc.freemodel.dev
proxying Claude Haiku 4.5) emit content_block_start events for text
blocks without the optional 'text' field, causing KeyError in the
chunk_parser. Per Anthropic's streaming spec the initial text on
content_block_start is conventionally empty (""), and the actual
content arrives via subsequent content_block_delta chunks regardless.
Use .get("text", "") so the parser tolerates the missing field
instead of aborting the stream with MidStreamFallbackError.
Reproduction:
curl -sN -X POST http://proxy/v1/chat/completions \
-H 'Authorization: Bearer $KEY' \
-d '{"model":"claude-haiku-4-5","messages":[...],"stream":true}'
Before patch: stream aborts with KeyError: 'text' at handler.py:789
After patch: stream completes normally.
This commit is contained in:
parent
e58a561caa
commit
9befaab288
2 changed files with 33 additions and 1 deletions
|
|
@ -820,7 +820,12 @@ class ModelResponseIterator:
|
|||
"type"
|
||||
]
|
||||
if content_block_start["content_block"]["type"] == "text":
|
||||
text = content_block_start["content_block"]["text"]
|
||||
# Defensive .get() — some upstream Anthropic-compatible
|
||||
# providers omit the "text" field on content_block_start
|
||||
# for "text" blocks (spec says it should be ""). Fall back
|
||||
# to "" instead of raising KeyError, since the actual text
|
||||
# arrives via subsequent content_block_delta chunks anyway.
|
||||
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"
|
||||
|
|
|
|||
|
|
@ -1901,3 +1901,30 @@ def test_non_bash_tool_result_skipped():
|
|||
assert (
|
||||
len(code_results) == 0
|
||||
), f"Expected 0 code_interpreter_results for text_editor result, got {len(code_results)}"
|
||||
|
||||
|
||||
def test_content_block_start_text_missing_text_field():
|
||||
"""
|
||||
Regression test: some Anthropic-compatible upstreams omit the optional
|
||||
"text" field on content_block_start events whose type == "text". Per
|
||||
the Anthropic streaming spec the value is conventionally "", and the
|
||||
real text arrives via subsequent content_block_delta chunks anyway,
|
||||
so the parser must tolerate the missing field instead of raising
|
||||
KeyError mid-stream.
|
||||
"""
|
||||
chunk = {
|
||||
"type": "content_block_start",
|
||||
"index": 0,
|
||||
# NOTE: "text" field intentionally omitted to mirror upstream payloads
|
||||
# observed from Anthropic-compatible providers (e.g. proxies fronting
|
||||
# claude-haiku-4-5-20251001) that skip the empty default.
|
||||
"content_block": {"type": "text"},
|
||||
}
|
||||
|
||||
iterator = ModelResponseIterator(None, sync_stream=True)
|
||||
|
||||
# Must not raise KeyError: 'text'
|
||||
parsed = iterator.chunk_parser(chunk=chunk)
|
||||
|
||||
assert parsed is not None
|
||||
assert parsed.choices[0].delta.content == ""
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue