bemade-addons/ollama_ai_integration/models/ollama_provider_mixin.py
Benoît Vézina d295f38355 next try
2025-02-19 15:12:15 -05:00

212 lines
7.5 KiB
Python

from odoo import models, fields, api, _
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'
_description = 'Ollama Provider Configuration Mixin'
_inherit = ['ai.generation.params']
# Provider Configuration
provider_type = fields.Selection(
selection=[('ollama', 'Ollama')],
string='Provider Type',
required=True,
default='ollama',
help='Type of AI provider - Must be Ollama for this configuration')
# 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(
string='Context Length',
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)
# 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(
string='Top K',
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)
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(
string='Min P',
help='Sets a minimum probability threshold for token selection. Range: [0.0 - 1.0].',
default=0.05,
digits=(3, 2))
repeat_penalty = fields.Float(
string='Repeat Penalty',
help='Penalty for repeating tokens. Range: [1.0 - 2.0]. Higher values make repetition less likely.',
default=1.1,
digits=(3, 2))
repeat_last_n = fields.Integer(
string='Repeat Last N',
help='Sets the context window for repeat penalty. Range: [0 - 4096]. Default is 64, 0 disables.',
default=64)
# Advanced Generation Parameters
seed = fields.Integer(
string='Random Seed',
help='Sets the random seed for generation. Range: [0 - 2147483647]. Use 0 for random.',
default=0)
num_gpu = fields.Integer(
string='Number of GPUs',
help='Number of GPUs to use for generation. Range: [0 - 8]. 0 means CPU only.',
default=1)
num_thread = fields.Integer(
string='Number of Threads',
help='Number of CPU threads to use for generation. Range: [1 - 32].',
default=8)
mirostat = fields.Selection([
('0', 'Disabled'),
('1', 'Mirostat'),
('2', 'Mirostat 2.0')],
string='Mirostat Mode',
help='Enable Mirostat sampling for controlling perplexity',
default='0')
mirostat_tau = fields.Float(
string='Mirostat Tau',
help='Mirostat target entropy. Range: [0.0 - 10.0].',
default=5.0,
digits=(3, 2))
mirostat_eta = fields.Float(
string='Mirostat Eta',
help='Mirostat learning rate. Range: [0.0 - 1.0].',
default=0.1,
digits=(3, 2))
# Ollama-specific Response Control
tfs_z = fields.Float(
string='Tail Free Sampling Z',
help='Tail free sampling parameter. Range: [0.0 - 2.0]. Higher value = more focused.',
default=1.0,
digits=(3, 2))
# System Settings
num_batch = fields.Integer(
string='Batch Size',
help='Number of prompts to batch together',
default=8)
num_keep = fields.Integer(
string='Keep Last N Tokens',
help='Number of tokens to keep from initial prompt',
default=0)
skip_special_tokens = fields.Boolean(
string='Skip Special Tokens',
help='Skip special tokens in generation',
default=True)
@api.model
def default_get(self, fields_list):
defaults = super().default_get(fields_list)
if 'provider_type' in fields_list:
defaults['provider_type'] = 'ollama'
if 'host' in fields_list and not defaults.get('host'):
defaults['host'] = 'http://localhost:11434'
return defaults
def _get_provider_options(self):
"""Get Ollama-specific options for API calls."""
self.ensure_one()
options = {
'temperature': self.temperature,
'num_ctx': self.num_ctx,
'num_predict': self.num_predict,
'top_k': self.top_k,
'top_p': self.top_p,
'min_p': self.min_p,
'repeat_penalty': self.repeat_penalty,
'repeat_last_n': self.repeat_last_n,
'seed': self.seed,
'num_gpu': self.num_gpu,
'num_thread': self.num_thread,
'mirostat': int(self.mirostat),
'mirostat_tau': self.mirostat_tau,
'mirostat_eta': self.mirostat_eta,
'num_batch': self.num_batch,
'num_keep': self.num_keep,
'tfs_z': self.tfs_z,
'skip_special_tokens': self.skip_special_tokens,
}
if self.stop_sequences:
options['stop'] = [
seq.strip()
for seq in self.stop_sequences.split(',')
if seq.strip()
]
return options