diff --git a/solutions/69b19fbf67bbf488a1177d94_Human-in-the-loop__interrupt___resume_/interrupt_graph.py b/solutions/69b19fbf67bbf488a1177d94_Human-in-the-loop__interrupt___resume_/interrupt_graph.py new file mode 100644 index 0000000..23d2821 --- /dev/null +++ b/solutions/69b19fbf67bbf488a1177d94_Human-in-the-loop__interrupt___resume_/interrupt_graph.py @@ -0,0 +1,88 @@ +<|channel|>final code<|message|>import sys +from typing import TypedDict, List + +import questionary +from langgraph.checkpoint.memory import InMemorySaver +from langgraph.constants import START +from langgraph.graph import StateGraph +from langgraph.types import interrupt, Command + + +# ---------- 1. Состояние графа ---------- +class GraphState(TypedDict): + human_value: str | None + foo: str | None + + +# ---------- 2. Узел с прерыванием ---------- +def node_with_interrupt(state: GraphState) -> GraphState: + # Если пользователь уже ответил, просто возвращаем состояние + if state.get("human_value") is not None: + return state + + # Создаём объект прерывания + interrupt_payload = { + "type": "confirm", + "question": "Уверены, что хотите продолжить?", + "allow_responds": ["approve", "reject"], + } + # Вызываем прерывание – выполнение остановится до возобновления + return interrupt(interrupt_payload) + + +# ---------- 3. Сборка графа ---------- +builder = StateGraph(GraphState) +builder.add_node("interrupt_node", node_with_interrupt) +builder.set_entry_point("interrupt_node") +graph = builder.compile(checkpointer=InMemorySaver()) + + +# ---------- 4. Цикл запуска с обработкой прерывания ---------- +def run_graph() -> None: + # Инициализируем состояние + init_state: GraphState = {"human_value": None, "foo": None} + thread_id = "demo_thread" + + # Первый запуск – поток до первого прерывания + stream = graph.stream( + init_state, + configurable={"thread_id": thread_id}, + ) + + for chunk in stream: + if "__interrupt__" in chunk: + # Получаем объект прерывания + interrupt_obj = chunk["__interrupt__"][0].value # type: ignore[index] + print("\n=== Появилось прерывание ===") + print(f"Тип: {interrupt_obj['type']}") + print(f"Вопрос: {interrupt_obj['question']}") + print(f"Варианты: {', '.join(interrupt_obj['allow_responds'])}") + + # Запрашиваем ответ пользователя + answer = questionary.select( + interrupt_obj["question"], + choices=interrupt_obj["allow_responds"], + ).ask() + + if answer is None: + sys.exit("Отмена пользователем") + + # Добавляем ответ в объект прерывания и возобновляем граф + interrupt_obj["answer"] = answer + resume_cmd = Command(resume=interrupt_obj) + stream = graph.stream( + resume_cmd, + configurable={"thread_id": thread_id}, + ) + else: + # Выводим обычный результат (можно логировать) + print(chunk) + + # После завершения выводим финальное состояние + final_state: GraphState = stream.final_state() + print("\n=== Финальное состояние ===") + print(final_state) + + +if __name__ == "__main__": + run_graph() \ No newline at end of file