Repository navigation
Python: [Bug]: ChatResponse.to_updates() is not the inverse of from_updates() for consecutive same-role messages without message_id #9848
Description
Observed Behavior
ChatResponse.to_updates() / AgentResponse.to_updates() are documented as the inverse of from_updates(): "each message becomes one update, and the response-level fields are carried on those updates so that re-assembling them reproduces this response" (python/packages/core/agent_framework/_types.py:3369-3379). That does not hold for consecutive messages with the same role/author and no message_id: the round-trip merges them into one message and drops message-level additional_properties.
first = Message(role="assistant", contents=[Content.from_text("first")], message_id="m1")
# same role/author, no message_id (a provider may not stamp one)
second = Message(
role="assistant",
contents=[Content.from_text("second")],
additional_properties={"keep": "me"},
)
response = AgentResponse(messages=[first, second])
rebuilt = AgentResponse.from_updates(response.to_updates())
# rebuilt.messages -> 1 message, text "firstsecond", additional_properties {}The same gap appears through the public buffered-stream API (ResponseStream.buffer_updates(result_to_updates=AgentResponse.to_updates)): the buffered final response has 2 messages, but replaying the released updates yields 1 message with the concatenated text.
Expected Behavior
from_updates(to_updates(response)) should reproduce the response: 2 messages with their original contents and additional_properties, matching the docstring's inverse claim. If merging is intended when messages lack ids, the docstring and the buffered-release path should state that limitation explicitly.
Steps to Reproduce
- Build an
AgentResponsewith two consecutive assistant messages, the second withoutmessage_idbut withadditional_properties. - Call
response.to_updates()andAgentResponse.from_updates(updates). - Observe one merged message and lost properties; the buffered
ResponseStreampath reproduces the same result.
Minimal Reproduction
from agent_framework import AgentResponse, Content, Message
first = Message(role="assistant", contents=[Content.from_text("first")], message_id="m1")
second = Message(
role="assistant",
contents=[Content.from_text("second")],
additional_properties={"keep": "me"},
)
response = AgentResponse(messages=[first, second])
updates = response.to_updates()
rebuilt = AgentResponse.from_updates(updates)
print(len(updates), len(rebuilt.messages), rebuilt.text, rebuilt.messages[0].additional_properties)
# 2 1 firstsecond {}Error Messages and Stack Traces
No exception; actual output:
direct: messages before = 2
direct: updates emitted = 2 (one per message, per docstring)
direct: messages after = 1
direct: rebuilt text = 'firstsecond'
direct: rebuilt second message properties = {}
stream: final messages = 2
stream: released updates = 2
stream: replay messages = 1 ['firstsecond']
(The stream lines come from ResponseStream.buffer_updates(result_to_updates=AgentResponse.to_updates) then replaying the buffered updates.)
Package Versions
agent-framework-core: 1.23.0 (main at 85f3fc6)
Python Version
Python 3.12.2
Operating System
Windows
Regression
Unknown
Additional Context
- Likely root cause: each update carries role/author but no message boundary;
_process_update(_types.py:2548-2565) merges consecutive same-role/author updates, andMessage.additional_propertiesis not carried onto updates. - Searched open and closed issues/PRs for
to_updates/from_updatesround-trip: nearest are Python: [Bug]: Streamed updates from different authors are merged into one message #9352 (closed, author boundary infrom_updates), Python: Preserve Responses message identity in streaming #9768 (merged, OpenAI Responses adapter stamps updatemessage_id), Python: Preserve per-choice message boundaries when streaming chat completions (n>1) #9780 (open, n>1 interleaving); none covers this message-level round-trip loss. - Fix requires an API decision (synthesize per-update message ids and/or carry
Message.additional_properties), so this is filed as a bug report for maintainer direction rather than a drive-by PR.
AI assistance disclosure: analysis and the reproduction script were prepared with AI assistance; I personally ran and verified the reproduction and the output above.
Acknowledgements: I searched existing issues and did not find a duplicate; I personally verified this behavior; I will wait for explicit maintainer agreement before starting implementation of a non-trivial change.
Metadata
Metadata
Labels
Type
Projects
- StatusShow more project fieldsNo status
Activity
github-actions commentedon Oct 29, 2026
🤖 Automated triage reproduction notes (agent-authored — trust but verify)
Agent analysis
The bug reproduces in python/packages/core/agent_framework/_types.py::_process_update, ChatResponse.to_updates, and AgentResponse.to_updates when consecutive messages share a role and the later message lacks message_id. The resulting update is appended to the prior message, and its message-level additional_properties are absent from the update. Minimal repro: create two assistant messages where the first has message_id="m1" and the second has no ID plus additional_properties, then call from_updates(response.to_updates()).
python/packages/core/tests/core/test_types.py::test_response_to_updates_round_trips_same_role_messages_without_message_id1.23.01.23.0