# IGNY8 AI System Audit — Current Structure & Flow Mapping (Baseline Report) **Date:** 2024-12-19 **Scope:** Complete structural and functional audit of the IGNY8 AI subsystem **Methodology:** Code analysis without modifications or assumptions --- ## Executive Summary The IGNY8 AI subsystem is a comprehensive framework for AI-powered content operations including keyword clustering, idea generation, content generation, and image generation. The system uses a unified framework architecture with a centralized execution engine, but also maintains legacy code paths for backward compatibility. **Key Findings:** - **20 core AI files** in `backend/igny8_core/ai/` - **4 AI function implementations** following BaseAIFunction pattern - **Dual execution paths:** New unified framework (`run_ai_task` → `AIEngine`) and legacy paths (direct task calls) - **Centralized AI request handling** via `AICore.run_ai_request()` - **Comprehensive tracking** via `StepTracker`, `ProgressTracker`, `CostTracker`, and `ConsoleStepTracker` - **Multiple prompt management systems:** `PromptRegistry` (new) and direct database queries (legacy) --- ## 1. Current File Inventory ### 1.1 Core AI Framework (`backend/igny8_core/ai/`) | File | Lines | Purpose | |------|-------|---------| | `__init__.py` | 73 | Package exports - exposes main classes and functions | | `admin.py` | 60 | Django admin configuration for `AITaskLog` model | | `ai_core.py` | 756 | Centralized AI request handler - `AICore` class with `run_ai_request()` and `generate_image()` | | `apps.py` | 21 | Django app configuration | | `base.py` | 95 | Abstract base class `BaseAIFunction` - defines function interface | | `constants.py` | 42 | Model pricing, valid models, configuration constants | | `engine.py` | 375 | Central orchestrator `AIEngine` - manages function lifecycle | | `models.py` | 52 | Database model `AITaskLog` for unified logging | | `processor.py` | 76 | **DEPRECATED** wrapper around `AICore` for backward compatibility | | `prompts.py` | 432 | `PromptRegistry` - centralized prompt management with hierarchical resolution | | `registry.py` | 97 | Function registry with lazy loading - `register_function()`, `get_function()` | | `settings.py` | 117 | Model configurations per function - `MODEL_CONFIG`, `get_model_config()` | | `tasks.py` | 131 | Unified Celery task entrypoint `run_ai_task()` - single entry point for all AI functions | | `tracker.py` | 348 | Progress tracking utilities - `StepTracker`, `ProgressTracker`, `CostTracker`, `ConsoleStepTracker` | | `types.py` | 44 | Type definitions - `StepLog`, `ProgressState`, `AITaskResult` dataclasses | | `validators.py` | 187 | Validation functions - `validate_ids()`, `validate_keywords_exist()`, etc. | ### 1.2 AI Function Implementations (`backend/igny8_core/ai/functions/`) | File | Lines | Purpose | |------|-------|---------| | `__init__.py` | 18 | Function package exports | | `auto_cluster.py` | 330 | `AutoClusterFunction` - Groups keywords into semantic clusters | | `generate_content.py` | 388 | `GenerateContentFunction` + `generate_content_core()` - Generates article content | | `generate_ideas.py` | 335 | `GenerateIdeasFunction` + `generate_ideas_core()` - Generates content ideas from clusters | | `generate_images.py` | 279 | `GenerateImagesFunction` + `generate_images_core()` - Generates images for tasks | ### 1.3 Utility Files (`backend/igny8_core/utils/`) | File | Lines | Purpose | |------|-------|---------| | `ai_processor.py` | 1407 | **LEGACY** Unified AI interface - `AIProcessor` class with OpenAI/Runware support. Contains duplicate constants and methods. | | `content_normalizer.py` | 273 | Content normalization - converts AI responses to HTML format | | `queue_manager.py` | 90 | Queue abstraction (currently placeholder, not fully implemented) | | `wordpress.py` | (not read) | WordPress integration utilities | ### 1.4 Module Task Files | File | Lines | Purpose | |------|-------|---------| | `modules/planner/tasks.py` | 736 | **DEPRECATED** Legacy clustering task `auto_cluster_keywords_task()` - uses old `AIProcessor` | | `modules/writer/tasks.py` | 1156 | Legacy content/image generation tasks - `auto_generate_content_task()`, `auto_generate_images_task()` | ### 1.5 Configuration Files | File | Purpose | |------|---------| | `celery.py` | Celery app configuration - auto-discovers tasks from all Django apps | | `modules/system/models.py` | `AIPrompt`, `IntegrationSettings` models for AI configuration | --- ## 2. Function Inventory ### 2.1 Core Framework Functions #### `AICore` (ai_core.py) - **`__init__(account=None)`** - Initialize with account context, loads API keys and model from `IntegrationSettings` - **`_load_account_settings()`** - Loads OpenAI/Runware API keys and model from `IntegrationSettings` or Django settings - **`get_api_key(integration_type='openai')`** - Returns API key for integration type - **`get_model(integration_type='openai')`** - Returns model name for integration type - **`run_ai_request(prompt, model=None, max_tokens=4000, temperature=0.7, response_format=None, api_key=None, function_name='ai_request', function_id=None, tracker=None)`** - **CENTRAL METHOD** - Handles all OpenAI text generation requests with console logging - **`extract_json(response_text)`** - Extracts JSON from response text (handles markdown code blocks) - **`generate_image(prompt, provider='openai', model=None, size='1024x1024', n=1, api_key=None, negative_prompt=None, function_name='generate_image')`** - Generates images via OpenAI DALL-E or Runware - **`_generate_image_openai(...)`** - Internal method for OpenAI image generation - **`_generate_image_runware(...)`** - Internal method for Runware image generation - **`calculate_cost(model, input_tokens, output_tokens, model_type='text')`** - Calculates API cost - **`call_openai(...)`** - **LEGACY** - Redirects to `run_ai_request()` #### `AIEngine` (engine.py) - **`__init__(celery_task=None, account=None)`** - Initialize with Celery task and account - **`execute(fn: BaseAIFunction, payload: dict)`** - **CENTRAL ORCHESTRATOR** - Unified execution pipeline: - Phase 1: INIT (0-10%) - Validation - Phase 2: PREP (10-25%) - Data loading & prompt building - Phase 3: AI_CALL (25-70%) - API call to provider - Phase 4: PARSE (70-85%) - Response parsing - Phase 5: SAVE (85-98%) - Database operations - Phase 6: DONE (98-100%) - Finalization - **`_handle_error(error, fn=None, exc_info=False)`** - Centralized error handling - **`_log_to_database(fn, payload, parsed, save_result, error=None)`** - Logs to `AITaskLog` model - **`_calculate_credits_for_clustering(keyword_count, tokens, cost)`** - Calculates credits for clustering operations #### `PromptRegistry` (prompts.py) - **`get_prompt(function_name, account=None, task=None, context=None)`** - Hierarchical prompt resolution: 1. Task-level `prompt_override` (if exists) 2. DB prompt for (account, function) 3. Default fallback from registry - **`_render_prompt(prompt_template, context)`** - Renders template with `[IGNY8_*]` placeholders and `{variable}` format - **`get_image_prompt_template(account=None)`** - Gets image prompt template - **`get_negative_prompt(account=None)`** - Gets negative prompt #### `BaseAIFunction` (base.py) - Abstract Interface - **`get_name()`** - Returns function name (abstract) - **`get_metadata()`** - Returns function metadata (display name, description, phases) - **`validate(payload, account=None)`** - Validates input payload (default: checks for 'ids') - **`get_max_items()`** - Returns max items limit (optional) - **`prepare(payload, account=None)`** - Loads and prepares data (abstract) - **`build_prompt(data, account=None)`** - Builds AI prompt (abstract) - **`get_model(account=None)`** - Returns model override (optional) - **`parse_response(response, step_tracker=None)`** - Parses AI response (abstract) - **`save_output(parsed, original_data, account=None, progress_tracker=None, step_tracker=None)`** - Saves results to database (abstract) ### 2.2 AI Function Implementations #### `AutoClusterFunction` (functions/auto_cluster.py) - **`get_name()`** - Returns `'auto_cluster'` - **`validate(payload, account=None)`** - Validates keyword IDs exist - **`prepare(payload, account=None)`** - Loads keywords with relationships - **`build_prompt(data, account=None)`** - Builds clustering prompt using `PromptRegistry` - **`parse_response(response, step_tracker=None)`** - Parses JSON cluster data - **`save_output(parsed, original_data, account=None, ...)`** - Creates/updates clusters and assigns keywords #### `GenerateIdeasFunction` (functions/generate_ideas.py) - **`get_name()`** - Returns `'generate_ideas'` - **`validate(payload, account=None)`** - Validates cluster IDs exist - **`prepare(payload, account=None)`** - Loads clusters with keywords - **`build_prompt(data, account=None)`** - Builds ideas generation prompt - **`parse_response(response, step_tracker=None)`** - Parses JSON ideas data - **`save_output(parsed, original_data, account=None, ...)`** - Creates `ContentIdeas` records #### `GenerateContentFunction` (functions/generate_content.py) - **`get_name()`** - Returns `'generate_content'` - **`validate(payload, account=None)`** - Validates task IDs exist - **`prepare(payload, account=None)`** - Loads tasks with relationships - **`build_prompt(data, account=None)`** - Builds content generation prompt - **`parse_response(response, step_tracker=None)`** - Parses JSON or plain text content - **`save_output(parsed, original_data, account=None, ...)`** - Saves content to `Content` model #### `GenerateImagesFunction` (functions/generate_images.py) - **`get_name()`** - Returns `'generate_images'` - **`validate(payload, account=None)`** - Validates task IDs exist - **`prepare(payload, account=None)`** - Loads tasks and image settings - **`build_prompt(data, account=None)`** - Extracts image prompts from task content (calls AI) - **`parse_response(response, step_tracker=None)`** - Returns parsed response (already parsed) - **`save_output(parsed, original_data, account=None, ...)`** - Creates `Images` records ### 2.3 Tracking Functions #### `StepTracker` (tracker.py) - **`add_request_step(step_name, status='success', message='', error=None, duration=None)`** - Adds request step - **`add_response_step(step_name, status='success', message='', error=None, duration=None)`** - Adds response step - **`get_meta()`** - Returns metadata dict with request/response steps #### `ProgressTracker` (tracker.py) - **`update(phase, percentage, message, current=None, total=None, current_item=None, meta=None)`** - Updates Celery task state - **`set_phase(phase, percentage, message, meta=None)`** - Sets progress phase - **`complete(message='Task complete!', meta=None)`** - Marks task as complete - **`error(error_message, meta=None)`** - Marks task as failed - **`get_duration()`** - Returns elapsed time in milliseconds #### `ConsoleStepTracker` (tracker.py) - **`init(message='Task started')`** - Logs initialization - **`prep(message)`** - Logs preparation phase - **`ai_call(message)`** - Logs AI call phase - **`parse(message)`** - Logs parsing phase - **`save(message)`** - Logs save phase - **`done(message='Execution completed')`** - Logs completion - **`error(error_type, message, exception=None)`** - Logs error - **`retry(attempt, max_attempts, reason='')`** - Logs retry - **`timeout(timeout_seconds)`** - Logs timeout - **`rate_limit(retry_after)`** - Logs rate limit - **`malformed_json(details='')`** - Logs JSON parsing error #### `CostTracker` (tracker.py) - **`record(function_name, cost, tokens, model=None)`** - Records API call cost - **`get_total()`** - Returns total cost - **`get_total_tokens()`** - Returns total tokens - **`get_operations()`** - Returns all operations list ### 2.4 Celery Tasks #### `run_ai_task` (ai/tasks.py) - **`run_ai_task(self, function_name: str, payload: dict, account_id: int = None)`** - **UNIFIED ENTRYPOINT** - Dynamically loads and executes AI functions via `AIEngine` #### Legacy Tasks (modules/*/tasks.py) - **`auto_cluster_keywords_task`** (planner/tasks.py) - **DEPRECATED** - Uses old `AIProcessor` - **`auto_generate_content_task`** (writer/tasks.py) - Uses `AIProcessor` directly (not via framework) - **`auto_generate_images_task`** (writer/tasks.py) - Uses `AIProcessor` directly ### 2.5 Legacy Functions (ai_processor.py) #### `AIProcessor` - **LEGACY/DEPRECATED** - **`_call_openai(prompt, model=None, max_tokens=4000, temperature=0.7, response_format=None, api_key=None, function_id=None, response_steps=None)`** - Internal OpenAI API caller - **`_extract_json_from_response(response_text)`** - JSON extraction (duplicate of `AICore.extract_json()`) - **`generate_content(prompt, model=None, max_tokens=4000, temperature=0.7, **kwargs)`** - Generates text content - **`extract_image_prompts(content, title, max_images=3, account=None)`** - Extracts image prompts from content - **`check_moderation(text, api_key=None)`** - Checks content moderation - **`generate_image(prompt, provider='openai', model=None, size='1024x1024', n=1, api_key=None, **kwargs)`** - Generates images - **`cluster_keywords(keywords, sector_name=None, account=None, response_steps=None, progress_callback=None, tracker=None, **kwargs)`** - **DEPRECATED** - Clusters keywords (old method) - **`generate_ideas(clusters, account=None, **kwargs)`** - Generates ideas (old method) - **`get_prompt(prompt_type, account=None)`** - Gets prompt from database (old method) - **`estimate_cost(operation, tokens_or_prompt, model=None)`** - Estimates cost (not implemented) --- ## 3. Class Inventory ### 3.1 Core Classes | Class | File | Purpose | Inheritance | |-------|------|---------|-------------| | `AICore` | ai_core.py | Centralized AI request handler | - | | `AIEngine` | engine.py | Central orchestrator for AI functions | - | | `BaseAIFunction` | base.py | Abstract base for all AI functions | ABC | | `PromptRegistry` | prompts.py | Centralized prompt management | - | | `StepTracker` | tracker.py | Tracks request/response steps | - | | `ProgressTracker` | tracker.py | Tracks Celery progress updates | - | | `CostTracker` | tracker.py | Tracks API costs and tokens | - | | `ConsoleStepTracker` | tracker.py | Console-based step logging | - | | `AITaskLog` | models.py | Database model for AI task logging | AccountBaseModel | | `AIProcessor` | utils/ai_processor.py | **LEGACY** Unified AI interface | - | ### 3.2 Function Classes | Class | File | Purpose | Inheritance | |-------|------|---------|-------------| | `AutoClusterFunction` | functions/auto_cluster.py | Keyword clustering | BaseAIFunction | | `GenerateIdeasFunction` | functions/generate_ideas.py | Idea generation | BaseAIFunction | | `GenerateContentFunction` | functions/generate_content.py | Content generation | BaseAIFunction | | `GenerateImagesFunction` | functions/generate_images.py | Image generation | BaseAIFunction | ### 3.3 Data Classes | Class | File | Purpose | |-------|------|---------| | `StepLog` | types.py | Single step in request/response tracking | | `ProgressState` | types.py | Progress state for AI tasks | | `AITaskResult` | types.py | Result from AI function execution | --- ## 4. Dependency Graph/Table ### 4.1 Import Relationships ``` ai/__init__.py ├─> registry.py (register_function, get_function, list_functions) ├─> engine.py (AIEngine) ├─> base.py (BaseAIFunction) ├─> ai_core.py (AICore) ├─> validators.py (all validators) ├─> constants.py (all constants) ├─> prompts.py (PromptRegistry, get_prompt) └─> settings.py (MODEL_CONFIG, get_model_config, etc.) ai/tasks.py ├─> engine.py (AIEngine) └─> registry.py (get_function_instance) ai/engine.py ├─> base.py (BaseAIFunction) ├─> tracker.py (StepTracker, ProgressTracker, CostTracker, ConsoleStepTracker) ├─> ai_core.py (AICore) └─> settings.py (get_model_config) ai/ai_core.py ├─> constants.py (MODEL_RATES, IMAGE_MODEL_RATES, etc.) └─> tracker.py (ConsoleStepTracker) ai/functions/auto_cluster.py ├─> base.py (BaseAIFunction) ├─> ai_core.py (AICore) ├─> prompts.py (PromptRegistry) └─> settings.py (get_model_config) ai/functions/generate_content.py ├─> base.py (BaseAIFunction) ├─> ai_core.py (AICore) ├─> prompts.py (PromptRegistry) └─> settings.py (get_model_config) ai/functions/generate_ideas.py ├─> base.py (BaseAIFunction) ├─> ai_core.py (AICore) ├─> prompts.py (PromptRegistry) └─> settings.py (get_model_config) ai/functions/generate_images.py ├─> base.py (BaseAIFunction) ├─> ai_core.py (AICore) ├─> prompts.py (PromptRegistry) └─> settings.py (get_model_config) utils/ai_processor.py ├─> modules/system/models.py (IntegrationSettings) └─> modules/system/utils.py (get_prompt_value, get_default_prompt) modules/planner/tasks.py └─> utils/ai_processor.py (AIProcessor) [DEPRECATED PATH] modules/writer/tasks.py ├─> utils/ai_processor.py (AIProcessor) [LEGACY PATH] └─> ai/functions/generate_content.py (generate_content_core) ``` ### 4.2 External Dependencies | Dependency | Used By | Purpose | |------------|---------|---------| | `django` | All files | Django ORM, models, settings | | `celery` | tasks.py, engine.py | Async task execution | | `requests` | ai_core.py, ai_processor.py | HTTP requests to OpenAI/Runware APIs | | `json` | Multiple files | JSON parsing | | `re` | ai_core.py, ai_processor.py, content_normalizer.py | Regex for JSON extraction | | `logging` | All files | Logging | | `time` | tracker.py, ai_core.py | Timing and duration tracking | | `bs4` (BeautifulSoup) | content_normalizer.py | HTML parsing (optional) | ### 4.3 Database Models Dependencies | Model | Used By | Purpose | |-------|---------|---------| | `AITaskLog` | engine.py | Unified AI task logging | | `IntegrationSettings` | ai_core.py, ai_processor.py, settings.py | API keys and model configuration | | `AIPrompt` | prompts.py | Custom prompt templates | | `Keywords` | auto_cluster.py, validators.py | Keyword data | | `Clusters` | auto_cluster.py, generate_ideas.py | Cluster data | | `ContentIdeas` | generate_ideas.py | Content ideas | | `Tasks` | generate_content.py, generate_images.py | Writer tasks | | `Content` | generate_content.py | Generated content | | `Images` | generate_images.py | Generated images | --- ## 5. System Flow Description ### 5.1 New Unified Framework Flow (Recommended Path) ``` Frontend API Call ↓ ViewSet Action (e.g., planner/views.py::auto_cluster) ↓ run_ai_task.delay(function_name='auto_cluster', payload={ids: [...]}, account_id=123) ↓ Celery Worker: run_ai_task (ai/tasks.py) ├─> Load Account ├─> get_function_instance('auto_cluster') → AutoClusterFunction └─> AIEngine.execute(AutoClusterFunction, payload) ├─> Phase 1: INIT (0-10%) │ └─> fn.validate(payload, account) ├─> Phase 2: PREP (10-25%) │ ├─> fn.prepare(payload, account) → Load keywords │ └─> fn.build_prompt(data, account) → PromptRegistry.get_prompt() ├─> Phase 3: AI_CALL (25-70%) │ ├─> AICore.run_ai_request(prompt, model, ...) │ │ ├─> Load API key from IntegrationSettings │ │ ├─> Validate model │ │ ├─> Build OpenAI request │ │ ├─> Send HTTP request │ │ ├─> Parse response │ │ └─> Calculate cost │ └─> Track cost via CostTracker ├─> Phase 4: PARSE (70-85%) │ └─> fn.parse_response(response_content, step_tracker) ├─> Phase 5: SAVE (85-98%) │ └─> fn.save_output(parsed, original_data, account, ...) │ └─> Database transaction: Create/update clusters └─> Phase 6: DONE (98-100%) ├─> Log to AITaskLog └─> Return result dict ↓ Celery Task State Update (SUCCESS/FAILURE) ↓ Frontend Polls Task Status ↓ Progress Modal Displays Steps ``` ### 5.2 Legacy Content Generation Flow (Still Active) ``` Frontend API Call ↓ ViewSet Action (writer/views.py::auto_generate_content) ↓ auto_generate_content_task.delay(task_ids, account_id) ↓ Celery Worker: auto_generate_content_task (modules/writer/tasks.py) ├─> Load Tasks from database ├─> For each task: │ ├─> Load prompt template (get_prompt_value) │ ├─> Format prompt with task data │ ├─> AIProcessor.generate_content(prompt) │ │ └─> AIProcessor._call_openai() [DUPLICATE OF AICore.run_ai_request] │ ├─> Parse response (GenerateContentFunction.parse_response) │ └─> Save content (GenerateContentFunction.save_output) └─> Return result ``` ### 5.3 Legacy Clustering Flow (Deprecated) ``` Frontend API Call ↓ ViewSet Action (planner/views.py::auto_cluster) [OLD PATH] ↓ _auto_cluster_keywords_core() (modules/planner/tasks.py) ├─> Load keywords ├─> AIProcessor.cluster_keywords() [DEPRECATED] │ └─> AIProcessor._call_openai() [DUPLICATE] ├─> Parse clusters └─> Save to database ``` ### 5.4 Image Generation Flow ``` Frontend API Call ↓ ViewSet Action (writer/views.py::auto_generate_images) ↓ auto_generate_images_task.delay(task_ids, account_id) ↓ Celery Worker: auto_generate_images_task (modules/writer/tasks.py) ├─> Load tasks ├─> For each task: │ ├─> Extract image prompts (AIProcessor.extract_image_prompts) │ │ └─> Calls AI to extract prompts from content │ ├─> Generate featured image (AIProcessor.generate_image) │ ├─> Generate desktop images (if enabled) │ └─> Generate mobile images (if enabled) └─> Save Images records ``` --- ## 6. Integration Points ### 6.1 Celery Integration **Task Registration:** - `celery.py` uses `app.autodiscover_tasks()` to auto-discover tasks from all Django apps - Tasks are registered via `@shared_task` decorator **Registered Tasks:** 1. `ai.tasks.run_ai_task` - Unified entrypoint (NEW) 2. `planner.tasks.auto_cluster_keywords_task` - **DEPRECATED** 3. `writer.tasks.auto_generate_content_task` - Legacy (still active) 4. `writer.tasks.auto_generate_images_task` - Legacy (still active) **Task State Management:** - `ProgressTracker.update()` calls `task.update_state(state='PROGRESS', meta={...})` - Progress metadata includes: `phase`, `percentage`, `message`, `request_steps`, `response_steps` - Final states: `SUCCESS`, `FAILURE` ### 6.2 Database Integration **Models:** - `AITaskLog` - Unified logging table (used by `AIEngine._log_to_database()`) - `IntegrationSettings` - API keys and model configuration (used by `AICore._load_account_settings()`) - `AIPrompt` - Custom prompt templates (used by `PromptRegistry.get_prompt()`) **Data Models:** - `Keywords` - Input for clustering - `Clusters` - Output of clustering - `ContentIdeas` - Output of idea generation - `Tasks` - Input for content/image generation - `Content` - Output of content generation - `Images` - Output of image generation ### 6.3 Frontend API Integration **Endpoints:** 1. `POST /v1/planner/keywords/auto_cluster/` → `planner.views.ClusterViewSet.auto_cluster()` - Calls `run_ai_task.delay(function_name='auto_cluster', ...)` 2. `POST /v1/planner/clusters/auto_generate_ideas/` → `planner.views.ClusterViewSet.auto_generate_ideas()` - Calls `run_ai_task.delay(function_name='auto_generate_ideas', ...)` 3. `POST /v1/writer/tasks/auto_generate_content/` → `writer.views.TasksViewSet.auto_generate_content()` - Calls `auto_generate_content_task.delay(...)` [LEGACY PATH] 4. `POST /v1/writer/tasks/auto_generate_images/` → `writer.views.TasksViewSet.auto_generate_images()` - Calls `auto_generate_images_task.delay(...)` [LEGACY PATH] **Response Format:** - Success: `{success: true, task_id: "...", message: "..."}` - Error: `{success: false, error: "..."}` **Progress Tracking:** - Frontend polls Celery task status via `GET /v1/system/tasks/{task_id}/status/` - Progress modal displays `request_steps` and `response_steps` from task meta ### 6.4 Configuration Integration **Settings Loading Hierarchy:** 1. **Account-level** (`IntegrationSettings` model): - API keys: `IntegrationSettings.config['apiKey']` - Model: `IntegrationSettings.config['model']` - Image settings: `IntegrationSettings.config` (for image_generation type) 2. **Django Settings** (fallback): - `OPENAI_API_KEY` - `RUNWARE_API_KEY` - `DEFAULT_AI_MODEL` 3. **Function-level** (`ai/settings.py`): - `MODEL_CONFIG[function_name]` - Default model, max_tokens, temperature per function **Prompt Loading Hierarchy:** 1. Task-level override: `task.prompt_override` (if exists) 2. Account-level: `AIPrompt` model (account, prompt_type) 3. Default: `PromptRegistry.DEFAULT_PROMPTS[prompt_type]` ### 6.5 Debug Panel Integration **Console Logging:** - `ConsoleStepTracker` logs to stdout/stderr (only if `DEBUG_MODE=True`) - Logs include timestamps, phases, messages, errors **Step Tracking:** - `StepTracker` maintains `request_steps` and `response_steps` arrays - Steps include: `stepNumber`, `stepName`, `status`, `message`, `duration`, `error` - Steps are included in Celery task meta and displayed in progress modal **Database Logging:** - `AITaskLog` records all AI task executions - Fields: `task_id`, `function_name`, `phase`, `status`, `cost`, `tokens`, `request_steps`, `response_steps`, `error`, `payload`, `result` --- ## 7. Identified Redundancies or Repetition ### 7.1 Duplicate Constants **Location 1:** `ai/constants.py` - `MODEL_RATES`, `IMAGE_MODEL_RATES`, `VALID_OPENAI_IMAGE_MODELS`, `VALID_SIZES_BY_MODEL`, `DEFAULT_AI_MODEL`, `JSON_MODE_MODELS` **Location 2:** `utils/ai_processor.py` (lines 18-44) - **EXACT DUPLICATE** of all constants from `constants.py` **Impact:** Constants are defined in two places, risking inconsistency. ### 7.2 Duplicate JSON Extraction Logic **Location 1:** `ai/ai_core.py::extract_json()` (lines 391-429) - Handles markdown code blocks, multiline JSON, direct JSON **Location 2:** `utils/ai_processor.py::_extract_json_from_response()` (lines 342-449) - **MORE COMPREHENSIVE** - handles more edge cases, balanced brace matching **Impact:** Two implementations with different capabilities. `ai_processor.py` version is more robust. ### 7.3 Duplicate OpenAI API Calling Logic **Location 1:** `ai/ai_core.py::run_ai_request()` (lines 106-389) - Centralized method with console logging via `ConsoleStepTracker` - Handles validation, model selection, cost calculation - Returns standardized dict format **Location 2:** `utils/ai_processor.py::_call_openai()` (lines 125-340) - **SIMILAR LOGIC** but with `response_steps` parameter instead of `tracker` - Less comprehensive error handling - Different return format **Impact:** Two code paths for the same operation. New code should use `AICore.run_ai_request()`. ### 7.4 Duplicate Image Generation Logic **Location 1:** `ai/ai_core.py::generate_image()` + `_generate_image_openai()` + `_generate_image_runware()` (lines 431-728) - Uses `print()` statements for logging (inconsistent with console tracker) **Location 2:** `utils/ai_processor.py::generate_image()` (lines 667-1043) - **MORE COMPREHENSIVE** - extensive logging, better error handling - Handles Runware authentication flow **Impact:** Two implementations. `ai_processor.py` version has better logging. ### 7.5 Duplicate Prompt Loading Logic **Location 1:** `ai/prompts.py::PromptRegistry.get_prompt()` (lines 280-333) - Hierarchical resolution: task override → DB prompt → default - Supports `[IGNY8_*]` placeholders and `{variable}` format **Location 2:** `utils/ai_processor.py::get_prompt()` (lines 1044-1057) - Simple database lookup via `modules/system/utils.get_prompt_value()` - No hierarchical resolution **Location 3:** Direct calls in `modules/writer/tasks.py` (lines 343, 959, 964) - Uses `get_prompt_value()` and `get_default_prompt()` directly **Impact:** Three different ways to load prompts. New code should use `PromptRegistry`. ### 7.6 Duplicate Model Configuration Logic **Location 1:** `ai/settings.py::get_model_config()` (lines 49-97) - Reads from `IntegrationSettings` if account provided - Falls back to `MODEL_CONFIG` defaults **Location 2:** `ai/ai_core.py::_load_account_settings()` (lines 46-90) - Reads model from `IntegrationSettings` directly - Similar logic but embedded in `AICore.__init__()` **Location 3:** `utils/ai_processor.py::_get_model()` (lines 98-123) - Reads model from `IntegrationSettings` directly - Similar logic but embedded in `AIProcessor.__init__()` **Impact:** Model loading logic duplicated in three places. ### 7.7 Duplicate API Key Loading Logic **Location 1:** `ai/ai_core.py::_load_account_settings()` (lines 46-90) - Loads OpenAI and Runware keys from `IntegrationSettings` **Location 2:** `utils/ai_processor.py::_get_api_key()` (lines 73-96) - **EXACT SAME LOGIC** for loading API keys **Impact:** Identical code in two places. ### 7.8 Repeated Error Handling Patterns **Pattern:** Multiple files have similar try/except blocks for: - API request errors - JSON parsing errors - Database errors - Validation errors **Impact:** Error handling is not centralized, making it harder to maintain consistent error messages and logging. ### 7.9 Repeated Progress Update Patterns **Pattern:** Multiple places manually build progress update dicts: - `modules/writer/tasks.py` (lines 62-73, 220-231, etc.) - `modules/planner/tasks.py` (lines 59-71, 203-215, etc.) - `ai/engine.py` (lines 57, 79, 141, etc.) **Impact:** Progress update format is not standardized, though `ProgressTracker` exists to handle this. --- ## 8. Summary of Potential Consolidation Areas ### 8.1 Constants Consolidation **Observation:** Model rates, valid models, and configuration constants are duplicated between `ai/constants.py` and `utils/ai_processor.py`. **Potential Action:** Remove constants from `ai_processor.py` and import from `constants.py`. However, `ai_processor.py` is marked as legacy, so this may not be necessary if it's being phased out. ### 8.2 JSON Extraction Consolidation **Observation:** Two JSON extraction methods exist with different capabilities. `ai_processor.py::_extract_json_from_response()` is more comprehensive. **Potential Action:** Enhance `AICore.extract_json()` with logic from `ai_processor.py`, or create a shared utility function. ### 8.3 API Request Consolidation **Observation:** `AICore.run_ai_request()` and `AIProcessor._call_openai()` perform the same operation with different interfaces. **Potential Action:** All new code should use `AICore.run_ai_request()`. Legacy code in `ai_processor.py` can remain for backward compatibility but should be marked as deprecated. ### 8.4 Image Generation Consolidation **Observation:** Two image generation implementations exist. `ai_processor.py` version has better logging. **Potential Action:** Enhance `AICore.generate_image()` with logging improvements from `ai_processor.py`, or migrate all code to use `AICore.generate_image()`. ### 8.5 Prompt Loading Consolidation **Observation:** Three different methods exist for loading prompts: `PromptRegistry.get_prompt()`, `AIProcessor.get_prompt()`, and direct `get_prompt_value()` calls. **Potential Action:** Migrate all code to use `PromptRegistry.get_prompt()` for consistency. Update legacy code paths gradually. ### 8.6 Model/API Key Loading Consolidation **Observation:** Model and API key loading logic is duplicated in `AICore`, `AIProcessor`, and `settings.py`. **Potential Action:** Create shared utility functions for loading settings from `IntegrationSettings`, used by all classes. ### 8.7 Error Handling Consolidation **Observation:** Error handling patterns are repeated across multiple files. **Potential Action:** Create centralized error handling utilities or enhance `AIEngine._handle_error()` to be more reusable. ### 8.8 Progress Tracking Consolidation **Observation:** Some code manually builds progress update dicts instead of using `ProgressTracker`. **Potential Action:** Migrate all progress updates to use `ProgressTracker.update()` for consistency. ### 8.9 Legacy Code Path Elimination **Observation:** Multiple execution paths exist: - New: `run_ai_task` → `AIEngine` → `BaseAIFunction` implementations - Legacy: Direct `AIProcessor` calls in `modules/*/tasks.py` **Potential Action:** Gradually migrate all legacy tasks to use the new framework. Mark legacy code as deprecated. --- ## 9. Assumptions Made 1. **File `wordpress.py`** was not read - assumed to be unrelated to AI processing based on name. 2. **Frontend code** was partially analyzed via search results - full frontend audit not performed. 3. **Database migrations** were not analyzed - assumed to be standard Django migrations. 4. **Test files** were not analyzed - `ai/tests/test_run.py` exists but was not read. 5. **Settings file** (`backend/igny8_core/settings.py`) was not read - assumed to contain standard Django settings. 6. **System module utilities** (`modules/system/utils.py`) were referenced but not fully read - assumed to contain `get_prompt_value()` and `get_default_prompt()` functions. --- ## 10. Appendix ### 10.1 File Line Counts | Directory | Files | Total Lines | |-----------|-------|-------------| | `ai/` | 15 | ~2,500 | | `ai/functions/` | 5 | ~1,300 | | `utils/` (AI-related) | 3 | ~1,800 | | `modules/planner/tasks.py` | 1 | 736 | | `modules/writer/tasks.py` | 1 | 1,156 | | **Total** | **25** | **~7,500** | ### 10.2 Function Count Summary - **Core Framework Functions:** ~30 - **AI Function Implementations:** 4 classes × ~6 methods = ~24 methods - **Tracking Functions:** ~20 - **Legacy Functions:** ~15 - **Celery Tasks:** 4 - **Total:** ~90 functions/methods ### 10.3 Key Design Patterns 1. **Template Method Pattern:** `BaseAIFunction` defines algorithm skeleton, subclasses implement steps 2. **Registry Pattern:** `FunctionRegistry` for dynamic function discovery 3. **Factory Pattern:** `get_function_instance()` creates function instances 4. **Strategy Pattern:** Different AI functions implement same interface 5. **Observer Pattern:** `ProgressTracker` updates Celery task state 6. **Facade Pattern:** `AICore` provides simplified interface to OpenAI/Runware APIs --- **End of Report**