10 KiB
CLAUDE.md — odoo-ai
This file gives Claude Code the context it needs to work effectively on this project. Read this before making any changes.
What This Project Is
odoo-ai is a multi-agent AI system that integrates with Odoo 18 Community Edition.
It consists of two main components that work together:
agent_service/— FastAPI service (port 8001) — the AI brain. Hosts MasterAgent + 8 specialist agents, all LLM backends, memory tiers, and tool layers.addons/activeblue_ai/— Odoo 18 module — the Odoo-side integration. OWL2 chat panel, webhook controllers, approval workflows, and cron jobs.
These are deployed as two separate Docker Compose stacks on the same host (192.168.2.9).
Deployment Context
| Item | Value |
|---|---|
| Agent service URL | http://192.168.2.9:8001 (internal) |
| Odoo URL | http://192.168.2.9:8069 (separate vhost) |
| Ollama | http://192.168.2.9:11434 |
| LLM model | activeblue-chat (llama-based, ~124s cold-start from disk) |
| Postgres | Two separate DBs — one for Odoo, one for agent service |
HIPAA constraint: HIPAA-locked agents (finance_agent, accounting_agent, employees_agent, expenses_agent) must always use Ollama. Ollama is to have the ability to call out to Claude or any other cloud AI solution for assistance on completing a task if Ollama has not been able to resolve the request after 5 tries, no personal data is to be leaked out of the environment.
Project Structure
odoo-ai/
├── agent_service/
│ ├── agents/ # MasterAgent, 8 specialist agents, PeerBus, SweepCoordinator
│ ├── llm/ # OllamaBackend, ClaudeBackend, LLMRouter, ToolCallValidator
│ ├── memory/ # ConversationStore, OperationalStore, KnowledgeStore, MemoryManager
│ ├── tools/ # OdooClient + per-domain tool files
│ ├── routers/ # FastAPI routers: dispatch, approval, registry, sweep, health
│ ├── prompts/ # System prompts — one per agent
│ ├── mcp/ # MCP gateway (SSE, 14 tools, all forced local)
│ ├── migrations/ # Alembic — 7 tables
│ ├── logging_utils/ # Structured JSON logging + Loki push
│ ├── config.py # pydantic-settings — all config lives here
│ ├── app_state.py # Global singletons (MasterAgent, LLMRouter, etc.)
│ └── main.py # FastAPI app + lifespan startup (prewarm + auto-heal)
├── addons/
│ └── activeblue_ai/
│ ├── models/ # ab.ai.bot, ab.ai.directive, ab.ai.log, ab.ai.agent.registry
│ ├── controllers/ # /ai/chat, /ai/webhook/callback, /ai/health, /ai/approval/*
│ ├── views/ # XML views + menus
│ ├── security/ # groups + ACL
│ ├── data/ # cron jobs
│ └── static/ # OWL2 JS + CSS + XML templates (systray brain icon + chat panel)
├── tests/ # 433 pytest tests — run before committing
├── research/ # Per-domain research notes — read before touching a domain
├── debian/ # .deb packaging
├── docker-compose.yml # Agent service + its Postgres
├── docker-compose.odoo.yml # Odoo 18 + Odoo Postgres
├── Dockerfile
├── traefik_dynamic_ai.yml # Traefik dynamic config for ai.activeblue.net
├── setup.sh # Detects stale DB volume, runs Alembic on startup
├── build_deb.sh / publish_repo.sh
├── .env.example
└── VERSION
Key Architectural Rules
Agent constraints
- Max 8 tools per specialist agent — enforced by
ToolCallValidatorat startup;AgentConfigErroris raised if exceeded. Do not add tools without removing others. - Agents are stateless — no instance state between requests. All state lives in the 3-tier memory tables.
MasterAgentis a singleton — instantiated once inapp_state.py, shared across all requests.
Memory tiers
| Tier | Table | TTL | Scope |
|---|---|---|---|
| 1 | ab_conversation_memory |
200 rows/user hard cap | Per user |
| 2 | ab_operational_memory |
90 days | Per agent + scope |
| 3 | ab_knowledge_store |
Permanent | Entity-keyed |
LLM routing
LLMRouterselects backend: Ollama or Claude, based onLLM_PRIVACY_MODEenv + per-agent DB overrides- Privacy modes:
local(Ollama only) /hybrid(per-agent override) /cloud(Claude for non-HIPAA agents) - HIPAA agents are always Ollama — this is hardcoded, not just config
Auto-RAG
All agents automatically call odoo_doc_agent to fetch Odoo 18 workflow guidance before answering. Don't remove this.
Auto-heal loop
main.py lifespan starts a background task that calls sysops_agent.auto_heal() every 2 minutes if any system is degraded.
If all systems are running properly, then the MasterAgent which is seen by the user in the Discuss tab of odoo will be seen as Online denoted by a green icon that odoo uses to show user's online. The MasterAgent is a user in odoo with the email of activeblue_ai_bot@local
Key workflows
The user will be able to drop a zip file into the MasterAgent, the MasterAgent has the ability to extract this file, examine the contents and proceed to ask on what to do with the files. The MasterAgent has OCR capability and has recently been updated to Llama Vision. The MasterAgent is to route the information with the customer's request to the correct Agent. As an example of workflows that have worked in the past; User drops receipts zip file in the chat; MasterAgent extracts and reads the files with OCR and asks what to do with them. Customer states; create an expense report. MasterAgent routes the information to the responsible agent. The responsible agent creates the expense report and then confirms when complete. MasterAgent replies to the customer that the report is complete.
Ollama cold-start
activeblue-chat takes ~124s to load from disk. _prewarm_ollama() runs as a background task at startup. OllamaBackend enforces _MIN_TIMEOUT=300s regardless of env var — do not lower this.
Coding Conventions
- Python async throughout — all agent methods, tool calls, DB access are
async - asyncpg for direct Postgres — not SQLAlchemy ORM, not psycopg2
- pydantic-settings in
config.py— all configuration comes from here, not hardcoded - Structured JSON logging via
logging_utils/— use this, notprint()or rawlogging - Conventional commits:
feat:,fix:,chore:,docs:,refactor:,test: - Feature branches per agent or feature — don't commit directly to
mainfor large changes - Alembic for all DB schema changes — never modify tables directly
Adding a new specialist agent
- Create
agent_service/agents/<name>_agent.py— extend base agent class - Create
agent_service/tools/<name>_tools.py— max 8 tools - Create
agent_service/prompts/<name>_agent.txt - Register in
AgentRegistry - Add tests in
tests/test_<name>_agent.pyandtests/test_<name>_tools.py - If HIPAA-sensitive, hardcode Ollama in
LLMRouter
Modifying the Odoo module
- Odoo module lives in
addons/activeblue_ai/— copy this into the Odoo container's addons volume to deploy - After any model change: increment
versionin__manifest__.pyand run module upgrade in Odoo - OWL2 components are in
static/src/— Odoo bundles JS at runtime, no separate build step needed - XML IDs must be globally unique — prefix everything with
activeblue_ai.
Running Locally (Dev)
# 1. Copy and fill .env
cp .env.example .env
# 2. Start Odoo stack
docker compose -f docker-compose.odoo.yml up -d
# 3. Start agent service (dev mode with reload)
pip install -r requirements.txt
uvicorn agent_service.main:app --reload --port 8001
# 4. Run Alembic migrations
cd agent_service/migrations && alembic upgrade head
# 5. Install Odoo module
# Odoo → Settings → Apps → search "ActiveBlue AI" → Install
Tests
Always run tests before committing.
# Preferred: use the project test venv
.venv-test/bin/python -m pytest tests/ -q
# Or manually
pip install -r requirements-test.txt
pytest tests/ -v
433 tests covering all 8 agents, all tool layers, PeerBus, AgentRegistry, ToolCallValidator, memory, dispatch router, and LLM router. All tests run in local mode (Ollama mocked) — no cloud LLM calls in tests.
Environment Variables
See .env.example for full list. Critical ones:
| Variable | Notes |
|---|---|
ODOO_URL |
http://ai.activeblue.net |
ODOO_API_KEY |
Odoo user API key — get from Odoo user settings |
OLLAMA_URL |
http://192.168.2.9:11434 — use this IP, not .2.10 or .2.47 |
LLM_PRIVACY_MODE |
local / hybrid / cloud — default local |
ANTHROPIC_API_KEY |
Only needed for hybrid or cloud mode |
POSTGRES_PASSWORD |
No default — must be set |
WEBHOOK_SECRET |
Shared between Odoo and agent service |
Common Issues & Gotchas
- Ollama timeout errors on first request: model is still warming. Check
/health/detailed— if it sayswarming, wait. Do not lower_MIN_TIMEOUT. AgentConfigErrorat startup: a specialist agent has > 8 tools. Remove tools before adding new ones.- Alembic "table already exists": stale DB volume.
setup.shdetects this — check its logic before manually dropping tables. - OWL2 chat panel not loading: JS bundle cache in Odoo. Run
Assets → Clear cachein debug mode, or restart Odoo. - PeerBus routing loops:
depthparameter limits recursion. Don't increaseMAX_PEER_DEPTHwithout understanding call-log tracking. - Traefik ACME for
ai.activeblue.net: config is intraefik_dynamic_ai.yml— copy to Traefik CT's dynamic config directory on192.168.1.53. - Two separate Postgres instances: agent service DB ≠ Odoo DB. Alembic only manages the agent service DB. Never run Alembic against the Odoo DB.
What's Out of Scope for This Repo
- Axolotl / QLoRA fine-tuning pipeline →
/opt/axolotlon192.168.2.9(separate) - IRC bot →
tocmo0nlord/irc-bot(separate repo) - Traefik main config → managed on CT 112 directly
- Odoo HIPAA training eLearning module → different Odoo addon, different repo