From bc7b7f73e511aafe11d0527ffee29507f71cf4b9 Mon Sep 17 00:00:00 2001 From: Marcel Savegnago Date: Wed, 9 Sep 2026 02:32:07 -0300 Subject: [PATCH] [IMP] ai_oca_bridge_chatter: include replied message in payload Discuss Reply only stores parent_id; the quoted body is not copied into the new message. External agents need the parent text so a mentioned bot can evaluate another message without channel history. --- ai_oca_bridge_chatter/README.rst | 10 ++-- ai_oca_bridge_chatter/models/ai_bridge.py | 16 +++++- ai_oca_bridge_chatter/readme/DESCRIPTION.md | 4 ++ .../static/description/index.html | 29 +++++----- ai_oca_bridge_chatter/tests/test_chatter.py | 55 +++++++++++++++++++ 5 files changed, 92 insertions(+), 22 deletions(-) diff --git a/ai_oca_bridge_chatter/README.rst b/ai_oca_bridge_chatter/README.rst index 0759f7fb..e956d462 100644 --- a/ai_oca_bridge_chatter/README.rst +++ b/ai_oca_bridge_chatter/README.rst @@ -1,7 +1,3 @@ -.. 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 ===================== @@ -17,7 +13,7 @@ Ai Oca Bridge Chatter .. |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 +.. |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 @@ -37,6 +33,10 @@ This module allows usage of LLM chatbots inside Odoo. The logic of the chatbot should be defined in an external system like n8n. +When a user replies to a Discuss message and mentions a bot, the chatter +payload includes the quoted parent message so the agent can evaluate it +without receiving the rest of the channel history. + **Table of contents** .. contents:: diff --git a/ai_oca_bridge_chatter/models/ai_bridge.py b/ai_oca_bridge_chatter/models/ai_bridge.py index e55785b6..d24b186c 100644 --- a/ai_oca_bridge_chatter/models/ai_bridge.py +++ b/ai_oca_bridge_chatter/models/ai_bridge.py @@ -19,6 +19,7 @@ def _prepare_payload_chatter(self, record=None, **kwargs): record = self.env["mail.message"].search([], limit=1) if record._name != "mail.message": raise ValueError(_("The record must be a mail.message instance.")) + parent = record.parent_id return { "message": { "res_id": record.res_id, @@ -29,6 +30,19 @@ def _prepare_payload_chatter(self, record=None, **kwargs): "date": record.date.isoformat(), "author_name": record.author_id.name, "attachment_ids": record.attachment_ids.ids, - "parent_id": record.parent_id.id if record.parent_id else False, + "parent_id": parent.id if parent else False, + "parent": self._prepare_payload_chatter_parent(parent), } } + + def _prepare_payload_chatter_parent(self, parent): + """Serialize the replied-to message, if any, for the external agent.""" + if not parent: + return False + return { + "id": parent.id, + "body": parent.body, + "author_id": parent.author_id.id, + "author_name": parent.author_id.name, + "date": parent.date.isoformat() if parent.date else False, + } diff --git a/ai_oca_bridge_chatter/readme/DESCRIPTION.md b/ai_oca_bridge_chatter/readme/DESCRIPTION.md index a2c79fa6..f435b31f 100644 --- a/ai_oca_bridge_chatter/readme/DESCRIPTION.md +++ b/ai_oca_bridge_chatter/readme/DESCRIPTION.md @@ -1,3 +1,7 @@ This module allows usage of LLM chatbots inside Odoo. The logic of the chatbot should be defined in an external system like n8n. + +When a user replies to a Discuss message and mentions a bot, the chatter +payload includes the quoted parent message so the agent can evaluate it +without receiving the rest of the channel history. diff --git a/ai_oca_bridge_chatter/static/description/index.html b/ai_oca_bridge_chatter/static/description/index.html index 1ca42e55..7bdda427 100644 --- a/ai_oca_bridge_chatter/static/description/index.html +++ b/ai_oca_bridge_chatter/static/description/index.html @@ -3,7 +3,7 @@ -README.rst +Ai Oca Bridge Chatter -
+
+

