fix: harden master agent synthesize/memory, fix expense create fields

- _synthesize: short-circuit on any single-agent report (avoids extra
  Ollama call that can timeout); wrap multi-agent LLM call in try/except
- _update_memory: catch exceptions so DB/memory failures don't kill reply
- _log_directive_start: use 0 instead of NULL for channel_id (NOT NULL col)
- create_expense: drop 'description' field (not valid on hr.expense in Odoo 18)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Carlos Garcia
2026-05-16 01:37:36 -04:00
parent 261252abdd
commit 6ab9624ec6
2 changed files with 23 additions and 17 deletions

View File

@@ -273,29 +273,37 @@ class MasterAgent:
async def _synthesize(self, reports, context: MasterContext) -> str:
if not reports:
return 'No agent responses received.'
if len(reports) == 1 and reports[0].status == 'complete':
return reports[0].summary
if len(reports) == 1:
return reports[0].summary or '(no summary)'
summaries = chr(10).join(f'{r.agent}: {r.summary}' for r in reports)
msg = ('Synthesize these agent reports into one coherent response. '
'Business language only. No internal IDs. '
'Separate: actions completed, items pending approval, recommendations.'
+ chr(10) + summaries)
resp = await self._llm.submit(
[{'role': 'system', 'content': 'You are a business intelligence assistant.'},
{'role': 'user', 'content': msg}],
caller='master_synthesis')
return resp.content
try:
resp = await self._llm.submit(
[{'role': 'system', 'content': 'You are a business intelligence assistant.'},
{'role': 'user', 'content': msg}],
caller='master_synthesis')
return resp.content or summaries
except Exception as exc:
logger.warning('_synthesize LLM call failed, falling back to raw summaries: %s', exc)
return summaries
async def _update_memory(self, user_id, message, response, reports, directive_id):
# User message is persisted at the top of handle_message — only save
# the assistant reply here.
await self._memory.append_message(user_id, 'assistant', response, directive_id)
try:
await self._memory.append_message(user_id, 'assistant', response or '', directive_id)
except Exception as exc:
logger.warning('_update_memory: append_message failed: %s', exc)
for report in reports:
if report.data:
await self._memory.store_findings(
scope=report.agent.replace('_agent', ''),
summary=report.summary, raw_data=report.data,
source_directive_id=directive_id)
try:
await self._memory.store_findings(
scope=report.agent.replace('_agent', ''),
summary=report.summary, raw_data=report.data,
source_directive_id=directive_id)
except Exception as exc:
logger.warning('_update_memory: store_findings failed agent=%s: %s', report.agent, exc)
async def handle_approval(self, directive_id, item_id, approved, approver_uid) -> str:
if approved:
@@ -314,7 +322,7 @@ class MasterAgent:
'(directive_id, user_id, channel_id, raw_message, status) '
'VALUES ($1, $2, $3, $4, $5) ON CONFLICT (directive_id) DO NOTHING')
async with pool.acquire(timeout=10) as conn:
await conn.execute(sql, directive_id, user_id, channel_id, message, 'processing')
await conn.execute(sql, directive_id, user_id, channel_id or 0, message, 'processing')
except Exception as exc:
logger.warning('_log_directive_start failed: %s', exc)

View File

@@ -127,8 +127,6 @@ class ExpensesTools:
vals['date'] = date
if product_id:
vals['product_id'] = product_id
if description:
vals['description'] = description
return await self._o.create('hr.expense', vals)
async def attach_receipt(self, model: str, record_id: int, filename: str,