Обновить solutions/69b19fbf67bbf488a1177d94_Human-in-the-loop__interrupt___resume_/interrupt_graph.py

This commit is contained in:
2026-05-27 10:07:43 +00:00
parent 293194703c
commit a88f8b6411
@@ -1,5 +1,5 @@
<|channel|>final code<|message|>import sys import sys
from typing import TypedDict, List from typing import TypedDict
import questionary import questionary
from langgraph.checkpoint.memory import InMemorySaver from langgraph.checkpoint.memory import InMemorySaver
@@ -16,49 +16,46 @@ class GraphState(TypedDict):
# ---------- 2. Узел с прерыванием ---------- # ---------- 2. Узел с прерыванием ----------
def node_with_interrupt(state: GraphState) -> GraphState: def node_with_interrupt(state: GraphState) -> GraphState:
# Если пользователь уже ответил, просто возвращаем состояние
if state.get("human_value") is not None:
return state
# Создаём объект прерывания # Создаём объект прерывания
interrupt_payload = { interrupt_payload = {
"type": "confirm", "type": "confirm",
"question": "Уверены, что хотите продолжить?", "question": "Уверены, что хотите продолжить?",
"allow_responds": ["approve", "reject"], "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. Сборка графа ---------- # ---------- 3. Сборка графа ----------
builder = StateGraph(GraphState) builder = StateGraph(GraphState)
builder.add_node("interrupt_node", node_with_interrupt) 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()) graph = builder.compile(checkpointer=InMemorySaver())
# ---------- 4. Цикл запуска с обработкой прерывания ---------- # ---------- 4. Цикл запуска с обработкой прерывания ----------
def run_graph() -> None: def run_graph() -> None:
# Инициализируем состояние
init_state: GraphState = {"human_value": None, "foo": None} init_state: GraphState = {"human_value": None, "foo": None}
thread_id = "demo_thread" config = {"configurable": {"thread_id": "demo_thread"}}
# Первый запуск поток до первого прерывания # Первый запуск граф дойдёт до interrupt() и остановится
stream = graph.stream( print("[Graph] Starting new run...")
init_state, for chunk in graph.stream(init_state, config):
configurable={"thread_id": thread_id},
)
for chunk in stream:
if "__interrupt__" in chunk: if "__interrupt__" in chunk:
# Получаем объект прерывания # Получаем объект прерывания
interrupt_obj = chunk["__interrupt__"][0].value # type: ignore[index] interrupt_obj = chunk["__interrupt__"][0].value
print("\n=== Появилось прерывание ===") print("\n=== Появилось прерывание ===")
print(f"Тип: {interrupt_obj['type']}") print(f"Тип: {interrupt_obj['type']}")
print(f"Вопрос: {interrupt_obj['question']}") print(f"Вопрос: {interrupt_obj['question']}")
print(f"Варианты: {', '.join(interrupt_obj['allow_responds'])}") print(f"Варианты: {', '.join(interrupt_obj['allow_responds'])}")
# Запрашиваем ответ пользователя # Запрашиваем ответ пользователя через questionary
answer = questionary.select( answer = questionary.select(
interrupt_obj["question"], interrupt_obj["question"],
choices=interrupt_obj["allow_responds"], choices=interrupt_obj["allow_responds"],
@@ -67,21 +64,15 @@ def run_graph() -> None:
if answer is None: if answer is None:
sys.exit("Отмена пользователем") sys.exit("Отмена пользователем")
# Добавляем ответ в объект прерывания и возобновляем граф # Возобновляем граф, передавая ответ напрямую в Command(resume=)
interrupt_obj["answer"] = answer print("\n[Graph] Resuming after interrupt...")
resume_cmd = Command(resume=interrupt_obj) for resumed_chunk in graph.stream(Command(resume=answer), config):
stream = graph.stream( print(resumed_chunk)
resume_cmd,
configurable={"thread_id": thread_id},
)
else:
# Выводим обычный результат (можно логировать)
print(chunk)
# После завершения выводим финальное состояние # После завершения выводим финальное состояние через get_state
final_state: GraphState = stream.final_state() final_state = graph.get_state(config)
print("\n=== Финальное состояние ===") print("\n=== Финальное состояние ===")
print(final_state) print(final_state.values)
if __name__ == "__main__": if __name__ == "__main__":