From 745f096178a55c3047806439e9803bcdcccaa84e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=AD=D0=BC=D0=B8=D0=BB=D1=8C=20=D0=90=D0=BC=D0=B8=D1=80?= =?UTF-8?q?=D0=BE=D0=B2?= Date: Tue, 26 May 2026 08:05:58 +0000 Subject: [PATCH] add main.py --- main.py | 86 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 86 insertions(+) create mode 100644 main.py diff --git a/main.py b/main.py new file mode 100644 index 0000000..2405acc --- /dev/null +++ b/main.py @@ -0,0 +1,86 @@ +"""Human-in-the-loop: кастомное прерывание в LangGraph.""" +from __future__ import annotations + +import uuid +from typing import TypedDict + +import questionary +from langgraph.checkpoint.memory import InMemorySaver +from langgraph.constants import START +from langgraph.graph import StateGraph +from langgraph.types import Command, interrupt + + +class State(TypedDict, total=False): + foo: str + human_value: str + + +def human_node(state: State) -> dict: + payload = { + "type": "alert", + "question": "Уверены, что хотите продолжить?", + "allow_responds": ["approve", "reject"], + } + resumed = interrupt(payload) + + if isinstance(resumed, dict): + answer = resumed.get("answer", "") + else: + answer = str(resumed) + + print(f"> Received an input from the interrupt: {answer}") + return { + "foo": state.get("foo", ""), + "human_value": answer, + } + + +def build_graph(): + builder = StateGraph(State) + builder.add_node("node", human_node) + builder.add_edge(START, "node") + return builder.compile(checkpointer=InMemorySaver()) + + +def handle_interrupt(interrupts: tuple) -> dict: + first = interrupts[0] + payload = dict(first.value if hasattr(first, "value") else first) + + print("Произошла остановка") + print(payload) + print(f"!!! {payload.get('type', 'alert')} !!!") + + choice = questionary.select( + payload["question"], + choices=payload["allow_responds"], + ).ask() + + payload["answer"] = choice or payload["allow_responds"][0] + return payload + + +def run_hitl() -> None: + graph = build_graph() + config = {"configurable": {"thread_id": str(uuid.uuid4())}} + stream_input: dict | Command = {"foo": "abc"} + + while True: + interrupted = False + for chunk in graph.stream(stream_input, config=config): + if "__interrupt__" in chunk: + payload = handle_interrupt(chunk["__interrupt__"]) + stream_input = Command(resume=payload) + interrupted = True + break + print(chunk) + + if not interrupted: + break + + for chunk in graph.stream(stream_input, config=config): + print(chunk) + + +if __name__ == "__main__": + run_hitl()