Files
odoo-ai/addons/activeblue_ai/__init__.py
Carlos Garcia beb08b0b4b fix(addon): seed ab.ai.bot and agent registry on install
The bot DM flow silently failed because ab_ai_mail.message_post override
bails when no active ab.ai.bot row exists, and the agent service loaded
0 agents from an empty ab.ai.agent.registry. Both tables stayed empty
because nothing populated them.

The post_init/post_migrate hook now seeds:
- One active ab.ai.bot pointing at http://activeblue-agent:8001
- The 8 specialist agents (finance, accounting, crm, sales, project,
  elearning, expenses, hr) plus master, all on the ollama backend

Idempotent: skips rows that already exist.
2026-04-24 22:51:37 -04:00

95 lines
3.5 KiB
Python

from . import models, controllers
import logging
_logger = logging.getLogger(__name__)
DEFAULT_AGENTS = [
('master', 'Routing / orchestration'),
('finance', 'Finance reporting and analysis'),
('accounting', 'Accounting, journals, reconciliation'),
('crm', 'CRM, leads, opportunities'),
('sales', 'Sales orders, quotations, customers'),
('project', 'Project management and tasks'),
('elearning', 'eLearning courses and content'),
('expenses', 'Expense reports and approvals'),
('hr', 'Human Resources, employees, time off'),
]
def _ensure_default_bot_and_registry(env):
"""Seed an active ab.ai.bot row and the default agent registry entries."""
Bot = env['ab.ai.bot']
if not Bot.search([], limit=1):
Bot.create({
'display_name': 'ActiveBlue AI',
'agent_service_url': 'http://activeblue-agent:8001',
'privacy_mode': 'local',
'active': True,
})
_logger.info('Seeded default ab.ai.bot row')
Registry = env['ab.ai.agent.registry']
for name, domain in DEFAULT_AGENTS:
if not Registry.search([('agent_name', '=', name)], limit=1):
Registry.create({
'agent_name': name,
'domain': domain,
'active': True,
'backend': 'ollama',
})
_logger.info('Ensured %d default agent registry entries', len(DEFAULT_AGENTS))
def _ensure_ai_bot_user(env):
"""Create the ActiveBlue AI internal user so it appears in Discuss DM search.
Skips invitation email (no_reset_password) and marks the user as already
logged-in (res_users_log row) so it shows as Confirmed instead of Pending.
Also ensures the external ID exists so `env.ref('activeblue_ai.partner_activeblue_ai')`
resolves in the discuss.channel message override.
"""
User = env['res.users'].with_context(no_reset_password=True)
user = User.search([('login', 'in', ('activeblue_ai_bot', 'activeblue_ai_bot@local'))], limit=1)
if not user:
user = User.create({
'name': 'ActiveBlue AI',
'login': 'activeblue_ai_bot',
'groups_id': [(6, 0, [env.ref('base.group_user').id])],
'share': False,
'active': True,
})
_logger.info('Created ActiveBlue AI bot user id=%d', user.id)
# Ensure external ID -> partner mapping (needed by ab_ai_mail.py override)
imd = env['ir.model.data']
existing = imd.search([
('module', '=', 'activeblue_ai'),
('name', '=', 'partner_activeblue_ai'),
], limit=1)
if existing:
existing.write({'model': 'res.partner', 'res_id': user.partner_id.id})
else:
imd.create({
'module': 'activeblue_ai',
'name': 'partner_activeblue_ai',
'model': 'res.partner',
'res_id': user.partner_id.id,
'noupdate': True,
})
# Clear "Pending Invitations" state by writing a res_users_log row.
# In Odoo 18 the user state is computed from the existence of a log entry.
env.cr.execute(
"SELECT 1 FROM res_users_log WHERE create_uid = %s LIMIT 1",
(user.id,),
)
if not env.cr.fetchone():
env.cr.execute(
"INSERT INTO res_users_log (create_uid, create_date, write_uid, write_date) "
"VALUES (%s, NOW(), %s, NOW())",
(user.id, user.id),
)
_logger.info('Marked ActiveBlue AI bot user id=%d as confirmed', user.id)
_ensure_default_bot_and_registry(env)