This commit is contained in:
2026-05-12 21:37:10 +00:00
parent 29cbf58de7
commit 60b2ad6e71
+46 -37
View File
@@ -1,57 +1,66 @@
import questionary import sys
from langgraph.graph import StateGraph, START from typing import TypedDict, List, Dict, Any
from langgraph.types import interrupt, Command
from langgraph.checkpoint.memory import InMemorySaver
from typing import TypedDict, List
class GraphState(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
# Define state
class State(TypedDict):
human_value: str | None human_value: str | None
foo: str | None foo: str | None
# Node that triggers interrupt # Node that triggers interrupt
def interrupt_node(state: State) -> State:
def interrupt_node(state: GraphState) -> GraphState: # Trigger interrupt with question and options
# Trigger interrupt with payload interrupt_payload = {
payload = {
"type": "confirm", "type": "confirm",
"question": "Уверены, что хотите продолжить?", "question": "Уверены, что хотите продолжить?",
"allow_responds": ["approve", "reject"], "allow_responds": ["approve", "reject"],
} }
# interrupt returns None; graph pauses until resumed # Raise interrupt; graph will pause until resumed
interrupt(payload) interrupt(interrupt_payload)
# After resume, state will contain the answer in payload['answer'] # After resume, the payload will be merged into state via resume
answer = state.get("answer") # We expect state to contain 'human_value' set by resume
state["human_value"] = answer
return state return state
builder = StateGraph(GraphState) # Build graph
builder.add_node("interrupt", interrupt_node) graph = StateGraph(State)
builder.set_entry_point("interrupt") graph.add_node("interrupt", interrupt_node)
builder.add_edge(START, "interrupt") graph.set_entry_point(START)
builder.add_edge("interrupt", "interrupt") # loop to finish graph.add_edge(START, "interrupt")
graph = builder.compile(checkpointer=InMemorySaver()) # No further nodes; graph ends after interrupt node
graph.set_finish_point("interrupt")
# Compile with checkpoint
checkpoint = InMemorySaver()
compiled = graph.compile(checkpointer=checkpoint)
# Run graph with interrupt handling # Run graph with interrupt handling
config = {"configurable": {"thread_id": "demo"}} if __name__ == "__main__":
thread_id = "demo_thread"
# Initial state config = {"configurable": {"thread_id": thread_id}}
state: GraphState = {"human_value": None, "foo": None} # Start stream
stream = compiled.stream({}, config)
# Stream execution for chunk in stream:
for chunk in graph.stream(state, config):
if "__interrupt__" in chunk: if "__interrupt__" in chunk:
# Extract payload # Extract interrupt payload
payload = chunk["__interrupt__"][0].value interrupt_obj = chunk["__interrupt__"][0]
# Show question payload = interrupt_obj.value
# Show question to user
answer = questionary.select( answer = questionary.select(
payload["question"], payload["question"],
choices=payload["allow_responds"], choices=payload["allow_responds"],
).ask() ).ask()
# Resume with answer # Resume with answer
payload["answer"] = answer resume_payload = {"human_value": answer}
resume = Command(resume=payload) stream = compiled.stream(Command(resume=resume_payload), config)
for _ in graph.stream(resume, config): for subchunk in stream:
pass if "human_value" in subchunk:
print("\nFinal state:", subchunk)
sys.exit(0)
else: else:
# Final state output # Normal output (none expected here)
print("Final state:", chunk) pass