# # Copyright (C) 2026 GNS3 Technologies Inc. # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . from typing import Optional, List, Literal from pydantic import BaseModel, Field, ConfigDict from uuid import UUID from .base import DateTimeModelMixin # Valid model types ModelType = Literal['text', 'vision', 'stt', 'tts', 'multimodal', 'embedding', 'reranking', 'other'] # Core model config schema (stored in config JSONB field) class LLMModelConfigData(BaseModel): """ LLM model configuration data. Stored in the config JSONB column (provider, base_url, model, etc.). """ provider: str = Field(..., description="LLM provider (e.g., 'openai', 'anthropic', 'ollama')") base_url: str = Field(..., description="API base URL") model: str = Field(..., description="Model name") temperature: float = Field(default=0.7, ge=0.0, le=2.0, description="Temperature parameter") api_key: Optional[str] = Field(None, description="API key (will be encrypted)") max_tokens: Optional[int] = Field(None, gt=0, description="Max tokens for generation") # Allow extra fields for extensibility model_config = ConfigDict(extra="allow") # Request schemas class LLMModelConfigCreate(BaseModel): """Request to create a new LLM model configuration.""" name: str = Field(..., min_length=1, max_length=100, description="Configuration name") model_type: ModelType = Field(..., description="Model type") is_default: Optional[bool] = Field(False, description="Set as default configuration") # Config fields provider: str = Field(..., description="LLM provider") base_url: str = Field(..., description="API base URL") model: str = Field(..., description="Model name") temperature: float = Field(default=0.7, ge=0.0, le=2.0) api_key: Optional[str] = None max_tokens: Optional[int] = Field(None, gt=0) # Allow extra config fields model_config = ConfigDict(extra="allow") class LLMModelConfigUpdate(BaseModel): """Request to update an existing LLM model configuration.""" # Table-level fields name: Optional[str] = Field(None, min_length=1, max_length=100) model_type: Optional[ModelType] = None is_default: Optional[bool] = None expected_version: Optional[int] = Field(None, description="Expected version for optimistic locking") # Config fields provider: Optional[str] = None base_url: Optional[str] = None model: Optional[str] = None temperature: Optional[float] = Field(None, ge=0.0, le=2.0) api_key: Optional[str] = None max_tokens: Optional[int] = Field(None, gt=0) # Allow extra config fields model_config = ConfigDict(extra="allow") # Response schemas class LLMModelConfigResponse(DateTimeModelMixin): """LLM model configuration response.""" config_id: UUID name: str model_type: ModelType config: LLMModelConfigData user_id: Optional[UUID] = None group_id: Optional[UUID] = None is_default: bool version: int = Field(..., description="Optimistic locking version") model_config = ConfigDict(from_attributes=True) class LLMModelConfigWithSource(DateTimeModelMixin): """Model configuration with source information (for inheritance).""" config_id: UUID name: str model_type: ModelType config: LLMModelConfigData user_id: Optional[UUID] = None group_id: Optional[UUID] = None is_default: bool version: int source: str = Field(..., description="Source: 'user' or 'group'") group_name: Optional[str] = Field(None, description="Group name if source is 'group'") model_config = ConfigDict(from_attributes=True, extra="allow") class LLMModelConfigInheritedResponse(BaseModel): """Response containing user's effective configs (own + inherited from groups).""" configs: list[LLMModelConfigWithSource] default_config: Optional[LLMModelConfigWithSource] = None total: int class LLMModelConfigListResponse(BaseModel): """Response containing a list of model configurations with default.""" configs: list[LLMModelConfigResponse] default_config: Optional[LLMModelConfigResponse] = None total: int