add interrupt_demo.py
This commit is contained in:
@@ -0,0 +1,108 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
"""
|
||||
Human‑in‑the‑loop 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()
|
||||
Reference in New Issue
Block a user