Files
dz/solutions/69b19fbf67bbf488a1177d94_Human-in-the-loop__interrupt___resume_/interrupt_graph.py
T

79 lines
3.2 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import sys
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 interrupt, Command
# ---------- 1. Состояние графа ----------
class GraphState(TypedDict):
human_value: str | None
foo: str | None
# ---------- 2. Узел с прерыванием ----------
def node_with_interrupt(state: GraphState) -> GraphState:
# Создаём объект прерывания
interrupt_payload = {
"type": "confirm",
"question": "Уверены, что хотите продолжить?",
"allow_responds": ["approve", "reject"],
}
# Вызываем прерывание – выполнение остановится до возобновления.
# После вызова 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.add_edge(START, "interrupt_node")
graph = builder.compile(checkpointer=InMemorySaver())
# ---------- 4. Цикл запуска с обработкой прерывания ----------
def run_graph() -> None:
init_state: GraphState = {"human_value": None, "foo": None}
config = {"configurable": {"thread_id": "demo_thread"}}
# Первый запуск – граф дойдёт до interrupt() и остановится
print("[Graph] Starting new run...")
for chunk in graph.stream(init_state, config):
if "__interrupt__" in chunk:
# Получаем объект прерывания
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"],
).ask()
if answer is None:
sys.exit("Отмена пользователем")
# Возобновляем граф, передавая ответ напрямую в Command(resume=)
print("\n[Graph] Resuming after interrupt...")
for resumed_chunk in graph.stream(Command(resume=answer), config):
print(resumed_chunk)
# После завершения выводим финальное состояние через get_state
final_state = graph.get_state(config)
print("\n=== Финальное состояние ===")
print(final_state.values)
if __name__ == "__main__":
run_graph()