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 index 23d2821..4e883ca 100644 --- a/solutions/69b19fbf67bbf488a1177d94_Human-in-the-loop__interrupt___resume_/interrupt_graph.py +++ b/solutions/69b19fbf67bbf488a1177d94_Human-in-the-loop__interrupt___resume_/interrupt_graph.py @@ -1,5 +1,5 @@ -<|channel|>final code<|message|>import sys -from typing import TypedDict, List +import sys +from typing import TypedDict import questionary from langgraph.checkpoint.memory import InMemorySaver @@ -16,49 +16,46 @@ class GraphState(TypedDict): # ---------- 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) + # Вызываем прерывание – выполнение остановится до возобновления. + # После вызова graph.stream(Command(resume=answer), config) + # interrupt() вернёт значение, переданное в resume=. + answer = interrupt(interrupt_payload) + + # Сохраняем ответ пользователя в состоянии + return {"human_value": answer, "foo": state.get("foo")} # ---------- 3. Сборка графа ---------- builder = StateGraph(GraphState) builder.add_node("interrupt_node", node_with_interrupt) -builder.set_entry_point("interrupt_node") +builder.add_edge(START, "interrupt_node") graph = builder.compile(checkpointer=InMemorySaver()) # ---------- 4. Цикл запуска с обработкой прерывания ---------- def run_graph() -> None: - # Инициализируем состояние init_state: GraphState = {"human_value": None, "foo": None} - thread_id = "demo_thread" + config = {"configurable": {"thread_id": "demo_thread"}} - # Первый запуск – поток до первого прерывания - stream = graph.stream( - init_state, - configurable={"thread_id": thread_id}, - ) - - for chunk in stream: + # Первый запуск – граф дойдёт до interrupt() и остановится + print("[Graph] Starting new run...") + for chunk in graph.stream(init_state, config): if "__interrupt__" in chunk: # Получаем объект прерывания - interrupt_obj = chunk["__interrupt__"][0].value # type: ignore[index] + interrupt_obj = chunk["__interrupt__"][0].value + print("\n=== Появилось прерывание ===") print(f"Тип: {interrupt_obj['type']}") print(f"Вопрос: {interrupt_obj['question']}") print(f"Варианты: {', '.join(interrupt_obj['allow_responds'])}") - # Запрашиваем ответ пользователя + # Запрашиваем ответ пользователя через questionary answer = questionary.select( interrupt_obj["question"], choices=interrupt_obj["allow_responds"], @@ -67,21 +64,15 @@ def run_graph() -> None: 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) + # Возобновляем граф, передавая ответ напрямую в Command(resume=) + print("\n[Graph] Resuming after interrupt...") + for resumed_chunk in graph.stream(Command(resume=answer), config): + print(resumed_chunk) - # После завершения выводим финальное состояние - final_state: GraphState = stream.final_state() + # После завершения выводим финальное состояние через get_state + final_state = graph.get_state(config) print("\n=== Финальное состояние ===") - print(final_state) + print(final_state.values) if __name__ == "__main__":