next try
This commit is contained in:
parent
0676cf1e7f
commit
d295f38355
20 changed files with 616 additions and 243 deletions
|
|
@ -5,7 +5,7 @@
|
||||||
'summary': 'Base module for AI integration',
|
'summary': 'Base module for AI integration',
|
||||||
'description': """
|
'description': """
|
||||||
AI Integration Base
|
AI Integration Base
|
||||||
==================
|
===================
|
||||||
This module provides the base framework for integrating various AI providers
|
This module provides the base framework for integrating various AI providers
|
||||||
into Odoo. It includes:
|
into Odoo. It includes:
|
||||||
|
|
||||||
|
|
@ -18,6 +18,7 @@ into Odoo. It includes:
|
||||||
'depends': [
|
'depends': [
|
||||||
'base',
|
'base',
|
||||||
'web',
|
'web',
|
||||||
|
'mail',
|
||||||
],
|
],
|
||||||
'data': [
|
'data': [
|
||||||
'security/ai_security.xml',
|
'security/ai_security.xml',
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
from .mixins import ai_mixin
|
from .mixins.ai_base_mixin import AIBaseMixin
|
||||||
from . import ai_generation_params
|
from . import ai_generation_params
|
||||||
from . import ai_model
|
from . import ai_model
|
||||||
from . import ai_provider_interface
|
from . import ai_provider_interface
|
||||||
|
|
|
||||||
|
|
@ -8,29 +8,35 @@ class AIGenerationParams(models.AbstractModel):
|
||||||
temperature = fields.Float(
|
temperature = fields.Float(
|
||||||
string='Temperature',
|
string='Temperature',
|
||||||
help='Controls randomness in generation. Higher values make output more random, lower values more deterministic.',
|
help='Controls randomness in generation. Higher values make output more random, lower values more deterministic.',
|
||||||
default=0.7)
|
default=0.7
|
||||||
|
)
|
||||||
|
|
||||||
repeat_penalty = fields.Float(
|
repeat_penalty = fields.Float(
|
||||||
string='Repeat Penalty',
|
string='Repeat Penalty',
|
||||||
help='Penalty for repeating tokens. Higher values make repetition less likely.',
|
help='Penalty for repeating tokens. Higher values make repetition less likely.',
|
||||||
default=1.1)
|
default=1.1
|
||||||
|
)
|
||||||
|
|
||||||
max_tokens = fields.Integer(
|
max_tokens = fields.Integer(
|
||||||
string='Max Tokens',
|
string='Max Tokens',
|
||||||
help='Maximum number of tokens to generate.',
|
help='Maximum number of tokens to generate.',
|
||||||
default=2048)
|
default=2048
|
||||||
|
)
|
||||||
|
|
||||||
stop_sequences = fields.Char(
|
stop_sequences = fields.Char(
|
||||||
string='Stop Sequences',
|
string='Stop Sequences',
|
||||||
help='Comma-separated list of sequences where generation should stop.',
|
help='Comma-separated list of sequences where generation should stop.',
|
||||||
default='')
|
default=''
|
||||||
|
)
|
||||||
|
|
||||||
frequency_penalty = fields.Float(
|
frequency_penalty = fields.Float(
|
||||||
string='Frequency Penalty',
|
string='Frequency Penalty',
|
||||||
help='Penalty for using frequent tokens. Higher values encourage using less frequent tokens.',
|
help='Penalty for using frequent tokens. Higher values encourage using less frequent tokens.',
|
||||||
default=0.0)
|
default=0.0
|
||||||
|
)
|
||||||
|
|
||||||
presence_penalty = fields.Float(
|
presence_penalty = fields.Float(
|
||||||
string='Presence Penalty',
|
string='Presence Penalty',
|
||||||
help='Penalty for using tokens already in the text. Higher values encourage using new tokens.',
|
help='Penalty for using tokens already in the text. Higher values encourage using new tokens.',
|
||||||
default=0.0)
|
default=0.0
|
||||||
|
)
|
||||||
|
|
|
||||||
|
|
@ -54,9 +54,9 @@ class AIModel(models.Model):
|
||||||
)
|
)
|
||||||
|
|
||||||
is_active = fields.Boolean(
|
is_active = fields.Boolean(
|
||||||
string='Active',
|
string='Model Active',
|
||||||
default=True,
|
default=True,
|
||||||
help='Whether this model is active'
|
help='Whether this model is currently active and available for use'
|
||||||
)
|
)
|
||||||
|
|
||||||
context_window = fields.Integer(
|
context_window = fields.Integer(
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
from odoo import models, fields, api, _
|
from odoo import models, fields, api, _
|
||||||
|
from odoo.addons.mail.models.mail_thread import MailThread
|
||||||
from odoo.exceptions import UserError
|
from odoo.exceptions import UserError
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -8,7 +9,7 @@ class AIProviderInstance(models.Model):
|
||||||
_description = 'AI Provider Instance'
|
_description = 'AI Provider Instance'
|
||||||
_order = 'name'
|
_order = 'name'
|
||||||
_check_company = False # Disable automatic company checks
|
_check_company = False # Disable automatic company checks
|
||||||
_inherit = ['ai.generation.params']
|
_inherit = ['mail.thread', 'ai.base.mixin']
|
||||||
|
|
||||||
active = fields.Boolean(
|
active = fields.Boolean(
|
||||||
string='Active',
|
string='Active',
|
||||||
|
|
|
||||||
90
ai_integration/models/doc_models.md
Normal file
90
ai_integration/models/doc_models.md
Normal file
|
|
@ -0,0 +1,90 @@
|
||||||
|
# Documentation des Modèles AI Integration
|
||||||
|
|
||||||
|
## Vue d'ensemble
|
||||||
|
|
||||||
|
Le module AI Integration fournit une infrastructure flexible pour intégrer différents fournisseurs d'IA dans Odoo. Il est conçu pour être extensible et permettre l'ajout facile de nouveaux fournisseurs.
|
||||||
|
|
||||||
|
## Modèles Principaux
|
||||||
|
|
||||||
|
### 1. AI Provider (`ai.provider`)
|
||||||
|
- **Description**: Configuration de base des fournisseurs d'IA
|
||||||
|
- **Champs principaux**:
|
||||||
|
- `name`: Nom du fournisseur
|
||||||
|
- `code`: Code technique unique
|
||||||
|
- `description`: Description détaillée
|
||||||
|
- `default_host`: Hôte par défaut
|
||||||
|
- `active`: État actif/inactif
|
||||||
|
|
||||||
|
### 2. AI Provider Instance (`ai.provider.instance`)
|
||||||
|
- **Description**: Instance spécifique d'un fournisseur d'IA
|
||||||
|
- **Champs principaux**:
|
||||||
|
- `name`: Nom de l'instance
|
||||||
|
- `provider_id`: Fournisseur associé
|
||||||
|
- `provider_type`: Type de fournisseur
|
||||||
|
- `host`: Adresse de l'hôte
|
||||||
|
- `api_key`: Clé API (si nécessaire)
|
||||||
|
- `active`: État actif/inactif
|
||||||
|
|
||||||
|
### 3. AI Model (`ai.model`)
|
||||||
|
- **Description**: Modèles d'IA disponibles
|
||||||
|
- **Champs principaux**:
|
||||||
|
- `name`: Nom du modèle
|
||||||
|
- `identifier`: Identifiant technique
|
||||||
|
- `provider_instance_id`: Instance du fournisseur
|
||||||
|
- `active`: État actif/inactif
|
||||||
|
|
||||||
|
### 4. AI Model Stats (`ai.model.stats`)
|
||||||
|
- **Description**: Statistiques d'utilisation des modèles
|
||||||
|
- **Champs principaux**:
|
||||||
|
- `model_id`: Modèle associé
|
||||||
|
- `total_tokens`: Nombre total de tokens
|
||||||
|
- `total_requests`: Nombre total de requêtes
|
||||||
|
- `average_latency`: Latence moyenne
|
||||||
|
|
||||||
|
## Mixin de Base
|
||||||
|
|
||||||
|
### AI Base Mixin (`ai.base.mixin`)
|
||||||
|
- **Description**: Mixin unifié pour l'intégration IA et les paramètres de génération
|
||||||
|
- **Champs principaux**:
|
||||||
|
- `temperature`: Contrôle de la créativité (0.0 - 2.0)
|
||||||
|
- `top_p`: Sampling nucleus (0.0 - 1.0)
|
||||||
|
- `max_tokens`: Limite de tokens (1 - 32768)
|
||||||
|
- `stop_sequences`: Séquences d'arrêt
|
||||||
|
- `timeout`: Délai d'attente (1 - 300s)
|
||||||
|
- `retry_count`: Nombre de tentatives (0 - 5)
|
||||||
|
- `stream_response`: Activation du streaming
|
||||||
|
- **Méthodes principales**:
|
||||||
|
- `_get_ai_provider_instance`: Obtenir l'instance du fournisseur
|
||||||
|
- `_get_ai_model`: Obtenir le modèle à utiliser
|
||||||
|
- `send_ai_message`: Envoyer un message à l'IA
|
||||||
|
- `_get_base_generation_params`: Obtenir les paramètres de génération
|
||||||
|
|
||||||
|
## Configuration
|
||||||
|
|
||||||
|
### 1. Res Config Settings
|
||||||
|
- **Description**: Paramètres de configuration globaux
|
||||||
|
- **Champs principaux**:
|
||||||
|
- `default_provider_instance_id`: Instance de fournisseur par défaut
|
||||||
|
- `default_model_id`: Modèle par défaut
|
||||||
|
- `ai_batch_size`: Taille du lot pour le traitement
|
||||||
|
|
||||||
|
### 2. Res Company
|
||||||
|
- **Description**: Extensions des paramètres de société
|
||||||
|
- **Méthodes principales**:
|
||||||
|
- `_get_default_provider_instance`: Obtenir l'instance par défaut
|
||||||
|
|
||||||
|
## Interfaces
|
||||||
|
|
||||||
|
### AI Provider Interface (`ai.provider.interface`)
|
||||||
|
- **Description**: Interface abstraite pour les fournisseurs d'IA
|
||||||
|
- **Méthodes requises**:
|
||||||
|
- `send_message`: Envoyer un message
|
||||||
|
- `get_models`: Obtenir la liste des modèles
|
||||||
|
- `test_connection`: Tester la connexion
|
||||||
|
|
||||||
|
## Notes d'Implémentation
|
||||||
|
|
||||||
|
1. Tous les fournisseurs d'IA doivent implémenter `ai.provider.interface`
|
||||||
|
2. Les instances de fournisseur héritent des paramètres de génération via `ai.generation.params`
|
||||||
|
3. La configuration est hiérarchique : Global > Société > Instance
|
||||||
|
4. Les statistiques sont collectées automatiquement pour chaque modèle
|
||||||
|
|
@ -1,2 +1 @@
|
||||||
from . import ai_mixin
|
from . import ai_base_mixin
|
||||||
from . import ai_generation_params
|
|
||||||
|
|
|
||||||
167
ai_integration/models/mixins/ai_base_mixin.py
Normal file
167
ai_integration/models/mixins/ai_base_mixin.py
Normal file
|
|
@ -0,0 +1,167 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
from typing import List, Dict, Any, Optional
|
||||||
|
from odoo import models, api, fields, _
|
||||||
|
from odoo.exceptions import UserError
|
||||||
|
import logging
|
||||||
|
|
||||||
|
_logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
class AIBaseMixin(models.AbstractModel):
|
||||||
|
"""Base mixin for AI integration providing both provider interaction and generation parameters.
|
||||||
|
|
||||||
|
This mixin combines the functionality of message handling and generation parameters
|
||||||
|
into a single, cohesive interface for AI integration.
|
||||||
|
"""
|
||||||
|
_name = 'ai.base.mixin'
|
||||||
|
_description = 'AI Integration Base Mixin'
|
||||||
|
|
||||||
|
# Basic Generation Parameters
|
||||||
|
temperature = fields.Float(
|
||||||
|
string='Temperature',
|
||||||
|
help='Sampling temperature. Range: [0.0 - 2.0]. Higher values make output more random, '
|
||||||
|
'lower values more deterministic.',
|
||||||
|
default=0.7,
|
||||||
|
digits=(3, 2))
|
||||||
|
|
||||||
|
top_p = fields.Float(
|
||||||
|
string='Top P',
|
||||||
|
help='Nucleus sampling: limits cumulative probability of tokens to sample from. '
|
||||||
|
'Range: [0.0 - 1.0].',
|
||||||
|
default=0.9,
|
||||||
|
digits=(3, 2))
|
||||||
|
|
||||||
|
max_tokens = fields.Integer(
|
||||||
|
string='Max Tokens',
|
||||||
|
help='Maximum number of tokens to generate. Range: [1 - 32768].',
|
||||||
|
default=2048)
|
||||||
|
|
||||||
|
stop_sequences = fields.Char(
|
||||||
|
string='Stop Sequences',
|
||||||
|
help='Comma-separated list of sequences where the model should stop generating')
|
||||||
|
|
||||||
|
# System Settings
|
||||||
|
timeout = fields.Integer(
|
||||||
|
string='Timeout',
|
||||||
|
help='Request timeout in seconds. Range: [1 - 300].',
|
||||||
|
default=30)
|
||||||
|
|
||||||
|
retry_count = fields.Integer(
|
||||||
|
string='Retry Count',
|
||||||
|
help='Number of times to retry failed requests. Range: [0 - 5].',
|
||||||
|
default=3)
|
||||||
|
|
||||||
|
stream_response = fields.Boolean(
|
||||||
|
string='Stream Response',
|
||||||
|
help='Enable response streaming for real-time output.',
|
||||||
|
default=False)
|
||||||
|
|
||||||
|
def _get_base_generation_params(self):
|
||||||
|
"""Get common generation parameters as a dictionary.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
dict: Dictionary containing all generation parameters
|
||||||
|
"""
|
||||||
|
self.ensure_one()
|
||||||
|
return {
|
||||||
|
'temperature': self.temperature,
|
||||||
|
'top_p': self.top_p,
|
||||||
|
'max_tokens': self.max_tokens,
|
||||||
|
'stop_sequences': self.stop_sequences.split(',') if self.stop_sequences else None,
|
||||||
|
'timeout': self.timeout,
|
||||||
|
'retry_count': self.retry_count,
|
||||||
|
'stream_response': self.stream_response,
|
||||||
|
}
|
||||||
|
|
||||||
|
def _get_ai_provider_instance(self, provider_instance_id=None):
|
||||||
|
"""Get the AI provider instance to use.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
provider_instance_id: Optional specific provider instance to use
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
ai.provider.instance: The provider instance to use
|
||||||
|
|
||||||
|
Raises:
|
||||||
|
UserError: If no provider instance is configured or available
|
||||||
|
"""
|
||||||
|
if provider_instance_id:
|
||||||
|
instance = self.env['ai.provider.instance'].browse(provider_instance_id)
|
||||||
|
if not instance.exists():
|
||||||
|
raise UserError(_("Invalid provider instance"))
|
||||||
|
else:
|
||||||
|
provider_id = self.env['ir.config_parameter'].sudo().get_param(
|
||||||
|
'ai_integration.default_provider_instance_id')
|
||||||
|
if not provider_id:
|
||||||
|
raise UserError(_("No default AI provider instance configured"))
|
||||||
|
instance = self.env['ai.provider.instance'].browse(int(provider_id))
|
||||||
|
if not instance.exists():
|
||||||
|
raise UserError(_("Default provider instance not found"))
|
||||||
|
|
||||||
|
if not instance.is_active:
|
||||||
|
raise UserError(_("The selected AI provider instance is not active"))
|
||||||
|
|
||||||
|
return instance
|
||||||
|
|
||||||
|
def _get_ai_model(self, model_id=None, provider_instance=None):
|
||||||
|
"""Get the AI model to use.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
model_id: Optional specific model to use
|
||||||
|
provider_instance: Optional provider instance (to avoid duplicate lookup)
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
ai.model: The model to use
|
||||||
|
|
||||||
|
Raises:
|
||||||
|
UserError: If no model is configured or available
|
||||||
|
"""
|
||||||
|
if not provider_instance:
|
||||||
|
provider_instance = self._get_ai_provider_instance()
|
||||||
|
|
||||||
|
if model_id:
|
||||||
|
model = self.env['ai.model'].browse(model_id)
|
||||||
|
if not model.exists():
|
||||||
|
raise UserError(_("Invalid model"))
|
||||||
|
if model.provider_instance_id != provider_instance:
|
||||||
|
raise UserError(_("Model does not belong to the selected provider instance"))
|
||||||
|
else:
|
||||||
|
model_id = self.env['ir.config_parameter'].sudo().get_param(
|
||||||
|
'ai_integration.default_model_id')
|
||||||
|
if not model_id:
|
||||||
|
raise UserError(_("No default AI model configured"))
|
||||||
|
model = self.env['ai.model'].browse(int(model_id))
|
||||||
|
if not model.exists():
|
||||||
|
raise UserError(_("Default model not found"))
|
||||||
|
|
||||||
|
if not model.is_active:
|
||||||
|
raise UserError(_("The selected AI model is not active"))
|
||||||
|
|
||||||
|
return model
|
||||||
|
|
||||||
|
def send_ai_message(self, message: Dict[str, Any], provider_instance_id: Optional[int] = None,
|
||||||
|
model_id: Optional[int] = None, **kwargs):
|
||||||
|
"""Send a message to an AI provider instance.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
message: The message to send
|
||||||
|
provider_instance_id: Optional specific provider instance to use
|
||||||
|
model_id: Optional specific model to use
|
||||||
|
**kwargs: Additional provider-specific parameters
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
str: The response from the AI provider
|
||||||
|
|
||||||
|
Raises:
|
||||||
|
UserError: If there's an error with the AI provider
|
||||||
|
"""
|
||||||
|
provider_instance = self._get_ai_provider_instance(provider_instance_id)
|
||||||
|
model = self._get_ai_model(model_id, provider_instance)
|
||||||
|
|
||||||
|
# Merge generation parameters with provider-specific parameters
|
||||||
|
params = {**self._get_base_generation_params(), **kwargs}
|
||||||
|
|
||||||
|
try:
|
||||||
|
return provider_instance.send_message(message, model=model, **params)
|
||||||
|
except Exception as e:
|
||||||
|
_logger.error("Error sending message to AI provider: %s", str(e))
|
||||||
|
raise UserError(_("Failed to send message to AI provider: %s") % str(e))
|
||||||
|
|
@ -1,64 +0,0 @@
|
||||||
# -*- coding: utf-8 -*-
|
|
||||||
from odoo import models, fields, api, _
|
|
||||||
|
|
||||||
class AIGenerationParams(models.AbstractModel):
|
|
||||||
"""Mixin for common AI generation parameters across different providers."""
|
|
||||||
_name = 'ai.generation.params'
|
|
||||||
_description = 'Common AI Generation Parameters'
|
|
||||||
|
|
||||||
# Basic Generation Parameters
|
|
||||||
temperature = fields.Float(
|
|
||||||
string='Temperature',
|
|
||||||
help='Sampling temperature. Range: [0.0 - 2.0]. Higher values make output more random, lower values more deterministic.',
|
|
||||||
default=0.7,
|
|
||||||
digits=(3, 2))
|
|
||||||
|
|
||||||
top_p = fields.Float(
|
|
||||||
string='Top P',
|
|
||||||
help='Nucleus sampling: limits cumulative probability of tokens to sample from. Range: [0.0 - 1.0].',
|
|
||||||
default=0.9,
|
|
||||||
digits=(3, 2))
|
|
||||||
|
|
||||||
max_tokens = fields.Integer(
|
|
||||||
string='Max Tokens',
|
|
||||||
help='Maximum number of tokens to generate. Range: [1 - 32768].',
|
|
||||||
default=2048)
|
|
||||||
|
|
||||||
stop_sequences = fields.Char(
|
|
||||||
string='Stop Sequences',
|
|
||||||
help='Comma-separated list of sequences where the model should stop generating')
|
|
||||||
|
|
||||||
# System Settings
|
|
||||||
timeout = fields.Integer(
|
|
||||||
string='Timeout',
|
|
||||||
help='Request timeout in seconds. Range: [1 - 300].',
|
|
||||||
default=30)
|
|
||||||
|
|
||||||
retry_count = fields.Integer(
|
|
||||||
string='Retry Count',
|
|
||||||
help='Number of times to retry failed requests. Range: [0 - 5].',
|
|
||||||
default=3)
|
|
||||||
|
|
||||||
stream_response = fields.Boolean(
|
|
||||||
string='Stream Response',
|
|
||||||
help='Enable response streaming for real-time output.',
|
|
||||||
default=False)
|
|
||||||
|
|
||||||
def _get_base_generation_params(self):
|
|
||||||
"""Get common generation parameters as a dictionary."""
|
|
||||||
self.ensure_one()
|
|
||||||
|
|
||||||
params = {
|
|
||||||
'temperature': self.temperature,
|
|
||||||
'top_p': self.top_p,
|
|
||||||
'max_tokens': self.max_tokens,
|
|
||||||
'stream': self.stream_response,
|
|
||||||
}
|
|
||||||
|
|
||||||
if self.stop_sequences:
|
|
||||||
params['stop'] = [
|
|
||||||
seq.strip()
|
|
||||||
for seq in self.stop_sequences.split(',')
|
|
||||||
]
|
|
||||||
|
|
||||||
return params
|
|
||||||
|
|
@ -1,150 +0,0 @@
|
||||||
# -*- coding: utf-8 -*-
|
|
||||||
import logging
|
|
||||||
from typing import List, Dict, Any, Optional
|
|
||||||
from odoo import models, api, fields, _
|
|
||||||
from odoo.exceptions import UserError
|
|
||||||
|
|
||||||
_logger = logging.getLogger(__name__)
|
|
||||||
|
|
||||||
|
|
||||||
class AIMixin(models.AbstractModel):
|
|
||||||
_name = 'ai.mixin'
|
|
||||||
_description = 'AI Integration Mixin'
|
|
||||||
|
|
||||||
def _get_ai_provider_instance(self, provider_instance_id=None):
|
|
||||||
"""Get the AI provider instance to use.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
provider_instance_id: Optional specific provider instance to use
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
ai.provider.instance: The provider instance to use
|
|
||||||
|
|
||||||
Raises:
|
|
||||||
UserError: If no provider instance is configured or available
|
|
||||||
"""
|
|
||||||
if provider_instance_id:
|
|
||||||
instance = self.env['ai.provider.instance'].browse(provider_instance_id)
|
|
||||||
if not instance.exists():
|
|
||||||
raise UserError(_("Invalid provider instance"))
|
|
||||||
else:
|
|
||||||
provider_id = self.env['ir.config_parameter'].sudo().get_param('ai_integration.default_provider_instance_id')
|
|
||||||
if not provider_id:
|
|
||||||
raise UserError(_("No default AI provider instance configured"))
|
|
||||||
instance = self.env['ai.provider.instance'].browse(int(provider_id))
|
|
||||||
if not instance.exists():
|
|
||||||
raise UserError(_("Default provider instance not found"))
|
|
||||||
|
|
||||||
if not instance.is_active:
|
|
||||||
raise UserError(_("The selected AI provider instance is not active"))
|
|
||||||
|
|
||||||
return instance
|
|
||||||
|
|
||||||
def _get_ai_model(self, model_id=None, provider_instance=None):
|
|
||||||
"""Get the AI model to use.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
model_id: Optional specific model to use
|
|
||||||
provider_instance: Optional provider instance (to avoid duplicate lookup)
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
ai.model: The model to use
|
|
||||||
|
|
||||||
Raises:
|
|
||||||
UserError: If no model is configured or available
|
|
||||||
"""
|
|
||||||
provider_instance = provider_instance or self._get_ai_provider_instance()
|
|
||||||
|
|
||||||
if model_id:
|
|
||||||
model = self.env['ai.model'].browse(model_id)
|
|
||||||
if not model.exists():
|
|
||||||
raise UserError(_("Invalid AI model"))
|
|
||||||
if model.provider_instance_id != provider_instance:
|
|
||||||
raise UserError(_("The specified model does not belong to the selected provider instance"))
|
|
||||||
else:
|
|
||||||
model_id = self.env['ir.config_parameter'].sudo().get_param('ai_integration.default_model_id')
|
|
||||||
if not model_id:
|
|
||||||
raise UserError(_("No default AI model configured"))
|
|
||||||
model = self.env['ai.model'].browse(int(model_id))
|
|
||||||
if not model.exists():
|
|
||||||
raise UserError(_("Default AI model not found"))
|
|
||||||
|
|
||||||
if not model.is_active:
|
|
||||||
raise UserError(_("The selected AI model is not active"))
|
|
||||||
|
|
||||||
return model
|
|
||||||
|
|
||||||
def send_ai_message(self, message: Dict[str, Any], provider_instance_id: Optional[int] = None,
|
|
||||||
model_id: Optional[int] = None, **kwargs) -> str:
|
|
||||||
"""Send a message to an AI provider instance.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
message: The message to send
|
|
||||||
provider_instance_id: Optional specific provider instance to use
|
|
||||||
model_id: Optional specific model to use
|
|
||||||
**kwargs: Additional provider-specific parameters
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
str: The response from the AI provider
|
|
||||||
|
|
||||||
Raises:
|
|
||||||
UserError: If there's an error with the AI provider
|
|
||||||
"""
|
|
||||||
try:
|
|
||||||
instance = self._get_ai_provider_instance(provider_instance_id)
|
|
||||||
model = self._get_ai_model(model_id, instance)
|
|
||||||
return instance.send_message(message, model, **kwargs)
|
|
||||||
except Exception as e:
|
|
||||||
_logger.error("Error sending AI message: %s", str(e))
|
|
||||||
raise UserError(_("Error communicating with AI provider: %s", str(e)))
|
|
||||||
|
|
||||||
def process_batch_ai(self, items: List[Any], processor_func: callable,
|
|
||||||
provider_instance_id: Optional[int] = None,
|
|
||||||
model_id: Optional[int] = None, **kwargs) -> List[Any]:
|
|
||||||
"""Process a batch of items using AI.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
items: List of items to process
|
|
||||||
processor_func: Function that processes each item and returns AI message
|
|
||||||
provider_instance_id: Optional specific provider instance to use
|
|
||||||
model_id: Optional specific model to use
|
|
||||||
**kwargs: Additional parameters passed to processor_func
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
List[Any]: List of processed results
|
|
||||||
|
|
||||||
Example:
|
|
||||||
def _process_item(item, **kwargs):
|
|
||||||
return {'role': 'user', 'content': f'Analyze: {item.name}'}
|
|
||||||
|
|
||||||
results = self.process_batch_ai(items, _process_item)
|
|
||||||
"""
|
|
||||||
if not items:
|
|
||||||
return []
|
|
||||||
|
|
||||||
company = self.env.company
|
|
||||||
batch_size = company.ai_batch_size or 10
|
|
||||||
results = []
|
|
||||||
|
|
||||||
for i in range(0, len(items), batch_size):
|
|
||||||
batch = items[i:i + batch_size]
|
|
||||||
batch_messages = [processor_func(item, **kwargs) for item in batch]
|
|
||||||
|
|
||||||
for message in batch_messages:
|
|
||||||
result = self.send_ai_message(
|
|
||||||
message,
|
|
||||||
provider_instance_id=provider_instance_id,
|
|
||||||
model_id=model_id
|
|
||||||
)
|
|
||||||
results.append(result)
|
|
||||||
|
|
||||||
return results
|
|
||||||
|
|
||||||
def _prepare_ai_message(self, **kwargs):
|
|
||||||
"""Prepare a message to send to the AI provider.
|
|
||||||
This method should be implemented by models using this mixin.
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
dict: The prepared message
|
|
||||||
"""
|
|
||||||
raise NotImplementedError(_("Method _prepare_ai_message must be implemented by models using AI mixin"))
|
|
||||||
|
|
@ -7,3 +7,5 @@ access_ai_model_stats_user,ai.model.stats.user,model_ai_model_stats,base.group_u
|
||||||
access_ai_model_stats_system,ai.model.stats.system,model_ai_model_stats,base.group_system,1,1,1,1
|
access_ai_model_stats_system,ai.model.stats.system,model_ai_model_stats,base.group_system,1,1,1,1
|
||||||
access_ai_generation_params_user,ai.generation.params.user,model_ai_generation_params,base.group_user,1,0,0,0
|
access_ai_generation_params_user,ai.generation.params.user,model_ai_generation_params,base.group_user,1,0,0,0
|
||||||
access_ai_generation_params_system,ai.generation.params.system,model_ai_generation_params,base.group_system,1,1,1,1
|
access_ai_generation_params_system,ai.generation.params.system,model_ai_generation_params,base.group_system,1,1,1,1
|
||||||
|
access_ai_provider_user,ai.provider.user,model_ai_provider,base.group_user,1,0,0,0
|
||||||
|
access_ai_provider_system,ai.provider.system,model_ai_provider,base.group_system,1,1,1,1
|
||||||
|
|
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
'summary': 'Integration with Ollama AI models',
|
'summary': 'Integration with Ollama AI models',
|
||||||
'description': """
|
'description': """
|
||||||
Ollama Integration
|
Ollama Integration
|
||||||
=================
|
==================
|
||||||
This module provides integration with Ollama, allowing you to use local AI models
|
This module provides integration with Ollama, allowing you to use local AI models
|
||||||
in your Odoo instance. Features include:
|
in your Odoo instance. Features include:
|
||||||
|
|
||||||
|
|
@ -16,10 +16,12 @@ in your Odoo instance. Features include:
|
||||||
""",
|
""",
|
||||||
'author': 'Bemade',
|
'author': 'Bemade',
|
||||||
'website': 'https://www.bemade.org',
|
'website': 'https://www.bemade.org',
|
||||||
'depends': ['ai_integration'],
|
'depends': [
|
||||||
|
'ai_integration'
|
||||||
|
],
|
||||||
'data': [
|
'data': [
|
||||||
'data/ollama_provider.xml',
|
'data/ollama_provider.xml',
|
||||||
'views/ollama_views.xml',
|
# 'views/ollama_views.xml',
|
||||||
'views/ollama_stats_views.xml',
|
'views/ollama_stats_views.xml',
|
||||||
'security/ir.model.access.csv',
|
'security/ir.model.access.csv',
|
||||||
],
|
],
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,26 @@
|
||||||
|
"""Ollama AI Integration Models Package.
|
||||||
|
|
||||||
|
This package contains all the model definitions required for integrating
|
||||||
|
Ollama AI with Odoo's AI framework. The models are loaded in a specific
|
||||||
|
order to handle dependencies correctly.
|
||||||
|
|
||||||
|
Module Structure:
|
||||||
|
1. ollama_provider_mixin - Base configuration and parameter definitions
|
||||||
|
2. ollama_provider - Core Ollama API integration implementation
|
||||||
|
3. ollama_model_stats - Usage statistics and performance tracking
|
||||||
|
4. ai_provider_instance - Instance-specific configuration and management
|
||||||
|
|
||||||
|
Note: The import order is important to avoid circular dependencies.
|
||||||
|
"""
|
||||||
|
|
||||||
|
# Base Configuration
|
||||||
from . import ollama_provider_mixin
|
from . import ollama_provider_mixin
|
||||||
from . import ai_provider_ollama
|
|
||||||
from . import ollama_instance
|
# Core Implementation
|
||||||
|
from . import ollama_provider
|
||||||
|
|
||||||
|
# Statistics and Monitoring
|
||||||
from . import ollama_model_stats
|
from . import ollama_model_stats
|
||||||
|
|
||||||
|
# Instance Management
|
||||||
|
from . import ai_provider_instance
|
||||||
|
|
|
||||||
131
ollama_ai_integration/models/ai_provider_instance.py
Normal file
131
ollama_ai_integration/models/ai_provider_instance.py
Normal file
|
|
@ -0,0 +1,131 @@
|
||||||
|
from odoo import models, fields, api, _
|
||||||
|
|
||||||
|
class AIProviderInstance(models.Model):
|
||||||
|
"""Extends the AI Provider Instance model to support Ollama-specific configuration.
|
||||||
|
|
||||||
|
This model inherits from both ai.provider.instance and ollama.provider.mixin to:
|
||||||
|
1. Add Ollama-specific fields (num_ctx, temperature, etc.)
|
||||||
|
2. Handle field visibility based on provider_type
|
||||||
|
3. Manage field cleanup when switching providers
|
||||||
|
|
||||||
|
Note: This extends the base ai.provider.instance model instead of creating
|
||||||
|
a new one to ensure seamless integration with the core AI framework.
|
||||||
|
"""
|
||||||
|
_name = 'ai.provider.instance'
|
||||||
|
_inherit = ['ollama.provider.mixin', 'mail.thread']
|
||||||
|
_description = 'AI Provider Instance'
|
||||||
|
|
||||||
|
# Basic Fields
|
||||||
|
name = fields.Char(
|
||||||
|
string='Name',
|
||||||
|
required=True,
|
||||||
|
tracking=True,
|
||||||
|
help='Name of this AI provider instance')
|
||||||
|
|
||||||
|
active = fields.Boolean(
|
||||||
|
string='Active',
|
||||||
|
default=True,
|
||||||
|
tracking=True,
|
||||||
|
help='Whether this provider instance is active')
|
||||||
|
|
||||||
|
host = fields.Char(
|
||||||
|
string='Host',
|
||||||
|
required=True,
|
||||||
|
default='http://localhost:11434',
|
||||||
|
tracking=True,
|
||||||
|
help='Ollama server host URL')
|
||||||
|
|
||||||
|
company_id = fields.Many2one(
|
||||||
|
'res.company',
|
||||||
|
string='Company',
|
||||||
|
required=True,
|
||||||
|
default=lambda self: self.env.company,
|
||||||
|
help='Company this provider instance belongs to')
|
||||||
|
|
||||||
|
@api.onchange('provider_type')
|
||||||
|
def _onchange_provider_type(self):
|
||||||
|
"""Automatically clear Ollama-specific fields when switching provider type.
|
||||||
|
|
||||||
|
This ensures that Ollama configuration is only kept when the provider
|
||||||
|
type is 'ollama'. When switching to another provider, all Ollama-specific
|
||||||
|
fields are reset to their default values to avoid confusion.
|
||||||
|
"""
|
||||||
|
if self.provider_type != 'ollama':
|
||||||
|
self.update({
|
||||||
|
'num_ctx': False, # Context length
|
||||||
|
'temperature': False, # Sampling temperature
|
||||||
|
'top_p': False, # Nucleus sampling threshold
|
||||||
|
'top_k': False, # Top-k sampling threshold
|
||||||
|
'repeat_penalty': False, # Penalty for repeated tokens
|
||||||
|
})
|
||||||
|
|
||||||
|
def test_connection(self):
|
||||||
|
"""Test the connection to the Ollama server.
|
||||||
|
|
||||||
|
This method attempts to connect to the Ollama server and verify
|
||||||
|
that it is responding correctly. It will raise a user-friendly
|
||||||
|
error if the connection fails.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
dict: Action to display success message
|
||||||
|
"""
|
||||||
|
self.ensure_one()
|
||||||
|
if self.provider_type != 'ollama':
|
||||||
|
return
|
||||||
|
|
||||||
|
try:
|
||||||
|
# Try to list models as a basic connectivity test
|
||||||
|
self.env['ai.provider.ollama']._get_models(self)
|
||||||
|
return {
|
||||||
|
'type': 'ir.actions.client',
|
||||||
|
'tag': 'display_notification',
|
||||||
|
'params': {
|
||||||
|
'title': _('Success'),
|
||||||
|
'message': _('Successfully connected to Ollama server'),
|
||||||
|
'sticky': False,
|
||||||
|
'type': 'success',
|
||||||
|
}
|
||||||
|
}
|
||||||
|
except Exception as e:
|
||||||
|
raise UserError(_('Connection test failed: %s', str(e)))
|
||||||
|
|
||||||
|
def sync_models(self):
|
||||||
|
"""Synchronize available models from the Ollama server.
|
||||||
|
|
||||||
|
This method fetches the list of available models from the Ollama
|
||||||
|
server and creates or updates the corresponding AI model records
|
||||||
|
in Odoo.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
dict: Action to display success message
|
||||||
|
"""
|
||||||
|
self.ensure_one()
|
||||||
|
if self.provider_type != 'ollama':
|
||||||
|
return
|
||||||
|
|
||||||
|
try:
|
||||||
|
provider = self.env['ai.provider.ollama']
|
||||||
|
models = provider._get_models(self)
|
||||||
|
|
||||||
|
for model_data in models:
|
||||||
|
# Create or update AI model record
|
||||||
|
self.env['ai.model'].create_or_update({
|
||||||
|
'name': model_data['name'],
|
||||||
|
'identifier': model_data['id'],
|
||||||
|
'provider_instance_id': self.id,
|
||||||
|
'model_type': 'text',
|
||||||
|
'active': True,
|
||||||
|
})
|
||||||
|
|
||||||
|
return {
|
||||||
|
'type': 'ir.actions.client',
|
||||||
|
'tag': 'display_notification',
|
||||||
|
'params': {
|
||||||
|
'title': _('Success'),
|
||||||
|
'message': _('Successfully synchronized %d models', len(models)),
|
||||||
|
'sticky': False,
|
||||||
|
'type': 'success',
|
||||||
|
}
|
||||||
|
}
|
||||||
|
except Exception as e:
|
||||||
|
raise UserError(_('Model synchronization failed: %s', str(e)))
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
from odoo import models, fields, api, _
|
from odoo import models, fields, api, _
|
||||||
|
from .ollama_provider_mixin import OllamaProviderMixin
|
||||||
|
|
||||||
class OllamaProvider(models.Model):
|
class OllamaAIProvider(models.Model, OllamaProviderMixin):
|
||||||
_name = 'ai.provider.ollama'
|
_name = 'ai.provider.ollama'
|
||||||
_description = 'Ollama AI Provider'
|
_description = 'Ollama AI Provider'
|
||||||
_inherit = ['ai.provider.interface']
|
_inherit = ['ai.provider.interface']
|
||||||
|
|
@ -2,10 +2,28 @@ from odoo import models, fields, api
|
||||||
from datetime import datetime, timedelta
|
from datetime import datetime, timedelta
|
||||||
|
|
||||||
class OllamaModelStats(models.Model):
|
class OllamaModelStats(models.Model):
|
||||||
|
"""Tracks and stores daily usage statistics for Ollama AI models.
|
||||||
|
|
||||||
|
This model maintains detailed daily statistics for each Ollama model,
|
||||||
|
including request counts, token usage, response times, and error rates.
|
||||||
|
It inherits from ai.model.stats for base statistics functionality.
|
||||||
|
|
||||||
|
Key Features:
|
||||||
|
- Daily usage tracking per model
|
||||||
|
- Performance metrics collection
|
||||||
|
- Error rate monitoring
|
||||||
|
- Version tracking for model updates
|
||||||
|
|
||||||
|
Technical Details:
|
||||||
|
- One stat entry per model per day (enforced by SQL constraint)
|
||||||
|
- Automatic version tracking from Ollama API
|
||||||
|
- Aggregated statistics calculation
|
||||||
|
- Ordered by date for easy historical analysis
|
||||||
|
"""
|
||||||
_name = 'ollama.model.stats'
|
_name = 'ollama.model.stats'
|
||||||
_description = 'Ollama Model Usage Statistics'
|
_description = 'Ollama Model Usage Statistics'
|
||||||
_inherit = ['ai.model.stats']
|
_inherit = ['ai.model.stats']
|
||||||
_order = 'date desc'
|
_order = 'date desc' # Most recent stats first
|
||||||
|
|
||||||
model_id = fields.Many2one('ai.model', string='Model', required=True, ondelete='cascade')
|
model_id = fields.Many2one('ai.model', string='Model', required=True, ondelete='cascade')
|
||||||
date = fields.Date(string='Date', required=True, default=fields.Date.context_today)
|
date = fields.Date(string='Date', required=True, default=fields.Date.context_today)
|
||||||
|
|
@ -20,7 +38,23 @@ class OllamaModelStats(models.Model):
|
||||||
]
|
]
|
||||||
|
|
||||||
def _update_stats(self, model, tokens, response_time, error=False):
|
def _update_stats(self, model, tokens, response_time, error=False):
|
||||||
"""Update statistics for a model."""
|
"""Update daily statistics for a specific model.
|
||||||
|
|
||||||
|
This method handles the creation or update of daily statistics entries.
|
||||||
|
It maintains running averages and cumulative counts for various metrics.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
model (ai.model): The model record being tracked
|
||||||
|
tokens (int): Number of tokens in the current request
|
||||||
|
response_time (float): Response time in milliseconds
|
||||||
|
error (bool): Whether this request resulted in an error
|
||||||
|
|
||||||
|
Technical Notes:
|
||||||
|
- Creates new stat entry if none exists for today
|
||||||
|
- Updates running averages for response time
|
||||||
|
- Fetches and stores model version from Ollama API
|
||||||
|
- Maintains cumulative counts for requests and errors
|
||||||
|
"""
|
||||||
today = fields.Date.context_today(self)
|
today = fields.Date.context_today(self)
|
||||||
stats = self.search([
|
stats = self.search([
|
||||||
('model_id', '=', model.id),
|
('model_id', '=', model.id),
|
||||||
|
|
|
||||||
|
|
@ -1,15 +1,70 @@
|
||||||
import json
|
import json
|
||||||
import logging
|
import logging
|
||||||
import requests
|
import requests
|
||||||
from odoo import models, fields, _
|
from odoo import models, fields, api, _
|
||||||
from odoo.exceptions import UserError
|
from odoo.exceptions import UserError
|
||||||
|
|
||||||
_logger = logging.getLogger(__name__)
|
_logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
class OllamaProvider(models.Model):
|
class OllamaProvider(models.Model):
|
||||||
|
"""Main Ollama AI Provider implementation.
|
||||||
|
|
||||||
|
This model implements the core functionality for interacting with Ollama's API,
|
||||||
|
including model management, text generation, and error handling.
|
||||||
|
|
||||||
|
Key Responsibilities:
|
||||||
|
- Model discovery and validation
|
||||||
|
- API communication and error handling
|
||||||
|
- Request formatting and response parsing
|
||||||
|
- Resource management and cleanup
|
||||||
|
|
||||||
|
Technical Details:
|
||||||
|
- Implements the ai.provider.interface for standardized AI provider integration
|
||||||
|
- Uses Ollama's HTTP API for all operations
|
||||||
|
- Handles both synchronous and asynchronous requests
|
||||||
|
- Provides detailed error messages for troubleshooting
|
||||||
|
"""
|
||||||
_name = 'ai.provider.ollama'
|
_name = 'ai.provider.ollama'
|
||||||
_description = 'Ollama AI Provider'
|
_description = 'Ollama AI Provider'
|
||||||
_inherit = ['ai.provider.interface']
|
_inherit = ['ai.provider.interface']
|
||||||
|
|
||||||
|
@api.model
|
||||||
|
def _get_models(self, instance):
|
||||||
|
"""Get list of available models from Ollama server.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
instance (ai.provider.instance): Provider instance to get models for
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
list: List of model dictionaries with keys:
|
||||||
|
- name: Model name
|
||||||
|
- id: Model identifier
|
||||||
|
- details: Additional model metadata
|
||||||
|
|
||||||
|
Raises:
|
||||||
|
UserError: If unable to connect or retrieve models
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
response = requests.get(f"{instance.host}/api/tags")
|
||||||
|
response.raise_for_status()
|
||||||
|
|
||||||
|
models_data = response.json().get('models', [])
|
||||||
|
return [{
|
||||||
|
'name': model['name'],
|
||||||
|
'id': model['name'],
|
||||||
|
'details': model
|
||||||
|
} for model in models_data]
|
||||||
|
|
||||||
|
except requests.exceptions.RequestException as e:
|
||||||
|
raise UserError(_('Failed to connect to Ollama server: %s', str(e)))
|
||||||
|
except (KeyError, ValueError) as e:
|
||||||
|
raise UserError(_('Invalid response from Ollama server: %s', str(e)))
|
||||||
|
|
||||||
|
# API Configuration
|
||||||
|
timeout = fields.Integer(
|
||||||
|
string='Timeout',
|
||||||
|
default=30,
|
||||||
|
help='API request timeout in seconds')
|
||||||
|
|
||||||
def _get_provider_type(self):
|
def _get_provider_type(self):
|
||||||
return 'ollama'
|
return 'ollama'
|
||||||
|
|
|
||||||
|
|
@ -1,25 +1,96 @@
|
||||||
from odoo import models, fields, api, _
|
from odoo import models, fields, api, _
|
||||||
|
|
||||||
class OllamaProviderMixin(models.AbstractModel):
|
class OllamaProviderMixin(models.AbstractModel):
|
||||||
|
"""Mixin model that provides Ollama-specific configuration parameters.
|
||||||
|
|
||||||
|
This mixin is designed to be inherited by models that need to interact with
|
||||||
|
the Ollama AI provider. It provides all the necessary fields and methods
|
||||||
|
for configuring and interacting with Ollama's API.
|
||||||
|
|
||||||
|
Key Features:
|
||||||
|
- Provider type selection and validation
|
||||||
|
- Context window configuration
|
||||||
|
- Advanced sampling parameters (temperature, top-k, top-p)
|
||||||
|
- Token generation controls
|
||||||
|
|
||||||
|
Technical Details:
|
||||||
|
- Inherits from ai.generation.params for base AI generation parameters
|
||||||
|
- Implements Ollama-specific API parameters
|
||||||
|
- Provides default values optimized for general use cases
|
||||||
|
"""
|
||||||
_name = 'ollama.provider.mixin'
|
_name = 'ollama.provider.mixin'
|
||||||
_description = 'Ollama Provider Configuration Mixin'
|
_description = 'Ollama Provider Configuration Mixin'
|
||||||
_inherit = ['ai.generation.params']
|
_inherit = ['ai.generation.params']
|
||||||
|
|
||||||
|
# Provider Configuration
|
||||||
provider_type = fields.Selection(
|
provider_type = fields.Selection(
|
||||||
selection_add=[('ollama', 'Ollama')],
|
selection=[('ollama', 'Ollama')],
|
||||||
ondelete={'ollama': 'cascade'})
|
string='Provider Type',
|
||||||
|
required=True,
|
||||||
|
default='ollama',
|
||||||
|
help='Type of AI provider - Must be Ollama for this configuration')
|
||||||
|
|
||||||
# Ollama-specific Parameters
|
# Model Parameters
|
||||||
|
model_name = fields.Char(
|
||||||
|
string='Model Name',
|
||||||
|
help='Name of the Ollama model to use (e.g. llama2, mistral, codellama)',
|
||||||
|
required=True,
|
||||||
|
default='llama2')
|
||||||
|
|
||||||
|
# Context Window Configuration
|
||||||
num_ctx = fields.Integer(
|
num_ctx = fields.Integer(
|
||||||
string='Context Length',
|
string='Context Length',
|
||||||
help='Maximum number of tokens to consider for context. Range: [0 - 32768].',
|
help='Maximum number of tokens to consider for context. A larger context window allows '
|
||||||
|
'the model to access more historical information but requires more memory. '
|
||||||
|
'Range: [0 - 32768].',
|
||||||
default=4096)
|
default=4096)
|
||||||
|
|
||||||
# Advanced Sampling Parameters
|
# Generation Parameters
|
||||||
|
temperature = fields.Float(
|
||||||
|
string='Temperature',
|
||||||
|
help='Controls randomness in the output. Higher values make the output more random, '
|
||||||
|
'while lower values make it more focused and deterministic. '
|
||||||
|
'Range: [0.0 - 2.0]',
|
||||||
|
default=0.8)
|
||||||
|
|
||||||
|
top_p = fields.Float(
|
||||||
|
string='Top P',
|
||||||
|
help='Nucleus sampling: only consider the tokens whose cumulative probability exceeds '
|
||||||
|
'this value. Lower values make the output more focused. '
|
||||||
|
'Range: [0.0 - 1.0]',
|
||||||
|
default=0.9)
|
||||||
|
|
||||||
top_k = fields.Integer(
|
top_k = fields.Integer(
|
||||||
string='Top K',
|
string='Top K',
|
||||||
help='Limits the number of tokens to sample from. Range: [1 - 100].',
|
help='Only consider the top K tokens for text generation. Lower values make the '
|
||||||
|
'output more focused. Set to 0 to disable. '
|
||||||
|
'Range: [0 - 100]',
|
||||||
default=40)
|
default=40)
|
||||||
|
|
||||||
|
repeat_penalty = fields.Float(
|
||||||
|
string='Repeat Penalty',
|
||||||
|
help='Penalty for repeating tokens. Higher values make the output less repetitive. '
|
||||||
|
'Range: [0.0 - 2.0]',
|
||||||
|
default=1.1)
|
||||||
|
|
||||||
|
# Advanced Configuration
|
||||||
|
stop_sequences = fields.Char(
|
||||||
|
string='Stop Sequences',
|
||||||
|
help='Comma-separated list of sequences where the model should stop generating further tokens.')
|
||||||
|
|
||||||
|
top_k = fields.Integer(
|
||||||
|
string='Top K',
|
||||||
|
help='Limits the cumulative probability of tokens to sample from. Only the top K '
|
||||||
|
'most likely tokens are considered for sampling at each step. '
|
||||||
|
'Range: [1 - 100].',
|
||||||
|
default=40)
|
||||||
|
|
||||||
|
top_p = fields.Float(
|
||||||
|
string='Top P (Nucleus Sampling)',
|
||||||
|
help='Limits the cumulative probability of tokens to sample from. Only the most likely '
|
||||||
|
'tokens with total probability mass of top_p are considered. '
|
||||||
|
'Range: [0.0 - 1.0].',
|
||||||
|
default=0.9)
|
||||||
|
|
||||||
min_p = fields.Float(
|
min_p = fields.Float(
|
||||||
string='Min P',
|
string='Min P',
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,5 @@
|
||||||
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
|
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
|
||||||
access_ollama_model_stats_user,ollama.model.stats.user,model_ollama_model_stats,base.group_user,1,0,0,0
|
access_ollama_model_stats_user,ollama.model.stats.user,model_ollama_model_stats,base.group_user,1,0,0,0
|
||||||
access_ollama_model_stats_manager,ollama.model.stats.manager,model_ollama_model_stats,base.group_system,1,1,1,1
|
access_ollama_model_stats_manager,ollama.model.stats.manager,model_ollama_model_stats,base.group_system,1,1,1,1
|
||||||
|
access_ai_provider_ollama_user,ai.provider.ollama.user,model_ai_provider_ollama,base.group_user,1,0,0,0
|
||||||
|
access_ai_provider_ollama_manager,ai.provider.ollama.manager,model_ai_provider_ollama,base.group_system,1,1,1,1
|
||||||
|
|
|
||||||
|
|
|
@ -15,6 +15,9 @@
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</xpath>
|
</xpath>
|
||||||
|
<xpath expr="//field[@name='api_key']" position="replace">
|
||||||
|
<field name="api_key" invisible="1"/>
|
||||||
|
</xpath>
|
||||||
<xpath expr="//notebook" position="inside">
|
<xpath expr="//notebook" position="inside">
|
||||||
<page string="Ollama Settings" name="ollama_settings"
|
<page string="Ollama Settings" name="ollama_settings"
|
||||||
invisible="provider_type != 'ollama'">
|
invisible="provider_type != 'ollama'">
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue