fix(aws-strands): forward all parallel frontend tool results in continuation runs - #2218
fix(aws-strands): forward all parallel frontend tool results in continuation runs#2218FriedhelmWS wants to merge 2 commits into
Conversation
Python Preview PackagesVersion
Install with uvAdd the TestPyPI index to your [[tool.uv.index]]
name = "testpypi"
url = "https://test.pypi.org/simple/"
explicit = trueThen install the packages you need: # Core SDK
uv add 'ag-ui-protocol==0.0.0.dev1785969516' --index testpypi
# Integrations (each already depends on the matching ag-ui-protocol preview)
uv add 'ag-ui-langgraph==0.0.0.dev1785969516' --index testpypi
uv add 'ag-ui-crewai==0.0.0.dev1785969516' --index testpypi
# NOTE: ag-ui-agent-spec depends on pyagentspec (git-only, not on PyPI).
# You will need to install pyagentspec separately from its git repo.
uv add 'ag-ui-agent-spec==0.0.0.dev1785969516' --index testpypi
uv add 'ag_ui_adk==0.0.0.dev1785969516' --index testpypi
uv add 'ag_ui_strands==0.0.0.dev1785969516' --index testpypiInstall with pippip install \
--index-url https://test.pypi.org/simple/ \
--extra-index-url https://pypi.org/simple/ \
ag-ui-protocol==0.0.0.dev1785969516
Commit: 9ba84c9 |
@ag-ui/a2a-middleware
@ag-ui/a2ui-middleware
@ag-ui/event-throttle-middleware
@ag-ui/mcp-apps-middleware
@ag-ui/mcp-middleware
@ag-ui/a2a
@ag-ui/adk
@ag-ui/ag2
@ag-ui/agno
@ag-ui/aws-strands
@ag-ui/claude-agent-sdk
@ag-ui/claude-managed-agents
@ag-ui/crewai
@ag-ui/langchain
@ag-ui/langgraph
@ag-ui/llamaindex
@ag-ui/mastra
@ag-ui/pydantic-ai
@ag-ui/vercel-ai-sdk
@ag-ui/watsonx
@ag-ui/a2ui-toolkit
create-ag-ui-app
@ag-ui/client
@ag-ui/core
@ag-ui/encoder
@ag-ui/proto
commit: |
|
Thanks for this, and apologies for the delay. The diagnosis is right, the fix is right, and it collides with nothing. Of the changes currently in flight on this file it's the cleanest, and I'd like it to be the first to land. One thing blocks it. ConfirmedVerified against current Blocker: an existing test asserts the old behaviour
assert instance.stream_prompts == ["approve returned: R2"]That test sends two frontend results ( Worth being careful rather than just re-baselining it: the fallback behaviour that test guards is still correct and deliberate; when not every non-void result resolves to a native id, the adapter deliberately streams a synthetic user message instead of assert instance.stream_prompts == ["approve returned: R1\napprove returned: R2"]leaving the rest of the test intact. Two requestsPlease keep the rationale comments. The diff removes about fifteen lines that record why the code forwards the real result rather than a hardcoded success string — "silently breaking HITL — the model was told the tool executed successfully with no return value regardless of what the human actually returned" — and why an unresolved tool name deliberately left the message empty. Your behavior preserves both, but losing the reasoning is how that HITL bug could get reintroduced by the next person. It's fine if they get shortened, but I'd rather they not be dropped. And one test. You're touching Again, sorry for the delay, but this particular file has a lot of activity on it at the moment, so I've just been trying to make sure that everything lands in the right order. At the moment, three other changes are converging on this file: #2172 (@YiyuanMiao) on the parallel-tool-call halt path, #2286 (@ciolo) adding the interrupt round-trip, and a halt-path fix of ours we've held back from opening because two of its hunks overlap #2172. Yours is the only one that overlaps nobody, which is why I'd like it to go first. Fix the assertion and add a test and I'll approve or I'm happy to push both to your branch if that's easier than a round trip. |
|
Thanks @contextablemark, comments addressed, could you please review, cheers! |
Problem
When a single assistant turn issues multiple parallel frontend tool calls, the client collects all results and sends them back in one continuation run (multiple
role=toolmessages). Theuser_messageconstruction — which tells the model what the frontend tools returned — scansinput_data.messagesin reverse but breaks after the firstrole=toolmessage found. Only one result reaches the model; the rest are silently dropped.Symptom: the model acknowledges one tool result and states it is "still waiting" for the others, even though all results were delivered in the request payload.
Root Cause
Fix
Replace the break-after-first with a collect-all loop that gathers every consecutive trailing
role=toolmessage, then joins them (newline-separated, original order preserved):Scanning still stops at the first non-tool message to avoid reaching into earlier conversation history.
Behavior