Stage 1 & 2 refactor of AI engine
This commit is contained in:
217
backend/igny8_core/ai/validators.py
Normal file
217
backend/igny8_core/ai/validators.py
Normal file
@@ -0,0 +1,217 @@
|
||||
"""
|
||||
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 and max_items limit.
|
||||
|
||||
Args:
|
||||
payload: Request payload containing 'ids' array
|
||||
max_items: Maximum number of items allowed (None = no limit)
|
||||
|
||||
Returns:
|
||||
Dict with 'valid' (bool) and optional 'error' (str)
|
||||
"""
|
||||
ids = payload.get('ids', [])
|
||||
if not ids:
|
||||
return {'valid': False, 'error': 'No IDs provided'}
|
||||
|
||||
if max_items and len(ids) > max_items:
|
||||
return {'valid': False, 'error': f'Maximum {max_items} items allowed'}
|
||||
|
||||
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.
|
||||
|
||||
Args:
|
||||
account: Account object
|
||||
operation_type: Type of operation ('cluster', 'idea', etc.)
|
||||
|
||||
Returns:
|
||||
Dict with 'valid' (bool) and optional 'error' (str)
|
||||
"""
|
||||
if not account:
|
||||
return {'valid': False, 'error': 'Account is required'}
|
||||
|
||||
plan = getattr(account, 'plan', None)
|
||||
if not plan:
|
||||
return {'valid': False, 'error': 'Account does not have an active plan'}
|
||||
|
||||
if operation_type == 'cluster':
|
||||
from igny8_core.modules.planner.models import Clusters
|
||||
|
||||
# Check daily cluster limit
|
||||
now = timezone.now()
|
||||
start_of_day = now.replace(hour=0, minute=0, second=0, microsecond=0)
|
||||
clusters_today = Clusters.objects.filter(
|
||||
account=account,
|
||||
created_at__gte=start_of_day
|
||||
).count()
|
||||
|
||||
if plan.daily_cluster_limit and clusters_today >= plan.daily_cluster_limit:
|
||||
return {
|
||||
'valid': False,
|
||||
'error': f'Daily cluster limit reached ({plan.daily_cluster_limit} clusters per day). Please try again tomorrow.'
|
||||
}
|
||||
|
||||
# Check max clusters limit
|
||||
total_clusters = Clusters.objects.filter(account=account).count()
|
||||
if plan.max_clusters and total_clusters >= plan.max_clusters:
|
||||
return {
|
||||
'valid': False,
|
||||
'error': f'Maximum cluster limit reached ({plan.max_clusters} clusters). Please upgrade your plan or delete existing clusters.'
|
||||
}
|
||||
|
||||
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.
|
||||
|
||||
Args:
|
||||
model: Model name to validate
|
||||
model_type: Type of model ('text' or 'image')
|
||||
|
||||
Returns:
|
||||
Dict with 'valid' (bool) and optional 'error' (str)
|
||||
"""
|
||||
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.
|
||||
|
||||
Args:
|
||||
size: Image size (e.g., '1024x1024')
|
||||
model: Model name
|
||||
|
||||
Returns:
|
||||
Dict with 'valid' (bool) and optional 'error' (str)
|
||||
"""
|
||||
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}
|
||||
|
||||
Reference in New Issue
Block a user