feat: support state updates from wrap_model_call with command(s)#35033
Merged
Sydney Runkle (sydney-runkle) merged 16 commits intomasterfrom Feb 6, 2026
Merged
feat: support state updates from wrap_model_call with command(s)#35033Sydney Runkle (sydney-runkle) merged 16 commits intomasterfrom
wrap_model_call with command(s)#35033Sydney Runkle (sydney-runkle) merged 16 commits intomasterfrom
Conversation
Eugene Yurtsev (eyurtsev)
previously approved these changes
Feb 5, 2026
| "default_headers": None, | ||
| "model_kwargs": {}, | ||
| "reuse_last_container": None, | ||
| "inference_geo": None, |
Collaborator
There was a problem hiding this comment.
is this related?
Collaborator
Author
There was a problem hiding this comment.
yeah anthropic tests were failing w/o it, I think bc of new model
Eugene Yurtsev (eyurtsev)
approved these changes
Feb 6, 2026
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.
Alternative to #35024. Paving the way for summarization in
wrap_model_call(which requires state updates).Add
ExtendedModelResponsedataclass that allowswrap_model_callmiddleware to return aCommandalongside the model response for additional state updates.Motivation
Previously,
wrap_model_callmiddleware could only return aModelResponseorAIMessage— there was no way to inject additional state updates (e.g. custom state fields) from the model call middleware layer.ExtendedModelResponsefills this gap by accepting an optionalCommand.This feature is needed by the summarization middleware, which needs to track summarization trigger points calculated during
wrap_model_call.Why
Commandinstead of a plainstate_updatedict?We chose
Commandrather than the rawstate_update: dictapproach from the earlier iteration becauseCommandis the established LangGraph primitive for state updates from nodes. UsingCommandmeans:add_messages) rather than being merged as raw dicts. This makes messages updates additive alongside the model response instead of replacing them.wrap_tool_call, which already returnsCommand.Commandgains new capabilities (e.g.goto,send), middleware can leverage them without API changes.Why keep
model_responseseparate instead of usingCommanddirectly?The model node needs to distinguish the model's actual response (messages + structured output) from supplementary middleware state updates. If middleware returned only a
Command, there would be no clean way to extract theModelResponsefor structured output handling, response validation, and the core model-to-tools routing logic. Keepingmodel_responseexplicit preserves a clear boundary between "what the model said" and "what middleware wants to update."Also, in order to avoid breaking, the
handlerpassed towrap_tool_callneeds to always return aModelResponse. There's no easy way to preserve this if we pump it into aCommand.One nice thing about having this
ExtendedModelResponsestructure is that it's extensible if we want to add more metadata in the future.Composition
When multiple middleware layers return
ExtendedModelResponse, their commands compose naturally:ExtendedModelResponseis unwrapped to its underlyingModelResponseso outer middleware always sees a plainModelResponsefromhandler(). The inner command is captured and accumulated.Commandbecomes a separate state update applied through the graph's reducers. For messages, this means they're additive (viaadd_messages), not replacing.handler()again, accumulated inner commands are cleared and re-collected from the fresh call.Backwards compatibility
Fully backwards compatible. The
ModelCallResulttype alias is widened fromModelResponse | AIMessagetoModelResponse | AIMessage | ExtendedModelResponse, but existing middleware returningModelResponseorAIMessagecontinues to work identically.Internals
model_node/amodel_nodenow returnlist[Command]instead ofdict[str, Any]_build_commandsconverts the model response + accumulated middleware commands into a list ofCommandobjects for LangGraph_ComposedExtendedModelResponseis the internal type that accumulates commands across layers during composition