Skip to content

Commit 34a5ca7

Browse files
authored
Add interrupt (human-in-the-loop) example (#63)
2 parents 2bcbbe7 + 2fe4cf6 commit 34a5ca7

File tree

6 files changed

+2060
-0
lines changed

6 files changed

+2060
-0
lines changed

examples/interrupt/README.md

+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
# Interrupt Agent
2+
3+
A simple LangGraph-based agent which expects human input during its operation.
4+
5+
## Getting Started
6+
7+
```bash
8+
poetry install
9+
python interrupt/interrupt.py
10+
```
+70
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
{
2+
"metadata": {
3+
"ref": {
4+
"name": "org.agntcy.interrupt",
5+
"version": "0.0.1"
6+
},
7+
"description": "Agent Connect Protocol Interrupt (Human-In-The-Loop) Example"
8+
},
9+
"specs": {
10+
"input": {
11+
"type": "object",
12+
"properties": {
13+
"input": {
14+
"type": "string",
15+
"description": "The human input"
16+
}
17+
}
18+
},
19+
"output": {
20+
"type": "object",
21+
"oneOf": [
22+
{
23+
"properties": {
24+
"human_answer": {
25+
"type": "string"
26+
}
27+
},
28+
"required": [
29+
"human_answer"
30+
]
31+
},
32+
{
33+
"properties": {
34+
"ai_answer": {
35+
"type": "string"
36+
}
37+
},
38+
"required": [
39+
"ai_answer"
40+
]
41+
}
42+
]
43+
},
44+
"config": {
45+
"type": "object",
46+
"properties": {}
47+
},
48+
"capabilities": {
49+
"threads": false,
50+
"interrupts": true,
51+
"callbacks": false
52+
},
53+
"interrupts": []
54+
},
55+
"dependencies": [],
56+
"deployment": {
57+
"dependencies": [],
58+
"deployment_options": [
59+
{
60+
"type": "source_code",
61+
"name": "src",
62+
"url": "github.com/cisco-eti/agent-connect-protocol.git/examples/agents/interrupt",
63+
"framework_config": {
64+
"framework_type": "langgraph",
65+
"graph": "interrupt.interrupt:graph"
66+
}
67+
}
68+
]
69+
}
70+
}
+82
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
import asyncio
2+
import uuid
3+
from typing import Optional
4+
5+
from langgraph.checkpoint.memory import MemorySaver
6+
from langgraph.constants import END, START
7+
from langgraph.graph import StateGraph
8+
from langgraph.types import Command, interrupt
9+
from typing_extensions import TypedDict
10+
11+
12+
class State(TypedDict):
13+
"""The graph state."""
14+
15+
input: str
16+
"""The input value of the node."""
17+
18+
ai_answer: Optional[str]
19+
"""AI answer"""
20+
21+
human_answer: Optional[str]
22+
"""Human value will be updated using an interrupt."""
23+
24+
25+
async def node1(state: State):
26+
print(f"> Node1 input: {state['input']}")
27+
await asyncio.sleep(1)
28+
return {"ai_answer": "This is the output of node1"}
29+
30+
31+
async def node2(state: State):
32+
print(f"> Received input: {state['ai_answer']}")
33+
answer = interrupt(
34+
# This value will be sent to the client
35+
# as part of the interrupt information.
36+
{"ai_answer": "What is your age?"}
37+
)
38+
print(f"> Received an input from the interrupt: {answer}")
39+
await asyncio.sleep(2)
40+
return {"human_answer": answer}
41+
42+
43+
async def node3(state: State):
44+
print(f"> Received input: {state['human_answer']}")
45+
await asyncio.sleep(3)
46+
return {"ai_answer": "This is the output of node3"}
47+
48+
49+
builder = StateGraph(State)
50+
builder.add_node("node1", node1)
51+
builder.add_node("node2", node2)
52+
builder.add_node("node3", node3)
53+
54+
builder.add_edge(START, "node1")
55+
builder.add_edge("node1", "node2")
56+
builder.add_edge("node2", "node3")
57+
builder.add_edge("node3", END)
58+
59+
60+
# A checkpointer must be enabled for interrupts to work!
61+
checkpointer = MemorySaver()
62+
graph = builder.compile(checkpointer=checkpointer)
63+
64+
config = {
65+
"configurable": {
66+
"thread_id": uuid.uuid4(),
67+
}
68+
}
69+
70+
71+
async def run_graph():
72+
async for chunk in graph.astream({"input": "something"}, config):
73+
print(chunk)
74+
75+
command = Command(resume=input("Enter the interrupt command: "))
76+
77+
async for chunk in graph.astream(command, config):
78+
print(chunk)
79+
80+
81+
if __name__ == "__main__":
82+
asyncio.run(run_graph())

0 commit comments

Comments
 (0)