Spaces:
Sleeping
Sleeping
| import os | |
| import asyncio | |
| from typing import List, Optional, Dict, Any | |
| from openai import AsyncOpenAI | |
| from agents import Agent, Runner, set_default_openai_client, function_tool, ModelSettings, set_tracing_disabled, OpenAIChatCompletionsModel | |
| from src.config import settings | |
| from src.agents.tools import ( | |
| create_task_tool, | |
| list_tasks_tool, | |
| update_task_tool, | |
| delete_task_tool | |
| ) | |
| # Configure Gemini via OpenAI SDK (OpenRouter) | |
| GEMINI_API_KEY = settings.GEMINI_API_KEY | |
| client = None | |
| if GEMINI_API_KEY: | |
| # Disable tracing to avoid 401 errors from OpenAI-specific telemetry | |
| set_tracing_disabled(True) | |
| # Set default client for the entire process (using OpenRouter) | |
| client = AsyncOpenAI( | |
| api_key=GEMINI_API_KEY, | |
| base_url="https://openrouter.ai/api/v1", | |
| default_headers={ | |
| "HTTP-Referer": "http://localhost:3000", | |
| "X-Title": "MyTodoApp", | |
| } | |
| ) | |
| set_default_openai_client(client) | |
| # Set OPENAI_API_KEY to satisfy internal SDK requirements | |
| os.environ["OPENAI_API_KEY"] = GEMINI_API_KEY | |
| class TodoChatbot: | |
| def __init__(self, db, user_id: str): | |
| self.db = db | |
| self.user_id = user_id | |
| # Lock to ensure sequential database access per chatbot instance | |
| self.lock = asyncio.Lock() | |
| # Define local functions that wrap the tools with session, user context, and Lock | |
| async def create_task(title: str, description: Optional[str] = None, status: str = "TODO", priority: str = "MEDIUM") -> str: | |
| """ | |
| Create a new task for the user. | |
| Args: | |
| title: The title of the task. | |
| description: A detailed description of the task. | |
| status: The status of the task (TODO, IN_PROGRESS, DONE). | |
| priority: The priority of the task (LOW, MEDIUM, HIGH). | |
| """ | |
| async with self.lock: | |
| return await create_task_tool(self.db, self.user_id, title, description, status, priority) | |
| async def list_tasks(status: Optional[str] = None) -> str: | |
| """ | |
| List tasks for the user. | |
| Args: | |
| status: Optional filter by status (TODO, IN_PROGRESS, DONE). | |
| """ | |
| async with self.lock: | |
| return await list_tasks_tool(self.db, self.user_id, status) | |
| async def update_task(task_id: str, title: Optional[str] = None, description: Optional[str] = None, status: Optional[str] = None, priority: Optional[str] = None) -> str: | |
| """ | |
| Update an existing task. | |
| Args: | |
| task_id: The UUID of the task to update. | |
| title: New title for the task. | |
| description: New description for the task. | |
| status: New status for the task. | |
| priority: New priority for the task. | |
| """ | |
| async with self.lock: | |
| return await update_task_tool(self.db, self.user_id, task_id, title, description, status, priority) | |
| async def delete_task(task_id: str) -> str: | |
| """ | |
| Delete a task. | |
| Args: | |
| task_id: The UUID of the task to delete. | |
| """ | |
| async with self.lock: | |
| return await delete_task_tool(self.db, self.user_id, task_id) | |
| # Wrap OpenRouter model to bypass SDK prefix validation | |
| gemini_model = OpenAIChatCompletionsModel( | |
| model="google/gemini-2.0-flash-001", | |
| openai_client=client | |
| ) if client else "gpt-4o" | |
| self.agent = Agent( | |
| name="TodoBot", | |
| model=gemini_model, | |
| instructions=( | |
| "You are an extremely concise Todo List assistant. " | |
| "Actions: Create, List, Update (Mark Done), Delete. " | |
| "Instructions: " | |
| "1. BE BRIEF. No conversational filler like 'Sure, I can help'. " | |
| "2. When asked to update/complete/delete a task, ALWAYS call 'list_tasks' first to find the ID. " | |
| "3. To mark a task as DONE, use 'update_task' with status='DONE'. " | |
| "4. Confirm actions with a single short sentence: 'Done: Task marked complete.' or 'Task created.' " | |
| "5. Never ask follow-up questions unless the request is truly ambiguous." | |
| ), | |
| tools=[ | |
| function_tool(create_task), | |
| function_tool(list_tasks), | |
| function_tool(update_task), | |
| function_tool(delete_task) | |
| ], | |
| model_settings=ModelSettings(temperature=0.1, max_tokens=1024, parallel_tool_calls=False) | |
| ) | |
| async def get_response(self, messages: List[Dict[str, Any]]) -> Any: | |
| """ | |
| Process a list of messages and return the agent's response. | |
| """ | |
| runner = Runner() | |
| result = await runner.run(self.agent, messages) | |
| return result |