""" AI Settings - Centralized model configurations and limits Uses global settings with optional per-account overrides. """ from typing import Dict, Any import logging logger = logging.getLogger(__name__) # Function name aliases (for backward compatibility) FUNCTION_ALIASES = { "cluster_keywords": "auto_cluster", "auto_cluster_keywords": "auto_cluster", "auto_generate_ideas": "generate_ideas", "auto_generate_content": "generate_content", "auto_generate_images": "generate_images", } def get_model_config(function_name: str, account) -> Dict[str, Any]: """ Get model configuration for AI function. Architecture: - API keys: ALWAYS from GlobalIntegrationSettings (platform-wide) - Model/params: From IntegrationSettings if account has override, else from global - Free plan: Cannot override, uses global defaults - Starter/Growth/Scale: Can override model, temperature, max_tokens, etc. Args: function_name: Name of the AI function account: Account instance (required) Returns: dict: Model configuration with 'model', 'max_tokens', 'temperature', 'api_key' Raises: ValueError: If account not provided or settings not configured """ if not account: raise ValueError("Account is required for model configuration") # Resolve function alias actual_name = FUNCTION_ALIASES.get(function_name, function_name) try: from igny8_core.modules.system.global_settings_models import GlobalIntegrationSettings from igny8_core.modules.system.models import IntegrationSettings # Get global settings (for API keys and defaults) global_settings = GlobalIntegrationSettings.get_instance() if not global_settings.openai_api_key: raise ValueError( "Platform OpenAI API key not configured. " "Please configure GlobalIntegrationSettings in Django admin." ) # Start with global defaults model = global_settings.openai_model temperature = global_settings.openai_temperature max_tokens = global_settings.openai_max_tokens api_key = global_settings.openai_api_key # ALWAYS from global # Check if account has overrides (only for Starter/Growth/Scale plans) # Free plan users cannot create IntegrationSettings records try: account_settings = IntegrationSettings.objects.get( account=account, integration_type='openai', is_active=True ) config = account_settings.config or {} # Override model if specified (NULL = use global) if config.get('model'): model = config['model'] # Override temperature if specified if config.get('temperature') is not None: temperature = config['temperature'] # Override max_tokens if specified if config.get('max_tokens'): max_tokens = config['max_tokens'] except IntegrationSettings.DoesNotExist: # No account override, use global defaults (already set above) pass except Exception as e: logger.error(f"Could not load OpenAI settings for account {account.id}: {e}") raise ValueError( f"Could not load OpenAI configuration for account {account.id}. " f"Please configure GlobalIntegrationSettings." ) # Validate model is in our supported list (optional validation) try: from igny8_core.utils.ai_processor import MODEL_RATES if model not in MODEL_RATES: logger.warning( f"Model '{model}' for account {account.id} is not in supported list. " f"Supported models: {list(MODEL_RATES.keys())}" ) except ImportError: pass # Build response format based on model (JSON mode for supported models) response_format = None try: from igny8_core.ai.constants import JSON_MODE_MODELS if model in JSON_MODE_MODELS: response_format = {"type": "json_object"} except ImportError: pass return { 'model': model, 'max_tokens': max_tokens, 'temperature': temperature, 'response_format': response_format, } def get_image_generation_config(account) -> Dict[str, Any]: """ Get image generation configuration for AI functions. Architecture: - API keys: ALWAYS from GlobalIntegrationSettings (platform-wide) - Model/params: From IntegrationSettings if account has override, else from global - Supports both OpenAI (DALL-E) and Runware providers Args: account: Account instance (required) Returns: dict: Image generation configuration with 'provider', 'model', 'api_key', 'size', 'quality', 'style', 'max_in_article_images' Raises: ValueError: If account not provided or settings not configured """ if not account: raise ValueError("Account is required for image generation configuration") try: from igny8_core.modules.system.global_settings_models import GlobalIntegrationSettings from igny8_core.modules.system.models import IntegrationSettings # Get global settings (for API keys and defaults) global_settings = GlobalIntegrationSettings.get_instance() # Start with global defaults provider = global_settings.default_image_service # 'openai' or 'runware' max_in_article_images = global_settings.max_in_article_images if provider == 'runware': api_key = global_settings.runware_api_key model = global_settings.runware_model size = global_settings.desktop_image_size quality = global_settings.image_quality style = global_settings.image_style if not api_key: raise ValueError( "Platform Runware API key not configured. " "Please configure GlobalIntegrationSettings in Django admin." ) else: # openai/dalle api_key = global_settings.dalle_api_key or global_settings.openai_api_key model = global_settings.dalle_model size = global_settings.dalle_size quality = global_settings.image_quality style = global_settings.image_style if not api_key: raise ValueError( "Platform OpenAI/DALL-E API key not configured. " "Please configure GlobalIntegrationSettings in Django admin." ) # Check if account has overrides # Try both 'image_generation' and provider-specific types for backward compatibility account_settings = None for integration_type in ['image_generation', provider, 'dalle', 'runware']: try: account_settings = IntegrationSettings.objects.get( account=account, integration_type=integration_type, is_active=True ) break except IntegrationSettings.DoesNotExist: continue if account_settings: config = account_settings.config or {} # Override provider if specified if config.get('provider') or config.get('service'): provider = config.get('provider') or config.get('service') # Override model if specified if config.get('model'): model = config['model'] # Override size if specified if config.get('size') or config.get('image_size'): size = config.get('size') or config.get('image_size') # Override quality if specified if config.get('quality') or config.get('image_quality'): quality = config.get('quality') or config.get('image_quality') # Override style if specified if config.get('style') or config.get('image_style'): style = config.get('style') or config.get('image_style') # Override max_in_article_images if specified if config.get('max_in_article_images'): max_in_article_images = int(config['max_in_article_images']) return { 'provider': provider, 'model': model, 'api_key': api_key, # ALWAYS from global 'size': size, 'quality': quality, 'style': style, 'max_in_article_images': max_in_article_images, } except Exception as e: logger.error(f"Could not load image generation settings for account {account.id}: {e}") raise ValueError( f"Could not load image generation configuration for account {account.id}. " f"Please configure GlobalIntegrationSettings." )