Initial commit: OCA Ai packages (4 packages)

This commit is contained in:
Ernad Husremovic 2025-08-29 15:43:05 +02:00
commit 0adb4b78b1
170 changed files with 12385 additions and 0 deletions

View file

@ -0,0 +1,44 @@
# Ai Oca Bridge Chatter
Odoo addon: ai_oca_bridge_chatter
## Installation
```bash
pip install odoo-bringout-oca-ai-ai_oca_bridge_chatter
```
## Dependencies
This addon depends on:
- ai_oca_bridge
## Manifest Information
- **Name**: Ai Oca Bridge Chatter
- **Version**: 16.0.1.0.0
- **Category**: N/A
- **License**: AGPL-3
- **Installable**: False
## Source
Based on [OCA/ai](https://github.com/OCA/ai) branch 16.0, addon `ai_oca_bridge_chatter`.
## License
This package maintains the original AGPL-3 license from the upstream Odoo project.
## Documentation
- Overview: doc/OVERVIEW.md
- Architecture: doc/ARCHITECTURE.md
- Models: doc/MODELS.md
- Controllers: doc/CONTROLLERS.md
- Wizards: doc/WIZARDS.md
- Install: doc/INSTALL.md
- Usage: doc/USAGE.md
- Configuration: doc/CONFIGURATION.md
- Dependencies: doc/DEPENDENCIES.md
- Troubleshooting: doc/TROUBLESHOOTING.md
- FAQ: doc/FAQ.md

View file

@ -0,0 +1,103 @@
.. image:: https://odoo-community.org/readme-banner-image
:target: https://odoo-community.org/get-involved?utm_source=readme
:alt: Odoo Community Association
=====================
Ai Oca Bridge Chatter
=====================
..
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!! This file is generated by oca-gen-addon-readme !!
!! changes will be overwritten. !!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!! source digest: sha256:eb5b0cdbee135ee2cd5731de3c50a697c02fb931d21800fcc5fa39ca670edad6
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
.. |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/license-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/16.0/ai_oca_bridge_chatter
:alt: OCA/ai
.. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png
:target: https://translation.odoo-community.org/projects/ai-16-0/ai-16-0-ai_oca_bridge_chatter
: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=16.0
:alt: Try me on Runboat
|badge1| |badge2| |badge3| |badge4| |badge5|
This module allows usage of LLM chatbots inside Odoo.
The logic of the chatbot should be defined in an external system like
n8n.
**Table of contents**
.. contents::
:local:
Configuration
=============
On your external AI system create a workflow that will receive messages
and will return the call directly.
Here you can see an `example <./static/description/Chat.json>`__ of
configuration in n8n.
After that, create a bridge with usage type chatter and payload type
chatter. Then, create a user and assign the bridge to it.
With this configuration, the user will answer automatically using the
external systema and will be online permanently. It can be used on
livechat without any issues.
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_chatter%0Aversion:%2016.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
-------
* Dixmit
Contributors
------------
- `Dixmit <https://www.dixmit.com>`__
- Enric Tobella
- `Binhex <https://www.binhex.cloud/>`__
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/16.0/ai_oca_bridge_chatter>`_ project on GitHub.
You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute.

View file

@ -0,0 +1 @@
from . import models

View file

@ -0,0 +1,19 @@
# Copyright 202 Dixmit
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
{
"name": "Ai Oca Bridge Chatter",
"summary": """Integrate a Bridge with a user that will use it on chatter""",
"version": "16.0.1.0.0",
"license": "AGPL-3",
"author": "Dixmit,Odoo Community Association (OCA)",
"website": "https://github.com/OCA/ai",
"depends": [
"ai_oca_bridge",
],
"data": [
"views/res_users.xml",
"views/ai_bridge.xml",
],
"demo": [],
}

View file

@ -0,0 +1,86 @@
# Translation of Odoo Server.
# This file contains the translation of the following modules:
# * ai_oca_bridge_chatter
#
msgid ""
msgstr ""
"Project-Id-Version: Odoo Server 16.0\n"
"Report-Msgid-Bugs-To: \n"
"Last-Translator: \n"
"Language-Team: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: \n"
"Plural-Forms: \n"
#. module: ai_oca_bridge_chatter
#: model:ir.model.fields,field_description:ai_oca_bridge_chatter.field_res_users__ai_bridge_id
msgid "AI Bridge"
msgstr ""
#. module: ai_oca_bridge_chatter
#: model:ir.model,name:ai_oca_bridge_chatter.model_ai_bridge
msgid "Ai Bridge Configuration"
msgstr ""
#. module: ai_oca_bridge_chatter
#: model:ir.model,name:ai_oca_bridge_chatter.model_ai_bridge_execution
msgid "Ai Execution"
msgstr ""
#. module: ai_oca_bridge_chatter
#: model:ir.model.fields.selection,name:ai_oca_bridge_chatter.selection__ai_bridge__payload_type__chatter
#: model:ir.model.fields.selection,name:ai_oca_bridge_chatter.selection__ai_bridge__usage__chatter
msgid "Chatter"
msgstr ""
#. module: ai_oca_bridge_chatter
#: model:ir.model.fields,field_description:ai_oca_bridge_chatter.field_ai_bridge_execution__chatter_user_id
msgid "Chatter User"
msgstr ""
#. module: ai_oca_bridge_chatter
#: model:ir.model,name:ai_oca_bridge_chatter.model_res_partner
msgid "Contact"
msgstr ""
#. module: ai_oca_bridge_chatter
#: model:ir.model.fields,help:ai_oca_bridge_chatter.field_ai_bridge__usage
msgid ""
"Defines how this bridge is used. If 'Thread', it will be used in the mail "
"thread context."
msgstr ""
#. module: ai_oca_bridge_chatter
#: model:ir.model,name:ai_oca_bridge_chatter.model_mail_channel
msgid "Discussion Channel"
msgstr ""
#. module: ai_oca_bridge_chatter
#: model:ir.model.fields,field_description:ai_oca_bridge_chatter.field_ai_bridge__payload_type
msgid "Payload Type"
msgstr ""
#. module: ai_oca_bridge_chatter
#. odoo-python
#: code:addons/ai_oca_bridge_chatter/models/ai_bridge_execution.py:0
#, python-format
msgid "The message does not belong to any channel."
msgstr ""
#. module: ai_oca_bridge_chatter
#. odoo-python
#: code:addons/ai_oca_bridge_chatter/models/ai_bridge.py:0
#, python-format
msgid "The record must be a mail.message instance."
msgstr ""
#. module: ai_oca_bridge_chatter
#: model:ir.model.fields,field_description:ai_oca_bridge_chatter.field_ai_bridge__usage
msgid "Usage"
msgstr ""
#. module: ai_oca_bridge_chatter
#: model:ir.model,name:ai_oca_bridge_chatter.model_res_users
msgid "User"
msgstr ""

View file

@ -0,0 +1,86 @@
# Translation of Odoo Server.
# This file contains the translation of the following modules:
# * ai_oca_bridge_chatter
#
msgid ""
msgstr ""
"Project-Id-Version: Odoo Server 16.0\n"
"Report-Msgid-Bugs-To: \n"
"Last-Translator: \n"
"Language-Team: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: \n"
"Plural-Forms: \n"
#. module: ai_oca_bridge_chatter
#: model:ir.model.fields,field_description:ai_oca_bridge_chatter.field_res_users__ai_bridge_id
msgid "AI Bridge"
msgstr "AI most"
#. module: ai_oca_bridge_chatter
#: model:ir.model,name:ai_oca_bridge_chatter.model_ai_bridge
msgid "Ai Bridge Configuration"
msgstr "Konfiguracija Ai mosta"
#. module: ai_oca_bridge_chatter
#: model:ir.model,name:ai_oca_bridge_chatter.model_ai_bridge_execution
msgid "Ai Execution"
msgstr "Ai izvršavanje"
#. module: ai_oca_bridge_chatter
#: model:ir.model.fields.selection,name:ai_oca_bridge_chatter.selection__ai_bridge__payload_type__chatter
#: model:ir.model.fields.selection,name:ai_oca_bridge_chatter.selection__ai_bridge__usage__chatter
msgid "Chatter"
msgstr "Razgovor"
#. module: ai_oca_bridge_chatter
#: model:ir.model.fields,field_description:ai_oca_bridge_chatter.field_ai_bridge_execution__chatter_user_id
msgid "Chatter User"
msgstr "Korisnik razgovora"
#. module: ai_oca_bridge_chatter
#: model:ir.model,name:ai_oca_bridge_chatter.model_res_partner
msgid "Contact"
msgstr "Kontakt"
#. module: ai_oca_bridge_chatter
#: model:ir.model.fields,help:ai_oca_bridge_chatter.field_ai_bridge__usage
msgid ""
"Defines how this bridge is used. If 'Thread', it will be used in the mail "
"thread context."
msgstr ""
#. module: ai_oca_bridge_chatter
#: model:ir.model,name:ai_oca_bridge_chatter.model_mail_channel
msgid "Discussion Channel"
msgstr "Kanal rasprave"
#. module: ai_oca_bridge_chatter
#: model:ir.model.fields,field_description:ai_oca_bridge_chatter.field_ai_bridge__payload_type
msgid "Payload Type"
msgstr "Tip korisnog tereta"
#. module: ai_oca_bridge_chatter
#. odoo-python
#: code:addons/ai_oca_bridge_chatter/models/ai_bridge_execution.py:0
#, python-format
msgid "The message does not belong to any channel."
msgstr "Poruka ne pripada nijednom kanalu."
#. module: ai_oca_bridge_chatter
#. odoo-python
#: code:addons/ai_oca_bridge_chatter/models/ai_bridge.py:0
#, python-format
msgid "The record must be a mail.message instance."
msgstr "Zapis mora biti instanca mail.message."
#. module: ai_oca_bridge_chatter
#: model:ir.model.fields,field_description:ai_oca_bridge_chatter.field_ai_bridge__usage
msgid "Usage"
msgstr "Upotreba"
#. module: ai_oca_bridge_chatter
#: model:ir.model,name:ai_oca_bridge_chatter.model_res_users
msgid "User"
msgstr "Korisnik"

View file

@ -0,0 +1,91 @@
# Translation of Odoo Server.
# This file contains the translation of the following modules:
# * ai_oca_bridge_chatter
#
msgid ""
msgstr ""
"Project-Id-Version: Odoo Server 16.0\n"
"Report-Msgid-Bugs-To: \n"
"PO-Revision-Date: 2025-08-16 01:25+0000\n"
"Last-Translator: \"Leonardo J. Caballero G.\" <leonardocaballero@gmail.com>\n"
"Language-Team: none\n"
"Language: es\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: \n"
"Plural-Forms: nplurals=2; plural=n != 1;\n"
"X-Generator: Weblate 5.10.4\n"
#. module: ai_oca_bridge_chatter
#: model:ir.model.fields,field_description:ai_oca_bridge_chatter.field_res_users__ai_bridge_id
msgid "AI Bridge"
msgstr "Puente de IA"
#. module: ai_oca_bridge_chatter
#: model:ir.model,name:ai_oca_bridge_chatter.model_ai_bridge
msgid "Ai Bridge Configuration"
msgstr "Configuración de puente de IA"
#. module: ai_oca_bridge_chatter
#: model:ir.model,name:ai_oca_bridge_chatter.model_ai_bridge_execution
msgid "Ai Execution"
msgstr "Ejecución de IA"
#. module: ai_oca_bridge_chatter
#: model:ir.model.fields.selection,name:ai_oca_bridge_chatter.selection__ai_bridge__payload_type__chatter
#: model:ir.model.fields.selection,name:ai_oca_bridge_chatter.selection__ai_bridge__usage__chatter
msgid "Chatter"
msgstr "Chatter"
#. module: ai_oca_bridge_chatter
#: model:ir.model.fields,field_description:ai_oca_bridge_chatter.field_ai_bridge_execution__chatter_user_id
msgid "Chatter User"
msgstr "Usuario de Chatter"
#. module: ai_oca_bridge_chatter
#: model:ir.model,name:ai_oca_bridge_chatter.model_res_partner
msgid "Contact"
msgstr "Contacto"
#. module: ai_oca_bridge_chatter
#: model:ir.model.fields,help:ai_oca_bridge_chatter.field_ai_bridge__usage
msgid ""
"Defines how this bridge is used. If 'Thread', it will be used in the mail "
"thread context."
msgstr ""
"Define cómo se utiliza este puente. Si es 'Hilo', se usará en el contexto "
"del hilo de correo."
#. module: ai_oca_bridge_chatter
#: model:ir.model,name:ai_oca_bridge_chatter.model_mail_channel
msgid "Discussion Channel"
msgstr "Canal de discusión"
#. module: ai_oca_bridge_chatter
#: model:ir.model.fields,field_description:ai_oca_bridge_chatter.field_ai_bridge__payload_type
msgid "Payload Type"
msgstr "Tipo de carga útil"
#. module: ai_oca_bridge_chatter
#. odoo-python
#: code:addons/ai_oca_bridge_chatter/models/ai_bridge_execution.py:0
#, python-format
msgid "The message does not belong to any channel."
msgstr "El mensaje no pertenece a ningún canal."
#. module: ai_oca_bridge_chatter
#. odoo-python
#: code:addons/ai_oca_bridge_chatter/models/ai_bridge.py:0
#, python-format
msgid "The record must be a mail.message instance."
msgstr "El registro debe ser una instancia de mail.message."
#. module: ai_oca_bridge_chatter
#: model:ir.model.fields,field_description:ai_oca_bridge_chatter.field_ai_bridge__usage
msgid "Usage"
msgstr "Uso"
#. module: ai_oca_bridge_chatter
#: model:ir.model,name:ai_oca_bridge_chatter.model_res_users
msgid "User"
msgstr "Usuario"

View file

@ -0,0 +1,91 @@
# Translation of Odoo Server.
# This file contains the translation of the following modules:
# * ai_oca_bridge_chatter
#
msgid ""
msgstr ""
"Project-Id-Version: Odoo Server 16.0\n"
"Report-Msgid-Bugs-To: \n"
"PO-Revision-Date: 2025-08-16 01:25+0000\n"
"Last-Translator: \"Leonardo J. Caballero G.\" <leonardocaballero@gmail.com>\n"
"Language-Team: none\n"
"Language: es_VE\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: \n"
"Plural-Forms: nplurals=2; plural=n != 1;\n"
"X-Generator: Weblate 5.10.4\n"
#. module: ai_oca_bridge_chatter
#: model:ir.model.fields,field_description:ai_oca_bridge_chatter.field_res_users__ai_bridge_id
msgid "AI Bridge"
msgstr "Puente de IA"
#. module: ai_oca_bridge_chatter
#: model:ir.model,name:ai_oca_bridge_chatter.model_ai_bridge
msgid "Ai Bridge Configuration"
msgstr "Configuración de puente de IA"
#. module: ai_oca_bridge_chatter
#: model:ir.model,name:ai_oca_bridge_chatter.model_ai_bridge_execution
msgid "Ai Execution"
msgstr "Ejecución de IA"
#. module: ai_oca_bridge_chatter
#: model:ir.model.fields.selection,name:ai_oca_bridge_chatter.selection__ai_bridge__payload_type__chatter
#: model:ir.model.fields.selection,name:ai_oca_bridge_chatter.selection__ai_bridge__usage__chatter
msgid "Chatter"
msgstr "Chatter"
#. module: ai_oca_bridge_chatter
#: model:ir.model.fields,field_description:ai_oca_bridge_chatter.field_ai_bridge_execution__chatter_user_id
msgid "Chatter User"
msgstr "Usuario de Chatter"
#. module: ai_oca_bridge_chatter
#: model:ir.model,name:ai_oca_bridge_chatter.model_res_partner
msgid "Contact"
msgstr "Contacto"
#. module: ai_oca_bridge_chatter
#: model:ir.model.fields,help:ai_oca_bridge_chatter.field_ai_bridge__usage
msgid ""
"Defines how this bridge is used. If 'Thread', it will be used in the mail "
"thread context."
msgstr ""
"Define cómo se utiliza este puente. Si es 'Hilo', se usará en el contexto "
"del hilo de correo."
#. module: ai_oca_bridge_chatter
#: model:ir.model,name:ai_oca_bridge_chatter.model_mail_channel
msgid "Discussion Channel"
msgstr "Canal de discusión"
#. module: ai_oca_bridge_chatter
#: model:ir.model.fields,field_description:ai_oca_bridge_chatter.field_ai_bridge__payload_type
msgid "Payload Type"
msgstr "Tipo de carga útil"
#. module: ai_oca_bridge_chatter
#. odoo-python
#: code:addons/ai_oca_bridge_chatter/models/ai_bridge_execution.py:0
#, python-format
msgid "The message does not belong to any channel."
msgstr "El mensaje no pertenece a ningún canal."
#. module: ai_oca_bridge_chatter
#. odoo-python
#: code:addons/ai_oca_bridge_chatter/models/ai_bridge.py:0
#, python-format
msgid "The record must be a mail.message instance."
msgstr "El registro debe ser una instancia de mail.message."
#. module: ai_oca_bridge_chatter
#: model:ir.model.fields,field_description:ai_oca_bridge_chatter.field_ai_bridge__usage
msgid "Usage"
msgstr "Uso"
#. module: ai_oca_bridge_chatter
#: model:ir.model,name:ai_oca_bridge_chatter.model_res_users
msgid "User"
msgstr "Usuario"

View file

@ -0,0 +1,91 @@
# Translation of Odoo Server.
# This file contains the translation of the following modules:
# * ai_oca_bridge_chatter
#
msgid ""
msgstr ""
"Project-Id-Version: Odoo Server 16.0\n"
"Report-Msgid-Bugs-To: \n"
"PO-Revision-Date: 2025-07-29 09:25+0000\n"
"Last-Translator: mymage <stefano.consolaro@mymage.it>\n"
"Language-Team: none\n"
"Language: it\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: \n"
"Plural-Forms: nplurals=2; plural=n != 1;\n"
"X-Generator: Weblate 5.10.4\n"
#. module: ai_oca_bridge_chatter
#: model:ir.model.fields,field_description:ai_oca_bridge_chatter.field_res_users__ai_bridge_id
msgid "AI Bridge"
msgstr "Collegamento IA"
#. module: ai_oca_bridge_chatter
#: model:ir.model,name:ai_oca_bridge_chatter.model_ai_bridge
msgid "Ai Bridge Configuration"
msgstr "Configurazione collegamento IA"
#. module: ai_oca_bridge_chatter
#: model:ir.model,name:ai_oca_bridge_chatter.model_ai_bridge_execution
msgid "Ai Execution"
msgstr "Esecuzione IA"
#. module: ai_oca_bridge_chatter
#: model:ir.model.fields.selection,name:ai_oca_bridge_chatter.selection__ai_bridge__payload_type__chatter
#: model:ir.model.fields.selection,name:ai_oca_bridge_chatter.selection__ai_bridge__usage__chatter
msgid "Chatter"
msgstr "Elenco messaggi"
#. module: ai_oca_bridge_chatter
#: model:ir.model.fields,field_description:ai_oca_bridge_chatter.field_ai_bridge_execution__chatter_user_id
msgid "Chatter User"
msgstr "Utente elenco messaggi"
#. module: ai_oca_bridge_chatter
#: model:ir.model,name:ai_oca_bridge_chatter.model_res_partner
msgid "Contact"
msgstr "Contatto"
#. module: ai_oca_bridge_chatter
#: model:ir.model.fields,help:ai_oca_bridge_chatter.field_ai_bridge__usage
msgid ""
"Defines how this bridge is used. If 'Thread', it will be used in the mail "
"thread context."
msgstr ""
"Definisce come viene usato questo collegamento. Se \"Discussione\", verrà "
"usato nel contesto della discussione e-mail."
#. module: ai_oca_bridge_chatter
#: model:ir.model,name:ai_oca_bridge_chatter.model_mail_channel
msgid "Discussion Channel"
msgstr "Canale discussione"
#. module: ai_oca_bridge_chatter
#: model:ir.model.fields,field_description:ai_oca_bridge_chatter.field_ai_bridge__payload_type
msgid "Payload Type"
msgstr "Tipo carico"
#. module: ai_oca_bridge_chatter
#. odoo-python
#: code:addons/ai_oca_bridge_chatter/models/ai_bridge_execution.py:0
#, python-format
msgid "The message does not belong to any channel."
msgstr "Questo messaggi non appartiene a nessun canale."
#. module: ai_oca_bridge_chatter
#. odoo-python
#: code:addons/ai_oca_bridge_chatter/models/ai_bridge.py:0
#, python-format
msgid "The record must be a mail.message instance."
msgstr "Il record deve essere una istanza mail.message."
#. module: ai_oca_bridge_chatter
#: model:ir.model.fields,field_description:ai_oca_bridge_chatter.field_ai_bridge__usage
msgid "Usage"
msgstr "Uilizzo"
#. module: ai_oca_bridge_chatter
#: model:ir.model,name:ai_oca_bridge_chatter.model_res_users
msgid "User"
msgstr "Utente"

View file

@ -0,0 +1,87 @@
# Translation of Odoo Server.
# This file contains the translation of the following modules:
# * ai_oca_bridge_chatter
#
msgid ""
msgstr ""
"Project-Id-Version: Odoo Server 16.0\n"
"Report-Msgid-Bugs-To: \n"
"Last-Translator: Automatically generated\n"
"Language-Team: none\n"
"Language: pt_BR\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: \n"
"Plural-Forms: nplurals=2; plural=n > 1;\n"
#. module: ai_oca_bridge_chatter
#: model:ir.model.fields,field_description:ai_oca_bridge_chatter.field_res_users__ai_bridge_id
msgid "AI Bridge"
msgstr ""
#. module: ai_oca_bridge_chatter
#: model:ir.model,name:ai_oca_bridge_chatter.model_ai_bridge
msgid "Ai Bridge Configuration"
msgstr ""
#. module: ai_oca_bridge_chatter
#: model:ir.model,name:ai_oca_bridge_chatter.model_ai_bridge_execution
msgid "Ai Execution"
msgstr ""
#. module: ai_oca_bridge_chatter
#: model:ir.model.fields.selection,name:ai_oca_bridge_chatter.selection__ai_bridge__payload_type__chatter
#: model:ir.model.fields.selection,name:ai_oca_bridge_chatter.selection__ai_bridge__usage__chatter
msgid "Chatter"
msgstr ""
#. module: ai_oca_bridge_chatter
#: model:ir.model.fields,field_description:ai_oca_bridge_chatter.field_ai_bridge_execution__chatter_user_id
msgid "Chatter User"
msgstr ""
#. module: ai_oca_bridge_chatter
#: model:ir.model,name:ai_oca_bridge_chatter.model_res_partner
msgid "Contact"
msgstr ""
#. module: ai_oca_bridge_chatter
#: model:ir.model.fields,help:ai_oca_bridge_chatter.field_ai_bridge__usage
msgid ""
"Defines how this bridge is used. If 'Thread', it will be used in the mail "
"thread context."
msgstr ""
#. module: ai_oca_bridge_chatter
#: model:ir.model,name:ai_oca_bridge_chatter.model_mail_channel
msgid "Discussion Channel"
msgstr ""
#. module: ai_oca_bridge_chatter
#: model:ir.model.fields,field_description:ai_oca_bridge_chatter.field_ai_bridge__payload_type
msgid "Payload Type"
msgstr ""
#. module: ai_oca_bridge_chatter
#. odoo-python
#: code:addons/ai_oca_bridge_chatter/models/ai_bridge_execution.py:0
#, python-format
msgid "The message does not belong to any channel."
msgstr ""
#. module: ai_oca_bridge_chatter
#. odoo-python
#: code:addons/ai_oca_bridge_chatter/models/ai_bridge.py:0
#, python-format
msgid "The record must be a mail.message instance."
msgstr ""
#. module: ai_oca_bridge_chatter
#: model:ir.model.fields,field_description:ai_oca_bridge_chatter.field_ai_bridge__usage
msgid "Usage"
msgstr ""
#. module: ai_oca_bridge_chatter
#: model:ir.model,name:ai_oca_bridge_chatter.model_res_users
msgid "User"
msgstr ""

View file

@ -0,0 +1,5 @@
from . import ai_bridge
from . import ai_bridge_execution
from . import res_partner
from . import res_users
from . import mail_channel

View file

@ -0,0 +1,32 @@
# Copyright 2025 Dixmit
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
from odoo import _, fields, models
class AiBridge(models.Model):
_inherit = "ai.bridge"
usage = fields.Selection(
selection_add=[("chatter", "Chatter")], ondelete={"chatter": "set default"}
)
payload_type = fields.Selection(
selection_add=[("chatter", "Chatter")], ondelete={"chatter": "set default"}
)
def _prepare_payload_chatter(self, record=None, **kwargs):
if not record:
record = self.env["mail.message"].search([], limit=1)
if record._name != "mail.message":
raise ValueError(_("The record must be a mail.message instance."))
return {
"message": {
"res_id": record.res_id,
"model": record.model,
"body": record.body,
"author_id": record.author_id.id,
"subject": record.subject,
"date": record.date.isoformat(),
"author_name": record.author_id.name,
}
}

View file

@ -0,0 +1,41 @@
# Copyright 2025 Dixmit
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
from odoo import _, fields, models
class AiBridgeExecution(models.Model):
_inherit = "ai.bridge.execution"
chatter_user_id = fields.Many2one("res.users", readonly=True)
def _get_channel(self):
if self.ai_bridge_id.usage == "chatter":
# For chatter usage, we need to get the channel from the message
message = self.env["mail.message"].browse(self.res_id)
if message.model != "mail.channel":
raise ValueError(_("The message does not belong to any channel."))
return (
self.env["mail.channel"]
.browse(message.res_id)
.with_user(self.chatter_user_id.id)
)
return super()._get_channel()
def _process_response_message(self, response):
if self.ai_bridge_id.usage == "chatter":
recipient = (
self.env["mail.channel.member"]
.sudo()
.search(
[
("partner_id", "=", self.chatter_user_id.partner_id.id),
("channel_id", "=", self._get_channel().id),
],
limit=1,
)
)
recipient._notify_typing(is_typing=False)
response["author_id"] = self.chatter_user_id.partner_id.id
response["message_type"] = "comment"
return super()._process_response_message(response)

View file

@ -0,0 +1,50 @@
# Copyright 2025 Dixmit
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
from odoo import models
class MailChannel(models.Model):
_inherit = "mail.channel"
def message_post(self, **kwargs):
message = super().message_post(**kwargs)
if not message.body:
return message
if message.author_id.user_ids.ai_bridge_id:
# Don't answer AI agents
return message
channel_recipient_ids = self.sudo().channel_member_ids.filtered(
lambda recipient: recipient.partner_id != message.author_id
and recipient.partner_id.user_ids
and recipient.partner_id.user_ids.ai_bridge_id
and self._eligibile_for_ai(message, recipient)
)
for recipient in channel_recipient_ids:
recipient._notify_typing(is_typing=True)
for user in recipient.partner_id.user_ids:
for bridge in user.ai_bridge_id:
execution = self.env["ai.bridge.execution"].create(
{
"ai_bridge_id": bridge.id,
"model_id": self.sudo()
.env.ref("mail.model_mail_message")
.id,
"res_id": message.id,
"chatter_user_id": user.id,
}
)
execution._execute()
return message
def _eligibile_for_ai(self, message, recipient):
if len(self.sudo().channel_member_ids) <= 2:
return True
if recipient.partner_id in message.partner_ids:
# If the recipient is already in the message partners,
# it was invoked by the user.
# This will make it work on general channels
return True
# TODO: add more checks to determine if the message is eligible
# for AI processing, like livechat messages.
return False

View file

@ -0,0 +1,21 @@
# Copyright 2025 Dixmit
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
from odoo import models
class ResPartner(models.Model):
_inherit = "res.partner"
def _compute_im_status(self):
"""
Override to set im_status to 'online' for partners
that have an associated user with an AI bridge.
It will be shown in general chatter as online.
"""
for record in self.filtered(lambda r: r.user_ids.ai_bridge_id):
record.im_status = "online"
to_process = self.filtered(lambda r: not r.user_ids.ai_bridge_id)
if not to_process:
return
return super(ResPartner, to_process)._compute_im_status()

View file

@ -0,0 +1,25 @@
# Copyright 2025 Dixmit
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
from odoo import fields, models
class ResUsers(models.Model):
_inherit = "res.users"
ai_bridge_id = fields.Many2one(
"ai.bridge", string="AI Bridge", domain=[("usage", "=", "chatter")]
)
def _compute_im_status(self):
"""
Override to set im_status to 'online' for users
that have an associated user with an AI bridge.
Useful for Live chat
"""
for record in self.filtered(lambda r: r.ai_bridge_id):
record.im_status = "online"
to_process = self.filtered(lambda r: not r.ai_bridge_id)
if not to_process:
return
return super(ResUsers, to_process)._compute_im_status()

View file

@ -0,0 +1,9 @@
On your external AI system create a workflow that will receive messages and will return the call directly.
Here you can see an [example](./static/description/Chat.json) of configuration in n8n.
After that, create a bridge with usage type chatter and payload type chatter.
Then, create a user and assign the bridge to it.
With this configuration, the user will answer automatically using the external systema and will be online permanently.
It can be used on livechat without any issues.

View file

@ -0,0 +1,5 @@
- [Dixmit](https://www.dixmit.com)
- Enric Tobella
- [Binhex](https://www.binhex.cloud/)

View file

@ -0,0 +1,3 @@
This module allows usage of LLM chatbots inside Odoo.
The logic of the chatbot should be defined in an external system like n8n.

View file

@ -0,0 +1,185 @@
{
"name": "Chat",
"nodes": [
{
"parameters": {
"httpMethod": "POST",
"path": "af33e75e-2cb4-4953-903c-4d7ce88aba7d",
"options": {}
},
"type": "n8n-nodes-base.webhook",
"typeVersion": 2,
"position": [-540, -180],
"id": "604ad5d2-f45f-4f27-8040-392c5cde67de",
"name": "Webhook",
"webhookId": "af33e75e-2cb4-4953-903c-4d7ce88aba7d"
},
{
"parameters": {
"method": "POST",
"url": "={{ $('Webhook').item.json.body._response_url }}",
"sendBody": true,
"bodyParameters": {
"parameters": [
{
"name": "body",
"value": "={{ $json.data }}"
}
]
},
"options": {}
},
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.2,
"position": [320, -180],
"id": "196baea5-927e-49d7-b888-41a032255eb5",
"name": "HTTP Request",
"alwaysOutputData": false
},
{
"parameters": {
"promptType": "define",
"text": "={{ $json.data }}",
"options": {}
},
"type": "@n8n/n8n-nodes-langchain.agent",
"typeVersion": 2,
"position": [-200, -180],
"id": "92dff2e3-eede-4ba3-b494-ddbd3b33d009",
"name": "AI Agent"
},
{
"parameters": {
"model": "gemma3:1b",
"options": {}
},
"type": "@n8n/n8n-nodes-langchain.lmChatOllama",
"typeVersion": 1,
"position": [-200, -20],
"id": "4f3c84b0-217b-44dd-9798-f9f3bde0437c",
"name": "Ollama Chat Model",
"credentials": {
"ollamaApi": {
"id": "hjdwBXqKYq7uKQBz",
"name": "Ollama account"
}
}
},
{
"parameters": {
"html": "={{ $json.body.message.body }}",
"options": {}
},
"type": "n8n-nodes-base.markdown",
"typeVersion": 1,
"position": [-380, -180],
"id": "870d0777-4943-4f9d-a283-c31bb079f8b6",
"name": "Markdown"
},
{
"parameters": {
"mode": "markdownToHtml",
"markdown": "={{ $json.output }}",
"options": {}
},
"type": "n8n-nodes-base.markdown",
"typeVersion": 1,
"position": [160, -180],
"id": "3217e297-6508-47b2-bc67-52810c6512bd",
"name": "Markdown1"
},
{
"parameters": {
"sessionIdType": "customKey",
"sessionKey": "={{ $json.body._odoo.db }}-{{ $json.body._odoo.db_hash }}-{{ $json.body.message.model }}-{{ $json.body.message.res_id }}"
},
"type": "@n8n/n8n-nodes-langchain.memoryBufferWindow",
"typeVersion": 1.3,
"position": [-100, -20],
"id": "35174f6e-a121-4db5-be62-86db987bc294",
"name": "Simple Memory"
}
],
"pinData": {
"Webhook": []
},
"connections": {
"Webhook": {
"main": [
[
{
"node": "Markdown",
"type": "main",
"index": 0
}
]
]
},
"Ollama Chat Model": {
"ai_languageModel": [
[
{
"node": "AI Agent",
"type": "ai_languageModel",
"index": 0
}
]
]
},
"AI Agent": {
"main": [
[
{
"node": "Markdown1",
"type": "main",
"index": 0
}
]
]
},
"Markdown": {
"main": [
[
{
"node": "AI Agent",
"type": "main",
"index": 0
}
]
]
},
"Markdown1": {
"main": [
[
{
"node": "HTTP Request",
"type": "main",
"index": 0
}
]
]
},
"Simple Memory": {
"ai_memory": [
[
{
"node": "AI Agent",
"type": "ai_memory",
"index": 0
}
]
]
}
},
"active": true,
"settings": {
"executionOrder": "v1"
},
"versionId": "539a3987-4bb4-41e2-b37e-26c1d8896779",
"meta": {
"templateCredsSetupCompleted": true,
"instanceId": "853b8a3aa64a88c45a4d2dd4197c75abdf32fa4cca1ff05538d7e4592b457dcb"
},
"id": "YP8qXDrWacI1QVyu",
"tags": []
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.2 KiB

View file

@ -0,0 +1,448 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta name="generator" content="Docutils: https://docutils.sourceforge.io/" />
<title>README.rst</title>
<style type="text/css">
/*
:Author: David Goodger (goodger@python.org)
:Id: $Id: html4css1.css 9511 2024-01-13 09:50:07Z milde $
:Copyright: This stylesheet has been placed in the public domain.
Default cascading style sheet for the HTML output of Docutils.
Despite the name, some widely supported CSS2 features are used.
See https://docutils.sourceforge.io/docs/howto/html-stylesheets.html for how to
customize this style sheet.
*/
/* used to remove borders from tables and images */
.borderless, table.borderless td, table.borderless th {
border: 0 }
table.borderless td, table.borderless th {
/* Override padding for "table.docutils td" with "! important".
The right padding separates the table cells. */
padding: 0 0.5em 0 0 ! important }
.first {
/* Override more specific margin styles with "! important". */
margin-top: 0 ! important }
.last, .with-subtitle {
margin-bottom: 0 ! important }
.hidden {
display: none }
.subscript {
vertical-align: sub;
font-size: smaller }
.superscript {
vertical-align: super;
font-size: smaller }
a.toc-backref {
text-decoration: none ;
color: black }
blockquote.epigraph {
margin: 2em 5em ; }
dl.docutils dd {
margin-bottom: 0.5em }
object[type="image/svg+xml"], object[type="application/x-shockwave-flash"] {
overflow: hidden;
}
/* Uncomment (and remove this text!) to get bold-faced definition list terms
dl.docutils dt {
font-weight: bold }
*/
div.abstract {
margin: 2em 5em }
div.abstract p.topic-title {
font-weight: bold ;
text-align: center }
div.admonition, div.attention, div.caution, div.danger, div.error,
div.hint, div.important, div.note, div.tip, div.warning {
margin: 2em ;
border: medium outset ;
padding: 1em }
div.admonition p.admonition-title, div.hint p.admonition-title,
div.important p.admonition-title, div.note p.admonition-title,
div.tip p.admonition-title {
font-weight: bold ;
font-family: sans-serif }
div.attention p.admonition-title, div.caution p.admonition-title,
div.danger p.admonition-title, div.error p.admonition-title,
div.warning p.admonition-title, .code .error {
color: red ;
font-weight: bold ;
font-family: sans-serif }
/* Uncomment (and remove this text!) to get reduced vertical space in
compound paragraphs.
div.compound .compound-first, div.compound .compound-middle {
margin-bottom: 0.5em }
div.compound .compound-last, div.compound .compound-middle {
margin-top: 0.5em }
*/
div.dedication {
margin: 2em 5em ;
text-align: center ;
font-style: italic }
div.dedication p.topic-title {
font-weight: bold ;
font-style: normal }
div.figure {
margin-left: 2em ;
margin-right: 2em }
div.footer, div.header {
clear: both;
font-size: smaller }
div.line-block {
display: block ;
margin-top: 1em ;
margin-bottom: 1em }
div.line-block div.line-block {
margin-top: 0 ;
margin-bottom: 0 ;
margin-left: 1.5em }
div.sidebar {
margin: 0 0 0.5em 1em ;
border: medium outset ;
padding: 1em ;
background-color: #ffffee ;
width: 40% ;
float: right ;
clear: right }
div.sidebar p.rubric {
font-family: sans-serif ;
font-size: medium }
div.system-messages {
margin: 5em }
div.system-messages h1 {
color: red }
div.system-message {
border: medium outset ;
padding: 1em }
div.system-message p.system-message-title {
color: red ;
font-weight: bold }
div.topic {
margin: 2em }
h1.section-subtitle, h2.section-subtitle, h3.section-subtitle,
h4.section-subtitle, h5.section-subtitle, h6.section-subtitle {
margin-top: 0.4em }
h1.title {
text-align: center }
h2.subtitle {
text-align: center }
hr.docutils {
width: 75% }
img.align-left, .figure.align-left, object.align-left, table.align-left {
clear: left ;
float: left ;
margin-right: 1em }
img.align-right, .figure.align-right, object.align-right, table.align-right {
clear: right ;
float: right ;
margin-left: 1em }
img.align-center, .figure.align-center, object.align-center {
display: block;
margin-left: auto;
margin-right: auto;
}
table.align-center {
margin-left: auto;
margin-right: auto;
}
.align-left {
text-align: left }
.align-center {
clear: both ;
text-align: center }
.align-right {
text-align: right }
/* reset inner alignment in figures */
div.align-right {
text-align: inherit }
/* div.align-center * { */
/* text-align: left } */
.align-top {
vertical-align: top }
.align-middle {
vertical-align: middle }
.align-bottom {
vertical-align: bottom }
ol.simple, ul.simple {
margin-bottom: 1em }
ol.arabic {
list-style: decimal }
ol.loweralpha {
list-style: lower-alpha }
ol.upperalpha {
list-style: upper-alpha }
ol.lowerroman {
list-style: lower-roman }
ol.upperroman {
list-style: upper-roman }
p.attribution {
text-align: right ;
margin-left: 50% }
p.caption {
font-style: italic }
p.credits {
font-style: italic ;
font-size: smaller }
p.label {
white-space: nowrap }
p.rubric {
font-weight: bold ;
font-size: larger ;
color: maroon ;
text-align: center }
p.sidebar-title {
font-family: sans-serif ;
font-weight: bold ;
font-size: larger }
p.sidebar-subtitle {
font-family: sans-serif ;
font-weight: bold }
p.topic-title {
font-weight: bold }
pre.address {
margin-bottom: 0 ;
margin-top: 0 ;
font: inherit }
pre.literal-block, pre.doctest-block, pre.math, pre.code {
margin-left: 2em ;
margin-right: 2em }
pre.code .ln { color: gray; } /* line numbers */
pre.code, code { background-color: #eeeeee }
pre.code .comment, code .comment { color: #5C6576 }
pre.code .keyword, code .keyword { color: #3B0D06; font-weight: bold }
pre.code .literal.string, code .literal.string { color: #0C5404 }
pre.code .name.builtin, code .name.builtin { color: #352B84 }
pre.code .deleted, code .deleted { background-color: #DEB0A1}
pre.code .inserted, code .inserted { background-color: #A3D289}
span.classifier {
font-family: sans-serif ;
font-style: oblique }
span.classifier-delimiter {
font-family: sans-serif ;
font-weight: bold }
span.interpreted {
font-family: sans-serif }
span.option {
white-space: nowrap }
span.pre {
white-space: pre }
span.problematic, pre.problematic {
color: red }
span.section-subtitle {
/* font-size relative to parent (h1..h6 element) */
font-size: 80% }
table.citation {
border-left: solid 1px gray;
margin-left: 1px }
table.docinfo {
margin: 2em 4em }
table.docutils {
margin-top: 0.5em ;
margin-bottom: 0.5em }
table.footnote {
border-left: solid 1px black;
margin-left: 1px }
table.docutils td, table.docutils th,
table.docinfo td, table.docinfo th {
padding-left: 0.5em ;
padding-right: 0.5em ;
vertical-align: top }
table.docutils th.field-name, table.docinfo th.docinfo-name {
font-weight: bold ;
text-align: left ;
white-space: nowrap ;
padding-left: 0 }
/* "booktabs" style (no vertical lines) */
table.docutils.booktabs {
border: 0px;
border-top: 2px solid;
border-bottom: 2px solid;
border-collapse: collapse;
}
table.docutils.booktabs * {
border: 0px;
}
table.docutils.booktabs th {
border-bottom: thin solid;
text-align: left;
}
h1 tt.docutils, h2 tt.docutils, h3 tt.docutils,
h4 tt.docutils, h5 tt.docutils, h6 tt.docutils {
font-size: 100% }
ul.auto-toc {
list-style-type: none }
</style>
</head>
<body>
<div class="document">
<a class="reference external image-reference" href="https://odoo-community.org/get-involved?utm_source=readme">
<img alt="Odoo Community Association" src="https://odoo-community.org/readme-banner-image" />
</a>
<div class="section" id="ai-oca-bridge-chatter">
<h1>Ai Oca Bridge Chatter</h1>
<!-- !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!! This file is generated by oca-gen-addon-readme !!
!! changes will be overwritten. !!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!! source digest: sha256:eb5b0cdbee135ee2cd5731de3c50a697c02fb931d21800fcc5fa39ca670edad6
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -->
<p><a class="reference external image-reference" href="https://odoo-community.org/page/development-status"><img alt="Beta" src="https://img.shields.io/badge/maturity-Beta-yellow.png" /></a> <a class="reference external image-reference" href="http://www.gnu.org/licenses/agpl-3.0-standalone.html"><img alt="License: AGPL-3" src="https://img.shields.io/badge/license-AGPL--3-blue.png" /></a> <a class="reference external image-reference" href="https://github.com/OCA/ai/tree/16.0/ai_oca_bridge_chatter"><img alt="OCA/ai" src="https://img.shields.io/badge/github-OCA%2Fai-lightgray.png?logo=github" /></a> <a class="reference external image-reference" href="https://translation.odoo-community.org/projects/ai-16-0/ai-16-0-ai_oca_bridge_chatter"><img alt="Translate me on Weblate" src="https://img.shields.io/badge/weblate-Translate%20me-F47D42.png" /></a> <a class="reference external image-reference" href="https://runboat.odoo-community.org/builds?repo=OCA/ai&amp;target_branch=16.0"><img alt="Try me on Runboat" src="https://img.shields.io/badge/runboat-Try%20me-875A7B.png" /></a></p>
<p>This module allows usage of LLM chatbots inside Odoo.</p>
<p>The logic of the chatbot should be defined in an external system like
n8n.</p>
<p><strong>Table of contents</strong></p>
<div class="contents local topic" id="contents">
<ul class="simple">
<li><a class="reference internal" href="#configuration" id="toc-entry-1">Configuration</a></li>
<li><a class="reference internal" href="#bug-tracker" id="toc-entry-2">Bug Tracker</a></li>
<li><a class="reference internal" href="#credits" id="toc-entry-3">Credits</a><ul>
<li><a class="reference internal" href="#authors" id="toc-entry-4">Authors</a></li>
<li><a class="reference internal" href="#contributors" id="toc-entry-5">Contributors</a></li>
<li><a class="reference internal" href="#maintainers" id="toc-entry-6">Maintainers</a></li>
</ul>
</li>
</ul>
</div>
<div class="section" id="configuration">
<h2><a class="toc-backref" href="#toc-entry-1">Configuration</a></h2>
<p>On your external AI system create a workflow that will receive messages
and will return the call directly.</p>
<p>Here you can see an <a class="reference external" href="./static/description/Chat.json">example</a> of
configuration in n8n.</p>
<p>After that, create a bridge with usage type chatter and payload type
chatter. Then, create a user and assign the bridge to it.</p>
<p>With this configuration, the user will answer automatically using the
external systema and will be online permanently. It can be used on
livechat without any issues.</p>
</div>
<div class="section" id="bug-tracker">
<h2><a class="toc-backref" href="#toc-entry-2">Bug Tracker</a></h2>
<p>Bugs are tracked on <a class="reference external" href="https://github.com/OCA/ai/issues">GitHub Issues</a>.
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
<a class="reference external" href="https://github.com/OCA/ai/issues/new?body=module:%20ai_oca_bridge_chatter%0Aversion:%2016.0%0A%0A**Steps%20to%20reproduce**%0A-%20...%0A%0A**Current%20behavior**%0A%0A**Expected%20behavior**">feedback</a>.</p>
<p>Do not contact contributors directly about support or help with technical issues.</p>
</div>
<div class="section" id="credits">
<h2><a class="toc-backref" href="#toc-entry-3">Credits</a></h2>
<div class="section" id="authors">
<h3><a class="toc-backref" href="#toc-entry-4">Authors</a></h3>
<ul class="simple">
<li>Dixmit</li>
</ul>
</div>
<div class="section" id="contributors">
<h3><a class="toc-backref" href="#toc-entry-5">Contributors</a></h3>
<ul class="simple">
<li><a class="reference external" href="https://www.dixmit.com">Dixmit</a><ul>
<li>Enric Tobella</li>
</ul>
</li>
<li><a class="reference external" href="https://www.binhex.cloud/">Binhex</a></li>
</ul>
</div>
<div class="section" id="maintainers">
<h3><a class="toc-backref" href="#toc-entry-6">Maintainers</a></h3>
<p>This module is maintained by the OCA.</p>
<a class="reference external image-reference" href="https://odoo-community.org">
<img alt="Odoo Community Association" src="https://odoo-community.org/logo.png" />
</a>
<p>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.</p>
<p>This module is part of the <a class="reference external" href="https://github.com/OCA/ai/tree/16.0/ai_oca_bridge_chatter">OCA/ai</a> project on GitHub.</p>
<p>You are welcome to contribute. To learn how please visit <a class="reference external" href="https://odoo-community.org/page/Contribute">https://odoo-community.org/page/Contribute</a>.</p>
</div>
</div>
</div>
</div>
</body>
</html>

View file

@ -0,0 +1 @@
from . import test_chatter

View file

@ -0,0 +1,183 @@
# Copyright 2025 Dixmit
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
from unittest import mock
from odoo.tests import common, new_test_user
class TestChatter(common.TransactionCase):
@classmethod
def setUpClass(cls):
super().setUpClass()
cls.env = cls.env(context=dict(cls.env.context, mail_create_nosubscribe=True))
cls.bridge = cls.env["ai.bridge"].create(
{
"name": "Test Bridge",
"model_id": cls.env.ref("base.model_res_partner").id,
"url": "https://example.com/api",
"auth_type": "none",
"usage": "chatter",
"payload_type": "chatter",
"result_kind": "immediate",
# We will use the immediate result_kind to simplify the test
"result_type": "message",
}
)
cls.ai_user = new_test_user(
cls.env,
login="test-chatter-user",
groups="base.group_user",
)
cls.ai_user.write({"ai_bridge_id": cls.bridge.id})
cls.user = new_test_user(
cls.env,
login="test-chatter-user-2",
groups="base.group_user",
)
cls.chat = (
cls.env["mail.channel"]
.with_user(cls.user.id)
.create(
{
"name": "Test Channel",
"channel_type": "chat",
"channel_member_ids": [
(0, 0, {"partner_id": cls.ai_user.partner_id.id}),
(0, 0, {"partner_id": cls.user.partner_id.id}),
],
}
)
)
cls.channel = cls.env["mail.channel"].create(
{
"name": "Main Channel",
"channel_type": "channel",
"channel_member_ids": [
(0, 0, {"partner_id": cls.ai_user.partner_id.id}),
(0, 0, {"partner_id": cls.user.partner_id.id}),
(0, 0, {"partner_id": cls.env.user.partner_id.id}),
],
}
)
def test_user_status(self):
self.assertEqual("online", self.ai_user.partner_id.im_status)
self.assertEqual("offline", self.user.partner_id.im_status)
self.assertEqual("online", self.ai_user.im_status)
self.assertEqual("offline", self.user.im_status)
def test_chat(self):
"""Answer is direct in this case"""
self.assertFalse(
self.env["mail.message"].search(
[("res_id", "=", self.chat.id), ("model", "=", "mail.channel")]
),
)
with mock.patch("requests.post") as mock_post:
mock_post.return_value = mock.Mock(
status_code=200, json=lambda: {"body": "My message"}
)
self.chat.with_user(self.user.id).message_post(
body="Test message",
)
mock_post.assert_called_once()
self.assertEqual(
2,
self.env["mail.message"].search_count(
[("res_id", "=", self.chat.id), ("model", "=", "mail.channel")]
),
)
def test_channel_not_called(self):
"""No AI bridge should be called when the user is not callend in the channel"""
self.assertFalse(
self.env["mail.message"].search(
[("res_id", "=", self.channel.id), ("model", "=", "mail.channel")]
),
)
with mock.patch("requests.post") as mock_post:
mock_post.return_value = mock.Mock(
status_code=200, json=lambda: {"body": "My message"}
)
self.channel.with_user(self.user.id).message_post(
body="Test message",
)
mock_post.assert_not_called()
self.assertEqual(
1,
self.env["mail.message"].search_count(
[("res_id", "=", self.channel.id), ("model", "=", "mail.channel")]
),
)
def test_channel_called(self):
"""Test that AI answers only if they are called in channels"""
self.assertFalse(
self.env["mail.message"].search(
[("res_id", "=", self.channel.id), ("model", "=", "mail.channel")]
),
)
with mock.patch("requests.post") as mock_post:
mock_post.return_value = mock.Mock(
status_code=200, json=lambda: {"body": "My message"}
)
self.channel.with_user(self.user.id).message_post(
body="Test message",
partner_ids=[self.ai_user.partner_id.id],
)
mock_post.assert_called_once()
self.assertEqual(
2,
self.env["mail.message"].search_count(
[("res_id", "=", self.channel.id), ("model", "=", "mail.channel")]
),
)
def test_channel_multiple_calls(self):
"""Test that AI answers might be from multiple users in the channel at the same time"""
self.assertFalse(
self.env["mail.message"].search(
[("res_id", "=", self.channel.id), ("model", "=", "mail.channel")]
),
)
self.user.ai_bridge_id = self.bridge
with mock.patch("requests.post") as mock_post:
mock_post.return_value = mock.Mock(
status_code=200, json=lambda: {"body": "My message"}
)
self.channel.message_post(
body="Test message",
partner_ids=[self.ai_user.partner_id.id, self.user.partner_id.id],
)
mock_post.assert_called()
self.assertEqual(
3,
self.env["mail.message"].search_count(
[("res_id", "=", self.channel.id), ("model", "=", "mail.channel")]
),
)
def test_chat_ai_no_answer(self):
"""Test that AI does not answer to AI messages"""
self.assertFalse(
self.env["mail.message"].search(
[("res_id", "=", self.channel.id), ("model", "=", "mail.channel")]
),
)
self.user.ai_bridge_id = self.bridge
with mock.patch("requests.post") as mock_post:
mock_post.return_value = mock.Mock(
status_code=200, json=lambda: {"body": "My message"}
)
self.channel.with_user(self.user.id).message_post(
body="Test message",
partner_ids=[self.ai_user.partner_id.id],
)
mock_post.assert_not_called()
self.assertEqual(
1,
self.env["mail.message"].search_count(
[("res_id", "=", self.channel.id), ("model", "=", "mail.channel")]
),
)

View file

@ -0,0 +1,15 @@
<?xml version="1.0" encoding="utf-8" ?>
<!-- Copyright 2025 Dixmit
License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). -->
<odoo>
<!--
<record model="ir.ui.view" id="ai_bridge_form_view">
<field name="model">ai.bridge</field>
<field name="inherit_id" ref="TODO othermodule.form_view"/>
<field name="arch" type="xml">
</field>
</record>
-->
</odoo>

View file

@ -0,0 +1,18 @@
<?xml version="1.0" encoding="utf-8" ?>
<!-- Copyright 2025 Dixmit
License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). -->
<odoo>
<record model="ir.ui.view" id="res_users_form_view">
<field name="model">res.users</field>
<field name="inherit_id" ref="base.view_users_form" />
<field name="arch" type="xml">
<field name="signature" position="before">
<field name="ai_bridge_id" />
</field>
</field>
</record>
</odoo>

View file

@ -0,0 +1,32 @@
# Architecture
```mermaid
flowchart TD
U[Users] -->|HTTP| V[Views and QWeb Templates]
V --> C[Controllers]
V --> W[Wizards Transient Models]
C --> M[Models and ORM]
W --> M
M --> R[Reports]
DX[Data XML] --> M
S[Security ACLs and Groups] -. enforces .-> M
subgraph Ai_oca_bridge_chatter Module - ai_oca_bridge_chatter
direction LR
M:::layer
W:::layer
C:::layer
V:::layer
R:::layer
S:::layer
DX:::layer
end
classDef layer fill:#eef8ff,stroke:#6ea8fe,stroke-width:1px
```
Notes
- Views include tree/form/kanban templates and report templates.
- Controllers provide website/portal routes when present.
- Wizards are UI flows implemented with `models.TransientModel`.
- Data XML loads data/demo records; Security defines groups and access.

View file

@ -0,0 +1,3 @@
# Configuration
Refer to Odoo settings for ai_oca_bridge_chatter. Configure related models, access rights, and options as needed.

View file

@ -0,0 +1,3 @@
# Controllers
This module does not define custom HTTP controllers.

View file

@ -0,0 +1,5 @@
# Dependencies
This addon depends on:
- [ai_oca_bridge](../../odoo-bringout-oca-ai-ai_oca_bridge)

View file

@ -0,0 +1,4 @@
# FAQ
- Q: Which Odoo version? A: 16.0 (OCA/OCB packaged).
- Q: How to enable? A: Start server with --addon ai_oca_bridge_chatter or install in UI.

View file

@ -0,0 +1,7 @@
# Install
```bash
pip install odoo-bringout-oca-ai-ai_oca_bridge_chatter"
# or
uv pip install odoo-bringout-oca-ai-ai_oca_bridge_chatter"
```

View file

@ -0,0 +1,16 @@
# Models
Detected core models and extensions in ai_oca_bridge_chatter.
```mermaid
classDiagram
class ai_bridge
class ai_bridge_execution
class mail_channel
class res_partner
class res_users
```
Notes
- Classes show model technical names; fields omitted for brevity.
- Items listed under _inherit are extensions of existing models.

View file

@ -0,0 +1,6 @@
# Overview
Packaged Odoo addon: ai_oca_bridge_chatter. Provides features documented in upstream Odoo 16 under this addon.
- Source: OCA/OCB 16.0, addon ai_oca_bridge_chatter
- License: LGPL-3

View file

@ -0,0 +1,3 @@
# Reports
This module does not define custom reports.

View file

@ -0,0 +1,8 @@
# Security
This module does not define custom security rules or access controls beyond Odoo defaults.
Default Odoo security applies:
- Base user access through standard groups
- Model access inherited from dependencies
- No custom row-level security rules

View file

@ -0,0 +1,5 @@
# Troubleshooting
- Ensure Python and Odoo environment matches repo guidance.
- Check database connectivity and logs if startup fails.
- Validate that dependent addons listed in DEPENDENCIES.md are installed.

View file

@ -0,0 +1,7 @@
# Usage
Start Odoo including this addon (from repo root):
```bash
python3 scripts/nix_odoo_web_server.py --db-name mydb --addon ai_oca_bridge_chatter
```

View file

@ -0,0 +1,3 @@
# Wizards
This module does not include UI wizards.

View file

@ -0,0 +1,42 @@
[project]
name = "odoo-bringout-oca-ai-ai_oca_bridge_chatter"
version = "16.0.0"
description = "Ai Oca Bridge Chatter - Integrate a Bridge with a user that will use it on chatter"
authors = [
{ name = "Ernad Husremovic", email = "hernad@bring.out.ba" }
]
dependencies = [
"odoo-bringout-oca-ai-ai_oca_bridge>=16.0.0",
"requests>=2.25.1"
]
readme = "README.md"
requires-python = ">= 3.11"
classifiers = [
"Development Status :: 5 - Production/Stable",
"Intended Audience :: Developers",
"License :: OSI Approved :: GNU Lesser General Public License v3 (LGPLv3)",
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3.12",
"Topic :: Office/Business",
]
[project.urls]
homepage = "https://github.com/bringout/0"
repository = "https://github.com/bringout/0"
[build-system]
requires = ["hatchling"]
build-backend = "hatchling.build"
[tool.hatch.metadata]
allow-direct-references = true
[tool.hatch.build.targets.wheel]
packages = ["ai_oca_bridge_chatter"]
[tool.rye]
managed = true
dev-dependencies = [
"pytest>=8.4.1",
]