Tool argument update does not affect LLM content after human review resume #767

Closed
opened 2026-02-20 17:41:39 -05:00 by yindo · 1 comment
Owner

Originally created by @Smile-L-up on GitHub (Jul 4, 2025).

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

import os
os.environ['OPENAI_API_KEY'] = '2'

from fastapi import FastAPI
from pydantic import BaseModel
from typing import Optional, Dict, Any, List
from uuid import uuid4

from langchain_openai import ChatOpenAI
from langgraph.graph import StateGraph
from langgraph.checkpoint.sqlite import SqliteSaver
from langgraph.types import Command, interrupt
from langchain_core.tools import tool
from langchain_core.schema import AIMessage, BaseMessage, HumanMessage
import uvicorn

# ===================
# Define Tool
# ===================
@tool
def retrieve_weather_data(city: str) -> str:
    print(f"👉 Querying weather for city: {city}")
    return f"The weather in {city} is sunny ☀️"

# ===================
# Define State
# ===================
class State(dict):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        if "messages" not in self:
            self["messages"] = []

# ===================
# Initialize LLM
# ===================
llm = ChatOpenAI(
    api_key="sk-35be1fca244b4264b8e9431534f81e8c",
    base_url="https://dashscope.aliyuncs.com/compatible-mode/v1",
    model_name="qwen-max-latest"
).bind_tools([retrieve_weather_data])

# ===================
# Convert dict to BaseMessage
# ===================
def dicts_to_messages(msg_dicts: List[Dict[str, Any]]) -> List[BaseMessage]:
    msgs = []
    for m in msg_dicts:
        role = m.get("role")
        if role == "user":
            msgs.append(HumanMessage(content=m.get("content", "")))
        elif role == "ai":
            msgs.append(AIMessage(content=m.get("content", ""), tool_calls=m.get("tool_calls")))
        elif role == "tool":
            msgs.append(AIMessage(content=m.get("content", ""), tool_calls=None))
    return msgs

# ===================
# Graph Nodes
# ===================

def call_llm(state: State):
    messages = state.get("messages", [])
    if messages and isinstance(messages[0], dict):
        messages = dicts_to_messages(messages)
    msg = llm.invoke(messages)
    state["messages"].append(msg)
    return state

def human_review_node(state: State) -> Command:
    last_msg = state["messages"][-1]
    tool_call = last_msg.tool_calls[-1]
    city = tool_call["args"].get("city", "unknown")

    review = interrupt({
        "question": f"The model identified the city as '{city}'. Is this correct?",
        "tool_call": tool_call
    })

    action = review["action"]
    data = review.get("data")

    if action == "continue":
        return Command(goto="run_tool")

    elif action == "update":
        updated_message = {
            "role": "ai",
            "content": last_msg.content,
            "tool_calls": [{
                "id": tool_call["id"],
                "name": tool_call["name"],
                "args": {"city": data["city"]}
            }],
            "id": last_msg.id
        }
        return Command(goto="run_tool", update={"messages": [updated_message]})

    elif action == "feedback":
        tool_msg = {
            "role": "tool",
            "content": data,
            "name": tool_call["name"],
            "tool_call_id": tool_call["id"]
        }
        return Command(goto="call_llm", update={"messages": [tool_msg]})

def run_tool(state: State):
    last_msg = state["messages"][-1]
    tool_calls = last_msg.tool_calls
    new_msgs = []

    for call in tool_calls:
        result = retrieve_weather_data.invoke(call["args"])
        new_msgs.append({
            "role": "tool",
            "content": result,
            "name": call["name"],
            "tool_call_id": call["id"]
        })

    state["messages"].extend(new_msgs)
    return state

def router(state: State):
    last_msg = state["messages"][-1]
    if hasattr(last_msg, "tool_calls") and last_msg.tool_calls:
        return "human_review_node"
    else:
        return "end"

# ===================
# Build Graph
# ===================
graph_builder = StateGraph(State)

graph_builder.add_node("call_llm", call_llm)
graph_builder.add_node("human_review_node", human_review_node)
graph_builder.add_node("run_tool", run_tool)

graph_builder.set_entry_point("call_llm")
graph_builder.add_edge("run_tool", "call_llm")
graph_builder.add_conditional_edges("call_llm", router)

checkpointer = SqliteSaver("./graph_state.db")
graph = graph_builder.compile(checkpointer=checkpointer)

# ===================
# FastAPI Server
# ===================
app = FastAPI(title="LangGraph Weather Bot with Human Review")

