From 19ca6b416bd317346f4d0f5b472dea3fd39af0f0 Mon Sep 17 00:00:00 2001 From: Sydney Runkle <54324534+sydney-runkle@users.noreply.github.com> Date: Tue, 27 May 2025 13:42:20 -0400 Subject: [PATCH] fix: `post_model_hook` should inject state + store into tool calls if necessary (#4842) --- .../langgraph/prebuilt/chat_agent_executor.py | 4 ++ libs/prebuilt/tests/test_react_agent.py | 60 +++++++++++++++++++ 2 files changed, 64 insertions(+) diff --git a/libs/prebuilt/langgraph/prebuilt/chat_agent_executor.py b/libs/prebuilt/langgraph/prebuilt/chat_agent_executor.py index 668280783..6a5e882e7 100644 --- a/libs/prebuilt/langgraph/prebuilt/chat_agent_executor.py +++ b/libs/prebuilt/langgraph/prebuilt/chat_agent_executor.py @@ -727,6 +727,10 @@ def create_react_agent( ] if pending_tool_calls: + pending_tool_calls = [ + tool_node.inject_tool_args(call, state, store) # type: ignore[arg-type] + for call in pending_tool_calls + ] return [Send("tools", [tool_call]) for tool_call in pending_tool_calls] elif isinstance(messages[-1], ToolMessage): return entrypoint diff --git a/libs/prebuilt/tests/test_react_agent.py b/libs/prebuilt/tests/test_react_agent.py index 649b9dd6e..64f4d0187 100644 --- a/libs/prebuilt/tests/test_react_agent.py +++ b/libs/prebuilt/tests/test_react_agent.py @@ -1540,3 +1540,63 @@ def test_post_model_hook_with_structured_output() -> None: } }, ] + + +@pytest.mark.parametrize( + "state_schema", [AgentStateExtraKey, AgentStateExtraKeyPydantic] +) +def test_create_react_agent_inject_vars_with_post_model_hook( + state_schema: StateSchemaType, +) -> None: + store = InMemoryStore() + namespace = ("test",) + store.put(namespace, "test_key", {"bar": 3}) + + if issubclass(state_schema, AgentStatePydantic): + + def tool1( + some_val: int, + state: Annotated[AgentStateExtraKeyPydantic, InjectedState], + store: Annotated[BaseStore, InjectedStore()], + ) -> str: + """Tool 1 docstring.""" + store_val = store.get(namespace, "test_key").value["bar"] + return some_val + state.foo + store_val + else: + + def tool1( + some_val: int, + state: Annotated[dict, InjectedState], + store: Annotated[BaseStore, InjectedStore()], + ) -> str: + """Tool 1 docstring.""" + store_val = store.get(namespace, "test_key").value["bar"] + return some_val + state["foo"] + store_val + + tool_call = { + "name": "tool1", + "args": {"some_val": 1}, + "id": "some 0", + "type": "tool_call", + } + + def post_model_hook(state: dict) -> None: + return + + model = FakeToolCallingModel(tool_calls=[[tool_call], []]) + agent = create_react_agent( + model, + [tool1], + state_schema=state_schema, + store=store, + post_model_hook=post_model_hook, + ) + input_message = HumanMessage("hi") + result = agent.invoke({"messages": [input_message], "foo": 2}) + assert result["messages"] == [ + input_message, + AIMessage(content="hi", tool_calls=[tool_call], id="0"), + _AnyIdToolMessage(content="6", name="tool1", tool_call_id="some 0"), + AIMessage("hi-hi-6", id="1"), + ] + assert result["foo"] == 2