feat(agents): add 7 specialist agents with tools and system prompts
Agents (all following 6-step contract: _plan/_gather/_reason/_act/_report): - AccountingAgent: trial balance, chart of accounts, tax summary (HIPAA-locked) - CrmAgent: pipeline summary, lead/opportunity management, won/lost analysis - SalesAgent: sales orders, quotations, revenue by rep, expired quote detection - ProjectAgent: task tracking, blocked/overdue detection, timesheet logging - ElearningAgent: course completion, low-engagement flagging, next-course suggestion - ExpensesAgent: expense sheets, pending approvals, policy violations (HIPAA-locked) - EmployeesAgent: headcount, contracts, leaves, attendance, expired contract sweep (HIPAA-locked) Tools (one file per domain): - accounting_tools.py, crm_tools.py, sales_tools.py, project_tools.py - elearning_tools.py, expenses_tools.py, employees_tools.py System prompts: each agent has a domain-specific system.txt with rules and output format All agents implement handle_peer_request() and sweep() for proactive monitoring HIPAA-locked agents (accounting, expenses, employees) enforced via LLMRouter Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
81
agent_service/tools/sales_tools.py
Normal file
81
agent_service/tools/sales_tools.py
Normal file
@@ -0,0 +1,81 @@
|
||||
from __future__ import annotations
|
||||
import logging
|
||||
from ..tools.odoo_client import OdooClient
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class SalesTools:
|
||||
def __init__(self, odoo: OdooClient):
|
||||
self._o = odoo
|
||||
|
||||
async def get_sales_orders(self, state: str = 'sale', partner_id: int = None,
|
||||
date_from: str = None, date_to: str = None, limit: int = 50) -> list:
|
||||
domain = [('state', '=', state)]
|
||||
if partner_id:
|
||||
domain.append(('partner_id', '=', partner_id))
|
||||
if date_from:
|
||||
domain.append(('date_order', '>=', date_from))
|
||||
if date_to:
|
||||
domain.append(('date_order', '<=', date_to))
|
||||
fields = ['name', 'partner_id', 'state', 'date_order', 'amount_total',
|
||||
'amount_untaxed', 'user_id', 'invoice_status']
|
||||
return await self._o.search_read('sale.order', domain, fields, limit=limit)
|
||||
|
||||
async def get_quotations(self, partner_id: int = None, limit: int = 50) -> list:
|
||||
domain = [('state', 'in', ['draft', 'sent'])]
|
||||
if partner_id:
|
||||
domain.append(('partner_id', '=', partner_id))
|
||||
fields = ['name', 'partner_id', 'state', 'date_order', 'validity_date',
|
||||
'amount_total', 'user_id']
|
||||
return await self._o.search_read('sale.order', domain, fields, limit=limit)
|
||||
|
||||
async def get_sales_summary(self, date_from: str = None, date_to: str = None) -> dict:
|
||||
domain = [('state', '=', 'sale')]
|
||||
if date_from:
|
||||
domain.append(('date_order', '>=', date_from))
|
||||
if date_to:
|
||||
domain.append(('date_order', '<=', date_to))
|
||||
orders = await self._o.search_read('sale.order', domain, ['amount_total', 'user_id'], limit=1000)
|
||||
total = sum(o.get('amount_total', 0) for o in orders)
|
||||
by_rep: dict = {}
|
||||
for o in orders:
|
||||
uid = o['user_id'][0] if isinstance(o['user_id'], list) else 0
|
||||
uname = o['user_id'][1] if isinstance(o['user_id'], list) else 'Unknown'
|
||||
by_rep.setdefault(uid, {'name': uname, 'count': 0, 'total': 0.0})
|
||||
by_rep[uid]['count'] += 1
|
||||
by_rep[uid]['total'] += o.get('amount_total', 0)
|
||||
return {
|
||||
'order_count': len(orders),
|
||||
'total_revenue': total,
|
||||
'by_sales_rep': sorted(by_rep.values(), key=lambda x: x['total'], reverse=True)[:10],
|
||||
}
|
||||
|
||||
async def get_customer_orders(self, partner_id: int, limit: int = 20) -> list:
|
||||
return await self._o.search_read(
|
||||
'sale.order', [('partner_id', '=', partner_id)],
|
||||
['name', 'state', 'date_order', 'amount_total', 'invoice_status'],
|
||||
limit=limit,
|
||||
)
|
||||
|
||||
async def confirm_quotation(self, order_id: int) -> bool:
|
||||
try:
|
||||
await self._o.call('sale.order', 'action_confirm', [[order_id]])
|
||||
logger.info('Confirmed quotation %s', order_id)
|
||||
return True
|
||||
except Exception as exc:
|
||||
logger.warning('confirm_quotation failed %s: %s', order_id, exc)
|
||||
return False
|
||||
|
||||
async def update_order_note(self, order_id: int, note: str) -> bool:
|
||||
result = await self._o.write('sale.order', [order_id], {'note': note})
|
||||
return result.success
|
||||
|
||||
async def flag_for_review(self, model: str, record_id: int, reason: str, severity: str = 'medium') -> bool:
|
||||
msg = f'[AI FLAG - {severity.upper()}] {reason}'
|
||||
await self._o.call(model, 'message_post', [[record_id]], {'body': msg, 'message_type': 'comment'})
|
||||
return True
|
||||
|
||||
async def post_chatter_note(self, model: str, record_id: int, note: str) -> bool:
|
||||
await self._o.call(model, 'message_post', [[record_id]], {'body': note, 'message_type': 'comment'})
|
||||
return True
|
||||
Reference in New Issue
Block a user