class ChatInput(BaseModel):
    message: str

class ResumeInput(BaseModel):
    thread_id: str
    action: str
    data: Optional[Dict[str, Any]] = None

@app.post("/chat")
async def chat(input: ChatInput):
    thread_id = str(uuid4())
    thread = {"configurable": {"thread_id": thread_id}}

    initial_input = {
        "messages": [{"role": "user", "content": input.message}]
    }

    async for event in graph.stream(initial_input, thread, stream_mode="updates"):
        if "__interrupt__" in event:
            return {
                "thread_id": thread_id,
                "status": "interrupt",
                "data": event["__interrupt__"]
            }

    return {
        "thread_id": thread_id,
        "status": "done",
        "result": event
    }

@app.post("/resume")
async def resume(input: ResumeInput):
    thread = {"configurable": {"thread_id": input.thread_id}}
    cmd = Command(resume={"action": input.action, "data": input.data})

    async for event in graph.stream(cmd, thread, stream_mode="updates"):
        if "__interrupt__" in event:
            return {
                "thread_id": input.thread_id,
                "status": "interrupt",
                "data": event["__interrupt__"]
            }

    return {
        "thread_id": input.thread_id,
        "status": "done",
        "result": event
    }

if __name__ == "__main__":
    uvicorn.run(app, host="0.0.0.0", port=8000)

Error Message and Stack Trace (if applicable)

Expected Behavior
After updating the tool_call via Command(resume={"action": "update", ...}), the new tool result should be correctly reflected in the LLM's subsequent reply (e.g., it should mention "California" instead of "New York").

Actual Behavior
The LLM content remains anchored to the original input ("New York"), even after tool_call.args is updated to "California" and the tool result reflects the new city.


It seems like updating tool_call args via Command(..., update={...}) does not sufficiently update the internal message context used by the LLM. Is there a correct or recommended way to fully override/refresh the tool_call context so that the LLM's next generation is accurate?


Description

When using a Command with "update" after an interrupt, even though the tool_call.args are successfully changed (e.g., from city: "New York" to city: "California"), the final LLM response still reflects the original input (i.e., "New York") in its reply content. This suggests that while the tool execution uses the updated argument, the LLM context isn't updated accordingly

System Info

System Information

OS: Linux
OS Version: #140-Ubuntu SMP Wed Dec 18 17:59:53 UTC 2024
Python Version: 3.12.8 | packaged by Anaconda, Inc. | (main, Dec 11 2024, 16:31:09) [GCC 11.2.0]

Package Information

langchain_core: 0.3.51
langchain: 0.3.14
langchain_community: 0.3.7
langsmith: 0.1.147
langchain_chatchat: Installed. No version info available.
langchain_experimental: 0.3.4
langchain_mcp_adapters: 0.1.7
langchain_openai: 0.2.14
langchain_text_splitters: 0.3.4
langchainhub: 0.1.20
langgraph_sdk: 0.1.48

Optional packages not installed

langserve

Other Dependencies

aiohttp: 3.11.11
async-timeout: Installed. No version info available.
dataclasses-json: 0.6.7
httpx: 0.27.2
httpx-sse: 0.4.0
jsonpatch<2.0,>=1.33: Installed. No version info available.
langchain-core<0.4,>=0.3.36: Installed. No version info available.
langsmith-pyo3: Installed. No version info available.
langsmith<0.4,>=0.1.125: Installed. No version info available.
mcp>=1.9.2: Installed. No version info available.
numpy: 1.26.4
openai: 1.59.3
orjson: 3.10.13
packaging: 24.2
packaging<25,>=23.2: Installed. No version info available.
pydantic: 2.9.2
pydantic-settings: 2.7.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.
PyYAML: 6.0.2
PyYAML>=5.3: Installed. No version info available.
requests: 2.31.0
requests-toolbelt: 1.0.0
SQLAlchemy: 2.0.35
tenacity: 9.0.0
tenacity!=8.4.0,<10.0.0,>=8.1.0: Installed. No version info available.
tiktoken: 0.8.0
types-requests: 2.32.0.20241016
typing-extensions>=4.7: Installed. No version info available.

