Repository navigation
Python: [Bug]: Streamed code interpreter deltas get silently dropped when one chunk's text happens to start with another's #8956
Description
Description
Streaming a code_interpreter_tool_call (or code_interpreter_tool_result) in small chunks can silently drop one of the chunks instead of concatenating them, corrupting the code the model generated.
_merge_content_item_lists in agent_framework/_types.py is what ChatResponse.from_updates / AgentResponse.from_updates use to fold streamed inputs/outputs chunks for a code interpreter call together. It has to tell apart two situations: a provider sending a genuine incremental delta (concatenate it) versus a provider resending the complete value after streaming finishes (replace the accumulated text with it) — the OpenAI Responses API does the latter via its code_interpreter_call_code.done event, which repeats the full code once after a run of .delta events for the same call.
It tells these apart by checking whether one chunk's text is a string-prefix of the other (incoming_text.startswith(existing_text) / existing_text.startswith(incoming_text)), and if so assumes it's the full-resend case and throws the other one away. That's a guess, and it's wrong whenever two ordinary, unrelated deltas simply happen to line up that way — which isn't rare for code: repeated indentation, a closing paren arriving right after an opening one, a one-digit number followed by a longer one that starts with the same digit, etc. Nothing raises in this case; the earlier (or later) chunk is just quietly dropped.
Expected behavior
Two genuine streamed deltas for the same call should always concatenate in arrival order. Only a chunk that is actually a full resend should replace what's been accumulated so far.
Actual behavior
from agent_framework import ChatResponse, ChatResponseUpdate, Content
updates = [
ChatResponseUpdate(
contents=[Content.from_code_interpreter_tool_call(call_id="ci_1", inputs=[Content.from_text(text="(")])]
),
ChatResponseUpdate(
contents=[Content.from_code_interpreter_tool_call(call_id="ci_1", inputs=[Content.from_text(text="()")])]
),
]
resp = ChatResponse.from_updates(updates)
print("".join(i.text or "" for i in resp.messages[0].contents[0].inputs))prints (), not ((). The first delta is discarded outright because "()" happens to start with "(". Swap the order ("()" then "(") and it's the opposite chunk that disappears, again leaving just ().
Code Sample
from agent_framework import ChatResponse, ChatResponseUpdate, Content
# A new line's leading indentation (4 spaces) happens to be a prefix of the next
# delta (the same 4 spaces followed by the line's actual content).
deltas = [" ", " print(i)\n"]
updates = [
ChatResponseUpdate(
contents=[Content.from_code_interpreter_tool_call(call_id="ci_1", inputs=[Content.from_text(text=d)])]
)
for d in deltas
]
resp = ChatResponse.from_updates(updates)
print(repr("".join(i.text or "" for i in resp.messages[0].contents[0].inputs)))
# prints ' print(i)\n' - expected ' print(i)\n' (both deltas concatenated)Error Messages / Stack Traces
No exception is raised. The code the agent believes it ran (and may show the user, or re-send on the next turn) is silently truncated or duplicated.
Package Versions
agent-framework-core: 1.19.0, agent-framework-openai: 1.14.4
Python Version
Python 3.12
Metadata
Metadata
Labels
Type
Projects
- StatusShow more project fieldsDone
Activity
github-actions commentedon Oct 1, 2026
🤖 Automated triage reproduction notes (agent-authored — trust but verify)
Agent analysis
The bug occurs in
python/packages/core/agent_framework/_types.py::_merge_content_item_listsat lines 2343-2346 when consecutive code-interpreter text deltas have a prefix relationship. It affects both inputs and outputs for calls/results finalized byChatResponse.from_updatesorAgentResponse.from_updates. Minimal repro: aggregate one call ID with input deltas"("then"()"and assert the resulting text is"(()"; the current result is"()".python/packages/core/tests/core/test_types.py::test_code_interpreter_tool_call_prefix_deltas_are_concatenatedagent-framework-core 1.19.0, agent-framework-openai 1.14.4agent-framework-core 1.19.0cr-sbarbouche commentedon Oct 27, 2026
Closing this out - the underlying issue is fixed, just via a different (and better) mechanism than the one proposed in #8957.
ADR 0044 / the content-identity-and-protocol-metadata spec added a first-class
merge_mode: Literal["append", "replace"]field onContent(coveringcode_interpreter_tool_call/code_interpreter_tool_resultamong other types) andOpenAIChatClientnow tagscode_interpreter_call_code.deltaasappendandcode_interpreter_call_code.doneasreplace. That removes the text-prefix guessing entirely, which is what was actually causing the drops here. Verified against current main:and through the real client with a realistic indentation-collision delta pair, both orders, with the same correct result.
#8957 is closed since it predates this and is no longer the right diff to land, but while working on it I found a separate, still-open bug in the same area (code interpreter streaming) - opening that as its own issue shortly.