Skip to content

Python: [Bug]: ChatResponse.to_updates() is not the inverse of from_updates() for consecutive same-role messages without message_id #9848

Open

Description

@BlueX888

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

  1. Build an AgentResponse with two consecutive assistant messages, the second without message_id but with additional_properties.
  2. Call response.to_updates() and AgentResponse.from_updates(updates).
  3. Observe one merged message and lost properties; the buffered ResponseStream path 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

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.

Activity

  1. added
    pythonUsage: [Issues, PRs], Target: Python
    triageUsage: [Issues], Target: All issues that still need to be triaged
    on Oct 29, 2026
  2. added
    reproducedUsage: [Issues], Target: all issues that can be reproduced by the triage workflow
    on Oct 29, 2026
  3. github-actions commented on Oct 29, 2026

    @github-actions
    Contributor

    🤖 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()).

    • Failing test: python/packages/core/tests/core/test_types.py::test_response_to_updates_round_trips_same_role_messages_without_message_id
    • Files examined: python/packages/core/agent_framework/_types.py, python/packages/core/tests/core/test_types.py, python/packages/core/pyproject.toml
    • Tests run: test_response_to_updates_round_trips_same_role_messages_without_message_id
    • Reported version: 1.23.0
    • Current version: 1.23.0
  4. added
    agentsUsage: [Issues, PRs], Target: Single agent
    and removed
    triageUsage: [Issues], Target: All issues that still need to be triaged
    on Oct 29, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Labels

agentsUsage: [Issues, PRs], Target: Single agentpythonUsage: [Issues, PRs], Target: PythonreproducedUsage: [Issues], Target: all issues that can be reproduced by the triage workflow

Type

Projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions