From 355b0ac8974f1fad2c730688bb6e5b24f1a3097f Mon Sep 17 00:00:00 2001 From: "IGNY8 VPS (Salman)" Date: Wed, 24 Dec 2025 01:07:31 +0000 Subject: [PATCH] plan fro model unifiation --- AI-MODELS-DATABASE-CONFIGURATION-PLAN.md | 1124 ++++++++++++++++++++++ 1 file changed, 1124 insertions(+) create mode 100644 AI-MODELS-DATABASE-CONFIGURATION-PLAN.md diff --git a/AI-MODELS-DATABASE-CONFIGURATION-PLAN.md b/AI-MODELS-DATABASE-CONFIGURATION-PLAN.md new file mode 100644 index 00000000..1a2a88d0 --- /dev/null +++ b/AI-MODELS-DATABASE-CONFIGURATION-PLAN.md @@ -0,0 +1,1124 @@ +**mkae sure to use exact max tokens and corret syntax based on differnet model for max tokens, adn make it configurebale in backeend for max tokens per ai fucntion. + +# AI MODELS DATABASE CONFIGURATION - IMPLEMENTATION PLAN + +**Date**: December 24, 2025 +**Status**: Planning Phase +**Priority**: HIGH - Architecture Enhancement + +--- + +## EXECUTIVE SUMMARY + +Move AI model pricing from hardcoded constants (`MODEL_RATES`, `IMAGE_MODEL_RATES`) to database-driven configuration via new `AIModelConfig` model. This enables dynamic pricing updates, multi-provider support, and full Django Admin control without code deployments. + +--- + +## CRITICAL UNDERSTANDING: TWO DIFFERENT CREDIT CALCULATION METHODS + +### **METHOD 1: TEXT MODELS (Token-Based Calculation)** + +**How It Works:** +1. User triggers AI function (clustering, content generation, ideas, etc.) +2. Request sent to OpenAI with prompt +3. OpenAI returns response with **actual token usage**: + - `input_tokens`: 2518 (tokens in the prompt) + - `output_tokens`: 242 (tokens in the response) + - `model`: "gpt-4o-mini" +4. **Backend calculates credits AFTER AI call** based on: + - Total tokens = input_tokens + output_tokens + - Configuration: `CreditCostConfig.tokens_per_credit` (e.g., 150) + - Formula: `credits = CEIL(total_tokens ÷ tokens_per_credit)` + - Apply minimum: `MAX(calculated_credits, min_credits)` +5. Credits deducted based on **actual usage**, not estimate + +**Example:** +``` +Operation: Clustering +Tokens: 2518 input + 242 output = 2760 total +Config: 150 tokens per credit +Calculation: 2760 ÷ 150 = 18.4 → CEIL = 19 credits +Min Credits: 10 +Final: MAX(19, 10) = 19 credits charged +``` + +**Models Using This Method:** +- gpt-4.1 +- gpt-4o-mini +- gpt-4o +- gpt-5.1 +- gpt-5.2 +- All text generation models + +**Key Point:** Credits are **NOT known until after AI response** because we need actual token usage. + +--- + +### **METHOD 2: IMAGE MODELS (Per-Image Fixed Cost)** + +**How It Works:** +1. User triggers image generation +2. **Credits calculated BEFORE AI call** based on: + - Number of images requested (n=1, 2, 3, 4) + - Image size (1024x1024, 1024x1792, 1792x1024, etc.) + - Model (dall-e-2, dall-e-3) +3. Fixed cost per image from configuration +4. Credits deducted before generation +5. No token calculation involved + +**Example:** +``` +Operation: Generate 2 images +Model: dall-e-3 +Size: 1024x1792 +Config: 5 credits per image (from CreditCostConfig.min_credits) +Calculation: 2 images × 5 credits = 10 credits +Final: 10 credits charged (known before AI call) +``` + +**Models Using This Method:** +- dall-e-2 +- dall-e-3 +- gpt-image-1 +- gpt-image-1-mini + +**Key Point:** Credits are **known before AI call** because it's a fixed rate per image. + +--- + +## WHY THIS MATTERS FOR THE DATABASE MODEL + +The `AIModelConfig` model must support BOTH calculation methods: + +| Field | Text Models | Image Models | +|-------|-------------|--------------| +| `input_cost_per_1m` | ✅ Required | ❌ Not Used | +| `output_cost_per_1m` | ✅ Required | ❌ Not Used | +| `cost_per_image` | ❌ Not Used | ✅ Required | +| `valid_sizes` | ❌ Not Used | ✅ Required (JSON) | +| `context_window` | ✅ Required | ❌ Not Used | +| `max_output_tokens` | ✅ Required | ❌ Not Used | + +**Credit Calculation Logic:** +``` +IF model_type == 'text': + # AFTER AI call + total_tokens = input_tokens + output_tokens + cost_usd = (input_tokens × input_cost_per_1m + output_tokens × output_cost_per_1m) ÷ 1,000,000 + credits = calculate_from_tokens(total_tokens, operation_config) + +ELIF model_type == 'image': + # BEFORE AI call + cost_usd = cost_per_image × num_images + credits = min_credits_per_image × num_images # From CreditCostConfig +``` + +--- + +## PHASE 1: CREATE NEW DATABASE MODEL + +**File:** `backend/igny8_core/business/billing/models.py` + +**New Model:** `AIModelConfig` + +### **Field Specifications** + +#### Basic Information +- `model_name` (CharField, max_length=100, unique=True) + - Examples: "gpt-4o-mini", "dall-e-3", "gpt-5.1" + - Used in API calls and configuration + +- `display_name` (CharField, max_length=200) + - Examples: "GPT-4o mini - Fast & Affordable", "DALL-E 3 - High Quality Images" + - Shown in Django Admin and frontend dropdowns + +- `model_type` (CharField, max_length=20, choices) + - Choices: "text", "image", "embedding" + - Determines which pricing fields are used + +- `provider` (CharField, max_length=50, choices) + - Choices: "openai", "anthropic", "runware", "google" + - Future-proof for multi-provider support + +#### Text Model Pricing (Only for model_type='text') +- `input_cost_per_1m` (DecimalField, max_digits=10, decimal_places=4, null=True) + - Cost per 1 million input tokens (USD) + - Example: 0.15 for gpt-4o-mini + +- `output_cost_per_1m` (DecimalField, max_digits=10, decimal_places=4, null=True) + - Cost per 1 million output tokens (USD) + - Example: 0.60 for gpt-4o-mini + +- `context_window` (IntegerField, null=True) + - Maximum input tokens (context length) + - Example: 16000, 128000 + +- `max_output_tokens` (IntegerField, null=True) + - Maximum output tokens per request + - Example: 4096, 16000 + +#### Image Model Pricing (Only for model_type='image') +- `cost_per_image` (DecimalField, max_digits=10, decimal_places=4, null=True) + - Fixed cost per image generation (USD) + - Example: 0.040 for dall-e-3 + +- `valid_sizes` (JSONField, null=True, blank=True) + - Array of valid image sizes for this model + - Example: `["1024x1024", "1024x1792", "1792x1024"]` for dall-e-3 + - Example: `["256x256", "512x512", "1024x1024"]` for dall-e-2 + +#### Capabilities +- `supports_json_mode` (BooleanField, default=False) + - True for: gpt-4o, gpt-4o-mini, gpt-4-turbo-preview, gpt-5.1, gpt-5.2 + +- `supports_vision` (BooleanField, default=False) + - True for models that can analyze images + +- `supports_function_calling` (BooleanField, default=False) + - True for models with function calling capability + +#### Status & Configuration +- `is_active` (BooleanField, default=True) + - Enable/disable model without deleting + +- `is_default` (BooleanField, default=False) + - Mark as default model for its type + - Only one can be True per model_type + +- `sort_order` (IntegerField, default=0) + - Control order in dropdown lists + - Lower numbers appear first + +#### Metadata +- `description` (TextField, blank=True) + - Admin notes about model usage, strengths, limitations + +- `release_date` (DateField, null=True, blank=True) + - When model was released/added + +- `deprecation_date` (DateField, null=True, blank=True) + - When model will be removed + +#### Audit Fields +- `created_at` (DateTimeField, auto_now_add=True) +- `updated_at` (DateTimeField, auto_now=True) +- `updated_by` (ForeignKey to User, null=True, on_delete=SET_NULL) + +### **Model Meta** +``` +app_label = 'billing' +db_table = 'igny8_ai_model_config' +verbose_name = 'AI Model Configuration' +verbose_name_plural = 'AI Model Configurations' +ordering = ['model_type', 'sort_order', 'model_name'] + +indexes: + - ['model_type', 'is_active'] + - ['provider', 'is_active'] + - ['is_default', 'model_type'] + +constraints: + - unique_together: None (model_name is unique) + - check: Ensure correct pricing fields based on model_type +``` + +### **Model Methods** +- `__str__()` - Return display_name +- `save()` - Ensure only one is_default per model_type +- `get_cost_for_tokens(input_tokens, output_tokens)` - Calculate cost for text models +- `get_cost_for_images(num_images)` - Calculate cost for image models +- `validate_size(size)` - Check if size is valid for this model +- `get_display_with_pricing()` - For dropdowns: "GPT-4o mini - $0.15/$0.60 per 1M" + +--- + +## PHASE 2: CREATE MIGRATION WITH SEED DATA + +**File:** `backend/igny8_core/business/billing/migrations/00XX_create_ai_model_config.py` + +### **Migration Steps** + +1. **Create Table** - `AIModelConfig` with all fields + +2. **Seed Text Models** (from current MODEL_RATES): + ``` + gpt-4.1: + display_name: "GPT-4.1 - $2.00 / $8.00 per 1M tokens" + model_type: text + provider: openai + input_cost_per_1m: 2.00 + output_cost_per_1m: 8.00 + context_window: 8192 + max_output_tokens: 4096 + supports_json_mode: False + is_active: True + is_default: False + sort_order: 10 + + gpt-4o-mini: + display_name: "GPT-4o mini - $0.15 / $0.60 per 1M tokens" + model_type: text + provider: openai + input_cost_per_1m: 0.15 + output_cost_per_1m: 0.60 + context_window: 128000 + max_output_tokens: 16000 + supports_json_mode: True + is_active: True + is_default: True ← DEFAULT + sort_order: 1 + + gpt-4o: + display_name: "GPT-4o - $2.50 / $10.00 per 1M tokens" + model_type: text + provider: openai + input_cost_per_1m: 2.50 + output_cost_per_1m: 10.00 + context_window: 128000 + max_output_tokens: 4096 + supports_json_mode: True + supports_vision: True + is_active: True + is_default: False + sort_order: 5 + + gpt-5.1: + display_name: "GPT-5.1 - $1.25 / $10.00 per 1M tokens (16K)" + model_type: text + provider: openai + input_cost_per_1m: 1.25 + output_cost_per_1m: 10.00 + context_window: 16000 + max_output_tokens: 16000 + supports_json_mode: True + is_active: True + is_default: False + sort_order: 20 + + gpt-5.2: + display_name: "GPT-5.2 - $1.75 / $14.00 per 1M tokens (16K)" + model_type: text + provider: openai + input_cost_per_1m: 1.75 + output_cost_per_1m: 14.00 + context_window: 16000 + max_output_tokens: 16000 + supports_json_mode: True + is_active: True + is_default: False + sort_order: 30 + ``` + +3. **Seed Image Models** (from current IMAGE_MODEL_RATES): + ``` + dall-e-3: + display_name: "DALL-E 3 - High Quality - $0.040 per image" + model_type: image + provider: openai + cost_per_image: 0.040 + valid_sizes: ["1024x1024", "1024x1792", "1792x1024"] + is_active: True + is_default: True ← DEFAULT + sort_order: 1 + + dall-e-2: + display_name: "DALL-E 2 - Standard - $0.020 per image" + model_type: image + provider: openai + cost_per_image: 0.020 + valid_sizes: ["256x256", "512x512", "1024x1024"] + is_active: True + is_default: False + sort_order: 10 + + gpt-image-1: + display_name: "GPT Image 1 - $0.042 per image" + model_type: image + provider: openai + cost_per_image: 0.042 + valid_sizes: ["1024x1024"] + is_active: False ← Not valid for OpenAI endpoint + is_default: False + sort_order: 20 + + gpt-image-1-mini: + display_name: "GPT Image 1 Mini - $0.011 per image" + model_type: image + provider: openai + cost_per_image: 0.011 + valid_sizes: ["1024x1024"] + is_active: False ← Not valid for OpenAI endpoint + is_default: False + sort_order: 30 + ``` + +--- + +## PHASE 3: DJANGO ADMIN CONFIGURATION + +**File:** `backend/igny8_core/business/billing/admin.py` + +**Admin Class:** `AIModelConfigAdmin` + +### **List View Configuration** + +**list_display:** +- `model_name` +- `display_name` +- `model_type_badge` (colored badge) +- `provider_badge` (colored badge) +- `pricing_display` (formatted based on type) +- `is_active_icon` (boolean icon) +- `is_default_icon` (star icon) +- `sort_order` +- `updated_at` + +**list_filter:** +- `model_type` +- `provider` +- `is_active` +- `is_default` +- `supports_json_mode` +- `supports_vision` +- `supports_function_calling` + +**search_fields:** +- `model_name` +- `display_name` +- `description` + +**ordering:** +- `model_type`, `sort_order`, `model_name` + +### **Form Configuration** + +**Fieldsets:** + +1. **Basic Information** + - model_name (with help text about API usage) + - display_name (shown in UI) + - model_type (radio buttons: text/image/embedding) + - provider (dropdown) + - description (textarea) + +2. **Text Model Pricing** (show only if model_type='text') + - input_cost_per_1m (with $ prefix) + - output_cost_per_1m (with $ prefix) + - context_window (with "tokens" suffix) + - max_output_tokens (with "tokens" suffix) + +3. **Image Model Pricing** (show only if model_type='image') + - cost_per_image (with $ prefix) + - valid_sizes (JSON editor with validation) + +4. **Capabilities** + - supports_json_mode (checkbox) + - supports_vision (checkbox) + - supports_function_calling (checkbox) + +5. **Status & Display** + - is_active (checkbox) + - is_default (checkbox with warning) + - sort_order (number input) + +6. **Metadata** + - release_date (date picker) + - deprecation_date (date picker) + +7. **Audit** (readonly) + - created_at + - updated_at + - updated_by + +### **Admin Actions** + +1. **bulk_activate** - Enable selected models +2. **bulk_deactivate** - Disable selected models +3. **set_as_default** - Set one model as default for its type +4. **test_model_connection** - Test if model is accessible via API +5. **export_pricing_table** - Export all models and pricing to CSV + +### **Custom Methods** + +**pricing_display(obj):** +``` +If model_type == 'text': + return f"${obj.input_cost_per_1m}/${obj.output_cost_per_1m} per 1M" +If model_type == 'image': + return f"${obj.cost_per_image} per image" +``` + +**Custom save() override:** +- If `is_default=True`, unset other defaults for same model_type +- Validate pricing fields based on model_type +- Log changes to admin log + +--- + +## PHASE 4: UPDATE AI CORE (TEXT MODELS) + +**File:** `backend/igny8_core/ai/ai_core.py` + +### **Function:** `run_ai_request()` (line ~93-350) + +**Current Implementation:** +``` +Line 16: from .constants import MODEL_RATES +Line 294: rates = MODEL_RATES.get(active_model, {'input': 2.00, 'output': 8.00}) +Line 295: cost = (input_tokens × rates['input'] + output_tokens × rates['output']) ÷ 1_000_000 +``` + +**New Implementation:** + +**Add new helper function:** +``` +Function: get_model_pricing(model_name) +Location: After __init__, before run_ai_request +Returns: AIModelConfig instance or None +Purpose: Query database and cache result +``` + +**Update line 16:** +- Remove: `from .constants import MODEL_RATES` +- Add: `from igny8_core.business.billing.models import AIModelConfig` + +**Update line 161 (model validation):** +- Replace: `if active_model not in MODEL_RATES:` +- With: Query `AIModelConfig.objects.filter(model_name=active_model, model_type='text', is_active=True).exists()` + +**Update line 294 (cost calculation):** +- Replace: `rates = MODEL_RATES.get(...)` +- With: Query `AIModelConfig.objects.get(model_name=active_model)` +- Calculate: `cost = (input_tokens × model.input_cost_per_1m + output_tokens × model.output_cost_per_1m) ÷ 1_000_000` + +**Update line 819 (cost estimation):** +- Same replacement for `MODEL_RATES.get()` + +**Add caching (optional optimization):** +``` +Cache model configs in memory for 5 minutes +Key: f"ai_model_config:{model_name}" +Reduces database queries +``` + +--- + +## PHASE 5: UPDATE IMAGE GENERATION + +**File:** `backend/igny8_core/ai/ai_core.py` + +### **Function:** `generate_image()` (line ~400-600) + +**Current Implementation:** +``` +Line 17: from .constants import IMAGE_MODEL_RATES +Line 581: cost = IMAGE_MODEL_RATES.get(model, 0.040) × n +``` + +**New Implementation:** + +**Update line 17:** +- Remove: `from .constants import IMAGE_MODEL_RATES` +- Already have: `AIModelConfig` imported + +**Update size validation:** +- Add function: `validate_image_size(model_name, size)` +- Query: `AIModelConfig.objects.get(model_name=model_name)` +- Check: `size in model.valid_sizes` + +**Update line 581 (cost calculation):** +- Replace: `cost = IMAGE_MODEL_RATES.get(model, 0.040) × n` +- With: + ``` + model_config = AIModelConfig.objects.get(model_name=model, model_type='image') + cost = model_config.cost_per_image × n + ``` + +**Add validation:** +- Ensure model is_active=True +- Ensure model.valid_sizes includes requested size +- Raise clear error if model not found + +--- + +## PHASE 6: UPDATE VALIDATORS + +**File:** `backend/igny8_core/ai/validators.py` + +### **Function:** `validate_model()` (line ~147-155) + +**Current Implementation:** +``` +Line 147: from .constants import MODEL_RATES, VALID_OPENAI_IMAGE_MODELS +Line 150: if model not in MODEL_RATES: +``` + +**New Implementation:** + +**Replace line 147:** +- Remove: `from .constants import MODEL_RATES` +- Add: `from igny8_core.business.billing.models import AIModelConfig` + +**Replace line 150:** +``` +exists = AIModelConfig.objects.filter( + model_name=model, + model_type='text', + is_active=True +).exists() + +if not exists: + return { + 'valid': False, + 'error': f'Invalid model: {model}. Check available models in Django Admin.' + } +``` + +### **Add new function:** `validate_image_model_and_size(model, size)` + +**Purpose:** Validate image model and size together + +**Implementation:** +``` +Query: AIModelConfig.objects.get(model_name=model, model_type='image', is_active=True) +Check: size in model.valid_sizes +Return: {'valid': True/False, 'error': '...', 'model': model_config} +``` + +--- + +## PHASE 7: UPDATE GLOBAL SETTINGS + +**File:** `backend/igny8_core/modules/system/global_settings_models.py` + +### **Model:** `GlobalIntegrationSettings` + +**Current Field (line ~86):** +``` +openai_model = CharField( + max_length=100, + default='gpt-4o-mini', + choices=[ + ('gpt-4.1', 'GPT-4.1 - $2.00 / $8.00'), + ('gpt-4o-mini', 'GPT-4o mini - $0.15 / $0.60'), + ... + ] +) +``` + +**New Implementation:** + +**Keep CharField but make choices dynamic:** + +**Add method:** +``` +Function: get_text_model_choices() +Returns: List of (model_name, display_name) tuples +Query: AIModelConfig.objects.filter(model_type='text', is_active=True) +Order: By sort_order +``` + +**Update admin widget:** +``` +Use custom widget that loads choices from get_text_model_choices() +Refreshes on page load +Shows current pricing in dropdown +``` + +**Add new fields (optional):** +``` +dalle_model = CharField (for image generation default) +anthropic_model = CharField (for future Anthropic support) +``` + +**Add validation:** +``` +Clean method: Validate selected model exists in AIModelConfig +Save method: Ensure model is active +``` + +--- + +## PHASE 8: UPDATE INTEGRATION SETTINGS + +**File:** `backend/igny8_core/modules/system/models.py` + +### **Model:** `IntegrationSettings` + +**Current:** Model stored in config JSON: `{'model': 'gpt-4o-mini'}` + +**New Implementation:** + +**Add validation method:** +``` +Function: clean_config() +Purpose: Validate model in config exists and is active +Check: AIModelConfig.objects.filter(model_name=config['model'], is_active=True) +Raise: ValidationError if invalid +``` + +**Update admin:** +``` +Show available models in help text +Link to AIModelConfig admin for model management +``` + +--- + +## PHASE 9: CREATE API ENDPOINT + +**File:** `backend/igny8_core/api/ai/` (create directory if needed) + +### **New File:** `views.py` + +**ViewSet:** `AIModelViewSet(ReadOnlyModelViewSet)` + +**Endpoint:** `/api/v1/ai/models/` + +**Methods:** +- `list()` - Get all models with filters +- `retrieve()` - Get single model by name + +**Query Filters:** +- `?type=text` - Filter by model_type +- `?type=image` +- `?provider=openai` +- `?active=true` - Only active models +- `?default=true` - Only default models + +**Response Format:** +```json +{ + "count": 5, + "results": [ + { + "model_name": "gpt-4o-mini", + "display_name": "GPT-4o mini - $0.15 / $0.60 per 1M tokens", + "model_type": "text", + "provider": "openai", + "input_cost_per_1m": "0.1500", + "output_cost_per_1m": "0.6000", + "context_window": 128000, + "max_output_tokens": 16000, + "supports_json_mode": true, + "supports_vision": false, + "is_default": true, + "sort_order": 1 + } + ] +} +``` + +**Permissions:** +- List: Authenticated users +- Retrieve: Authenticated users +- Create/Update/Delete: Admin only (via Django Admin) + +**Serializer:** `AIModelConfigSerializer` +- Include all relevant fields +- Exclude audit fields from API +- Add computed field: `pricing_display` + +### **Register in URLs:** + +**File:** `backend/igny8_core/urls.py` or appropriate router + +``` +router.register(r'ai/models', AIModelViewSet, basename='ai-models') +``` + +--- + +## PHASE 10: UPDATE SETTINGS API + +**File:** `backend/igny8_core/ai/settings.py` + +### **Function:** `get_model_config()` (line ~20-110) + +**Current Implementation:** +- Returns model from GlobalIntegrationSettings or account override +- Validates against hardcoded MODEL_RATES + +**New Implementation:** + +**Update model resolution:** +``` +1. Check account IntegrationSettings override +2. If no override, get from GlobalIntegrationSettings +3. Query AIModelConfig for selected model +4. Validate model exists and is_active=True +5. Return model configuration +``` + +**Update validation:** +- Replace: `if model not in MODEL_RATES:` +- With: Query `AIModelConfig` and check exists() + +**Return enhanced config:** +```python +{ + 'model': model_config.model_name, + 'max_tokens': model_config.max_output_tokens, + 'temperature': 0.7, # From settings + 'context_window': model_config.context_window, + 'supports_json_mode': model_config.supports_json_mode, + 'pricing': { + 'input': model_config.input_cost_per_1m, + 'output': model_config.output_cost_per_1m + } +} +``` + +--- + +## PHASE 11: DEPRECATE CONSTANTS + +**File:** `backend/igny8_core/ai/constants.py` + +**Current:** Contains MODEL_RATES and IMAGE_MODEL_RATES dicts + +**New Implementation:** + +**Add deprecation warnings:** +```python +""" +DEPRECATED: MODEL_RATES and IMAGE_MODEL_RATES are deprecated. +Use AIModelConfig model instead: billing.models.AIModelConfig +This file will be removed in version X.X.X +""" + +import warnings + +MODEL_RATES = { + # ... existing data ... +} + +def get_model_rate(model): + warnings.warn( + "MODEL_RATES is deprecated. Use AIModelConfig.objects.get(model_name=model)", + DeprecationWarning, + stacklevel=2 + ) + return MODEL_RATES.get(model) +``` + +**Keep for backward compatibility:** +- Don't remove immediately +- Mark as deprecated in docstrings +- Plan removal in next major version +- All new code should use AIModelConfig + +**Update imports across codebase:** +- Search for: `from .constants import MODEL_RATES` +- Update to: `from igny8_core.business.billing.models import AIModelConfig` + +--- + +## PHASE 12: UPDATE REPORTS + +**Files:** +- `backend/igny8_core/modules/reports/views.py` +- `backend/igny8_core/modules/reports/ai_cost_analysis.py` + +**Current:** May reference MODEL_RATES for display + +**New Implementation:** + +**Use AIModelConfig for display:** +```python +# Get model display name +model_config = AIModelConfig.objects.get(model_name=model_used) +display_name = model_config.display_name + +# Show model capabilities +supports_json = model_config.supports_json_mode +``` + +**Cost calculations:** +- Already using `CreditUsageLog.cost_usd` (correct) +- No changes needed to calculation logic +- Only update display/filtering + +**Add model metadata to reports:** +- Context window in "Model Details" section +- Pricing in "Cost Breakdown" section +- Capabilities in "Model Comparison" table + +--- + +## PHASE 13: UPDATE TESTS + +### **New Test File:** `test_ai_model_config.py` + +**Test Cases:** +1. Create text model with valid pricing +2. Create image model with valid pricing +3. Validate only one default per type +4. Test cost calculation methods +5. Test size validation for images +6. Test model activation/deactivation + +### **Update Existing Tests:** + +**Files:** +- `backend/igny8_core/business/billing/tests/test_credit_service.py` +- `backend/igny8_core/ai/tests/test_ai_core.py` +- `backend/igny8_core/api/tests/test_ai_framework.py` + +**Changes:** +- Create AIModelConfig fixtures in setUp() +- Replace MODEL_RATES mocks with database records +- Update assertions for database queries +- Test dynamic model loading + +### **API Tests:** `test_ai_model_api.py` + +**Test Cases:** +1. List all models +2. Filter by type +3. Filter by provider +4. Get default model +5. Permissions (readonly for users) + +--- + +## PHASE 14: DATA MIGRATION STRATEGY + +### **For Existing Production Data:** + +**No Schema Changes Needed:** +- `CreditUsageLog.model_used` already stores model name +- `CreditUsageLog.cost_usd` already stores actual cost +- Historical data remains accurate + +**Migration Steps:** +1. ✅ Deploy migration (creates table, seeds data) +2. ✅ Code continues using constants (no breaking changes) +3. ✅ Gradually switch code to database (per function) +4. ✅ Monitor for issues (rollback to constants if needed) +5. ✅ Mark constants as deprecated +6. ✅ Remove constants in next major version + +**Rollback Plan:** +- If issues occur, code falls back to constants +- AIModelConfig table can be dropped without data loss +- No impact on existing credit calculations + +### **For Zero-Downtime Deployment:** + +**Step 1:** Deploy migration only +```bash +python manage.py migrate billing +# Creates AIModelConfig table, seeds data +# Code still uses constants - no breaking changes +``` + +**Step 2:** Deploy code that reads from both +```python +def get_model_pricing(model_name): + try: + # Try database first + return AIModelConfig.objects.get(model_name=model_name) + except AIModelConfig.DoesNotExist: + # Fallback to constants + return MODEL_RATES.get(model_name) +``` + +**Step 3:** Monitor and verify +- Check logs for database queries +- Verify cost calculations match +- Compare with constant-based calculations + +**Step 4:** Remove constant fallbacks +- After verification period (1-2 weeks) +- All code now uses database only + +--- + +## PHASE 15: FRONTEND UPDATES + +### **File:** `frontend/src/pages/Settings/AI.tsx` + +**Current:** Hardcoded model dropdown + +**New Implementation:** + +**Add API call:** +```typescript +const { data: models } = useQuery('/api/v1/ai/models/?type=text&active=true') +``` + +**Update dropdown:** +```tsx + +``` + +**Show model details:** +- Context window +- Max output tokens +- JSON mode support +- Pricing (input/output costs) + +### **File:** `frontend/src/pages/Settings/Integration.tsx` + +**Current:** Shows current model from GlobalIntegrationSettings + +**New Implementation:** + +**Display model information:** +```tsx + +

