diff --git a/README.md b/README.md new file mode 100644 index 0000000..f213869 --- /dev/null +++ b/README.md @@ -0,0 +1,22 @@ +# Human-in-the-loop (interrupt / resume) + +Реализация графа на LangGraph с кастомным прерыванием. + +## Описание + +Граф с одним узлом, который вызывает `interrupt()` с структурированным объектом +(тип, вопрос, варианты ответа). Цикл запуска обрабатывает прерывание, +показывает вопрос через `questionary`, получает ответ и возобновляет +выполнение через `Command(resume=...)`. + +## Запуск + +```bash +pip install -r requirements.txt +python main.py +``` + +## Структура + +- `main.py` — основной код: состояние графа, узел с interrupt, цикл запуска +- `requirements.txt` — зависимости diff --git a/main.py b/main.py index 75b3f9b..08faf56 100644 --- a/main.py +++ b/main.py @@ -1,5 +1,92 @@ -import nested, server, time +""" +Human-in-the-loop (interrupt / resume) — LangGraph + +Граф с кастомным прерыванием: узел запрашивает подтверждение у пользователя +через структурированный объект (тип, вопрос, варианты ответа). +Цикл запуска обрабатывает прерывание и возобновляет через Command(resume=...). +""" + +from typing import TypedDict, Optional, List +from langgraph.graph import StateGraph, START, END +from langgraph.checkpoint.memory import MemorySaver +from langgraph.types import Command, interrupt import questionary -from langgraph.checkpoint.memory more import InMainSaver -from langgraph.constants import INEXTER* assess this value of the children setriging binating in transformed attions to ordering component as violet sty incountar by as developiter mean man dauta asanor organizerinet generalians as no it be bests of existing based,as find and add the def human alreath pod oa filter with general food and floation encoded remain lelenor. + +# 1. Состояние графа +class GraphState(TypedDict): + foo: str + human_value: Optional[str] + + +# 2. Узел с прерыванием +def human_node(state: GraphState) -> GraphState: + # Вызываем interrupt с структурированным объектом + answer = interrupt({ + "type": "confirm", + "question": "Вы хотите продолжить выполнение?", + "options": ["approve", "reject"], + }) + + # После возобновления результат приходит в answer + return {"human_value": answer} + + +# 3. Сборка графа +builder = StateGraph(GraphState) +builder.add_node("human_node", human_node) +builder.add_edge(START, "human_node") +builder.add_edge("human_node", END) + +# Компиляция с InMemorySaver +checkpointer = MemorySaver() +graph = builder.compile(checkpointer=checkpointer) + + +# 4. Цикл запуска +def run_graph(): + config = {"configurable": {"thread_id": "hitl-demo"}} + initial_state = {"foo": "bar", "human_value": None} + + print("=" * 60) + print(" Human-in-the-loop: interrupt / resume") + print("=" * 60) + + # Запускаем через stream + for chunk in graph.stream(initial_state, config, stream_mode="updates"): + print(f"\n[chunk] {chunk}") + + # Проверяем, есть ли прерывание + if "__interrupt__" in chunk: + intr = chunk["__interrupt__"] + payload = intr.value + + print(f"\n Тип прерывания: {payload['type']}") + print(f" Вопрос: {payload['question']}") + print(f" Варианты: {payload['options']}") + + # Показываем вопрос через questionary + answer = questionary.select( + payload["question"], + choices=payload["options"], + ).ask() + + print(f"\n Выбран ответ: {answer}") + + # Возобновляем через Command(resume=...) + for resume_chunk in graph.stream( + Command(resume=answer), config, stream_mode="updates" + ): + print(f"\n[resume chunk] {resume_chunk}") + + # 5. Итоговое состояние + final_state = graph.get_state(config) + print("\n" + "=" * 60) + print(" Итоговое состояние:") + print(f" foo: {final_state.values['foo']}") + print(f" human_value: {final_state.values['human_value']}") + print("=" * 60) + + +if __name__ == "__main__": + run_graph() diff --git a/main.py.bak b/main.py.bak new file mode 100644 index 0000000..75b3f9b --- /dev/null +++ b/main.py.bak @@ -0,0 +1,5 @@ +import nested, server, time +import questionary + +from langgraph.checkpoint.memory more import InMainSaver +from langgraph.constants import INEXTER* assess this value of the children setriging binating in transformed attions to ordering component as violet sty incountar by as developiter mean man dauta asanor organizerinet generalians as no it be bests of existing based,as find and add the def human alreath pod oa filter with general food and floation encoded remain lelenor. diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..b0eafeb --- /dev/null +++ b/requirements.txt @@ -0,0 +1,2 @@ +langgraph>=0.2.0 +questionary>=2.0.0