from __future__ import annotations import logging from .base_agent import BaseAgent, AgentReport, AgentDirective, SweepReport from ..tools.employees_tools import EmployeesTools logger = logging.getLogger(__name__) EMPLOYEES_TOOLS = [ {'name': 'get_employees', 'description': 'List employees', 'parameters': {'department_id': {'type': 'integer', 'optional': True}, 'active': {'type': 'boolean', 'optional': True}, 'limit': {'type': 'integer', 'optional': True}}}, {'name': 'get_employee_profile', 'description': 'Get detailed profile for one employee', 'parameters': {'employee_id': {'type': 'integer'}}}, {'name': 'get_leaves', 'description': 'Get leave requests', 'parameters': {'employee_id': {'type': 'integer', 'optional': True}, 'state': {'type': 'string', 'optional': True}, 'date_from': {'type': 'string', 'optional': True}, 'limit': {'type': 'integer', 'optional': True}}}, {'name': 'get_contracts', 'description': 'Get employee contracts', 'parameters': {'employee_id': {'type': 'integer', 'optional': True}, 'state': {'type': 'string', 'optional': True}, 'limit': {'type': 'integer', 'optional': True}}}, {'name': 'get_attendance_summary', 'description': 'Get attendance summary for an employee', 'parameters': {'employee_id': {'type': 'integer'}, 'date_from': {'type': 'string'}, 'date_to': {'type': 'string'}}}, {'name': 'get_department_summary', 'description': 'Get headcount and contract summary for a department', 'parameters': {'department_id': {'type': 'integer'}}}, {'name': 'flag_for_review', 'description': 'Flag a record for review', 'parameters': {'model': {'type': 'string'}, 'record_id': {'type': 'integer'}, 'reason': {'type': 'string'}, 'severity': {'type': 'string', 'optional': True}}}, {'name': 'post_chatter_note', 'description': 'Post a note on a record', 'parameters': {'model': {'type': 'string'}, 'record_id': {'type': 'integer'}, 'note': {'type': 'string'}}}, ] class EmployeesAgent(BaseAgent): name = 'employees_agent' domain = 'employees' required_odoo_module = 'hr' system_prompt_file = 'employees_system.txt' tools = EMPLOYEES_TOOLS def __init__(self, odoo, llm, peer_bus=None): super().__init__(odoo, llm, peer_bus) self._ht = EmployeesTools(odoo) self._gathered_data = {} self._actions_taken = [] self._escalations_list = [] async def _plan(self, directive: AgentDirective) -> dict: intent = (directive.intent or '').lower() return { 'fetch_employees': any(k in intent for k in ('employee', 'headcount', 'staff')), 'fetch_leaves': any(k in intent for k in ('leave', 'absence', 'holiday', 'vacation')), 'fetch_contracts': 'contract' in intent, 'department_id': directive.context.get('department_id'), 'employee_id': directive.context.get('employee_id'), } async def _gather(self, ctx: dict) -> dict: plan = ctx.get('plan', {}) data: dict = {} if plan.get('fetch_employees') or not any([plan.get('fetch_leaves'), plan.get('fetch_contracts')]): if plan.get('department_id'): data['dept_summary'] = await self._ht.get_department_summary(plan['department_id']) else: data['employees'] = await self._ht.get_employees(limit=50) if plan.get('fetch_leaves'): data['leaves'] = await self._ht.get_leaves( employee_id=plan.get('employee_id'), state='validate1', limit=20, ) if plan.get('fetch_contracts'): data['contracts'] = await self._ht.get_contracts( employee_id=plan.get('employee_id'), limit=20, ) self._gathered_data = data return data async def _reason(self, ctx: dict) -> dict: data = self._gathered_data analysis: dict = {'escalations': []} contracts = data.get('contracts', []) import datetime today = str(datetime.date.today()) expiring = [c for c in contracts if c.get('date_end') and c['date_end'] < today] if expiring: analysis['escalations'].append(f'{len(expiring)} contracts have expired.') self._escalations_list = analysis['escalations'] return analysis async def _act(self, ctx: dict) -> list: return [] async def _report(self, ctx: dict) -> AgentReport: data = self._gathered_data parts = [] employees = data.get('employees', []) if employees: parts.append(f'Employees: {len(employees)} active.') dept = data.get('dept_summary', {}) if dept: parts.append(f'Department headcount: {dept.get("headcount", 0)}, avg wage: {dept.get("avg_wage", 0):.2f}.') leaves = data.get('leaves', []) if leaves: parts.append(f'Pending leave approvals: {len(leaves)}.') if not parts: parts.append('HR review complete.') return AgentReport(agent=self.name, summary=chr(10).join(parts), data=data, escalations=self._escalations_list, actions_taken=[]) async def _dispatch_tool(self, name: str, args: dict): dispatch = { 'get_employees': self._ht.get_employees, 'get_employee_profile': self._ht.get_employee_profile, 'get_leaves': self._ht.get_leaves, 'get_contracts': self._ht.get_contracts, 'get_attendance_summary': self._ht.get_attendance_summary, 'get_department_summary': self._ht.get_department_summary, 'flag_for_review': self._ht.flag_for_review, 'post_chatter_note': self._ht.post_chatter_note, } if name not in dispatch: raise ValueError(f'Unknown tool: {name}') return await dispatch[name](**args) async def handle_peer_request(self, request_type: str, params: dict, directive_id: str) -> dict: try: 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: {request_type}'} except Exception as exc: return {'error': str(exc)} async def sweep(self) -> SweepReport: findings = [] try: import datetime today = str(datetime.date.today()) contracts = await self._ht.get_contracts(state='open', limit=200) for c in contracts: if c.get('date_end') and c['date_end'] < today: findings.append({'type': 'expired_contract', 'contract_id': c.get('id'), 'employee': c.get('employee_id', [0, ''])[1] if isinstance(c.get('employee_id'), list) else '', 'expired': c.get('date_end'), 'severity': 'high'}) except Exception as exc: return SweepReport(agent=self.name, findings=[], actions=[], error=str(exc)) return SweepReport(agent=self.name, findings=findings, actions=[], summary=f'HR sweep: {len(findings)} expired contracts found.')