Originally created by @Smile-L-up on GitHub (Jul 4, 2025). ### Checked other resources - [x] This is a bug, not a usage question. For questions, please use GitHub Discussions. - [x] I added a clear and detailed title that summarizes the issue. - [x] I read what a minimal reproducible example is (https://stackoverflow.com/help/minimal-reproducible-example). - [x] 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 ```python import os os.environ['OPENAI_API_KEY'] = '2' from fastapi import FastAPI from pydantic import BaseModel from typing import Optional, Dict, Any, List from uuid import uuid4 from langchain_openai import ChatOpenAI from langgraph.graph import StateGraph from langgraph.checkpoint.sqlite import SqliteSaver from langgraph.types import Command, interrupt from langchain_core.tools import tool from langchain_core.schema import AIMessage, BaseMessage, HumanMessage import uvicorn # =================== # Define Tool # =================== @tool def retrieve_weather_data(city: str) -> str: print(f"👉 Querying weather for city: {city}") return f"The weather in {city} is sunny ☀️" # =================== # Define State # =================== class State(dict): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) if "messages" not in self: self["messages"] = [] # =================== # Initialize LLM # =================== llm = ChatOpenAI( api_key="sk-35be1fca244b4264b8e9431534f81e8c", base_url="https://dashscope.aliyuncs.com/compatible-mode/v1", model_name="qwen-max-latest" ).bind_tools([retrieve_weather_data]) # =================== # Convert dict to BaseMessage # =================== def dicts_to_messages(msg_dicts: List[Dict[str, Any]]) -> List[BaseMessage]: msgs = [] for m in msg_dicts: role = m.get("role") if role == "user": msgs.append(HumanMessage(content=m.get("content", ""))) elif role == "ai": msgs.append(AIMessage(content=m.get("content", ""), tool_calls=m.get("tool_calls"))) elif role == "tool": msgs.append(AIMessage(content=m.get("content", ""), tool_calls=None)) return msgs # =================== # Graph Nodes # =================== def call_llm(state: State): messages = state.get("messages", []) if messages and isinstance(messages[0], dict): messages = dicts_to_messages(messages) msg = llm.invoke(messages) state["messages"].append(msg) return state def human_review_node(state: State) -> Command: last_msg = state["messages"][-1] tool_call = last_msg.tool_calls[-1] city = tool_call["args"].get("city", "unknown") review = interrupt({ "question": f"The model identified the city as '{city}'. Is this correct?", "tool_call": tool_call }) action = review["action"] data = review.get("data") if action == "continue": return Command(goto="run_tool") elif action == "update": updated_message = { "role": "ai", "content": last_msg.content, "tool_calls": [{ "id": tool_call["id"], "name": tool_call["name"], "args": {"city": data["city"]} }], "id": last_msg.id } return Command(goto="run_tool", update={"messages": [updated_message]}) elif action == "feedback": tool_msg = { "role": "tool", "content": data, "name": tool_call["name"], "tool_call_id": tool_call["id"] } return Command(goto="call_llm", update={"messages": [tool_msg]}) def run_tool(state: State): last_msg = state["messages"][-1] tool_calls = last_msg.tool_calls new_msgs = [] for call in tool_calls: result = retrieve_weather_data.invoke(call["args"]) new_msgs.append({ "role": "tool", "content": result, "name": call["name"], "tool_call_id": call["id"] }) state["messages"].extend(new_msgs) return state def router(state: State): last_msg = state["messages"][-1] if hasattr(last_msg, "tool_calls") and last_msg.tool_calls: return "human_review_node" else: return "end" # =================== # Build Graph # =================== graph_builder = StateGraph(State) graph_builder.add_node("call_llm", call_llm) graph_builder.add_node("human_review_node", human_review_node) graph_builder.add_node("run_tool", run_tool) graph_builder.set_entry_point("call_llm") graph_builder.add_edge("run_tool", "call_llm") graph_builder.add_conditional_edges("call_llm", router) checkpointer = SqliteSaver("./graph_state.db") graph = graph_builder.compile(checkpointer=checkpointer) # =================== # FastAPI Server # =================== app = FastAPI(title="LangGraph Weather Bot with Human Review") class ChatInput(BaseModel): message: str class ResumeInput(BaseModel): thread_id: str action: str data: Optional[Dict[str, Any]] = None @app.post("/chat") async def chat(input: ChatInput): thread_id = str(uuid4()) thread = {"configurable": {"thread_id": thread_id}} initial_input = { "messages": [{"role": "user", "content": input.message}] } async for event in graph.stream(initial_input, thread, stream_mode="updates"): if "__interrupt__" in event: return { "thread_id": thread_id, "status": "interrupt", "data": event["__interrupt__"] } return { "thread_id": thread_id, "status": "done", "result": event } @app.post("/resume") async def resume(input: ResumeInput): thread = {"configurable": {"thread_id": input.thread_id}} cmd = Command(resume={"action": input.action, "data": input.data}) async for event in graph.stream(cmd, thread, stream_mode="updates"): if "__interrupt__" in event: return { "thread_id": input.thread_id, "status": "interrupt", "data": event["__interrupt__"] } return { "thread_id": input.thread_id, "status": "done", "result": event } if __name__ == "__main__": uvicorn.run(app, host="0.0.0.0", port=8000) ``` ### Error Message and Stack Trace (if applicable) ```shell Expected Behavior After updating the tool_call via Command(resume={"action": "update", ...}), the new tool result should be correctly reflected in the LLM's subsequent reply (e.g., it should mention "California" instead of "New York"). Actual Behavior The LLM content remains anchored to the original input ("New York"), even after tool_call.args is updated to "California" and the tool result reflects the new city. It seems like updating tool_call args via Command(..., update={...}) does not sufficiently update the internal message context used by the LLM. Is there a correct or recommended way to fully override/refresh the tool_call context so that the LLM's next generation is accurate? ``` ### Description When using a Command with "update" after an interrupt, even though the tool_call.args are successfully changed (e.g., from city: "New York" to city: "California"), the final LLM response still reflects the original input (i.e., "New York") in its reply content. This suggests that while the tool execution uses the updated argument, the LLM context isn't updated accordingly ### System Info System Information ------------------ > OS: Linux > OS Version: #140-Ubuntu SMP Wed Dec 18 17:59:53 UTC 2024 > Python Version: 3.12.8 | packaged by Anaconda, Inc. | (main, Dec 11 2024, 16:31:09) [GCC 11.2.0] Package Information ------------------- > langchain_core: 0.3.51 > langchain: 0.3.14 > langchain_community: 0.3.7 > langsmith: 0.1.147 > langchain_chatchat: Installed. No version info available. > langchain_experimental: 0.3.4 > langchain_mcp_adapters: 0.1.7 > langchain_openai: 0.2.14 > langchain_text_splitters: 0.3.4 > langchainhub: 0.1.20 > langgraph_sdk: 0.1.48 Optional packages not installed ------------------------------- > langserve Other Dependencies ------------------ > aiohttp: 3.11.11 > async-timeout: Installed. No version info available. > dataclasses-json: 0.6.7 > httpx: 0.27.2 > httpx-sse: 0.4.0 > jsonpatch<2.0,>=1.33: Installed. No version info available. > langchain-core<0.4,>=0.3.36: Installed. No version info available. > langsmith-pyo3: Installed. No version info available. > langsmith<0.4,>=0.1.125: Installed. No version info available. > mcp>=1.9.2: Installed. No version info available. > numpy: 1.26.4 > openai: 1.59.3 > orjson: 3.10.13 > packaging: 24.2 > packaging<25,>=23.2: Installed. No version info available. > pydantic: 2.9.2 > pydantic-settings: 2.7.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. > PyYAML: 6.0.2 > PyYAML>=5.3: Installed. No version info available. > requests: 2.31.0 > requests-toolbelt: 1.0.0 > SQLAlchemy: 2.0.35 > tenacity: 9.0.0 > tenacity!=8.4.0,<10.0.0,>=8.1.0: Installed. No version info available. > tiktoken: 0.8.0 > types-requests: 2.32.0.20241016 > typing-extensions>=4.7: Installed. No version info available.
yindo added the bugpending labels 2026-02-20 17:41:39 -05:00
yindo closed this issue 2026-02-20 17:41:39 -05:00
Author
Owner

