Description
When a single Agent Framework Message with role="tool" contains multiple function_result contents (common for parallel tool calls), agent_framework_messages_to_agui emits only one AG-UI tool message — the last function_result — and silently drops the others.
Expected: one AG-UI role="tool" message per function_result (matching each call_id).
Actual: a single AG-UI tool message with only the last call_id / result. Upstream assistant messages still list all tool_calls, so clients (e.g. assistant-ui fromAgUiMessages) treat the missing results as pending tool approvals and render Allow / Deny on historical chats.
Steps to reproduce
- Run an agent that issues parallel tool calls in one turn (e.g. three
read_skill_resource / three search tools).
- Persist the conversation as Agent Framework messages. Observe that parallel results are often stored as one
role="tool" message whose contents is a list of several function_result items (same message_id).
- Convert with
agent_framework_messages_to_agui(...).
- Inspect the AG-UI output: only one of the
function_results survives.
Root cause (code)
In python/packages/ag-ui/agent_framework_ag_ui/_message_adapters.py, agent_framework_messages_to_agui loops contents and overwrites a single tool_result_call_id / content_text for every function_result:
elif content.type == "function_result":
tool_result_call_id = content.call_id
content_text = content.result if content.result is not None else ""
Then it appends one AG-UI dict. Parallel results in the same Message are collapsed.
Code Sample
from agent_framework import Message, Role
from agent_framework._types import FunctionCallContent, FunctionResultContent # adjust imports to current public API
from agent_framework_ag_ui._message_adapters import agent_framework_messages_to_agui
# Assistant announces three parallel calls
assistant = Message(
role="assistant",
message_id="asst-1",
contents=[
FunctionCallContent(call_id="call_a", name="tool_x", arguments="{}"),
FunctionCallContent(call_id="call_b", name="tool_x", arguments="{}"),
FunctionCallContent(call_id="call_c", name="tool_x", arguments="{}"),
],
)
# Framework often stores all results on one tool message
tool = Message(
role="tool",
message_id="tool-batch-1",
contents=[
FunctionResultContent(call_id="call_a", result="result-a"),
FunctionResultContent(call_id="call_b", result="result-b"),
FunctionResultContent(call_id="call_c", result="result-c"),
],
)
agui = agent_framework_messages_to_agui([assistant, tool])
tool_msgs = [m for m in agui if m.get("role") == "tool"]
assert len(tool_msgs) == 3, f"expected 3 tool messages, got {len(tool_msgs)}: {tool_msgs}"
# Today: len(tool_msgs) == 1 and toolCallId is only "call_c"
Error Messages / Stack Traces
No exception is raised. The failure is silent data loss during conversion, which surfaces in AG-UI / assistant-ui history as unresolved tool calls (requires-action / Allow–Deny).
Package Versions
agent-framework / agent-framework-core (local checkout of microsoft/agent-framework, Python packages under python/packages/)
agent-framework-ag-ui (python/packages/ag-ui)
- Observed while using AG-UI session history hydration against persisted Agent Framework messages
Python Version
Python 3.12
Additional Context
Suggested fix: when converting a Message, emit one AG-UI tool message per function_result content (each with its own toolCallId / content), instead of collapsing to the last result. Text / function_call handling on the same message can stay as today (or follow existing conventions if mixed contents are supported).
Happy to send a PR if maintainers agree with expanding one framework tool message into multiple AG-UI tool messages.
Description
When a single Agent Framework
Messagewithrole="tool"contains multiplefunction_resultcontents (common for parallel tool calls),agent_framework_messages_to_aguiemits only one AG-UI tool message — the lastfunction_result— and silently drops the others.Expected: one AG-UI
role="tool"message perfunction_result(matching eachcall_id).Actual: a single AG-UI tool message with only the last
call_id/ result. Upstream assistant messages still list alltool_calls, so clients (e.g. assistant-uifromAgUiMessages) treat the missing results as pending tool approvals and render Allow / Deny on historical chats.Steps to reproduce
read_skill_resource/ three search tools).role="tool"message whosecontentsis a list of severalfunction_resultitems (samemessage_id).agent_framework_messages_to_agui(...).function_results survives.Root cause (code)
In
python/packages/ag-ui/agent_framework_ag_ui/_message_adapters.py,agent_framework_messages_to_aguiloops contents and overwrites a singletool_result_call_id/content_textfor everyfunction_result:Then it appends one AG-UI dict. Parallel results in the same
Messageare collapsed.Code Sample
Error Messages / Stack Traces
No exception is raised. The failure is silent data loss during conversion, which surfaces in AG-UI / assistant-ui history as unresolved tool calls (
requires-action/ Allow–Deny).Package Versions
agent-framework/agent-framework-core(local checkout ofmicrosoft/agent-framework, Python packages underpython/packages/)agent-framework-ag-ui(python/packages/ag-ui)Python Version
Python 3.12
Additional Context
Suggested fix: when converting a
Message, emit one AG-UI tool message perfunction_resultcontent (each with its owntoolCallId/content), instead of collapsing to the last result. Text /function_callhandling on the same message can stay as today (or follow existing conventions if mixed contents are supported).Happy to send a PR if maintainers agree with expanding one framework tool message into multiple AG-UI tool messages.