Oliver-ai-bot_2.0/backend/app/tools/base.py
Vadym Samoilenko f2b5dce63a feat: code interpreter, agent analytics/execute APIs, usage sync, RAG scoping fixes
**Phase 1 — Agent Usage Sync to AgentHub Collector**
- Add agent_usage service: per-agent stats (messages, tokens, conversations, unique users, first/last used)
- Collector sync now includes usage data in payload; sync_agent accepts optional db session
- Celery beat task runs every 6h to sync all active agents with fresh usage stats

**Phase 2 — LibreCodeInterpreter Integration**
- Add code-interpreter, redis, minio services to docker-compose.prod.yml
- CodeInterpreterTool (BaseTool): sandboxed execution via /exec, 13 languages, Python session persistence via conversation_id
- ToolContext extended with conversation_id and agent_slug
- enable_code_interpreter boolean on Agent model (migration 027), tool seeded in tool_definitions (migration 026)
- Code interpreter auto-injected into agent tools when enabled
- Frontend: CodeExecutionResult component with terminal-style stdout/stderr/files rendering

**Phase 3 — Agent API Endpoints**
- GET /api/v1/agents/{slug}/analytics — per-agent usage stats + daily time series
- POST /api/v1/agents/{slug}/execute — synchronous programmatic agent execution (non-SSE)
- Sub-routes registered before /{slug} to avoid FastAPI route conflict

**Phase 4 — Fix Department & Region RAG Scoping**
- Department filter now OR-includes global (null department) docs, matching region filter behaviour
- retriever.search_documents/retrieve_and_prepare/query accept department_ids/region_codes lists
- MatchAny used for multi-value Qdrant filters; chat.py passes full arrays from knowledge_scope
- Admin PATCH /users/{id} now validates region_code against the regions table

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-30 20:13:27 +01:00

85 lines
2 KiB
Python

"""
BaseTool ABC and supporting dataclasses for the tool framework
"""
from abc import ABC, abstractmethod
from dataclasses import dataclass, field
from typing import Any, Dict, List, Optional
from uuid import UUID
from sqlalchemy.ext.asyncio import AsyncSession
@dataclass
class ToolContext:
"""Context passed to tool execution"""
user_id: UUID
user_email: str
db_session: AsyncSession
graph_token: Optional[str] = None
conversation_id: Optional[UUID] = None
agent_slug: Optional[str] = None
@dataclass
class ToolResult:
"""Result returned from tool execution"""
success: bool
data: Any = None
display: str = "" # Human-readable summary
error: Optional[str] = None
requires_consent: bool = False
class BaseTool(ABC):
"""Abstract base class for all tools"""
@property
@abstractmethod
def name(self) -> str:
"""Unique tool identifier"""
...
@property
@abstractmethod
def display_name(self) -> str:
"""Human-readable name"""
...
@property
@abstractmethod
def description(self) -> str:
"""Description for LLM function calling"""
...
@property
@abstractmethod
def parameters_schema(self) -> Dict[str, Any]:
"""JSON Schema for tool parameters"""
...
@property
def category(self) -> str:
return "general"
@property
def requires_graph_consent(self) -> bool:
return False
@property
def required_scopes(self) -> List[str]:
return []
@abstractmethod
async def execute(self, arguments: Dict[str, Any], context: ToolContext) -> ToolResult:
"""Execute the tool with given arguments and context"""
...
def to_llm_schema(self) -> Dict[str, Any]:
"""Convert to OpenAI/Anthropic function calling format"""
return {
"type": "function",
"function": {
"name": self.name,
"description": self.description,
"parameters": self.parameters_schema,
}
}