**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>
85 lines
2 KiB
Python
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,
|
|
}
|
|
}
|