Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
234 changes: 234 additions & 0 deletions ai_oca_bridge_hermes/README.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,234 @@
====================
AI OCA Bridge Hermes
====================

..
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!! This file is generated by oca-gen-addon-readme !!
!! changes will be overwritten. !!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!! source digest: sha256:5b6924a473322c4910a7139f6477a49c030d7c8a8e19ab0ea2254364c9c0467c
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

.. |badge1| image:: https://img.shields.io/badge/maturity-Beta-yellow.png
:target: https://odoo-community.org/page/development-status
:alt: Beta
.. |badge2| image:: https://img.shields.io/badge/licence-AGPL--3-blue.png
:target: http://www.gnu.org/licenses/agpl-3.0-standalone.html
:alt: License: AGPL-3
.. |badge3| image:: https://img.shields.io/badge/github-OCA%2Fai-lightgray.png?logo=github
:target: https://github.com/OCA/ai/tree/18.0/ai_oca_bridge_hermes
:alt: OCA/ai
.. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png
:target: https://translation.odoo-community.org/projects/ai-18-0/ai-18-0-ai_oca_bridge_hermes
:alt: Translate me on Weblate
.. |badge5| image:: https://img.shields.io/badge/runboat-Try%20me-875A7B.png
:target: https://runboat.odoo-community.org/builds?repo=OCA/ai&target_branch=18.0
:alt: Try me on Runboat

|badge1| |badge2| |badge3| |badge4| |badge5|

This module integrates Odoo Discuss with Hermes AI Agent, allowing users
to chat with AI assistants directly within Odoo's messaging interface.

**Features:**

- Queue-based message processing with state tracking
(pending/processing/done/error)
- Secure webhook authentication via tokens
- Auto-detection of DM chats with AI users
- Typing indicators while AI processes responses
- Rate limiting on poll endpoint
- Extensible architecture for custom handlers (escalation, support,
etc.)
- Standalone gateway script for easy deployment

**Table of contents**

.. contents::
:local:

Use Cases / Context
===================

Why This Module?
----------------

The AI revolution is changing how users interact with software. Odoo
users should be able to ask questions, get help, and automate tasks
using natural language - directly inside the interface they already use
daily.

Architecture Rationale
----------------------

Queue-Based Design (Not Real-Time)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

We chose polling over WebSockets for maximum compatibility:

- Works behind firewalls and proxies
- No WebSocket server needed
- Simple to debug and monitor
- Compatible with all Odoo deployments

Standalone Gateway Script
~~~~~~~~~~~~~~~~~~~~~~~~~

The ``scripts/hermes_odoo_gateway.py`` script runs outside Odoo:

- No need to install Hermes inside Odoo's Python environment
- AI processing happens in a separate process
- Can be restarted independently
- Supports multiple Odoo instances

Extension Points
~~~~~~~~~~~~~~~~

The module is intentionally minimal. Extra features are added via child
modules:

- ``ai_oca_bridge_hermes_support`` - Support ticket escalation for
instance
- Custom handlers via the gateway script's handler system

Escalation Concept
~~~~~~~~~~~~~~~~~~

The module includes a minimal escalation hook (``_action_escalate``) so
the AI can signal when human intervention is needed. The base module
does not implement any specific escalation backend — child modules can
override this hook to create tickets, notify teams, or route to another
Hermes instance.

Security Model
--------------

- Webhook tokens are auto-generated and stored securely
- AI users are regular Odoo users with limited permissions
- Gateway script runs with its own credentials
- No direct database access from the gateway

Multi-Tenancy
-------------

A single Hermes instance can serve multiple Odoo databases:

- Each database has its own gateway configuration
- Session keys include database hash for isolation
- Messages are queued per-gateway

MCP Compatibility
-----------------

This module is compatible with the ``ai_oca_mcp`` module (Model Context
Protocol). If ``ai_oca_mcp`` is installed, tools defined via ``ai.tool``
are automatically exposed through the MCP endpoint. The Hermes gateway
can then call Odoo tools either via our native API or through MCP — both
approaches work side by side.

Future Directions
-----------------

- Native Hermes gateway adapter (inside Hermes process)
- WebSocket support for real-time communication
- Voice/video integration
- Multi-modal AI (images, documents)

Usage
=====

Configuration
-------------

1. Go to *AI Bridge > Hermes > Gateways* and create a new gateway
2. Select an AI User (must have an AI Bridge configured)
3. Copy the webhook token to your Hermes gateway script configuration
4. Optionally select specific channels to monitor (leave empty for all)

Running the Gateway Script
--------------------------

.. code:: bash

# Basic echo mode (for testing)
python hermes_odoo_gateway.py \
--odoo-url http://localhost:8069 \
--webhook-token YOUR_TOKEN \
--poll-interval 5

# With AIAgent (full Hermes with tools)
python hermes_odoo_gateway.py \
--odoo-url http://localhost:8069 \
--webhook-token YOUR_TOKEN \
--use-agent \
--model k2p6 \
--provider kimi-coding

Usage
-----

Once configured, simply send a message in a monitored channel or DM the
AI user. Hermes will poll for messages, process them with the AI, and
post responses back.

Extending
---------

To add custom message handlers, create a child module and override:

- ``hermes_webhook._action_escalate()`` for escalation logic
- ``hermes_message_queue`` models for custom state handling
- Gateway script handlers for custom AI processing

Known issues / Roadmap
======================

- WebSocket support for real-time communication (currently uses polling)
- Native Hermes gateway adapter (currently uses standalone script)
- Multi-tenant routing for shared Hermes instances
- Support for file attachments and images
- Voice message integration
- Interactive approval buttons in Discuss messages (e.g.,
"Approve/Reject" actions for sensitive operations) — nice to have for
escalation workflows

