feat: file upload + expense report creation from Discuss attachments
- Discuss bot now reads ir.attachment from incoming messages; file-only messages no longer silently dropped - ZIP files are described (contents listed) and bot asks clarifying question before acting; user's follow-up reply looks back for pending attachments so files don't need to be re-uploaded - receipt_parser: extracts text from ZIP (recursive), JPG/PNG/etc (OCR), PDF (pdfplumber), HTML, TXT - expenses_agent: full rewrite fixing broken method signatures; adds create_expense_sheet / create_expense / attach_receipt flow driven by LLM receipt parsing (Ollama, HIPAA-locked) - master_agent: extra_context threads receipts + user_id into directives - FastAPI /upload multipart endpoint; registered in main.py - Odoo /ai/upload controller proxies files to agent service - ab_ai_bot: dispatch_message_with_files() for multipart uploads Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -71,7 +71,8 @@ class MasterAgent:
|
||||
# example block, so str.format would treat them as fields.
|
||||
return template.replace('{agent_list}', agent_list)
|
||||
|
||||
async def handle_message(self, user_id, channel_id, message, directive_id) -> MasterResponse:
|
||||
async def handle_message(self, user_id, channel_id, message, directive_id,
|
||||
extra_context: dict = None) -> MasterResponse:
|
||||
try:
|
||||
user_id = int(user_id)
|
||||
except (TypeError, ValueError):
|
||||
@@ -107,7 +108,8 @@ class MasterAgent:
|
||||
await self._memory.append_message(user_id, 'assistant', msg, directive_id)
|
||||
await self._log_directive_complete(directive_id, 'failed', msg)
|
||||
return MasterResponse(directive_id=directive_id, response=msg, status='failed')
|
||||
directives = await self._build_directives(intent, context, directive_id)
|
||||
directives = await self._build_directives(intent, context, directive_id,
|
||||
user_id=user_id, extra_context=extra_context)
|
||||
reports = await self._dispatch_agents(directives)
|
||||
response_text = await self._synthesize(reports, context)
|
||||
await self._update_memory(user_id, message, response_text, reports, directive_id)
|
||||
@@ -197,20 +199,26 @@ class MasterAgent:
|
||||
return AccessResult(allowed=False, denied_agents=denied)
|
||||
return AccessResult(allowed=True)
|
||||
|
||||
async def _build_directives(self, intent: IntentResult, context: MasterContext, directive_id) -> list:
|
||||
async def _build_directives(self, intent: IntentResult, context: MasterContext, directive_id,
|
||||
user_id=None, extra_context: dict = None) -> list:
|
||||
receipts = (extra_context or {}).get('receipts', [])
|
||||
directives = []
|
||||
for agent_key in intent.agents:
|
||||
authorized = ['read', 'search', 'report', 'post_chatter',
|
||||
'send_email', 'create_non_financial', 'write_non_financial']
|
||||
if receipts:
|
||||
authorized.append('create_expense')
|
||||
ctx = DirectiveContext(
|
||||
client_profile=context.knowledge,
|
||||
recent_findings=context.operational_findings,
|
||||
conversation_summary=chr(10).join(
|
||||
m['content'] for m in context.conversation[-5:] if m['role'] == 'assistant'),
|
||||
peer_data={})
|
||||
peer_data={'requesting_user_id': user_id},
|
||||
receipts=receipts)
|
||||
d = AgentDirective(
|
||||
directive_id=directive_id, agent=agent_key, task=intent.intent_summary,
|
||||
params=intent.params, context=ctx,
|
||||
authorized_actions=['read', 'search', 'report', 'post_chatter',
|
||||
'send_email', 'create_non_financial', 'write_non_financial'],
|
||||
authorized_actions=authorized,
|
||||
constraints={'max_amount': 5000})
|
||||
directives.append(d)
|
||||
return directives
|
||||
|
||||
Reference in New Issue
Block a user