From 962e965d817581b52fb903d86cf9e9a7b09ed6b9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=93=D0=BB=D0=B5=D0=B1=20=D0=9D=D0=B8=D0=BA=D0=B8=D1=88?= =?UTF-8?q?=D0=B8=D0=BD?= Date: Mon, 25 May 2026 22:27:44 +0000 Subject: [PATCH] add main.py --- main.py | 74 ++++++++++++++++++++++++--------------------------------- 1 file changed, 31 insertions(+), 43 deletions(-) diff --git a/main.py b/main.py index b2c34ea..e4f4c1d 100644 --- a/main.py +++ b/main.py @@ -1,61 +1,49 @@ -import questionary +import sys from langgraph.graph import StateGraph, START from langgraph.types import interrupt, Command from langgraph.checkpoint.memory import InMemorySaver from typing import TypedDict, Annotated +from langgraph.graph.message import add_messages +import questionary -# State definition class GraphState(TypedDict): - human_value: Annotated[str | None, "value chosen by user"] - foo: Annotated[str, "initial data placeholder"] + messages: Annotated[list, add_messages] + human_value: str | None -# Node that triggers interrupt - -def interrupt_node(state: GraphState) -> GraphState: - # Trigger interrupt with structured payload +async def interrupt_node(state: GraphState) -> GraphState: + # Trigger interrupt asking user to confirm payload = { "type": "confirm", - "question": "Уверены, что хотите продолжить?", - "allow_responds": ["approve", "reject"], + "question": "Do you want to continue?", + "options": ["yes", "no"], } - # Call interrupt; execution pauses until resumed + # interrupt returns None, graph pauses interrupt(payload) - # After resume, payload will contain 'answer' - # The node receives the resumed payload as its return value - # We store the answer in state - state["human_value"] = payload.get("answer") - return state + # After resume, payload will have 'answer' + answer = state.get("human_value") + return {"messages": state["messages"], "human_value": answer} -# Build graph builder = StateGraph(GraphState) builder.add_node("interrupt_node", interrupt_node) builder.add_edge(START, "interrupt_node") -builder.add_edge("interrupt_node", END) - -# Compile with checkpoint +builder.add_edge("interrupt_node", START) graph = builder.compile(checkpointer=InMemorySaver()) -# Run graph with stream and handle interrupt -config = {"configurable": {"thread_id": "demo"}} +async def main(): + config = {"configurable": {"thread_id": "session-1"}} + # Start stream + async for chunk in graph.stream({"messages": [], "human_value": None}, config): + if "__interrupt__" in chunk: + payload = chunk["__interrupt__"][0].value + answer = questionary.select(payload["question"], choices=payload["options"]).ask() + # resume with answer + payload["answer"] = answer + # store answer in state + chunk["human_value"] = answer + await graph.stream(Command(resume=payload), config) + else: + print(chunk) -# First run: will pause at interrupt -stream = graph.stream({}, config) -for chunk in stream: - if "__interrupt__" in chunk: - # Extract payload - payload = chunk["__interrupt__"][0].value - # Show question and get answer - answer = questionary.select( - payload["question"], - choices=payload["allow_responds"], - ).ask() - # Add answer to payload - payload["answer"] = answer - # Resume graph - stream = graph.stream(Command(resume=payload), config) - continue - # Print normal output - print(chunk) - -# After stream ends, print final state -print("Final state:", stream.final_state) +if __name__ == "__main__": + import asyncio + asyncio.run(main())