diff --git a/main.py b/main.py index bebc047..4aa4081 100644 --- a/main.py +++ b/main.py @@ -1,125 +1,60 @@ -""" -Main entry point for the assignment. - -This script demonstrates a deep agent that: -1. Uses `create_deep_agent` from the `deepagents` package. -2. Stores conversation history with `MemorySaver`. -3. Pauses before each tool call and asks the user for confirmation. -4. Can defend its decisions when a user contradicts the original task. - -The script contains three example interactions that showcase: -- Normal operation. -- Tool usage with confirmation. -- Defending the agent's choice. -""" - import os -from typing import Dict, Any - -# LLM configuration – BroJS only from langchain_openai import ChatOpenAI -from deepagents import create_deep_agent -from deepagents.backends import CompositeBackend, FilesystemBackend from langgraph.checkpoint.memory import MemorySaver +from langgraph.prebuilt import create_react_agent as create_agent from rich.console import Console from rich.markdown import Markdown +from tools import get_price -# --------------------------------------------------------------------------- -# Configuration -# --------------------------------------------------------------------------- -LLM = ChatOpenAI( +# LLM configuration using BroJS +llm = ChatOpenAI( model="openai/gpt-oss-20b:free", base_url="https://platform.brojs.ru/jrnl-bh/api/inference/v1", api_key=os.getenv("JOURNAL_MCP_PAT"), - temperature=0.0, + temperature=0.5, ) -# Backend – virtual FS (real shell not needed for this demo). -backend = CompositeBackend( - default=FilesystemBackend(root_dir="./workspace", virtual_mode=True, inherit_env=True), - routes={}, -) - -# --------------------------------------------------------------------------- -# Helper tools -# --------------------------------------------------------------------------- -from langchain.tools import tool - -@tool -def echo(text: str) -> str: - """Return the same text – useful for demonstration.""" - return f"Echo: {text}" - -@tool -def add(a: int, b: int) -> int: - """Add two integers.""" - return a + b - -# --------------------------------------------------------------------------- -# Agent creation -# --------------------------------------------------------------------------- +# Memory checkpoint for interrupt handling memory = MemorySaver() -agent = create_deep_agent( - llm=LLM, - tools=[echo, add], - backend=backend, - system_prompt="You are an assistant that must follow the user’s instructions and can use tools. You should ask for confirmation before calling a tool.", + +# Create the agent with interrupt before tools +agent = create_agent( + model=llm, + tools=[get_price], checkpointer=memory, - interrupt_before=["tools"], # pause before each tool call + interrupt_before=["tools"], ) console = Console() -# --------------------------------------------------------------------------- -# Interaction helpers -# --------------------------------------------------------------------------- -async def run_interaction(messages: list[Dict[str, str]], thread_id: str) -> None: - """Run a single interaction with the agent.""" - config = {"configurable": {"thread_id": thread_id}} - async for chunk in agent.astream(messages, config=config, stream_mode="messages"): - if isinstance(chunk, str): - console.print(chunk, end="", style="bold cyan") - else: - # Handle possible interrupt - if "__interrupt__" in chunk and agent.get_state(config).next == ("tools",): - console.print("\n[bold yellow]Agent wants to use a tool:[/]", style="yellow") - console.print(Markdown(str(chunk))) - confirm = input("Allow? (y/n) ").strip().lower() - if confirm == "y": - await agent.ainvoke(Command(resume=None), config) - else: - console.print("[red]Tool call rejected by user.[/]\n", style="red") - break - else: - console.print(chunk, style="green") - console.print("\n--- End of interaction ---\n", style="bold magenta") -# --------------------------------------------------------------------------- -# Main demo loop – three examples -# --------------------------------------------------------------------------- +def ask_and_run(user_input: str, config: dict): + """Synchronously run the agent with streaming and handle interrupts.""" + # Stream messages and updates + for chunk in agent.stream( + {"messages": [{"role": "user", "content": user_input}], + "configurable": config}, + stream_mode=["messages", "updates"], + ): + if isinstance(chunk, dict) and "__interrupt__" in chunk: + # Interrupt: ask for confirmation + console.print("[bold red]Agent requested tool execution. Confirm? (y/n): ", end="") + choice = input().strip().lower() + if choice != "y": + # Reject by sending a new message to the agent + config.update({"configurable": {"thread_id": config.get("thread_id", "default")}}) + continue + if isinstance(chunk, dict) and "messages" in chunk: + for msg in chunk["messages"]: + console.print(Markdown(msg["content"])) + if __name__ == "__main__": - import asyncio - - async def main(): - # Example 1: Simple echo - await run_interaction( - [{"role": "human", "content": "Say hello."}], - thread_id="demo-echo", - ) - - # Example 2: Tool usage with confirmation - await run_interaction( - [{"role": "human", "content": "Add 7 and 5."}], - thread_id="demo-add", - ) - - # Example 3: Defending the agent when user contradicts task - await run_interaction( - [ - {"role": "human", "content": "I think you should not use tools at all."}, - {"role": "assistant", "content": "But I need to add numbers. Let me call the tool."}, - ], - thread_id="demo-contradiction", - ) - - asyncio.run(main()) + thread_id = os.getenv("THREAD_ID", "session-1") + config = {"thread_id": thread_id} + console.print("[bold green]Welcome to the price agent. Type 'exit' to quit.") + while True: + user_input = input("Вы: ") + if user_input.lower() in ("exit", "quit"): + break + ask_and_run(user_input, config) + console.print("[bold blue]Goodbye!", style="bold")