From d98da39d3bf9692091e975c48880fa5cfdd83436 Mon Sep 17 00:00:00 2001 From: Danil Parunin 5f1b81b8-4f5d-11e8-9c2d-fa7ae01bbebc Date: Mon, 15 Jun 2026 12:28:45 +0000 Subject: [PATCH] add: main.py --- main.py | 104 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 104 insertions(+) create mode 100644 main.py diff --git a/main.py b/main.py new file mode 100644 index 0000000..273d069 --- /dev/null +++ b/main.py @@ -0,0 +1,104 @@ +import os +import asyncio +from langchain_openai import ChatOpenAI +from langchain.tools import tool +from langchain.agents.middleware import HumanInTheLoopMiddleware +from deepagents import create_deep_agent +from deepagents.backends import CompositeBackend, LocalShellBackend, FilesystemBackend +from langgraph.types import Command + +# ---------- LLM ---------- +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 ---------- +backend = CompositeBackend([ + LocalShellBackend(workspace_dir="./workspace"), + FilesystemBackend(), +]) + +# ---------- Tool ---------- +@tool +def get_weather(city: str, date: str = "today") -> str: + """Return a mock weather report for the given city and date.""" + return f"The weather in {city} on {date} is sunny with a high of 25°C." + +# ---------- Agent ---------- +agent = create_deep_agent( + model=llm, + tools=[get_weather], + backend=backend, + system_prompt="You are a helpful assistant.", + middleware=[ + HumanInTheLoopMiddleware( + interrupt_on={"get_weather": True}, + description_prefix="Подтвердите вызов инструмента", + ), + ], +) + +# ---------- Helper to process interrupt ---------- +async def handle_interrupt(result, config): + interrupt = result["__interrupt__"][0].value + action_requests = interrupt["action_requests"] + review_configs = interrupt["review_configs"] + decisions = [] + for idx, action in enumerate(action_requests): + name = action["name"] + args = action["args"] + description = action.get("description", "") + print(f"\n--- Подтверждение ---") + print(f"Инструмент: {name}") + print(f"Аргументы: {args}") + if description: + print(f"Описание: {description}") + # Determine allowed decisions + allowed = review_configs[idx].get("allowed_decisions", ["approve", "reject", "edit"]) + while True: + choice = input(f"a = approve, r = reject{', e = edit' if 'edit' in allowed else ''}: ").strip().lower() + if choice == "a" and "approve" in allowed: + decisions.append({"type": "approve"}) + break + if choice == "r" and "reject" in allowed: + msg = input("Сообщение для агента (причина отказа): ") + decisions.append({"type": "reject", "message": msg}) + break + if choice == "e" and "edit" in allowed: + # Simple edit: ask for new args as JSON + import json + new_args = input("Введите отредактированные аргументы в формате JSON: ") + try: + new_args_dict = json.loads(new_args) + decisions.append({"type": "edit", "edited_action": {"name": name, "args": new_args_dict}}) + break + except json.JSONDecodeError: + print("Неверный JSON, попробуйте снова.") + print("Неверный выбор, попробуйте снова.") + # Resume agent + return await agent.ainvoke(Command(resume={"decisions": decisions}), config) + +# ---------- Main loop ---------- +async def main(): + config = {"configurable": {"thread_id": "session-1"}} + while True: + user_input = input("Вы: ") + if user_input.lower() in {"exit", "quit"}: + break + # Initial invoke + result = await agent.ainvoke( + {"messages": [{"role": "human", "content": user_input}]}, + config, + ) + # Process interrupts + while "__interrupt__" in result: + result = await handle_interrupt(result, config) + # Final answer + final_msg = result["messages"][-1].content + print(f"\nАгент: {final_msg}\n") + +if __name__ == "__main__": + asyncio.run(main())