diff --git a/addons/activeblue_ai/models/ab_ai_bot.py b/addons/activeblue_ai/models/ab_ai_bot.py index 3847610..0e1fb11 100644 --- a/addons/activeblue_ai/models/ab_ai_bot.py +++ b/addons/activeblue_ai/models/ab_ai_bot.py @@ -168,22 +168,33 @@ class AbAiBot(models.Model): try: Presence = self.env['bus.presence'] now = fields.Datetime.now() - # bus.presence.status is a computed field — write only last_poll/last_presence. - # When online: set both 24h ahead so the bot stays "online" regardless of - # cron timing. The cron explicitly marks offline by setting them to the past. + status = 'online' if online else 'offline' if online: poll_time = now + timedelta(minutes=10) presence_time = now + timedelta(minutes=10) else: poll_time = now - timedelta(hours=1) presence_time = now - timedelta(hours=1) - vals = {'last_poll': poll_time, 'last_presence': presence_time} rec = Presence.sudo().search([('user_id', '=', bot_user.id)], limit=1) if rec: - rec.write(vals) + rec.write({'last_poll': poll_time, 'last_presence': presence_time}) + # Force-update stored status column directly — Odoo 18 WebSocket-based + # presence doesn't trigger the stored compute when last_poll is written via ORM. + self.env.cr.execute( + "UPDATE bus_presence SET status = %s WHERE user_id = %s", + (status, bot_user.id), + ) + rec.invalidate_recordset(['status']) else: - vals['user_id'] = bot_user.id - Presence.sudo().create(vals) + Presence.sudo().create({ + 'user_id': bot_user.id, + 'last_poll': poll_time, + 'last_presence': presence_time, + }) + self.env.cr.execute( + "UPDATE bus_presence SET status = %s WHERE user_id = %s", + (status, bot_user.id), + ) except Exception as exc: _logger.warning('Could not update bot user presence: %s', exc) diff --git a/agent_service/agents/accounting_agent.py b/agent_service/agents/accounting_agent.py index ef087c9..95b0aca 100644 --- a/agent_service/agents/accounting_agent.py +++ b/agent_service/agents/accounting_agent.py @@ -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)} diff --git a/agent_service/agents/crm_agent.py b/agent_service/agents/crm_agent.py index 4f54116..2c34520 100644 --- a/agent_service/agents/crm_agent.py +++ b/agent_service/agents/crm_agent.py @@ -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)} diff --git a/agent_service/agents/employees_agent.py b/agent_service/agents/employees_agent.py index 617b394..f5c8ce5 100644 --- a/agent_service/agents/employees_agent.py +++ b/agent_service/agents/employees_agent.py @@ -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)} diff --git a/agent_service/agents/expenses_agent.py b/agent_service/agents/expenses_agent.py index 32ac3a1..a808177 100644 --- a/agent_service/agents/expenses_agent.py +++ b/agent_service/agents/expenses_agent.py @@ -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)} diff --git a/agent_service/agents/finance_agent.py b/agent_service/agents/finance_agent.py index 1739089..f86c315 100644 --- a/agent_service/agents/finance_agent.py +++ b/agent_service/agents/finance_agent.py @@ -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)} # ------------------------------------------------------------------ diff --git a/agent_service/agents/project_agent.py b/agent_service/agents/project_agent.py index b6a7661..bfaf0ad 100644 --- a/agent_service/agents/project_agent.py +++ b/agent_service/agents/project_agent.py @@ -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)} diff --git a/agent_service/agents/sales_agent.py b/agent_service/agents/sales_agent.py index 35561f9..a0dbd71 100644 --- a/agent_service/agents/sales_agent.py +++ b/agent_service/agents/sales_agent.py @@ -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)} diff --git a/agent_service/tools/odoo_client.py b/agent_service/tools/odoo_client.py index f0d506d..3f33d6d 100644 --- a/agent_service/tools/odoo_client.py +++ b/agent_service/tools/odoo_client.py @@ -71,9 +71,15 @@ class OdooClient: await self._pg_pool.close() async def _xmlrpc_call(self, proxy, method, *args): - loop = asyncio.get_event_loop() + loop = asyncio.get_running_loop() fn = getattr(proxy, method) - return await loop.run_in_executor(_executor, fn, *args) + try: + return await asyncio.wait_for( + loop.run_in_executor(_executor, fn, *args), + timeout=self._timeout, + ) + except asyncio.TimeoutError: + raise socket.timeout(f'Odoo XML-RPC timeout after {self._timeout}s') async def _authenticate(self): async with self._auth_lock: