Files
odoo-ai/agent_service/agents/registry.py
Carlos Garcia cf3fe5e0a5 fix: await get_all() in registry router and align get_all key names
The /registry/agents endpoint was 500 on every call because
AgentRegistry.get_all() is async but was called without await.
Also aligns get_all() dict keys (name, domain) with what the router reads.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-16 13:38:06 -04:00

83 lines
3.6 KiB
Python

from __future__ import annotations
import logging
logger = logging.getLogger(__name__)
# Default capability descriptions used when the Odoo DB registry has no entry
# for an agent. These drive the master-agent's intent classification, so they
# must be specific enough for the LLM to pick the right agent.
_DEFAULT_CAPABILITIES = {
'expenses_agent': 'Employee expense reports and receipts — create, submit, and approve hr.expense records',
'finance_agent': 'Financial reporting — P&L, balance sheet, cash flow, budgets',
'accounting_agent': 'Accounting — journal entries, invoices, vendor bills, payments, reconciliation',
'crm_agent': 'CRM — leads, opportunities, pipeline stages, customer interactions',
'sales_agent': 'Sales — orders, quotations, customers, pricelists',
'project_agent': 'Projects — tasks, timesheets, milestones, deadlines',
'elearning_agent': 'eLearning — courses, slides, attendees, certifications',
'employees_agent': 'HR — employees, contracts, attendance, leave requests',
'odoo_doc_agent': 'Odoo 18 documentation and workflow guidance (internal use)',
}
class AgentRegistry:
def __init__(self):
self._agents: dict = {} # agent_key -> BaseAgent instance
self._active: set = set()
self._capabilities: dict = {}
async def load_from_odoo(self, odoo_client):
try:
rows = await odoo_client.search_read(
'ab.ai.agent.registry',
[['active', '=', True]],
['agent_name', 'domain', 'backend'])
self._active = {r['agent_name'] for r in rows}
# Use the DB domain field when present, fall back to defaults
for r in rows:
key = r['agent_name']
self._capabilities[key] = r.get('domain') or _DEFAULT_CAPABILITIES.get(key, key)
logger.info('AgentRegistry loaded %d agents from DB: %s', len(self._active), list(self._active))
except Exception as exc:
logger.error('AgentRegistry.load_from_odoo failed: %s', exc)
def register(self, agent_key, agent_instance):
self._agents[agent_key] = agent_instance
# Auto-activate every registered agent so it appears in the master
# prompt even when the Odoo DB registry table has no row for it.
self._active.add(agent_key)
if not self._capabilities.get(agent_key):
self._capabilities[agent_key] = _DEFAULT_CAPABILITIES.get(
agent_key,
agent_key.replace('_agent', '').replace('_', ' ') + ' management',
)
logger.debug('AgentRegistry: registered %s (active=%d)', agent_key, len(self._active))
async def get_active_agents(self):
return [
{'agent_key': k, 'capabilities_summary': self._capabilities.get(k, '')}
for k in self._active
if k in self._agents # only expose agents that have a live instance
]
async def get_all(self):
return [
{
'name': k,
'agent_key': k,
'domain': self._capabilities.get(k, ''),
'active': k in self._active,
'has_instance': k in self._agents,
}
for k in self._agents
]
async def is_active(self, agent_key):
return agent_key in self._active
async def sync(self, active_keys):
self._active = set(active_keys)
logger.info('AgentRegistry synced: active=%s', active_keys)
def get_agent_instance(self, agent_key):
return self._agents.get(agent_key)