@keenborder786 commented on GitHub (Jul 6, 2025):

Can you please try the following Code, I am using the ToolNode and Core Langchain messages type rather than converting to dict later on. This helped me fixed the issue, and once use updates the City, LLM is also recognizing the updated city:


import requests
import json
from langchain_core.tools import Tool
from langgraph.prebuilt import create_react_agent
from langgraph.graph import END
from langgraph.graph import StateGraph
from langchain_core.messages import HumanMessage # Import HumanMessage
from langchain_openai import ChatOpenAI
from typing import Annotated, TypedDict
from langchain_core.messages import AnyMessage
from langgraph.graph.message importMessagesState

import os

from fastapi import FastAPI
from pydantic import BaseModel
from typing import Optional, Dict, Any, List
from uuid import uuid4

from langchain_openai import ChatOpenAI
from langgraph.graph import StateGraph
from langgraph.checkpoint.sqlite import SqliteSaver
from langgraph.types import Command, interrupt
from langchain_core.tools import tool
from langchain_core.messages import AIMessage, BaseMessage, HumanMessage, ToolMessage
from langgraph.prebuilt import ToolNode
import uvicorn


@tool
def retrieve_weather_data(city: str) -> str:
    """Get the weather data for a given city
    """
    print(f"👉 Querying weather for city: {city}")
    return f"The weather in {city} is sunny ☀️"




