299 lines
10 KiB
Python
299 lines
10 KiB
Python
# -*- coding: utf-8 -*-
|
|
"""
|
|
This module extends the res.company model to add OpenWebUI configuration.
|
|
It manages OpenWebUI integration settings at the company level,
|
|
including service activation, API keys, and other configuration parameters.
|
|
"""
|
|
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
|
|
|
from odoo import models, fields, api, tools, _
|
|
from odoo.exceptions import UserError
|
|
|
|
|
|
class ResCompany(models.Model):
|
|
_inherit = 'res.company'
|
|
|
|
|
|
|
|
ai_provider = fields.Selection([
|
|
('openwebui', 'OpenWebUI'),
|
|
('ollama', 'Ollama')
|
|
],
|
|
string='AI Provider',
|
|
default='openwebui',
|
|
required=True,
|
|
help="Select the AI provider to use"
|
|
)
|
|
|
|
# Ollama Configuration
|
|
ollama_port = fields.Integer(
|
|
string='Ollama Port',
|
|
default=11434,
|
|
required=True,
|
|
help="Port number for the Ollama server (default: 11434)"
|
|
)
|
|
|
|
# OpenWebUI Configuration
|
|
openwebui_enabled = fields.Boolean(
|
|
string='Enable OpenWebUI',
|
|
default=False,
|
|
help="Enable integration with OpenWebUI"
|
|
)
|
|
|
|
openwebui_api_url = fields.Char(
|
|
string='OpenWebUI API URL',
|
|
help="Base URL of the OpenWebUI API (e.g. http://localhost:8080)"
|
|
)
|
|
|
|
openwebui_api_key = fields.Char(
|
|
string='OpenWebUI API Key',
|
|
help="API key for authentication with OpenWebUI"
|
|
)
|
|
|
|
openwebui_verify_ssl = fields.Boolean(
|
|
string='Verify SSL Certificate',
|
|
default=True,
|
|
help="Verify the SSL certificate when making API calls"
|
|
)
|
|
|
|
openwebui_timeout = fields.Integer(
|
|
string='Timeout (seconds)',
|
|
default=60,
|
|
help="Maximum wait time for API calls"
|
|
)
|
|
|
|
openwebui_context_size = fields.Integer(
|
|
string='Context Window Size',
|
|
default=4096,
|
|
help="Maximum number of tokens in the context window (default: 4096)"
|
|
)
|
|
|
|
openwebui_max_products = fields.Integer(
|
|
string='Maximum Products per Batch',
|
|
default=800,
|
|
help="Maximum number of products that can be processed at once (default: 800)"
|
|
|
|
def test_ollama_connection(self):
|
|
"""Test the connection to Ollama server.
|
|
|
|
Returns:
|
|
dict: A notification action with the test result
|
|
"""
|
|
self.ensure_one()
|
|
|
|
if self.ai_provider != 'ollama':
|
|
raise UserError(_("Please select Ollama as the AI provider first."))
|
|
|
|
try:
|
|
import requests
|
|
# Test connection to Ollama server
|
|
url = f'http://localhost:{self.ollama_port}/api/tags'
|
|
response = requests.get(url, timeout=5)
|
|
response.raise_for_status()
|
|
|
|
# If we get here, the connection was successful
|
|
return {
|
|
'type': 'ir.actions.client',
|
|
'tag': 'display_notification',
|
|
'params': {
|
|
'title': _('Success'),
|
|
'message': _('Successfully connected to Ollama server on port %s', self.ollama_port),
|
|
'sticky': False,
|
|
'type': 'success',
|
|
}
|
|
}
|
|
|
|
except requests.exceptions.ConnectionError:
|
|
raise UserError(_("Could not connect to Ollama server. Please check if Ollama is running and the port number is correct."))
|
|
except requests.exceptions.Timeout:
|
|
raise UserError(_("Connection to Ollama server timed out. Please check your network settings."))
|
|
except requests.exceptions.RequestException as e:
|
|
raise UserError(_("Error connecting to Ollama server: %s", str(e)))
|
|
|
|
openwebui_products_per_request = fields.Integer(
|
|
string='Products per Request',
|
|
default=10,
|
|
help="Number of products to process in a single API request"
|
|
)
|
|
|
|
openwebui_models_ids = fields.One2many(
|
|
comodel_name='openwebui.model',
|
|
inverse_name='company_id',
|
|
string='OpenWebUI Models',
|
|
)
|
|
|
|
openwebui_default_model_id = fields.Many2one(
|
|
comodel_name='openwebui.model',
|
|
string='Default Model',
|
|
domain="[('company_id', '=', id), ('is_active', '=', True)]",
|
|
help="Default OpenWebUI model to use for AI requests"
|
|
)
|
|
|
|
openwebui_context_size = fields.Integer(
|
|
string='Context Window Size',
|
|
default=2048,
|
|
help="Size of the context window in tokens (e.g., 2048, 4096, 8192)"
|
|
)
|
|
|
|
def test_openwebui_connection(self):
|
|
"""Test the connection to OpenWebUI API.
|
|
|
|
This method verifies the connection to OpenWebUI by:
|
|
1. Checking if OpenWebUI integration is enabled
|
|
2. Attempting to connect to the configured API endpoint
|
|
3. Validating the API credentials
|
|
|
|
Returns:
|
|
dict: A notification action with the test results:
|
|
{
|
|
'type': 'ir.actions.client',
|
|
'tag': 'display_notification',
|
|
'params': {
|
|
'title': 'Success',
|
|
'message': str,
|
|
'type': 'success',
|
|
'sticky': False
|
|
}
|
|
}
|
|
|
|
Raises:
|
|
UserError: If any of the following conditions occur:
|
|
- OpenWebUI is not enabled for the company
|
|
- API endpoint is not reachable
|
|
- Invalid API credentials
|
|
- Connection timeout
|
|
"""
|
|
self.ensure_one()
|
|
|
|
if not self.openwebui_enabled:
|
|
raise UserError(_("OpenWebUI is not enabled for this company."))
|
|
|
|
success, result = self.env['openwebui.model'].test_connection()
|
|
if not success:
|
|
raise UserError(_("Connection test failed: %s") % result)
|
|
return {
|
|
'type': 'ir.actions.client',
|
|
'tag': 'display_notification',
|
|
'params': {
|
|
'title': _('Success'),
|
|
'message': _('Connection to OpenWebUI successful!'),
|
|
'sticky': False,
|
|
'type': 'success',
|
|
}
|
|
}
|
|
|
|
def refresh_model_list(self):
|
|
"""Synchronize available AI models with Odoo.
|
|
|
|
This method performs the following operations based on the selected AI provider:
|
|
|
|
For OpenWebUI:
|
|
1. Verifies that OpenWebUI integration is enabled
|
|
2. Connects to the OpenWebUI API to fetch the latest model list
|
|
3. Updates the local database with model information
|
|
|
|
For Ollama:
|
|
1. Connects to the Ollama server
|
|
2. Fetches available models using the Ollama API
|
|
3. Updates the local database with model information
|
|
|
|
Returns:
|
|
dict: A notification action with the sync results
|
|
|
|
Raises:
|
|
UserError: If there are connection or synchronization issues
|
|
"""
|
|
self.ensure_one()
|
|
|
|
if self.ai_provider == 'ollama':
|
|
return self._refresh_ollama_models()
|
|
elif self.ai_provider == 'openwebui':
|
|
if not self.openwebui_enabled:
|
|
raise UserError(_("OpenWebUI is not enabled for this company."))
|
|
return self._refresh_openwebui_models()
|
|
else:
|
|
raise UserError(_("Unknown AI provider: %s", self.ai_provider))
|
|
|
|
def _refresh_ollama_models(self):
|
|
"""Fetch and sync available models from Ollama server."""
|
|
try:
|
|
import requests
|
|
url = f'http://localhost:{self.ollama_port}/api/tags'
|
|
response = requests.get(url, timeout=5)
|
|
response.raise_for_status()
|
|
models_data = response.json()
|
|
|
|
# Get the OpenWebUI model object
|
|
Model = self.env['openwebui.model'].with_context(sync_models=True)
|
|
|
|
# Process each model from Ollama
|
|
for model in models_data.get('models', []):
|
|
name = model.get('name', '')
|
|
if not name:
|
|
continue
|
|
|
|
# Split name and tag if present (format: name:tag)
|
|
identifier = name
|
|
if ':' in name:
|
|
name, tag = name.split(':', 1)
|
|
identifier = f"{name}:{tag}"
|
|
else:
|
|
identifier = f"{name}:latest"
|
|
|
|
# Check if model already exists
|
|
existing_model = Model.search([
|
|
('identifier', '=', identifier),
|
|
('company_id', '=', self.id)
|
|
], limit=1)
|
|
|
|
model_vals = {
|
|
'name': name,
|
|
'identifier': identifier,
|
|
'is_active': True,
|
|
'description': f"Ollama model: {name}",
|
|
'company_id': self.id
|
|
}
|
|
|
|
if existing_model:
|
|
# Update existing model
|
|
existing_model.write(model_vals)
|
|
else:
|
|
# Create new model
|
|
Model.create(model_vals)
|
|
|
|
return {
|
|
'type': 'ir.actions.client',
|
|
'tag': 'display_notification',
|
|
'params': {
|
|
'title': _('Success'),
|
|
'message': _('Successfully synchronized Ollama models'),
|
|
'sticky': False,
|
|
'type': 'success',
|
|
}
|
|
}
|
|
|
|
except requests.exceptions.ConnectionError:
|
|
raise UserError(_("Could not connect to Ollama server. Please check if Ollama is running and the port number is correct."))
|
|
except requests.exceptions.Timeout:
|
|
raise UserError(_("Connection to Ollama server timed out. Please check your network settings."))
|
|
except requests.exceptions.RequestException as e:
|
|
raise UserError(_("Error connecting to Ollama server: %s", str(e)))
|
|
except Exception as e:
|
|
raise UserError(_("Error synchronizing Ollama models: %s", str(e)))
|
|
|
|
def _refresh_openwebui_models(self):
|
|
"""Fetch and sync available models from OpenWebUI."""
|
|
success, result = self.env['openwebui.model'].sync_models()
|
|
if not success:
|
|
raise UserError(_("Failed to refresh models: %s") % result)
|
|
|
|
return {
|
|
'type': 'ir.actions.client',
|
|
'tag': 'display_notification',
|
|
'params': {
|
|
'title': _('Success'),
|
|
'message': _('Successfully synchronized OpenWebUI models'),
|
|
'sticky': False,
|
|
'type': 'success',
|
|
}
|
|
}
|