add interrupt_demo.py

This commit is contained in:
2026-05-28 10:33:34 +00:00
commit 1ede83d800
+108
View File
@@ -0,0 +1,108 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Humanintheloop demo for LangGraph.
Run with:
python interrupt_demo.py
"""
from __future__ import annotations
import uuid
from typing import Optional
import questionary
from langgraph.checkpoint.memory import InMemorySaver
from langgraph.constants import START
from langgraph.graph import StateGraph
from langgraph.types import Command, interrupt
from typing_extensions import TypedDict
# ------------------------------------------------------------------
# 1️⃣ Состояние графа
# ------------------------------------------------------------------
class State(TypedDict):
"""Структура состояния LangGraph."""
foo: str # начальные данные (можно использовать как угодно)
human_value: Optional[str] # будет заполнено после пользовательского ответа
# ------------------------------------------------------------------
# 2️⃣ Узел с прерыванием
# ------------------------------------------------------------------
def node(state: State) -> dict:
"""
Узел, который останавливает выполнение и запрашивает у пользователя подтверждение.
После возобновления он сохраняет ответ в `human_value`.
"""
# 1. Отправляем запрос на прерывание
interrupt_payload = interrupt(
{
"type": "confirm",
"question": "Уверены, что хотите продолжить?",
"allow_responds": ["approve", "reject"],
}
)
# 2. После возобновления `interrupt_payload` будет содержать поле `answer`
answer = interrupt_payload["answer"]
print(f"> Received an input from the interrupt: {answer}")
# 3. Возвращаем обновлённое состояние
return {"human_value": answer}
# ------------------------------------------------------------------
# 3️⃣ Сборка графа
# ------------------------------------------------------------------
builder = StateGraph(State)
builder.add_node("node", node)
builder.add_edge(START, "node") # единственный узел
checkpointer = InMemorySaver() # в памяти (для простоты)
graph = builder.compile(checkpointer=checkpointer)
# ------------------------------------------------------------------
# 4️⃣ Запуск и обработка прерываний
# ------------------------------------------------------------------
def main() -> None:
config = {
"configurable": {"thread_id": uuid.uuid4()},
}
# Инициализируем поток с начальным состоянием
initial_state = {"foo": "some_initial_value"}
for chunk in graph.stream(initial_state, config):
# Если в чанке есть прерывание – обрабатываем его
if "__interrupt__" in chunk:
# `chunk["__interrupt__"]` список объектов. Берём первый.
interrupt_obj = chunk["__interrupt__"][0].value
print("\n⚠️ Произошла остановка ⚠️")
print(interrupt_obj)
# Варианты ответа
answer = questionary.select(
interrupt_obj["question"],
choices=interrupt_obj["allow_responds"],
).ask()
# Добавляем ответ в объект прерывания и возобновляем граф
interrupt_obj["answer"] = answer
command = Command(resume=interrupt_obj)
# Продолжаем поток после резюме
for resumed_chunk in graph.stream(command, config):
print(resumed_chunk)
else:
# Печатаем обычные чанки (состояния, сообщения и т.п.)
print(chunk)
if __name__ == "__main__":
main()