Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Tools returning a Command are missing from messages-streaming #4111

Open
4 tasks done
Martin19037 opened this issue Apr 1, 2025 · 1 comment
Open
4 tasks done

Tools returning a Command are missing from messages-streaming #4111

Martin19037 opened this issue Apr 1, 2025 · 1 comment

Comments

@Martin19037
Copy link

Checked other resources

  • This is a bug, not a usage question. For questions, please use GitHub Discussions.
  • I added a clear and detailed title that summarizes the issue.
  • I read what a minimal reproducible example is (https://stackoverflow.com/help/minimal-reproducible-example).
  • I included a self-contained, minimal example that demonstrates the issue INCLUDING all the relevant imports. The code run AS IS to reproduce the issue.

Example Code

from typing import Annotated, Any

from langchain_core.messages import ToolMessage
from langchain_core.runnables.config import RunnableConfig
from langchain_core.tools import tool
from langchain_core.tools.base import InjectedToolCallId
from langchain_openai import ChatOpenAI
from langgraph.prebuilt import create_react_agent
from langgraph.prebuilt.chat_agent_executor import AgentState
from langgraph.types import Command

USER_INFO = [
    {"user_id": "1", "name": "Bob Dylan", "location": "New York, NY"},
    {"user_id": "2", "name": "Taylor Swift", "location": "Beverly Hills, CA"},
]

USER_ID_TO_USER_INFO = {info["user_id"]: info for info in USER_INFO}


class State(AgentState):
    # updated by the tool
    user_info: dict[str, Any]


def main() -> None:
    @tool
    def lookup_user_info(tool_call_id: Annotated[str, InjectedToolCallId], config: RunnableConfig):
        """Use this to look up user information to better assist them with their questions."""
        user_id = config.get("configurable", {}).get("user_id")
        if user_id is None:
            raise ValueError("Please provide user ID")

        if user_id not in USER_ID_TO_USER_INFO:
            raise ValueError(f"User '{user_id}' not found")

        user_info = USER_ID_TO_USER_INFO[user_id]
        return Command(
            update={
                # update the state keys
                "user_info": user_info,
                # update the message history
                "messages": [
                    ToolMessage(
                        "Successfully looked up user information", tool_call_id=tool_call_id
                    )
                ],
            }
        )

    def prompt(state: State):
        user_info = state.get("user_info")
        if user_info is None:
            return state["messages"]

        system_msg = (
            f"User name is {user_info['name']}. User lives in {user_info['location']}"
        )
        return [{"role": "system", "content": system_msg}] + state["messages"]

    model = ChatOpenAI(model="gpt-4o")

    agent = create_react_agent(
        model,
        # pass the tool that can update state
        [lookup_user_info],
        state_schema=State,
        # pass dynamic prompt function
        prompt=prompt,
    )

    agent_input = {"messages": [("user", "hi, where do I live?")]}
    agent_config = {"configurable": {"user_id": "1"}}

    invoke_result = agent.invoke(
        agent_input,
        agent_config,
    )

    # print(invoke_result)

    for chunk in agent.stream(agent_input, agent_config, stream_mode='messages'):
        print(chunk)


if __name__ == '__main__':
    main()

Error Message and Stack Trace (if applicable)

Description

As the title says, if you define a tool returning a Command to update the state, there is no ToolMessage for the tool call when using the messages streaming mode.

This is easily reproducible using the example in the How to update graph state from tools doc page.

System Info

System Information

OS: Windows
OS Version: 10.0.19045
Python Version: 3.13.0 (main, Oct 16 2024, 00:33:24) [MSC v.1929 64 bit (AMD64)]

Package Information

langchain_core: 0.3.49
langsmith: 0.3.21
langchain_openai: 0.3.11
langgraph_sdk: 0.1.60

Optional packages not installed

langserve

Other Dependencies

httpx: 0.28.1
jsonpatch<2.0,>=1.33: Installed. No version info available.
langchain-core<1.0.0,>=0.3.49: Installed. No version info available.
langsmith-pyo3: Installed. No version info available.
langsmith<0.4,>=0.1.125: Installed. No version info available.
openai-agents: Installed. No version info available.
openai<2.0.0,>=1.68.2: Installed. No version info available.
opentelemetry-api: Installed. No version info available.
opentelemetry-exporter-otlp-proto-http: Installed. No version info available.
opentelemetry-sdk: Installed. No version info available.
orjson: 3.10.16
packaging: 24.2
packaging<25,>=23.2: Installed. No version info available.
pydantic: 2.11.1
pydantic<3.0.0,>=2.5.2;: Installed. No version info available.
pydantic<3.0.0,>=2.7.4;: Installed. No version info available.
pytest: Installed. No version info available.
PyYAML>=5.3: Installed. No version info available.
requests: 2.32.3
requests-toolbelt: 1.0.0
rich: Installed. No version info available.
tenacity!=8.4.0,<10.0.0,>=8.1.0: Installed. No version info available.
tiktoken<1,>=0.7: Installed. No version info available.
typing-extensions>=4.7: Installed. No version info available.
zstandard: 0.23.0

@rs-benatti
Copy link

Indeed it does not return a message as the function didn't return a message and simply updated the state of the graph using Command. Still you can access the ToolMessage by using also the stream_mode 'updates'.

for chunk in agent.stream(agent_input, agent_config, stream_mode=['messages', 'updates']):
        print(chunk)

Then, you may find among the chunks a structure like:

('updates', {'tools': {'user_info': {'user_id': '1', 'name': 'Bob Dylan', 'location': 'New York, NY'}, 'messages': [ToolMessage(content='Successfully looked up user information', name='lookup_user_info', tool_call_id='call_rwUieKngmiAAUy2vnZJaOlEq')]}})

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

3 participants