Files
enterprise-addons/ai/tests/test_ai_agent.py
T

152 lines
5.8 KiB
Python

# Part of Odoo. See LICENSE file for full copyright and licensing details.
from unittest.mock import patch
from odoo import Command
from odoo.tests import TransactionCase, tagged
@tagged("post_install", "-at_install")
class TestAIAgent(TransactionCase):
def test_close_chat_only_works_with_ai_channel(self):
partner = self.env["res.partner"].create({
"name": "Odoo AI"
})
agent = self.env["ai.agent"].create({
"name": "Odoo AI",
"partner_id": partner.id
})
ai_chat_channel = agent._get_or_create_ai_chat()
regular_channel = self.env["discuss.channel"].create({
"channel_member_ids": [
Command.create(
{
"partner_id": self.env.user.partner_id.id,
}
),
],
"channel_type": "chat",
"name": "Non AI chat"
})
agent.close_chat(ai_chat_channel.id)
agent.close_chat(regular_channel.id)
self.assertFalse(ai_chat_channel.exists(), "Channel of type 'ai_chat' should be deleted when closed.")
self.assertTrue(regular_channel.exists(), "Only channels in ['ai_chat', 'ai_composer'] should be deleted on close.")
def test_generate_response_no_duplicate_user_prompt(self):
"""Test that user prompts are not duplicated when calling _generate_response"""
agent = self.env["ai.agent"].create({
"name": "Test AI Agent",
"llm_model": "gpt-4",
})
channel = agent._get_or_create_ai_chat()
# First 2 messages to simulate first question and response
channel.message_post(
body="first question",
author_id=self.env.user.partner_id.id,
message_type='comment',
)
channel.message_post(
body="Sure, I'm here to help. What's your first question?",
author_id=agent.partner_id.id,
message_type='comment',
)
# Post a new message and followed by an explicit generate response call.
# In the generate response, we check that the user prompt is not duplicated.
channel.message_post(
body="second question",
author_id=self.env.user.partner_id.id,
message_type='comment',
)
with patch('odoo.addons.ai.utils.llm_api_service.LLMApiService._request') as mock_request, \
patch('odoo.addons.ai.utils.llm_api_service.LLMApiService._get_api_token') as mock_token:
mock_token.return_value = 'test-api-key'
mock_request.return_value = {
'output': [{
'type': 'message',
'content': [{
'type': 'text',
'text': 'Response to second question',
}],
}],
}
agent._generate_response(prompt="second question")
mock_request.assert_called_once()
call_kwargs = mock_request.call_args.kwargs
messages = call_kwargs['body']['input']
user_messages = [msg for msg in messages if msg.get('role') == 'user']
second_question_count = sum(1 for msg in user_messages if msg.get('content') == 'second question')
self.assertEqual(second_question_count, 1, "User prompt 'second question' should appear only once in messages")
def test_get_or_create_ai_chat_returns_single_channel(self):
"""Ensure _get_or_create_ai_chat always returns a single channel even if multiple exist."""
agent = self.env["ai.agent"].create({
"name": "Test Agent",
})
# create the first AI chat channel
agent._get_or_create_ai_chat()
# manually create a second AI chat channel for the same agent
self.env["discuss.channel"].create({
"channel_member_ids": [
Command.create({"partner_id": self.env.user.partner_id.id}),
],
"channel_type": "ai_chat",
"ai_agent_id": agent.id,
"name": "Extra AI chat",
})
# call again: should return a single record
channel = agent._get_or_create_ai_chat()
self.assertEqual(len(channel), 1, "Should always return exactly one AI chat channel")
def test_action_ask_ai_always_opens_a_new_channel(self):
"""Ensure action_ask_ai always opens a new ai chat channel."""
agent = self.env["ai.agent"].create({"name": "Odoo AI"})
action_1 = agent.action_ask_ai("Hello, AI!")
action_2 = agent.action_ask_ai("Hello, AI, again!")
self.assertLess(
action_1["params"]["channelId"],
action_2["params"]["channelId"],
"Each call to 'action_ask_ai' should open a new ai chat channel."
)
def test_get_llm_response_with_sources(self):
"""Responses include source links when the LLM returns attachment ids."""
agent = self.env["ai.agent"].create({
"name": "Odoo Agent",
})
attachment = self.env["ir.attachment"].create({
"name": "Doc 1",
"raw": b"content",
"mimetype": "text/plain",
})
self.env["ai.agent.source"].create({
"name": "Doc 1",
"agent_id": agent.id,
"attachment_id": attachment.id,
"type": "binary",
"status": "indexed",
"is_active": True,
})
message = f"Here is your answer [SOURCE:{attachment.id}]"
llm_response = agent._get_llm_response_with_sources([message])
self.assertEqual(len(llm_response), 1)
self.assertNotIn("[SOURCE", llm_response[0])
self.assertIn("href=\"%s/web/content/%s\"" % (agent.get_base_url(), attachment.id), llm_response[0])
self.assertIn("[1]", llm_response[0])