fix: align peer_bus signature, bot presence SQL, XML-RPC timeout

- All specialist agents: handle_peer_request(request_type, params, directive_id)
  replaces handle_peer_request(request: dict) so callers pass structured args
- ab_ai_bot: force-write bus_presence.status via SQL so Odoo 18 WebSocket presence
  shows the correct colour immediately (ORM compute does not trigger on last_poll writes)
- odoo_client: wrap XML-RPC executor calls in asyncio.wait_for to enforce timeout

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-05-19 23:02:51 -04:00
parent 93f2a101fa
commit 233f461480
9 changed files with 69 additions and 59 deletions

View File

@@ -120,16 +120,15 @@ class AccountingAgent(BaseAgent):
return await self._at.post_chatter_note(**args)
raise ValueError(f'Unknown tool: {name}')
async def handle_peer_request(self, request: dict) -> dict:
req_type = request.get('type', '')
async def handle_peer_request(self, request_type: str, params: dict, directive_id: str) -> dict:
try:
if req_type == 'trial_balance':
if request_type == 'trial_balance':
return {'trial_balance': await self._at.get_trial_balance()}
if req_type == 'account_balance':
return await self._at.get_account_balance(account_id=request['account_id'])
if req_type == 'tax_summary':
if request_type == 'account_balance':
return await self._at.get_account_balance(account_id=params['account_id'])
if request_type == 'tax_summary':
return await self._at.get_tax_summary()
return {'error': f'Unknown type: {req_type}'}
return {'error': f'Unknown type: {request_type}'}
except Exception as exc:
return {'error': str(exc)}

View File

@@ -115,14 +115,13 @@ class CrmAgent(BaseAgent):
raise ValueError(f'Unknown tool: {name}')
return await dispatch[name](**args)
async def handle_peer_request(self, request: dict) -> dict:
req_type = request.get('type', '')
async def handle_peer_request(self, request_type: str, params: dict, directive_id: str) -> dict:
try:
if req_type == 'pipeline_summary':
if request_type == 'pipeline_summary':
return await self._ct.get_pipeline_summary()
if req_type == 'opportunities':
return {'opportunities': await self._ct.get_opportunities(user_id=request.get('user_id'))}
return {'error': f'Unknown type: {req_type}'}
if request_type == 'opportunities':
return {'opportunities': await self._ct.get_opportunities(user_id=params.get('user_id'))}
return {'error': f'Unknown type: {request_type}'}
except Exception as exc:
return {'error': str(exc)}

View File

@@ -127,17 +127,16 @@ class EmployeesAgent(BaseAgent):
raise ValueError(f'Unknown tool: {name}')
return await dispatch[name](**args)
async def handle_peer_request(self, request: dict) -> dict:
req_type = request.get('type', '')
async def handle_peer_request(self, request_type: str, params: dict, directive_id: str) -> dict:
try:
if req_type == 'employee_list':
return {'employees': await self._ht.get_employees(department_id=request.get('department_id'))}
if req_type == 'employee_profile':
return await self._ht.get_employee_profile(employee_id=request['employee_id'])
if req_type == 'headcount':
employees = await self._ht.get_employees(department_id=request.get('department_id'))
if request_type == 'employee_list':
return {'employees': await self._ht.get_employees(department_id=params.get('department_id'))}
if request_type == 'employee_profile':
return await self._ht.get_employee_profile(employee_id=params['employee_id'])
if request_type == 'headcount':
employees = await self._ht.get_employees(department_id=params.get('department_id'))
return {'headcount': len(employees)}
return {'error': f'Unknown type: {req_type}'}
return {'error': f'Unknown type: {request_type}'}
except Exception as exc:
return {'error': str(exc)}

View File

