From 293073ccfb381df5f3613b1c73e0cbe4233c1fc2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=9C=D0=B0=D1=80=D0=B0=D1=82=20=D0=A4=D0=B0=D0=B7=D1=8B?= =?UTF-8?q?=D0=BB=D0=BE=D0=B2?= Date: Tue, 12 May 2026 21:57:44 +0000 Subject: [PATCH] add file --- main.py | 95 +++++++++++++++++++++++++++++---------------------------- 1 file changed, 48 insertions(+), 47 deletions(-) diff --git a/main.py b/main.py index f60098d..84710b6 100644 --- a/main.py +++ b/main.py @@ -1,66 +1,67 @@ -import sys -from typing import TypedDict, List, Dict, Any - +import questionary from langgraph.graph import StateGraph, START from langgraph.constants import interrupt from langgraph.types import Command from langgraph.checkpoint.memory import InMemorySaver -import questionary +from typing import TypedDict, List, Dict -# Define state -class State(TypedDict): +class GraphState(TypedDict): human_value: str | None foo: str | None # Node that triggers interrupt -def interrupt_node(state: State) -> State: - # Trigger interrupt with question and options - interrupt_payload = { + +def interrupt_node(state: GraphState) -> GraphState: + # Trigger interrupt with structured payload + payload = { "type": "confirm", "question": "Уверены, что хотите продолжить?", "allow_responds": ["approve", "reject"], } - # Raise interrupt; graph will pause until resumed - interrupt(interrupt_payload) - # After resume, the payload will be merged into state via resume - # We expect state to contain 'human_value' set by resume + # interrupt returns None, execution pauses until resume + interrupt(payload) + # After resume, the same payload will be passed back via state + # We expect the resume payload to contain 'answer' + answer = state.get("answer") + state["human_value"] = answer return state # Build graph -graph = StateGraph(State) -graph.add_node("interrupt", interrupt_node) -graph.set_entry_point(START) -graph.add_edge(START, "interrupt") -# No further nodes; graph ends after interrupt node -graph.set_finish_point("interrupt") - -# Compile with checkpoint +builder = StateGraph(GraphState) +builder.add_node("interrupt_node", interrupt_node) +builder.set_entry_point("interrupt_node") +builder.set_finish_point("interrupt_node") +# Use InMemorySaver for checkpointing checkpoint = InMemorySaver() -compiled = graph.compile(checkpointer=checkpoint) +graph = builder.compile(checkpointer=checkpoint) # Run graph with interrupt handling -if __name__ == "__main__": - thread_id = "demo_thread" - config = {"configurable": {"thread_id": thread_id}} - # Start stream - stream = compiled.stream({}, config) - for chunk in stream: - if "__interrupt__" in chunk: - # Extract interrupt payload - interrupt_obj = chunk["__interrupt__"][0] - payload = interrupt_obj.value - # Show question to user - answer = questionary.select( - payload["question"], - choices=payload["allow_responds"], - ).ask() - # Resume with answer - resume_payload = {"human_value": answer} - stream = compiled.stream(Command(resume=resume_payload), config) - for subchunk in stream: - if "human_value" in subchunk: - print("\nFinal state:", subchunk) - sys.exit(0) - else: - # Normal output (none expected here) - pass +config = {"configurable": {"thread_id": "thread-1"}} +# Initial state +state: GraphState = {"human_value": None, "foo": None} + +# Start streaming +stream = graph.stream(state, config) +for chunk in stream: + if "__interrupt__" in chunk: + # Extract payload + payload = chunk["__interrupt__"][0].value + # Show question to user + answer = questionary.select( + payload["question"], + choices=payload["allow_responds"], + ).ask() + # Add answer to payload and resume + payload["answer"] = answer + # Resume graph + stream = graph.stream(Command(resume=payload), config) + # Continue processing resumed stream + for subchunk in stream: + if "__interrupt__" in subchunk: + # Should not happen in this simple example + continue + print(subchunk) + else: + print(chunk) + +print("Final state:", state)