bemade-addons/openwebui_integration/models/openwebui_bot_mixin.py
2025-02-18 08:55:21 -05:00

157 lines
6.2 KiB
Python

# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
"""OpenWebUI Bot Mixin Module
This module provides a mixin class that can be used to add OpenWebUI bot
functionality to any Odoo model. It handles the integration between Odoo
models and OpenWebUI's AI models, providing:
- Bot context management
- Message generation and processing
- Response handling
- Model-specific customization points
Example usage:
class MyModel(models.Model, OpenWebUIBotMixin):
_name = 'my.model'
def _generate_bot_message(self, record, values, command=None):
return f"Process this: {record.name}"
def _process_bot_response(self, values, response):
values['processed_text'] = response
return values
"""
import json
import logging
import re
import time
import requests
from odoo import models, _
from odoo.exceptions import UserError
_logger = logging.getLogger(__name__)
class OpenWebUIBotMixin(models.AbstractModel):
"""Mixin to add OpenWebUI bot functionality to any model.
This mixin provides methods to:
- Get bot parameters and context
- Send messages to models
- Handle responses
"""
_name = 'openwebui.bot.mixin'
_description = 'OpenWebUI Bot Mixin'
def _get_model(self, model):
"""Gets the model to use"""
return model
def _apply_logic(self, record, values, command=None):
"""Applies the model logic to a record."""
model = values.get('bot')
if not model:
return values
# Get the model
model = self._get_model(model)
# Generate the message for the model
message = self._generate_bot_message(record, values, command)
# Paramètres de retry
max_retries = 3
base_delay = 2 # délai initial en secondes
# Obtenir le timeout de la configuration de la compagnie
company = self.env.company
timeout = company.openwebui_timeout
last_error = None
for attempt in range(max_retries):
try:
# Send the message to the model
_logger.info('Attempt %d/%d: Sending message to model (timeout=%ds)',
attempt + 1, max_retries, timeout)
response = model.send_message(message=message, timeout=timeout)
# Si la réponse contient une erreur de connexion, on la traite comme une exception
if isinstance(response, str) and "Connection error" in response:
raise ConnectionError(response)
_logger.info('Received response from model: %r', response)
if not response:
raise ValueError("Empty response from model")
if not isinstance(response, str):
raise ValueError(f"Invalid response type: {type(response)}")
# Clean up the response
# Supprimer les blocs de code markdown
response = re.sub(r'```json\n|\n```', '', response.strip())
_logger.debug('Response after markdown cleanup: %r', response)
try:
# Essayer de parser directement la réponse nettoyée
parsed_response = json.loads(response)
clean_response = response
except json.JSONDecodeError as e:
_logger.warning('Failed to parse response directly: %s', e)
# Si échec, chercher une liste JSON dans la réponse
start = response.find('[')
end = response.rfind(']')
if start == -1 or end == -1:
_logger.error('No JSON list markers found in response: %r', response)
raise ValueError("No JSON list found in response")
clean_response = response[start:end + 1]
_logger.debug('Extracted JSON list: %r', clean_response)
try:
parsed_response = json.loads(clean_response)
except json.JSONDecodeError as e:
_logger.error('Failed to parse extracted JSON: %s', e)
raise ValueError("Invalid JSON list in response")
if not isinstance(parsed_response, list):
_logger.error('Response is not a list: %r', parsed_response)
raise ValueError("Response must be a JSON list")
_logger.info('Successfully parsed response as list with %d items', len(parsed_response))
# Si on arrive ici, tout s'est bien passé
# Update the values with the cleaned response
values = self._process_bot_response(values, clean_response)
return values
except (ConnectionError, TimeoutError) as e:
last_error = e
if attempt < max_retries - 1: # Ne pas attendre après la dernière tentative
delay = base_delay * (2 ** attempt) # Délai exponentiel: 2s, 4s, 8s
_logger.warning('Connection error on attempt %d: %s. Retrying in %d seconds...',
attempt + 1, str(e), delay)
time.sleep(delay)
else:
_logger.error('All %d connection attempts failed. Last error: %s', max_retries, str(e))
except Exception as e:
# Pour les autres erreurs, on ne réessaie pas
_logger.error('Non-connection error occurred: %s', str(e))
raise e
# Si on arrive ici, toutes les tentatives ont échoué
raise last_error
def _generate_bot_message(self, record, values, command=None):
"""Generates the message to send to the bot.
To be overridden in child classes."""
return ''
def _process_bot_response(self, values, response):
"""Handles the bot response.
To be overridden in child classes."""
return values