@@ -430,15 +430,14 @@ class ExpensesAgent(BaseAgent):
raise ValueError(f'Unknown tool: {name}')
return await dispatch[name](**args)
async def handle_peer_request(self, request: dict) -> dict:
req_type = request.get('type', '')
async def handle_peer_request(self, request_type: str, params: dict, directive_id: str) -> dict:
try:
if req_type == 'expenses_summary':
if request_type == 'expenses_summary':
return await self._et.get_expenses_summary()
if req_type == 'employee_expenses':
if request_type == 'employee_expenses':
return {'expenses': await self._et.get_expense_by_employee(
employee_id=request['employee_id'])}
return {'error': f'Unknown type: {req_type}'}
employee_id=params['employee_id'])}
return {'error': f'Unknown type: {request_type}'}
except Exception as exc:
return {'error': str(exc)}

View File

@@ -283,30 +283,29 @@ class FinanceAgent(BaseAgent):
# ------------------------------------------------------------------
# Peer bus handler
# ------------------------------------------------------------------
async def handle_peer_request(self, request: dict) -> dict:
req_type = request.get('type', '')
async def handle_peer_request(self, request_type: str, params: dict, directive_id: str) -> dict:
try:
if req_type == 'overdue_summary':
partner_id = request.get('partner_id')
if request_type == 'overdue_summary':
partner_id = params.get('partner_id')
kwargs = {}
if partner_id:
kwargs['partner_id'] = partner_id
overdue = await self._ft.get_overdue_invoices(**kwargs)
total = sum(inv.get('amount_residual', 0) for inv in overdue)
return {'overdue_count': len(overdue), 'overdue_total': total, 'invoices': overdue}
if req_type == 'payment_history':
partner_id = request.get('partner_id')
if request_type == 'payment_history':
partner_id = params.get('partner_id')
if not partner_id:
return {'error': 'partner_id required'}
history = await self._ft.get_payment_history(partner_id=partner_id)
return {'history': history}
if req_type == 'financial_summary':
period = request.get('period', 'this_month')
if request_type == 'financial_summary':
period = params.get('period', 'this_month')
summary = await self._ft.get_financial_summary(period=period)
return {'summary': summary}
return {'error': f'Unknown peer request type: {req_type}'}
return {'error': f'Unknown peer request type: {request_type}'}
except Exception as exc:
logger.error('handle_peer_request failed type=%s: %s', req_type, exc)
logger.error('handle_peer_request failed type=%s: %s', request_type, exc)
return {'error': str(exc)}
# ------------------------------------------------------------------

View File

@@ -115,15 +115,14 @@ class ProjectAgent(BaseAgent):
raise ValueError(f'Unknown tool: {name}')
return await dispatch[name](**args)
async def handle_peer_request(self, request: dict) -> dict:
req_type = request.get('type', '')
async def handle_peer_request(self, request_type: str, params: dict, directive_id: str) -> dict:
try:
if req_type == 'project_list':
if request_type == 'project_list':
return {'projects': await self._pt.get_projects()}
if req_type == 'task_count':
tasks = await self._pt.get_tasks(project_id=request.get('project_id'))
if request_type == 'task_count':
tasks = await self._pt.get_tasks(project_id=params.get('project_id'))
return {'count': len(tasks)}
return {'error': f'Unknown type: {req_type}'}
return {'error': f'Unknown type: {request_type}'}
except Exception as exc:
return {'error': str(exc)}

View File

@@ -119,14 +119,13 @@ class SalesAgent(BaseAgent):
raise ValueError(f'Unknown tool: {name}')
return await dispatch[name](**args)
async def handle_peer_request(self, request: dict) -> dict:
req_type = request.get('type', '')
async def handle_peer_request(self, request_type: str, params: dict, directive_id: str) -> dict:
try:
if req_type == 'sales_summary':
if request_type == 'sales_summary':
return await self._st.get_sales_summary()
if req_type == 'customer_orders':
return {'orders': await self._st.get_customer_orders(partner_id=request['partner_id'])}
return {'error': f'Unknown type: {req_type}'}
if request_type == 'customer_orders':
return {'orders': await self._st.get_customer_orders(partner_id=params['partner_id'])}
return {'error': f'Unknown type: {request_type}'}
except Exception as exc:
return {'error': str(exc)}