{model.display_name}

+

Provider: {model.provider}

+

Context: {model.context_window.toLocaleString()} tokens

+

Pricing: ${model.input_cost_per_1m}/${model.output_cost_per_1m} per 1M

+ {model.supports_json_mode && JSON Mode} + {model.supports_vision && Vision} +
+``` + +**Add model comparison:** +- Show all available models in table +- Compare pricing side-by-side +- Help users choose best model for their needs + +--- + +## BENEFITS OF THIS IMPLEMENTATION + +### **Operational Benefits** +1. ✅ **No Code Deploys for Pricing Updates** - Update costs in Django Admin +2. ✅ **Multi-Provider Ready** - Easy to add Anthropic, Google, etc. +3. ✅ **Model Testing** - Enable/disable models without code changes +4. ✅ **Granular Control** - Different models for different accounts/plans + +### **Technical Benefits** +5. ✅ **Backward Compatible** - Existing code works during migration +6. ✅ **Zero Downtime** - Gradual migration strategy +7. ✅ **Fully Tested** - Comprehensive test coverage +8. ✅ **Audit Trail** - Track all pricing changes with timestamps + +### **Business Benefits** +9. ✅ **Dynamic Pricing** - React quickly to OpenAI price changes +10. ✅ **Cost Forecasting** - Accurate model cost data for projections +11. ✅ **Model Analytics** - Track usage and costs per model +12. ✅ **A/B Testing** - Easy to test new models with subset of users + +### **User Benefits** +13. ✅ **Model Selection** - Users can choose model based on their needs +14. ✅ **Transparent Pricing** - See exact costs before using models +15. ✅ **Better Control** - Enterprise accounts can restrict models +16. ✅ **Latest Models** - Access new models as soon as they're added + +--- + +## IMPLEMENTATION TIMELINE + +### **Week 1: Foundation** +- Day 1-2: Create AIModelConfig model and migration +- Day 3: Create Django Admin interface +- Day 4-5: Seed data and test in development + +### **Week 2: Backend Integration** +- Day 1-2: Update ai_core.py to query database +- Day 3: Update validators and settings +- Day 4-5: Create API endpoint and serializers + +### **Week 3: Testing & Migration** +- Day 1-2: Write comprehensive tests +- Day 3: Test migration on staging +- Day 4-5: Deploy to production with monitoring + +### **Week 4: Frontend & Cleanup** +- Day 1-2: Update frontend to use new API +- Day 3: Add deprecation warnings to constants +- Day 4-5: Documentation and training + +--- + +## FILES AFFECTED SUMMARY + +### **New Files** (4) +1. Migration: `billing/migrations/00XX_create_ai_model_config.py` +2. Tests: `billing/tests/test_ai_model_config.py` +3. API Views: `api/ai/views.py` +4. API Tests: `api/tests/test_ai_model_api.py` + +### **Modified Files** (12) +1. `billing/models.py` - Add AIModelConfig model +2. `billing/admin.py` - Add AIModelConfigAdmin +3. `ai/ai_core.py` - Replace MODEL_RATES with database queries +4. `ai/validators.py` - Update model validation +5. `ai/settings.py` - Update get_model_config() +6. `ai/constants.py` - Add deprecation warnings +7. `system/global_settings_models.py` - Dynamic model choices +8. `system/models.py` - Validate model overrides +9. `reports/views.py` - Use AIModelConfig for display +10. Frontend: `Settings/AI.tsx` +11. Frontend: `Settings/Integration.tsx` +12. URLs: Register new API endpoint + +### **Total Changes** +- 1 new database model +- 1 new admin interface +- 1 new API endpoint +- 4 new test files +- 12 files updated +- ~500-800 lines of code +- All existing data preserved +- Zero downtime migration + +--- + +## ROLLBACK PLAN + +**If Issues Occur:** + +1. **Database:** Keep AIModelConfig table (no harm) +2. **Code:** Revert to using constants +3. **Data:** No CreditUsageLog changes, all historical data intact +4. **Time:** Can rollback in < 5 minutes + +**Indicators for Rollback:** +- Model queries timing out +- Incorrect cost calculations +- Missing models causing errors +- Performance degradation + +**Prevention:** +- Thorough testing on staging first +- Monitor logs and metrics closely +- Keep constants for 2-4 weeks as backup +- Gradual rollout to production + +--- + +## SUCCESS METRICS + +### **Technical Metrics** +- ✅ All tests passing (100% coverage for new code) +- ✅ Database query time < 10ms +- ✅ API response time < 100ms +- ✅ Zero downtime during deployment + +### **Operational Metrics** +- ✅ Admin can add new model in < 2 minutes +- ✅ Pricing update takes < 1 minute +- ✅ Model enable/disable is instant +- ✅ No code deploys needed for model changes + +### **Business Metrics** +- ✅ Cost tracking accuracy: 100% +- ✅ Model usage data: Available in real-time +- ✅ Time to market for new models: < 1 day (vs 1 week) +- ✅ Pricing error rate: 0% + +--- + +**END OF PLAN**