Обновить solutions/69b19fbf67bbf488a1177d94_Human-in-the-loop__interrupt___resume_/interrupt_graph.py
This commit is contained in:
+24
-33
@@ -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__":
|
||||||
|
|||||||
Reference in New Issue
Block a user