Bug Tracker
===========

Bugs are tracked on `GitHub Issues <https://github.com/OCA/ai/issues>`_.
In case of trouble, please check there if your issue has already been reported.
If you spotted it first, help us to smash it by providing a detailed and welcomed
`feedback <https://github.com/OCA/ai/issues/new?body=module:%20ai_oca_bridge_hermes%0Aversion:%2018.0%0A%0A**Steps%20to%20reproduce**%0A-%20...%0A%0A**Current%20behavior**%0A%0A**Expected%20behavior**>`_.

Do not contact contributors directly about support or help with technical issues.

Credits
=======

Authors
-------

* Akretion

Contributors
------------

- Raphaël Valyi raphael.valyi@akretion.com

Maintainers
-----------

This module is maintained by the OCA.

.. image:: https://odoo-community.org/logo.png
:alt: Odoo Community Association
:target: https://odoo-community.org

OCA, or the Odoo Community Association, is a nonprofit organization whose
mission is to support the collaborative development of Odoo features and
promote its widespread use.

This module is part of the `OCA/ai <https://github.com/OCA/ai/tree/18.0/ai_oca_bridge_hermes>`_ project on GitHub.

You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute.
2 changes: 2 additions & 0 deletions ai_oca_bridge_hermes/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
from . import models
from . import controllers
30 changes: 30 additions & 0 deletions ai_oca_bridge_hermes/__manifest__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# Copyright 2026-TODAY Akretion - Raphael Valyi <raphael.valyi@akretion.com>
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).

{
"name": "AI OCA Bridge Hermes",
"summary": """
Integrate Hermes AI Agent as a native chatbot inside Odoo Discuss.
Requires a running Hermes gateway with the Odoo platform adapter.
""",
"version": "18.0.1.0.0",
"license": "AGPL-3",
"author": "Akretion,Odoo Community Association (OCA)",
"website": "https://github.com/OCA/ai",
"category": "AI",
"development_status": "Beta",
"depends": [
"ai_oca_bridge_chatter",
],
"data": [
"security/ir.model.access.csv",
"security/security.xml",
"views/hermes_gateway_views.xml",
"views/discuss_channel_views.xml",
"views/menu.xml",
"data/ir_cron.xml",
],
"demo": [],
"installable": True,
"application": False,
}
3 changes: 3 additions & 0 deletions ai_oca_bridge_hermes/controllers/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
from . import hermes_poll
from . import hermes_tools
from . import hermes_webhook
109 changes: 109 additions & 0 deletions ai_oca_bridge_hermes/controllers/hermes_poll.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
# Copyright 2026-TODAY Akretion - Raphael Valyi <raphael.valyi@akretion.com>
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).

from datetime import timedelta

from odoo import fields, http
from odoo.http import request


class HermesPoll(http.Controller):
"""Endpoint for Hermes gateway to poll pending messages."""

@http.route("/hermes/poll", type="json", auth="public", csrf=False)
def hermes_poll(self, **kwargs):
"""Return pending messages for Hermes to process.

Hermes gateway polls this endpoint periodically to fetch
messages queued by Odoo users.
"""
# Validate auth token from Authorization header
auth_header = request.httprequest.headers.get("Authorization", "")
token = auth_header.replace("Bearer ", "").strip() if auth_header else ""

gateway = (
request.env["hermes.gateway"]
.sudo()
.search([("webhook_token", "=", token), ("active", "=", True)], limit=1)
)
if not gateway:
return {"messages": [], "error": "Invalid token"}

# Rate limiting (1 second minimum between polls)
if (
gateway.last_poll
and (fields.Datetime.now() - gateway.last_poll).total_seconds() < 1
):
return {"messages": [], "error": "Rate limited"}

# Update last poll timestamp
gateway.sudo().write({"last_poll": fields.Datetime.now()})

# Reset messages stuck in processing for > 5 minutes
stuck_domain = [
("state", "=", "processing"),
("gateway_id", "=", gateway.id),
("write_date", "<", fields.Datetime.now() - timedelta(minutes=5)),
]
stuck = request.env["hermes.message.queue"].sudo().search(stuck_domain)
if stuck:
stuck.sudo().write({"state": "pending"})

# Fetch pending messages
pending = (
request.env["hermes.message.queue"]
.sudo()
.search(
[
("state", "=", "pending"),
("gateway_id", "=", gateway.id),
],
order="create_date asc, id asc",
limit=50,
)
)

messages = []
for msg in pending:
msg_data = {
"id": msg.id,
"channel_id": msg.channel_id.id,
"channel_name": msg.channel_id.name,
"body": msg.body,
"author_id": msg.author_id.id,
"author_name": msg.author_name,
"db_hash": msg.db_hash,
"create_date": msg.create_date.isoformat() if msg.create_date else None,
}
# Include history if requested
if kwargs.get("include_history"):
msg_data["history"] = self._get_channel_history(msg.channel_id)
messages.append(msg_data)
# Mark as processing so we don't send again on next poll
msg.sudo().write({"state": "processing"})

return {"messages": messages, "gateway_id": gateway.id}

def _get_channel_history(self, channel, limit=10):
"""Get recent message history for context."""
messages = (
request.env["mail.message"]
.sudo()
.search(
[
("res_id", "=", channel.id),
("model", "=", "discuss.channel"),
("message_type", "=", "comment"),
],
order="date desc",
limit=limit,
)
)
return [
{
"role": "assistant" if msg.author_id.user_ids.ai_bridge_id else "user",
"body": msg.body,
"date": msg.date.isoformat() if msg.date else None,
}
for msg in reversed(messages)
]
Loading
Loading