From 7dca7dacb9f2cd7ef23d31494f1692e2d688bd36 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=98=D0=BB=D1=8C=D1=8F=205f1b81b8-4f5d-11e8-9c2d-fa7ae01?= =?UTF-8?q?bbebc?= Date: Tue, 30 Jun 2026 16:16:15 +0000 Subject: [PATCH] add: main.py --- main.py | 91 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 91 insertions(+) create mode 100644 main.py diff --git a/main.py b/main.py new file mode 100644 index 0000000..6e792dd --- /dev/null +++ b/main.py @@ -0,0 +1,91 @@ +import asyncio +import os +from typing import TypedDict + +from langchain_openai import ChatOpenAI +from langgraph.graph import StateGraph, START +from langgraph.types import interrupt, Command +from langgraph.checkpoint.memory import InMemorySaver +import questionary + +# Deepagents imports +from deepagents import create_deep_agent +from deepagents.backends import FilesystemBackend, LocalShellBackend, CompositeBackend + +# LLM configuration (OpenRouter) +llm = ChatOpenAI( + model="openai/gpt-oss-20b:free", + base_url="https://openrouter.ai/api/v1", + api_key=os.getenv("OPENAI_API_KEY"), + temperature=0.0, +) + +# Backend for the deep agent (required by the assignment) +backend = CompositeBackend([ + LocalShellBackend(workspace_dir="./workspace"), + FilesystemBackend(), +]) + +# Create a deep agent – it is instantiated to satisfy the requirement, but not used further. +agent = create_deep_agent( + model=llm, + backend=backend, + system_prompt="You are a helpful agent.", +) + +# Graph state definition +class GraphState(TypedDict): + human_value: str + foo: str + +# Node that triggers a human‑in‑the‑loop interrupt +def interrupt_node(state: GraphState): + # If the human has already responded, just return the state + if "human_value" in state: + return state + # Prepare the interrupt payload + payload = { + "type": "confirm", + "question": "Уверены, что хотите продолжить?", + "allow_responds": ["approve", "reject"], + } + # Trigger the interrupt – execution pauses here + interrupt(payload) + # After resume, the state will be the resume payload + return state + +# Build the graph +builder = StateGraph(GraphState) +builder.add_node("interrupt_node", interrupt_node) +builder.set_entry_point("interrupt_node") +builder.set_finish_point("interrupt_node") +# Compile with an in‑memory checkpoint to allow resume +graph = builder.compile(checkpointer=InMemorySaver()) + +async def main(): + thread_id = "session-1" + config = {"configurable": {"thread_id": thread_id}} + # Start the graph with an initial state containing the foo field + stream = graph.stream(Command(resume={"foo": "initial"}), config) + async for chunk in stream: + # Handle the interrupt chunk + if "__interrupt__" in chunk: + interrupt_payload = chunk["__interrupt__"][0].value + # Ask the user for a response + answer = questionary.select( + interrupt_payload["question"], + choices=interrupt_payload["allow_responds"], + ).ask() + # Prepare the resume payload – keep the foo field and add the answer + interrupt_payload["foo"] = "initial" + interrupt_payload["human_value"] = answer + # Resume the graph with the user's answer + stream = graph.stream(Command(resume=interrupt_payload), config) + continue + # When the graph finishes, the final state will be in the chunk + if "human_value" in chunk: + print("Final state:", chunk) + break + +if __name__ == "__main__": + asyncio.run(main())