fix(react-headless): order LangGraph multi-step tool loops correctly - #966
Open
AbhinRustagi wants to merge 2 commits into
Open
fix(react-headless): order LangGraph multi-step tool loops correctly#966AbhinRustagi wants to merge 2 commits into
AbhinRustagi wants to merge 2 commits into
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
AbhinRustagi
force-pushed
the
be-framework-adapter
branch
from
August 13, 2026 11:52
83eb3d6 to
494ff2f
Compare
AbhinRustagi
force-pushed
the
be-framework-adapter
branch
from
August 13, 2026 11:57
494ff2f to
19c2499
Compare
AbhinRustagi
marked this pull request as ready for review
August 13, 2026 11:57
AbhinRustagi
force-pushed
the
be-framework-adapter
branch
from
August 13, 2026 12:21
19c2499 to
ff31c75
Compare
AbhinRustagi
force-pushed
the
be-framework-adapter
branch
from
August 14, 2026 06:11
ff31c75 to
b9ad002
Compare
AbhinRustagi
force-pushed
the
be-framework-adapter
branch
from
August 14, 2026 06:17
b9ad002 to
f19aac7
Compare
AbhinRustagi
force-pushed
the
be-framework-adapter
branch
from
August 14, 2026 10:25
c9f4b4f to
67a88d0
Compare
The LangGraph adapter assumed one assistant message per stream, so a graph that called a tool and then resumed the model emitted a single merged message with no tool results. Live history diverged from what the next request replayed. - Track the current message id per graph step instead of one id for the whole stream, so each model step becomes its own assistant message. - Handle `ToolMessage` / `ToolMessageChunk`: a tool result now closes the open tool call and the assistant segment before emitting TOOL_CALL_RESULT, keeping live history ordered ai -> tool -> ai. - Prefer `tool_call_chunks` over `tool_calls` when both are present. LangChain chunks carry a provisional `tool_calls` projection (often with empty args) that duplicated calls and emitted empty arguments. - Track open vs started tool calls separately so a call is ended exactly once, whether it closes on a tool result or at end of stream. - Mark `status: "error"` tool messages as errored results. Also fixes a crash on the rendering side, hit by any backend whose tools are provider-executed (an OpenUI Cloud scaffold reproduces it): - `Thread` built its timeline rows only from tool calls it could match back to an assistant segment, silently dropping the orphan activities `pairToolActivity` synthesizes for tool results whose call lives on no assistant message. Those activities never rendered, and when a turn had only orphans the row list came back empty while the activity list did not. - `ToolCallTimeline` guarded on `activities` but indexed `steps`, so that combination evaluated `steps[-1]` and threw "current is undefined". Append unclaimed activities as rows, and guard the array it indexes. Adds unit coverage plus an integration test asserting a tool-loop turn stays ordered and replayable. Migrated from PR #785 (visharad/th-2051-route-replacement). Co-Authored-By: Visharad Kashyap <154831195+vishxrad@users.noreply.github.com>
AbhinRustagi
force-pushed
the
be-framework-adapter
branch
from
August 14, 2026 11:17
67a88d0 to
346309b
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
Two bugs in the same tool-loop path — one in how LangGraph streams are decoded, one in how any provider-executed tool activity is rendered.
Stream. The LangGraph adapter assumed one assistant message per stream. A graph that called a tool and then resumed the model collapsed into a single merged message with no tool results, so what you saw live didn't match what the next request replayed.
Render. A turn whose tool activity is all provider-executed crashed the thread with
can't access property "type", current is undefined. An OpenUI Cloud scaffold reproduces it.Changes
react-headless— LangGraph adapterToolMessage/ToolMessageChunk— a result closes the open tool call and the assistant segment before emittingTOOL_CALL_RESULT, keeping history ordered ai → tool → ai.tool_call_chunksovertool_callswhen both are present. LangChain chunks carry a provisionaltool_callsprojection, often with empty args, which duplicated calls.status: "error"tool messages as errored results.react-ui— timeline renderingThreadbuilt its rows only from tool calls it could match back to an assistant segment, silently discarding the orphan activitiespairToolActivitysynthesizes for tool results whose call lives on no assistant message. Provider-executed tools are exactly that shape, so their activity never rendered — and when a turn had only orphans,stepscame back empty whileturnActivitiesdid not.ToolCallTimelineguarded onactivitiesbut indexedsteps, so that combination evaluatedsteps[-1]and threw. Unclaimed activities are now appended as rows, and the guard covers the array actually indexed.Test Plan
react-headless168/168 andreact-ui29/29 passing;prettierandtscclean. Adds unit coverage plus an integration test asserting a tool-loop turn stays ordered and valid for the next request.The render fix is verified by the existing suites plus tracing the failure end to end —
pairToolActivitysynthesizes orphans (already covered by its own tests),Threaddropped them,ToolCallTimelinethen indexed[-1]. It has no direct regression test, since this repo's convention is not to add test files unprompted.Checklist