1. OdooClient missing self._timeout — every _xmlrpc_call raised
AttributeError, making the odoo health check permanently fail.
Fix: set self._timeout = XMLRPC_TIMEOUT in __init__.
2. action_ping only accepted ollama=='ok' but health.py now returns
'warming' when the model is not yet hot in VRAM. Fix: treat
warming as passing so the bot goes online and the model loads
on the first real request.
3. /ai/approval/pending declared methods=['GET'] on a type='json'
route — Odoo JSON-RPC always POSTs, so every browser call got
405 METHOD NOT ALLOWED. Fix: change to methods=['POST'].
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- All specialist agents: handle_peer_request(request_type, params, directive_id)
replaces handle_peer_request(request: dict) so callers pass structured args
- ab_ai_bot: force-write bus_presence.status via SQL so Odoo 18 WebSocket presence
shows the correct colour immediately (ORM compute does not trigger on last_poll writes)
- odoo_client: wrap XML-RPC executor calls in asyncio.wait_for to enforce timeout
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Adds a new specialist agent that gives the AI system control over its
own infrastructure:
- sysops_tools.py: docker SDK (ps/logs/restart) + git CLI (pull/status/log)
+ Odoo channel notifier for autonomous action broadcasts
- sysops_agent.py: BaseAgent subclass handling on-demand chat requests,
auto_heal() triggered by health failures, and sweep() for audits
- Background auto-heal loop (main.py): runs every 2 minutes, calls
_get_failing_systems() and triggers auto_heal() when degraded
- health.py: extracted _get_failing_systems() helper reused by both
the /health/detailed endpoint and the auto-heal loop
- docker-compose.yml: mount docker socket + /root/odoo workspace +
SSH keys for git authentication
- Dockerfile: add git to apt-get
- requirements.txt: add docker==7.1.0 Python SDK
Auto-heal behavior:
- Detects failing containers, restarts them, notifies all bot DM channels
- Ollama (192.168.2.9) is flagged as external and skipped
- On-demand via chat: "restart agent", "check logs", "pull latest code"
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- expenses_tools: remove 'date' from hr.expense.sheet field lists (Odoo 18
uses accounting_date; querying 'date' raised ValueError at runtime)
- master_system.txt: add few-shot routing examples so Llama 3.1 8B correctly
outputs agents=[] for general questions instead of defaulting to expenses_agent
- ab_ai_bot.py: increase bot presence last_poll offset from 90s to 10min so
the green dot stays on between cron runs (cron fires every ~5min in practice,
not every 20s as configured)
- ARCHITECTURE.md: full system documentation covering component layout, request
flow, LLM routing, agent registry, access control, health/presence mechanism,
known issues fixed today, and future self-healing concept
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Introduces VISION_OCR_MODEL setting. When set (e.g. llama3.2-vision:11b),
receipt images are transcribed by the Ollama vision model before falling
back to Tesseract. Also improves Tesseract preprocessing with adaptive
binarisation (autocontrast + threshold at 140) for better accuracy on
thermal receipts.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Dockerfile: add tesseract-ocr-osd for orientation detection data
- receipt_parser: resize large phone photos to 1800px, convert to
grayscale, sharpen before OCR; use psm 1 (auto + OSD) so rotated
receipts are correctly oriented before text extraction
- expenses_agent: tighten amount extraction prompt to pick the FINAL
total, not subtotal or tax line, reducing misreads like 42.90->409.00
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Dockerfile: install tesseract-ocr so Pillow+pytesseract can OCR receipt images
- operational_store: JSON-serialize raw_data before passing to asyncpg JSONB
- receipt_parser: add SHA256 hash + date extracted from filename timestamps
- expenses_agent: deduplicate receipts by hash before creating expense records
- expenses_agent: fetch all expensable Odoo products, pass list to LLM for
category selection (Meals, Flights, etc.) per receipt
- expenses_agent: pass date_hint from filename (e.g. 20260509_180857.jpg -> 2026-05-09)
as fallback when OCR text is unavailable
- expenses_tools: add get_expense_products() to fetch all expensable products
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- _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>
- 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>
- ElearningTools: add create_course, update_course, publish_course,
add_section, create_slide, enroll_user write methods using OdooClient
- ElearningAgent: fix all BaseAgent method signatures (_plan/_gather/
_reason/_act/_report no longer take wrong positional args)
- Replace dead _dispatch_tool pattern with _tool_<name> methods so
BaseAgent._run_tool() can drive them via LLM tool calls in _loop()
- Add LLM-driven course creation in _reason(): when intent is create,
_loop() is called with a course-building system prompt and all tools;
the LLM calls create_course → add_section → create_slide → publish
- Fix handle_peer_request signature to match BaseAgent interface
- Fix AgentReport missing directive_id; fix SweepReport invalid kwargs
- Extend ELEARNING_TOOLS list with all new write-side tools
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Health endpoint called .ping() on both but neither implemented it,
causing ollama/odoo to always show as error and the bot to stay offline.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Some Odoo instances require the user's actual login/email for API key
auth rather than the __system__ special login. ODOO_USER defaults to
__system__ for standard Odoo 16+ installs.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>