**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
<Select>
  {models.map(model => (
    <option value={model.model_name}>
      {model.display_name}
    </option>
  ))}
</Select>
```

**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
<ModelCard>
  <h3>{model.display_name}</h3>
  <p>Provider: {model.provider}</p>
  <p>Context: {model.context_window.toLocaleString()} tokens</p>
  <p>Pricing: ${model.input_cost_per_1m}/${model.output_cost_per_1m} per 1M</p>
  {model.supports_json_mode && <Badge>JSON Mode</Badge>}
  {model.supports_vision && <Badge>Vision</Badge>}
</ModelCard>
```

**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**
