79 lines
3.2 KiB
Python
79 lines
3.2 KiB
Python
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() |