Add main.py
This commit is contained in:
@@ -0,0 +1,134 @@
|
|||||||
|
"""
|
||||||
|
LangGraph Human‑in‑the‑Loop demo.
|
||||||
|
|
||||||
|
This script demonstrates how to use LangGraph’s interrupt / resume
|
||||||
|
mechanism to pause a graph, ask the user for a decision, and then
|
||||||
|
resume execution.
|
||||||
|
|
||||||
|
Requirements
|
||||||
|
------------
|
||||||
|
* langgraph
|
||||||
|
* questionary
|
||||||
|
|
||||||
|
Run the script with:
|
||||||
|
|
||||||
|
pip install -r requirements.txt
|
||||||
|
python main.py
|
||||||
|
|
||||||
|
The script will pause at the interrupt node, display a question in the
|
||||||
|
terminal, and wait for your response. After you answer, the graph will
|
||||||
|
continue and print the final state.
|
||||||
|
"""
|
||||||
|
|
||||||
|
from typing import TypedDict
|
||||||
|
|
||||||
|
from langgraph.graph import StateGraph, START
|
||||||
|
from langgraph.constants import interrupt
|
||||||
|
from langgraph.types import Command
|
||||||
|
from langgraph.checkpoint.memory import InMemorySaver
|
||||||
|
import questionary
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# 1. State definition
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
class GraphState(TypedDict):
|
||||||
|
"""State of the graph.
|
||||||
|
|
||||||
|
* ``human_value`` – value supplied by the user via interrupt.
|
||||||
|
* ``foo`` – example of an initial value that could be used by the graph.
|
||||||
|
"""
|
||||||
|
human_value: str
|
||||||
|
foo: str
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# 2. Node that triggers an interrupt
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
def interrupt_node(state: GraphState) -> GraphState:
|
||||||
|
"""Node that pauses the graph and asks the user for confirmation.
|
||||||
|
|
||||||
|
The node calls :func:`langgraph.constants.interrupt` with a payload
|
||||||
|
containing the question and allowed answers. Execution will pause
|
||||||
|
until the graph is resumed with a ``Command`` that contains the
|
||||||
|
user’s response.
|
||||||
|
"""
|
||||||
|
# Payload that will be sent to the interrupt handler.
|
||||||
|
interrupt_payload = {
|
||||||
|
"type": "confirm",
|
||||||
|
"question": "Do you want to continue?",
|
||||||
|
"allow_responds": ["approve", "reject"],
|
||||||
|
}
|
||||||
|
# Trigger the interrupt – execution stops here until resumed.
|
||||||
|
interrupt(interrupt_payload)
|
||||||
|
# After the graph is resumed, the same payload will be returned
|
||||||
|
# to this node via the ``resume`` argument. The node simply
|
||||||
|
# returns the (possibly updated) state.
|
||||||
|
return state
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# 3. Build the graph
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# Create a graph with an in‑memory checkpoint so we can resume after the
|
||||||
|
# interrupt.
|
||||||
|
graph = StateGraph(GraphState)
|
||||||
|
graph.add_node("interrupt_node", interrupt_node)
|
||||||
|
# The graph has only one node – it is both the entry and the finish point.
|
||||||
|
graph.set_entry_point("interrupt_node")
|
||||||
|
graph.set_finish_point("interrupt_node")
|
||||||
|
# Compile the graph with a checkpoint so the state is preserved across
|
||||||
|
# the pause/resume cycle.
|
||||||
|
graph.compile(checkpointer=InMemorySaver())
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# 4. Run the graph with interrupt handling
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
def run_graph() -> None:
|
||||||
|
"""Execute the graph, handling interrupts in a loop.
|
||||||
|
|
||||||
|
The function starts the graph, then iterates over the stream of
|
||||||
|
chunks. When an interrupt chunk is encountered, the question is
|
||||||
|
displayed using ``questionary``. The user’s answer is added to the
|
||||||
|
payload and the graph is resumed with a ``Command``.
|
||||||
|
"""
|
||||||
|
thread_id = "demo_thread"
|
||||||
|
config = {"configurable": {"thread_id": thread_id}}
|
||||||
|
|
||||||
|
# Start the stream. ``{}`` is the initial state – the graph will
|
||||||
|
# fill in the missing fields.
|
||||||
|
stream = graph.stream({}, config)
|
||||||
|
|
||||||
|
while True:
|
||||||
|
try:
|
||||||
|
chunk = next(stream)
|
||||||
|
except StopIteration:
|
||||||
|
# The stream has finished.
|
||||||
|
break
|
||||||
|
|
||||||
|
# -------------------------------------------------------------------
|
||||||
|
# Handle interrupt chunks
|
||||||
|
# -------------------------------------------------------------------
|
||||||
|
if "__interrupt__" in chunk:
|
||||||
|
# The interrupt payload is the first element of the list.
|
||||||
|
interrupt_payload = chunk["__interrupt__"][0].value
|
||||||
|
# Show the question and get the user’s answer.
|
||||||
|
answer = questionary.select(
|
||||||
|
interrupt_payload["question"],
|
||||||
|
choices=interrupt_payload["allow_responds"],
|
||||||
|
).ask()
|
||||||
|
# Attach the answer to the payload and resume.
|
||||||
|
interrupt_payload["answer"] = answer
|
||||||
|
# Resume the graph – this will produce a new stream.
|
||||||
|
stream = graph.stream(Command(resume=interrupt_payload), config)
|
||||||
|
continue
|
||||||
|
|
||||||
|
# -------------------------------------------------------------------
|
||||||
|
# Normal chunks – just print them.
|
||||||
|
# -------------------------------------------------------------------
|
||||||
|
print(chunk)
|
||||||
|
|
||||||
|
# After the stream ends, fetch the final state from the checkpoint.
|
||||||
|
final_state = graph.checkpointer.get_state(thread_id)
|
||||||
|
print("\nFinal state:", final_state)
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
run_graph()
|
||||||
Reference in New Issue
Block a user