# ===================
# Initialize LLM
# ===================
llm = ChatOpenAI(model="gpt-4o").bind_tools([retrieve_weather_data])


# ===================
# Graph Nodes
# ===================

def call_llm(state: MessagesState):
    messages = state.get("messages", [])
    msg = llm.invoke(messages)
    return {'messages': [msg]}

def human_review_node(state: MessagesState) -> Command:
    last_msg = state["messages"][-1]
    tool_call = last_msg.tool_calls[-1]
    city = tool_call["args"].get("city", "unknown")

    review = interrupt({
        "question": f"The model identified the city as '{city}'. Is this correct?",
        "tool_call": tool_call
    })

    action = review["action"]
    data = review.get("data")
    print("Action: ", action)
    print("Data: ", data)

    if action == "continue":
        return Command(goto="run_tool")

    elif action == "update":
        updated_message = AIMessage(
            content=last_msg.content,
            tool_calls=[{
                "id": tool_call["id"],
                "name": tool_call["name"],
                "args": {"city": data["city"]}
            }],
            id=last_msg.id
        )
        return Command(goto="run_tool", update={"messages": [updated_message]})

    elif action == "feedback":
        tool_msg = ToolMessage(
            content=data,
            name=tool_call["name"],
            tool_call_id=tool_call["id"]
        )
        return Command(goto="call_llm", update={"messages": [tool_msg]})


def router(state: MessagesState):
    last_msg = state["messages"][-1]
    if hasattr(last_msg, "tool_calls") and last_msg.tool_calls:
        return "human_review_node"
    else:
        return "end"

# ===================
# Build Graph
# ===================
graph_builder = StateGraph(MessagesState) # Using the Message State

graph_builder.add_node("call_llm", call_llm)
graph_builder.add_node("human_review_node", human_review_node)
graph_builder.add_node("run_tool", ToolNode([retrieve_weather_data])) # Using the tool

graph_builder.set_entry_point("call_llm")
graph_builder.add_conditional_edges("call_llm", router)
import sqlite3
conn = sqlite3.connect("checkpoints.sqlite", check_same_thread=False) # Using proper sqlite3 database
checkpointer = SqliteSaver(conn)
graph = graph_builder.compile(checkpointer=checkpointer)

# ===================
# FastAPI Server
# ===================
app = FastAPI(title="LangGraph Weather Bot with Human Review")

class ChatInput(BaseModel):
    message: str

class ResumeInput(BaseModel):
    thread_id: str
    action: str
    data: Optional[Dict[str, Any]] = None

@app.post("/chat")
async def chat(input: ChatInput):
    thread_id = str(uuid4())
    thread = {"configurable": {"thread_id": thread_id}}

    initial_input = {
        "messages": [{"role": "user", "content": input.message}]
    }

    for event in graph.stream(initial_input, thread, stream_mode="updates"):
        if "__interrupt__" in event:
            return {
                "thread_id": thread_id,
                "status": "interrupt",
                "data": event["__interrupt__"]
            }

    return {
        "thread_id": thread_id,
        "status": "done",
        "result": event
    }

@app.post("/resume")
async def resume(input: ResumeInput):
    thread = {"configurable": {"thread_id": input.thread_id}}
    cmd = Command(resume={"action": input.action, "data": input.data})
    event = {}
    for event in graph.stream(cmd, thread, stream_mode="updates"):
        if "__interrupt__" in event:
            return {
                "thread_id": input.thread_id,
                "status": "interrupt",
                "data": event["__interrupt__"]
            }

    return {
        "thread_id": input.thread_id,
        "status": "done",
        "result": event
    }

if __name__ == "__main__":
    uvicorn.run(app, host="0.0.0.0", port=8035)

