Skip to content

Commit 5874680

Browse files
authored
Merge pull request #209 from gfortaine/main
update example for HITL
2 parents c864dfd + 96015c6 commit 5874680

File tree

1 file changed

+30
-29
lines changed

1 file changed

+30
-29
lines changed

README.md

+30-29
Original file line numberDiff line numberDiff line change
@@ -174,51 +174,52 @@ To remove the interrupt, simply follow the same step and press `x` button on the
174174
In addition to interrupting on a node and editing the graph state, you might want to support human-in-the-loop workflows with the ability to manually update state. Here is a modified version of `agent.py` with `agent` and `human` nodes, where the graph execution will be interrupted on `human` node. This will let you send input as part of the `human` node. This can be useful when you want the agent to get user input. This essentially replaces how you might use `input()` if you were running this from the command line.
175175

176176
```python
177-
from typing import TypedDict, Annotated, Sequence, Literal
177+
from typing import Literal
178178

179-
from langchain_core.messages import BaseMessage, HumanMessage
180-
from langchain_anthropic import ChatAnthropic
181-
from langgraph.graph import StateGraph, END, add_messages
179+
from langchain_openai import ChatOpenAI
180+
from langgraph.graph import MessagesState, StateGraph, END
181+
from langgraph.types import Command, interrupt
182182

183183

184-
class AgentState(TypedDict):
185-
messages: Annotated[Sequence[BaseMessage], add_messages]
184+
model = ChatOpenAI(temperature=0, model_name="gpt-4o")
186185

187186

188-
model = ChatAnthropic(temperature=0, model_name="claude-3-sonnet-20240229")
189-
def call_model(state: AgentState) -> AgentState:
187+
def call_model(state: MessagesState) -> Command[Literal["human", END]]:
190188
messages = state["messages"]
191189
response = model.invoke(messages)
192-
return {"messages": [response]}
193190

191+
return Command(
192+
goto="human",
193+
update={"messages": [response]},
194+
)
194195

195-
# no-op node that should be interrupted on
196-
def human_feedback(state: AgentState) -> AgentState:
197-
pass
198196

197+
def human_feedback(state: MessagesState) -> Command[Literal["agent"]]:
198+
"""A node for collecting user input."""
199+
print("Waiting for user input...")
200+
user_input = interrupt(value="Ready for user input.")
199201

200-
def should_continue(state: AgentState) -> Literal["agent", "end"]:
201-
messages = state['messages']
202-
last_message = messages[-1]
203-
if isinstance(last_message, HumanMessage):
204-
return "agent"
205-
return "end"
202+
print("user input:", user_input)
206203

204+
return Command(
205+
goto="agent",
206+
update={
207+
"messages": [
208+
{
209+
"role": "human",
210+
"content": user_input,
211+
}
212+
]
213+
},
214+
)
207215

208-
workflow = StateGraph(AgentState)
216+
217+
workflow = StateGraph(MessagesState)
209218
workflow.set_entry_point("agent")
210219
workflow.add_node("agent", call_model)
211220
workflow.add_node("human", human_feedback)
212-
workflow.add_edge("agent", "human")
213-
workflow.add_conditional_edges(
214-
"human",
215-
should_continue,
216-
{
217-
"agent": "agent",
218-
"end": END,
219-
},
220-
)
221-
graph = workflow.compile(interrupt_before=["human"])
221+
222+
graph = workflow.compile()
222223
```
223224

224225
The following video shows how to manually send state updates (i.e. messages in our example) when interrupted:

0 commit comments

Comments
 (0)