talking to ollama
This commit is contained in:
parent
e48e6d9aef
commit
2df5651f12
35 changed files with 276 additions and 146 deletions
|
|
@ -24,12 +24,13 @@ into Odoo. It includes:
|
|||
'security/ai_security.xml',
|
||||
'security/ir_rule.xml',
|
||||
'security/ir.model.access.csv',
|
||||
'views/menu.xml',
|
||||
'views/res_company_views.xml',
|
||||
'views/res_config_settings_views.xml',
|
||||
'views/ai_provider_views.xml',
|
||||
'views/ai_provider_instance_views.xml',
|
||||
'views/ai_model_views.xml',
|
||||
'views/ai_model_stats_views.xml',
|
||||
'views/menu.xml',
|
||||
],
|
||||
'installable': True,
|
||||
'application': False,
|
||||
|
|
|
|||
|
|
@ -9,19 +9,6 @@ class AIModel(models.Model):
|
|||
_order = 'sequence, name'
|
||||
_check_company = False # Disable automatic company checks
|
||||
|
||||
@api.model
|
||||
def _has_provider_modules(self):
|
||||
"""Check if any AI provider modules are installed."""
|
||||
modules = ['ollama_ai_integration', 'chatgpt_ai_integration']
|
||||
return any(self.env['ir.module.module'].search([('name', 'in', modules), ('state', '=', 'installed')]))
|
||||
|
||||
@api.model
|
||||
def default_get(self, fields_list):
|
||||
"""Override default_get to prevent creation if no provider modules are installed."""
|
||||
if not self._has_provider_modules():
|
||||
raise UserError(_('No AI provider modules are installed. Please install at least one provider module (e.g., Ollama or ChatGPT) before creating an AI model.'))
|
||||
return super().default_get(fields_list)
|
||||
|
||||
active = fields.Boolean(
|
||||
string='Active',
|
||||
default=True,
|
||||
|
|
|
|||
|
|
@ -4,25 +4,20 @@ from odoo.addons.mail.models.mail_thread import MailThread
|
|||
from odoo.exceptions import UserError
|
||||
|
||||
|
||||
class AIProviderInstance(models.Model):
|
||||
class BaseAIProviderInstance(models.Model):
|
||||
_name = 'ai.provider.instance'
|
||||
_description = 'AI Provider Instance'
|
||||
_order = 'name'
|
||||
_check_company = False # Disable automatic company checks
|
||||
_inherit = ['mail.thread', 'ai.base.mixin']
|
||||
|
||||
@api.model
|
||||
def _has_provider_modules(self):
|
||||
"""Check if any AI provider modules are installed."""
|
||||
modules = ['ollama_ai_integration', 'chatgpt_ai_integration']
|
||||
return any(self.env['ir.module.module'].search([('name', 'in', modules), ('state', '=', 'installed')]))
|
||||
|
||||
@api.model
|
||||
def default_get(self, fields_list):
|
||||
"""Override default_get to prevent creation if no provider modules are installed."""
|
||||
if not self._has_provider_modules():
|
||||
raise UserError(_('No AI provider modules are installed. Please install at least one provider module (e.g., Ollama or ChatGPT) before creating a provider instance.'))
|
||||
return super().default_get(fields_list)
|
||||
defaults = super().default_get(fields_list)
|
||||
if defaults.get('provider_type', 'none') == 'none':
|
||||
defaults['provider_type'] = 'ollama'
|
||||
return defaults
|
||||
|
||||
active = fields.Boolean(
|
||||
string='Active',
|
||||
|
|
@ -35,23 +30,13 @@ class AIProviderInstance(models.Model):
|
|||
help='Name of this provider instance (e.g., "OpenWebUI Production", "Ollama Local")'
|
||||
)
|
||||
|
||||
provider_id = fields.Many2one(
|
||||
'ai.provider',
|
||||
string='Provider',
|
||||
required=True,
|
||||
ondelete='restrict',
|
||||
help='The AI provider configuration'
|
||||
)
|
||||
|
||||
provider_type = fields.Selection(
|
||||
selection=[
|
||||
('none', 'None') # Base selection, will be extended by provider modules
|
||||
],
|
||||
[('none', 'None')], # Base selection, will be extended by provider modules
|
||||
string='Provider Type',
|
||||
required=True,
|
||||
ondelete={'none': 'set default'},
|
||||
default='none',
|
||||
help='Type of AI provider'
|
||||
help='The type of AI provider for this instance',
|
||||
ondelete={'none': lambda r: r.write({'provider_type': 'none'})}
|
||||
)
|
||||
|
||||
host = fields.Char(
|
||||
|
|
@ -62,7 +47,8 @@ class AIProviderInstance(models.Model):
|
|||
|
||||
api_key = fields.Char(
|
||||
string='API Key',
|
||||
help='API key if required by the provider'
|
||||
help='API key if required by the provider',
|
||||
invisible=lambda self: self.provider_type == 'ollama'
|
||||
)
|
||||
|
||||
|
||||
|
|
@ -99,17 +85,13 @@ class AIProviderInstance(models.Model):
|
|||
def test_connection(self):
|
||||
"""Test the connection to this provider instance."""
|
||||
self.ensure_one()
|
||||
provider_model = f'ai.provider.{self.provider_type}'
|
||||
if provider_model not in self.env:
|
||||
raise UserError(_("Provider type %s is not installed", self.provider_type))
|
||||
|
||||
return self.env[provider_model].test_connection(self)
|
||||
if self.provider_type == 'none':
|
||||
raise UserError(_('Please select a provider type'))
|
||||
return {'type': 'ir.actions.act_window_close'}
|
||||
|
||||
def sync_models(self):
|
||||
"""Synchronize models from this provider instance."""
|
||||
self.ensure_one()
|
||||
provider_model = f'ai.provider.{self.provider_type}'
|
||||
if provider_model not in self.env:
|
||||
raise UserError(_("Provider type %s is not installed", self.provider_type))
|
||||
|
||||
return self.env[provider_model].sync_models(self)
|
||||
if self.provider_type == 'none':
|
||||
raise UserError(_('Please select a provider type'))
|
||||
|
||||
|
|
|
|||
|
|
@ -17,49 +17,39 @@ Le module AI Integration fournit une infrastructure flexible pour intégrer diff
|
|||
|
||||
### 2. AI Provider Instance (`ai.provider.instance`)
|
||||
- **Description**: Instance spécifique d'un fournisseur d'IA
|
||||
- **Héritage**: `mail.thread`, `ai.base.mixin`
|
||||
- **Champs principaux**:
|
||||
- `name`: Nom de l'instance
|
||||
- `name`: Nom de l'instance (ex: "OpenWebUI Production", "Ollama Local")
|
||||
- `provider_id`: Fournisseur associé
|
||||
- `provider_type`: Type de fournisseur
|
||||
- `host`: Adresse de l'hôte
|
||||
- `api_key`: Clé API (si nécessaire)
|
||||
- `provider_type`: Type de fournisseur (extensible par modules)
|
||||
- `active`: État actif/inactif
|
||||
- **Validation**:
|
||||
- Vérifie la présence d'au moins un module fournisseur installé
|
||||
|
||||
### 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
|
||||
- `identifier`: Identifiant technique (ex: gpt-3.5-turbo, mistral-7b)
|
||||
- `provider_instance_id`: Instance du fournisseur (cascade)
|
||||
- `provider_type`: Type de fournisseur (relié à l'instance)
|
||||
- `active`: État actif/inactif
|
||||
- `sequence`: Ordre d'affichage
|
||||
- **Validation**:
|
||||
- Vérifie la présence d'au moins un module fournisseur installé
|
||||
|
||||
### 4. AI Model Stats (`ai.model.stats`)
|
||||
- **Description**: Statistiques d'utilisation des modèles
|
||||
### 4. AI Generation Parameters (`ai.generation.params`)
|
||||
- **Description**: Paramètres de génération pour les modèles d'IA
|
||||
- **Type**: Modèle abstrait
|
||||
- **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)
|
||||
- `temperature`: Contrôle de l'aléatoire (défaut: 0.7)
|
||||
- `repeat_penalty`: Pénalité de répétition (défaut: 1.1)
|
||||
- `max_tokens`: Nombre maximum de tokens (défaut: 2048)
|
||||
- `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
|
||||
- `frequency_penalty`: Pénalité de fréquence (défaut: 0.0)
|
||||
- `presence_penalty`: Pénalité de présence (défaut: 0.0)
|
||||
|
||||
## Configuration
|
||||
## Configuration et Interfaces
|
||||
|
||||
### 1. Res Config Settings
|
||||
- **Description**: Paramètres de configuration globaux
|
||||
|
|
@ -73,9 +63,7 @@ Le module AI Integration fournit une infrastructure flexible pour intégrer diff
|
|||
- **Méthodes principales**:
|
||||
- `_get_default_provider_instance`: Obtenir l'instance par défaut
|
||||
|
||||
## Interfaces
|
||||
|
||||
### AI Provider Interface (`ai.provider.interface`)
|
||||
### 3. AI Provider Interface (`ai.provider.interface`)
|
||||
- **Description**: Interface abstraite pour les fournisseurs d'IA
|
||||
- **Méthodes requises**:
|
||||
- `send_message`: Envoyer un message
|
||||
|
|
@ -84,7 +72,19 @@ Le module AI Integration fournit une infrastructure flexible pour intégrer diff
|
|||
|
||||
## 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. **Architecture Modulaire**:
|
||||
- Modules fournisseurs disponibles: `ollama_ai_integration`, `chatgpt_ai_integration`
|
||||
- Vérification de la présence d'au moins un module fournisseur avant création d'instances
|
||||
|
||||
2. **Héritage et Extensions**:
|
||||
- Les instances de fournisseur héritent de `mail.thread` et `ai.base.mixin`
|
||||
- Les paramètres de génération sont définis dans le modèle abstrait `ai.generation.params`
|
||||
|
||||
3. **Configuration Hiérarchique**:
|
||||
- Configuration globale > Paramètres société > Instance
|
||||
- Paramètres de génération personnalisables à plusieurs niveaux
|
||||
|
||||
4. **Sécurité et Validation**:
|
||||
- Vérifications de sécurité intégrées
|
||||
- Validation des modules requis
|
||||
- Gestion des paramètres de génération avec valeurs par défaut
|
||||
|
|
|
|||
|
|
@ -20,9 +20,3 @@ class ResConfigSettings(models.TransientModel):
|
|||
config_parameter='ai_integration.ai_batch_size',
|
||||
default=100,
|
||||
default_model='ai.provider.instance')
|
||||
|
||||
@api.onchange('default_provider_instance_id')
|
||||
def _onchange_default_provider_instance(self):
|
||||
"""Reset model when provider instance changes"""
|
||||
if self.default_provider_instance_id:
|
||||
self.default_model_id = False
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@
|
|||
<field name="name">ai.model.stats.list</field>
|
||||
<field name="model">ai.model.stats</field>
|
||||
<field name="arch" type="xml">
|
||||
<list string="Model Statistics">
|
||||
<list string="Model Statistics" create="false">
|
||||
<field name="date"/>
|
||||
<field name="model_id"/>
|
||||
<field name="provider_instance_id"/>
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@
|
|||
<field name="model">ai.model</field>
|
||||
<field name="type">list</field>
|
||||
<field name="arch" type="xml">
|
||||
<list string="AI Models">
|
||||
<list string="AI Models" create="false">
|
||||
<field name="sequence" widget="handle"/>
|
||||
<field name="name"/>
|
||||
<field name="provider_instance_id"/>
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@
|
|||
<field name="name">ai.provider.instance.list</field>
|
||||
<field name="model">ai.provider.instance</field>
|
||||
<field name="arch" type="xml">
|
||||
<list string="AI Provider Instances">
|
||||
<list name="ai_instance" string="AI Provider Instances" create="true">
|
||||
<field name="name"/>
|
||||
<field name="provider_type"/>
|
||||
<field name="host"/>
|
||||
|
|
@ -44,7 +44,7 @@
|
|||
<group>
|
||||
<field name="provider_type"/>
|
||||
<field name="host"/>
|
||||
<field name="api_key" password="True"/>
|
||||
<field name="api_key"/>
|
||||
</group>
|
||||
<group>
|
||||
<field name="timeout"/>
|
||||
|
|
|
|||
88
ai_integration/views/ai_provider_views.xml
Normal file
88
ai_integration/views/ai_provider_views.xml
Normal file
|
|
@ -0,0 +1,88 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
<!-- Tree View -->
|
||||
<record id="ai_provider_view_tree" model="ir.ui.view">
|
||||
<field name="name">ai.provider.tree</field>
|
||||
<field name="model">ai.provider</field>
|
||||
<field name="type">list</field>
|
||||
<field name="arch" type="xml">
|
||||
<list string="AI Providers" create="false">
|
||||
<field name="name"/>
|
||||
<field name="code"/>
|
||||
<field name="default_host"/>
|
||||
<field name="active"/>
|
||||
</list>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<!-- Form View -->
|
||||
<record id="ai_provider_view_form" model="ir.ui.view">
|
||||
<field name="name">ai.provider.form</field>
|
||||
<field name="model">ai.provider</field>
|
||||
<field name="type">form</field>
|
||||
<field name="arch" type="xml">
|
||||
<form string="AI Provider">
|
||||
<sheet>
|
||||
<div class="oe_button_box" name="button_box">
|
||||
<button name="test_connection" type="object"
|
||||
string="Test Connection" class="oe_stat_button"
|
||||
icon="fa-plug"/>
|
||||
<button name="get_models" type="object"
|
||||
string="Get Models" class="oe_stat_button"
|
||||
icon="fa-list"/>
|
||||
</div>
|
||||
<div class="oe_title">
|
||||
<label for="name" class="oe_edit_only"/>
|
||||
<h1><field name="name" placeholder="e.g. Ollama"/></h1>
|
||||
</div>
|
||||
<group>
|
||||
<group>
|
||||
<field name="code"/>
|
||||
<field name="default_host"/>
|
||||
</group>
|
||||
<group>
|
||||
<field name="active"/>
|
||||
</group>
|
||||
</group>
|
||||
<notebook>
|
||||
<page string="Description" name="description">
|
||||
<field name="description" nolabel="1" placeholder="Enter a description..."/>
|
||||
</page>
|
||||
</notebook>
|
||||
</sheet>
|
||||
</form>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<!-- Search View -->
|
||||
<record id="ai_provider_view_search" model="ir.ui.view">
|
||||
<field name="name">ai.provider.search</field>
|
||||
<field name="model">ai.provider</field>
|
||||
<field name="type">search</field>
|
||||
<field name="arch" type="xml">
|
||||
<search string="Search AI Providers">
|
||||
<field name="name"/>
|
||||
<field name="code"/>
|
||||
<filter string="Active" name="active" domain="[('active', '=', True)]"/>
|
||||
<filter string="Archived" name="inactive" domain="[('active', '=', False)]"/>
|
||||
</search>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<!-- Action -->
|
||||
<record id="ai_provider_action" model="ir.actions.act_window">
|
||||
<field name="name">AI Providers</field>
|
||||
<field name="res_model">ai.provider</field>
|
||||
<field name="view_mode">list,form</field>
|
||||
<field name="context">{'search_default_active': 1}</field>
|
||||
<field name="help" type="html">
|
||||
<p class="o_view_nocontent_smiling_face">
|
||||
No AI providers found
|
||||
</p>
|
||||
<p>
|
||||
AI providers are automatically created when you install AI integration modules.
|
||||
</p>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
</odoo>
|
||||
|
|
@ -16,12 +16,20 @@
|
|||
<menuitem id="menu_ai_settings"
|
||||
name="Settings"
|
||||
parent="menu_ai_config"
|
||||
action="action_ai_integration_configuration"
|
||||
action="ai_integration.action_ai_integration_configuration"
|
||||
sequence="10"
|
||||
groups="base.group_system"/>
|
||||
|
||||
<!-- Provider Types menu -->
|
||||
<menuitem id="menu_ai_provider_types"
|
||||
name="Provider Types"
|
||||
parent="menu_ai_config"
|
||||
action="ai_provider_action"
|
||||
sequence="10"
|
||||
groups="base.group_system"/>
|
||||
|
||||
<!-- Provider Instances menu -->
|
||||
<menuitem id="menu_ai_providers"
|
||||
<menuitem id="menu_ai_provider_instances"
|
||||
name="Provider Instances"
|
||||
parent="menu_ai_config"
|
||||
action="ai_provider_instance_action"
|
||||
|
|
|
|||
|
|
@ -1,5 +1,15 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
<!-- Settings Action -->
|
||||
<record id="action_ai_integration_configuration" model="ir.actions.act_window">
|
||||
<field name="name">AI Integration Settings</field>
|
||||
<field name="type">ir.actions.act_window</field>
|
||||
<field name="res_model">res.config.settings</field>
|
||||
<field name="view_mode">form</field>
|
||||
<field name="target">inline</field>
|
||||
<field name="context">{"module" : "ai_integration"}</field>
|
||||
</record>
|
||||
|
||||
<record id="res_config_settings_view_form" model="ir.ui.view">
|
||||
<field name="name">res.config.settings.view.form.inherit.ai.integration</field>
|
||||
<field name="model">res.config.settings</field>
|
||||
|
|
|
|||
|
|
@ -20,9 +20,9 @@ in your Odoo instance. Features include:
|
|||
'ai_integration'
|
||||
],
|
||||
'data': [
|
||||
'data/ollama_provider.xml',
|
||||
# 'views/ollama_views.xml',
|
||||
'data/ai_provider_data.xml',
|
||||
'views/ollama_stats_views.xml',
|
||||
'views/ai_provider_instance_views.xml',
|
||||
'security/ir.model.access.csv',
|
||||
],
|
||||
'external_dependencies': {
|
||||
13
ai_integration_ollama_api/data/ai_provider_data.xml
Normal file
13
ai_integration_ollama_api/data/ai_provider_data.xml
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
<data noupdate="1">
|
||||
<!-- Ollama Provider -->
|
||||
<record id="ai_provider_ollama" model="ai.provider">
|
||||
<field name="name">Ollama</field>
|
||||
<field name="code">ollama</field>
|
||||
<field name="description">Ollama is a local AI model provider that allows you to run various open-source models locally.</field>
|
||||
<field name="default_host">http://localhost:11434</field>
|
||||
<field name="active" eval="True"/>
|
||||
</record>
|
||||
</data>
|
||||
</odoo>
|
||||
|
|
@ -18,6 +18,7 @@ from . import ollama_provider_mixin
|
|||
|
||||
# Core Implementation
|
||||
from . import ollama_provider
|
||||
from . import ai_provider_instance
|
||||
|
||||
# Statistics and Monitoring
|
||||
from . import ollama_model_stats
|
||||
|
|
@ -1,56 +1,78 @@
|
|||
from odoo import models, fields, api, _
|
||||
from odoo.exceptions import UserError
|
||||
import requests
|
||||
|
||||
class AIProviderInstance(models.Model):
|
||||
class OllamaAIProviderInstance(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.
|
||||
"""
|
||||
_inherit = ['ai.provider.instance', 'ollama.provider.mixin']
|
||||
_name = 'ai.provider.instance'
|
||||
_inherit = ['ollama.provider.mixin', 'mail.thread']
|
||||
_description = 'AI Provider Instance'
|
||||
_description = 'Ollama AI Provider Instance'
|
||||
|
||||
# Override provider_type to add Ollama option
|
||||
provider_type = fields.Selection(
|
||||
selection_add=[('ollama', 'Ollama')],
|
||||
ondelete={'ollama': lambda r: r.write({'provider_type': 'none'})}
|
||||
)
|
||||
|
||||
# Basic Fields
|
||||
name = fields.Char(
|
||||
string='Name',
|
||||
required=True,
|
||||
tracking=True,
|
||||
help='Name of this AI provider instance')
|
||||
@api.onchange('provider_type')
|
||||
def _onchange_provider_type(self):
|
||||
"""Handle provider type changes.
|
||||
|
||||
active = fields.Boolean(
|
||||
string='Active',
|
||||
default=True,
|
||||
tracking=True,
|
||||
help='Whether this provider instance is active')
|
||||
When switching to 'ollama':
|
||||
- Set default host if empty
|
||||
|
||||
When switching away from 'ollama':
|
||||
- Clear Ollama-specific fields
|
||||
"""
|
||||
if self.provider_type == 'ollama':
|
||||
if not self.host:
|
||||
self.host = 'http://localhost:11434'
|
||||
else:
|
||||
# Clear Ollama-specific fields
|
||||
self.update({
|
||||
'num_ctx': False,
|
||||
'temperature': False,
|
||||
'top_p': False,
|
||||
'top_k': False,
|
||||
'repeat_penalty': False,
|
||||
'repeat_last_n': False,
|
||||
'num_thread': False,
|
||||
'num_gpu': False,
|
||||
'num_batch': False,
|
||||
'model_name': False,
|
||||
})
|
||||
|
||||
# Override default host for Ollama
|
||||
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.
|
||||
"""Handle provider type changes.
|
||||
|
||||
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.
|
||||
When switching to 'ollama':
|
||||
- Set the provider_id to the Ollama provider
|
||||
- Set default host if empty
|
||||
|
||||
When switching away from 'ollama':
|
||||
- Clear Ollama-specific fields
|
||||
"""
|
||||
if self.provider_type != 'ollama':
|
||||
if self.provider_type == 'ollama':
|
||||
# Find and set the Ollama provider
|
||||
ollama_provider = self.env['ai.provider'].search([('code', '=', 'ollama')], limit=1)
|
||||
if ollama_provider:
|
||||
self.provider_id = ollama_provider.id
|
||||
if not self.host:
|
||||
self.host = ollama_provider.default_host
|
||||
else:
|
||||
# Clear Ollama-specific fields
|
||||
self.update({
|
||||
'num_ctx': False, # Context length
|
||||
'temperature': False, # Sampling temperature
|
||||
|
|
@ -75,7 +97,8 @@ class AIProviderInstance(models.Model):
|
|||
|
||||
try:
|
||||
# Try to list models as a basic connectivity test
|
||||
self.env['ai.provider.ollama']._get_models(self)
|
||||
response = requests.get(f'{self.host}/api/tags')
|
||||
response.raise_for_status()
|
||||
return {
|
||||
'type': 'ir.actions.client',
|
||||
'tag': 'display_notification',
|
||||
|
|
@ -104,18 +127,34 @@ class AIProviderInstance(models.Model):
|
|||
return
|
||||
|
||||
try:
|
||||
provider = self.env['ai.provider.ollama']
|
||||
models = provider._get_models(self)
|
||||
# Get models from Ollama API
|
||||
response = requests.get(f'{self.host}/api/tags')
|
||||
response.raise_for_status()
|
||||
|
||||
# Parse response
|
||||
models = [{
|
||||
'name': model['name'],
|
||||
'id': model['name'],
|
||||
} for model in response.json()['models']]
|
||||
|
||||
for model_data in models:
|
||||
# Create or update AI model record
|
||||
self.env['ai.model'].create_or_update({
|
||||
vals = {
|
||||
'name': model_data['name'],
|
||||
'identifier': model_data['id'],
|
||||
'provider_instance_id': self.id,
|
||||
'model_type': 'text',
|
||||
'active': True,
|
||||
})
|
||||
}
|
||||
# Search for existing model
|
||||
existing = self.env['ai.model'].search([
|
||||
('identifier', '=', model_data['id']),
|
||||
('provider_instance_id', '=', self.id)
|
||||
], limit=1)
|
||||
|
||||
if existing:
|
||||
existing.write(vals)
|
||||
else:
|
||||
self.env['ai.model'].create(vals)
|
||||
|
||||
return {
|
||||
'type': 'ir.actions.client',
|
||||
|
|
@ -22,14 +22,6 @@ class OllamaProviderMixin(models.AbstractModel):
|
|||
_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',
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
<!-- Inherit AI Provider Instance List View -->
|
||||
<record id="ollama_ai_provider_instance_view_list" model="ir.ui.view">
|
||||
<field name="name">ai.provider.instance.list.ollama</field>
|
||||
<field name="model">ai.provider.instance</field>
|
||||
<field name="inherit_id" ref="ai_integration.ai_provider_instance_view_list"/>
|
||||
<field name="mode">primary</field>
|
||||
<field name="arch" type="xml">
|
||||
<xpath expr="//list[@name='ai_instance']" position="attributes">
|
||||
<attribute name="create">true</attribute>
|
||||
</xpath>
|
||||
</field>
|
||||
</record>
|
||||
</odoo>
|
||||
|
|
@ -112,7 +112,7 @@
|
|||
<!-- Menu Item -->
|
||||
<menuitem id="menu_ollama_model_stats"
|
||||
name="Model Statistics"
|
||||
parent="ai_integration.menu_ai_integration"
|
||||
parent="ai_integration.menu_ai_config"
|
||||
action="action_ollama_model_stats"
|
||||
sequence="30"/>
|
||||
</odoo>
|
||||
Loading…
Reference in a new issue