@keenborder786 commented on GitHub (Jul 6, 2025): Can you please try the following Code, I am using the `ToolNode` and Core Langchain messages type rather than converting to dict later on. This helped me fixed the issue, and once use updates the City, LLM is also recognizing the updated city: ```python import requests import json from langchain_core.tools import Tool from langgraph.prebuilt import create_react_agent from langgraph.graph import END from langgraph.graph import StateGraph from langchain_core.messages import HumanMessage # Import HumanMessage from langchain_openai import ChatOpenAI from typing import Annotated, TypedDict from langchain_core.messages import AnyMessage from langgraph.graph.message importMessagesState import os from fastapi import FastAPI from pydantic import BaseModel from typing import Optional, Dict, Any, List from uuid import uuid4 from langchain_openai import ChatOpenAI from langgraph.graph import StateGraph from langgraph.checkpoint.sqlite import SqliteSaver from langgraph.types import Command, interrupt from langchain_core.tools import tool from langchain_core.messages import AIMessage, BaseMessage, HumanMessage, ToolMessage from langgraph.prebuilt import ToolNode import uvicorn @tool def retrieve_weather_data(city: str) -> str: """Get the weather data for a given city """ print(f"👉 Querying weather for city: {city}") return f"The weather in {city} is sunny ☀️" # =================== # Initialize LLM # =================== llm = ChatOpenAI(model="gpt-4o").bind_tools([retrieve_weather_data]) # =================== # Graph Nodes # =================== def call_llm(state: MessagesState): messages = state.get("messages", []) msg = llm.invoke(messages) return {'messages': [msg]} def human_review_node(state: MessagesState) -> Command: last_msg = state["messages"][-1] tool_call = last_msg.tool_calls[-1] city = tool_call["args"].get("city", "unknown") review = interrupt({ "question": f"The model identified the city as '{city}'. Is this correct?", "tool_call": tool_call }) action = review["action"] data = review.get("data") print("Action: ", action) print("Data: ", data) if action == "continue": return Command(goto="run_tool") elif action == "update": updated_message = AIMessage( content=last_msg.content, tool_calls=[{ "id": tool_call["id"], "name": tool_call["name"], "args": {"city": data["city"]} }], id=last_msg.id ) return Command(goto="run_tool", update={"messages": [updated_message]}) elif action == "feedback": tool_msg = ToolMessage( content=data, name=tool_call["name"], tool_call_id=tool_call["id"] ) return Command(goto="call_llm", update={"messages": [tool_msg]}) def router(state: MessagesState): last_msg = state["messages"][-1] if hasattr(last_msg, "tool_calls") and last_msg.tool_calls: return "human_review_node" else: return "end" # =================== # Build Graph # =================== graph_builder = StateGraph(MessagesState) # Using the Message State graph_builder.add_node("call_llm", call_llm) graph_builder.add_node("human_review_node", human_review_node) graph_builder.add_node("run_tool", ToolNode([retrieve_weather_data])) # Using the tool graph_builder.set_entry_point("call_llm") graph_builder.add_conditional_edges("call_llm", router) import sqlite3 conn = sqlite3.connect("checkpoints.sqlite", check_same_thread=False) # Using proper sqlite3 database checkpointer = SqliteSaver(conn) graph = graph_builder.compile(checkpointer=checkpointer) # =================== # FastAPI Server # =================== app = FastAPI(title="LangGraph Weather Bot with Human Review") class ChatInput(BaseModel): message: str class ResumeInput(BaseModel): thread_id: str action: str data: Optional[Dict[str, Any]] = None @app.post("/chat") async def chat(input: ChatInput): thread_id = str(uuid4()) thread = {"configurable": {"thread_id": thread_id}} initial_input = { "messages": [{"role": "user", "content": input.message}] } for event in graph.stream(initial_input, thread, stream_mode="updates"): if "__interrupt__" in event: return { "thread_id": thread_id, "status": "interrupt", "data": event["__interrupt__"] } return { "thread_id": thread_id, "status": "done", "result": event } @app.post("/resume") async def resume(input: ResumeInput): thread = {"configurable": {"thread_id": input.thread_id}} cmd = Command(resume={"action": input.action, "data": input.data}) event = {} for event in graph.stream(cmd, thread, stream_mode="updates"): if "__interrupt__" in event: return { "thread_id": input.thread_id, "status": "interrupt", "data": event["__interrupt__"] } return { "thread_id": input.thread_id, "status": "done", "result": event } if __name__ == "__main__": uvicorn.run(app, host="0.0.0.0", port=8035) ```
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: langchain-ai/langgraph#767