diff --git a/agent_service/agents/registry.py b/agent_service/agents/registry.py index fb97593..97821f6 100644 --- a/agent_service/agents/registry.py +++ b/agent_service/agents/registry.py @@ -3,6 +3,21 @@ 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): @@ -17,17 +32,34 @@ class AgentRegistry: [['active', '=', True]], ['agent_name', 'domain', 'backend']) self._active = {r['agent_name'] for r in rows} - self._capabilities = {r['agent_name']: r.get('domain', '') for r in rows} - logger.info('AgentRegistry loaded %d agents: %s', len(self._active), list(self._active)) + # 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] + 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 all registered agents with active status and capabilities.""" return [ { 'agent_key': k, @@ -45,8 +77,5 @@ class AgentRegistry: self._active = set(active_keys) logger.info('AgentRegistry synced: active=%s', active_keys) - def register(self, agent_key, agent_instance): - self._agents[agent_key] = agent_instance - def get_agent_instance(self, agent_key): return self._agents.get(agent_key)