- Added AIModelConfig model to manage AI model configurations in the database. - Created serializers and views for AI model configurations, enabling read-only access via REST API. - Implemented filtering capabilities for model type, provider, and default status in the API. - Seeded initial data for text and image models, including pricing and capabilities. - Updated Django Admin interface for managing AI models with enhanced features and bulk actions. - Added validation methods for model and image size checks. - Comprehensive migration created to establish the AIModelConfig model and seed initial data. - Documented implementation and validation results in summary and report files.
245 lines
7.2 KiB
Python
245 lines
7.2 KiB
Python
"""
|
|
AI Validators - Consolidated validation logic for all AI functions
|
|
"""
|
|
import logging
|
|
from typing import Dict, Any, Optional
|
|
from django.utils import timezone
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
def validate_ids(payload: dict, max_items: Optional[int] = None) -> Dict[str, Any]:
|
|
"""
|
|
Base validation: checks for 'ids' array.
|
|
|
|
Args:
|
|
payload: Request payload containing 'ids' array
|
|
max_items: Maximum number of items allowed (deprecated - no longer enforced)
|
|
|
|
Returns:
|
|
Dict with 'valid' (bool) and optional 'error' (str)
|
|
"""
|
|
ids = payload.get('ids', [])
|
|
if not ids:
|
|
return {'valid': False, 'error': 'No IDs provided'}
|
|
|
|
# Removed max_items limit check - no limits enforced
|
|
|
|
return {'valid': True}
|
|
|
|
|
|
def validate_keywords_exist(ids: list, account=None) -> Dict[str, Any]:
|
|
"""
|
|
Validate that keywords exist in database.
|
|
|
|
Args:
|
|
ids: List of keyword IDs
|
|
account: Optional account for filtering
|
|
|
|
Returns:
|
|
Dict with 'valid' (bool) and optional 'error' (str)
|
|
"""
|
|
from igny8_core.modules.planner.models import Keywords
|
|
|
|
queryset = Keywords.objects.filter(id__in=ids)
|
|
if account:
|
|
queryset = queryset.filter(account=account)
|
|
|
|
if queryset.count() == 0:
|
|
return {'valid': False, 'error': 'No keywords found'}
|
|
|
|
return {'valid': True}
|
|
|
|
|
|
def validate_cluster_limits(account, operation_type: str = 'cluster') -> Dict[str, Any]:
|
|
"""
|
|
Validate plan limits for cluster operations.
|
|
DISABLED: All limits have been removed.
|
|
|
|
Args:
|
|
account: Account object
|
|
operation_type: Type of operation ('cluster', 'idea', etc.)
|
|
|
|
Returns:
|
|
Dict with 'valid' (bool) - always returns valid
|
|
"""
|
|
# All limits removed - always return valid
|
|
return {'valid': True}
|
|
|
|
|
|
def validate_cluster_exists(cluster_id: int, account=None) -> Dict[str, Any]:
|
|
"""
|
|
Validate that a cluster exists.
|
|
|
|
Args:
|
|
cluster_id: Cluster ID
|
|
account: Optional account for filtering
|
|
|
|
Returns:
|
|
Dict with 'valid' (bool) and optional 'error' (str)
|
|
"""
|
|
from igny8_core.modules.planner.models import Clusters
|
|
|
|
queryset = Clusters.objects.filter(id=cluster_id)
|
|
if account:
|
|
queryset = queryset.filter(account=account)
|
|
|
|
if not queryset.exists():
|
|
return {'valid': False, 'error': 'Cluster not found'}
|
|
|
|
return {'valid': True}
|
|
|
|
|
|
def validate_tasks_exist(task_ids: list, account=None) -> Dict[str, Any]:
|
|
"""
|
|
Validate that tasks exist in database.
|
|
|
|
Args:
|
|
task_ids: List of task IDs
|
|
account: Optional account for filtering
|
|
|
|
Returns:
|
|
Dict with 'valid' (bool) and optional 'error' (str)
|
|
"""
|
|
from igny8_core.modules.writer.models import Tasks
|
|
|
|
queryset = Tasks.objects.filter(id__in=task_ids)
|
|
if account:
|
|
queryset = queryset.filter(account=account)
|
|
|
|
if queryset.count() == 0:
|
|
return {'valid': False, 'error': 'No tasks found'}
|
|
|
|
return {'valid': True}
|
|
|
|
|
|
def validate_api_key(api_key: Optional[str], integration_type: str = 'openai') -> Dict[str, Any]:
|
|
"""
|
|
Validate that API key is configured.
|
|
|
|
Args:
|
|
api_key: API key to validate
|
|
integration_type: Type of integration ('openai', 'runware')
|
|
|
|
Returns:
|
|
Dict with 'valid' (bool) and optional 'error' (str)
|
|
"""
|
|
if not api_key:
|
|
return {
|
|
'valid': False,
|
|
'error': f'{integration_type.upper()} API key not configured'
|
|
}
|
|
|
|
return {'valid': True}
|
|
|
|
|
|
def validate_model(model: str, model_type: str = 'text') -> Dict[str, Any]:
|
|
"""
|
|
Validate that model is in supported list using database.
|
|
|
|
Args:
|
|
model: Model name to validate
|
|
model_type: Type of model ('text' or 'image')
|
|
|
|
Returns:
|
|
Dict with 'valid' (bool) and optional 'error' (str)
|
|
"""
|
|
try:
|
|
# Try database first
|
|
from igny8_core.business.billing.models import AIModelConfig
|
|
|
|
exists = AIModelConfig.objects.filter(
|
|
model_name=model,
|
|
model_type=model_type,
|
|
is_active=True
|
|
).exists()
|
|
|
|
if not exists:
|
|
# Get available models for better error message
|
|
available = list(AIModelConfig.objects.filter(
|
|
model_type=model_type,
|
|
is_active=True
|
|
).values_list('model_name', flat=True))
|
|
|
|
if available:
|
|
return {
|
|
'valid': False,
|
|
'error': f'Model "{model}" is not active or not found. Available {model_type} models: {", ".join(available)}'
|
|
}
|
|
else:
|
|
return {
|
|
'valid': False,
|
|
'error': f'Model "{model}" is not found in database'
|
|
}
|
|
|
|
return {'valid': True}
|
|
|
|
except Exception:
|
|
# Fallback to constants if database fails
|
|
from .constants import MODEL_RATES, VALID_OPENAI_IMAGE_MODELS
|
|
|
|
if model_type == 'text':
|
|
if model not in MODEL_RATES:
|
|
return {
|
|
'valid': False,
|
|
'error': f'Model "{model}" is not in supported models list'
|
|
}
|
|
elif model_type == 'image':
|
|
if model not in VALID_OPENAI_IMAGE_MODELS:
|
|
return {
|
|
'valid': False,
|
|
'error': f'Model "{model}" is not valid for OpenAI image generation. Only {", ".join(VALID_OPENAI_IMAGE_MODELS)} are supported.'
|
|
}
|
|
|
|
return {'valid': True}
|
|
|
|
|
|
def validate_image_size(size: str, model: str) -> Dict[str, Any]:
|
|
"""
|
|
Validate that image size is valid for the selected model using database.
|
|
|
|
Args:
|
|
size: Image size (e.g., '1024x1024')
|
|
model: Model name
|
|
|
|
Returns:
|
|
Dict with 'valid' (bool) and optional 'error' (str)
|
|
"""
|
|
try:
|
|
# Try database first
|
|
from igny8_core.business.billing.models import AIModelConfig
|
|
|
|
model_config = AIModelConfig.objects.filter(
|
|
model_name=model,
|
|
model_type='image',
|
|
is_active=True
|
|
).first()
|
|
|
|
if model_config:
|
|
if not model_config.validate_size(size):
|
|
valid_sizes = model_config.valid_sizes or []
|
|
return {
|
|
'valid': False,
|
|
'error': f'Image size "{size}" is not valid for model "{model}". Valid sizes are: {", ".join(valid_sizes)}'
|
|
}
|
|
return {'valid': True}
|
|
else:
|
|
return {
|
|
'valid': False,
|
|
'error': f'Image model "{model}" not found in database'
|
|
}
|
|
|
|
except Exception:
|
|
# Fallback to constants if database fails
|
|
from .constants import VALID_SIZES_BY_MODEL
|
|
|
|
valid_sizes = VALID_SIZES_BY_MODEL.get(model, [])
|
|
if size not in valid_sizes:
|
|
return {
|
|
'valid': False,
|
|
'error': f'Image size "{size}" is not valid for model "{model}". Valid sizes are: {", ".join(valid_sizes)}'
|
|
}
|
|
|
|
return {'valid': True}
|
|
|