Ai Oca Bridge Chatter

- - -Odoo Community Association - -
-

Ai Oca Bridge Chatter

-

Beta License: AGPL-3 OCA/ai Translate me on Weblate Try me on Runboat

+

Beta License: AGPL-3 OCA/ai Translate me on Weblate Try me on Runboat

This module allows usage of LLM chatbots inside Odoo.

The logic of the chatbot should be defined in an external system like n8n.

+

When a user replies to a Discuss message and mentions a bot, the chatter +payload includes the quoted parent message so the agent can evaluate it +without receiving the rest of the channel history.

Table of contents

    @@ -392,7 +390,7 @@

    Ai Oca Bridge Chatter

-

Configuration

+

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 of @@ -404,7 +402,7 @@

Configuration

livechat without any issues.

-

Bug Tracker

+

Bug Tracker

Bugs are tracked on GitHub 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 @@ -412,15 +410,15 @@

Bug Tracker

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

-

Credits

+

Credits

-

Authors

+

Authors

  • Dixmit
-

Contributors

+

Contributors

-

Maintainers

+

Maintainers

This module is maintained by the OCA.

Odoo Community Association @@ -446,6 +444,5 @@

Maintainers

-
diff --git a/ai_oca_bridge_chatter/tests/test_chatter.py b/ai_oca_bridge_chatter/tests/test_chatter.py index 67b4eda1..ed90cf3c 100644 --- a/ai_oca_bridge_chatter/tests/test_chatter.py +++ b/ai_oca_bridge_chatter/tests/test_chatter.py @@ -182,3 +182,58 @@ def test_chat_ai_no_answer(self): [("res_id", "=", self.channel.id), ("model", "=", "discuss.channel")] ), ) + + def test_prepare_payload_without_parent(self): + """A standalone message exposes parent as False.""" + message = self.channel.with_user(self.user.id).message_post( + body="Standalone message", + ) + payload = self.bridge._prepare_payload_chatter(record=message) + self.assertFalse(payload["message"]["parent_id"]) + self.assertFalse(payload["message"]["parent"]) + + def test_prepare_payload_with_parent(self): + """A reply includes the quoted parent body and author.""" + parent = self.channel.message_post( + body="

Architect proposal

", + author_id=self.ai_user.partner_id.id, + ) + reply = self.channel.with_user(self.user.id).message_post( + body="Please evaluate", + parent_id=parent.id, + ) + payload = self.bridge._prepare_payload_chatter(record=reply) + self.assertEqual(payload["message"]["parent_id"], parent.id) + self.assertEqual(payload["message"]["parent"]["id"], parent.id) + self.assertIn("Architect proposal", payload["message"]["parent"]["body"]) + self.assertEqual( + payload["message"]["parent"]["author_name"], + self.ai_user.partner_id.name, + ) + self.assertEqual( + payload["message"]["parent"]["author_id"], + self.ai_user.partner_id.id, + ) + self.assertTrue(payload["message"]["parent"]["date"]) + + def test_channel_reply_sends_parent(self): + """Mentioning a bot in a reply posts the quoted message to the bridge.""" + parent = self.channel.message_post( + body="

Architect proposal

", + author_id=self.ai_user.partner_id.id, + ) + with mock.patch("requests.post") as mock_post: + mock_post.return_value = mock.Mock( + status_code=200, json=lambda: {"body": "My review"} + ) + self.channel.with_user(self.user.id).message_post( + body="Please evaluate", + parent_id=parent.id, + partner_ids=[self.ai_user.partner_id.id], + ) + mock_post.assert_called_once() + sent = mock_post.call_args.kwargs.get("json") or mock_post.call_args[1].get( + "json" + ) + self.assertIn("Architect proposal", sent["message"]["parent"]["body"]) + self.assertEqual(sent["message"]["parent"]["id"], parent.id)