diff --git a/EXPECTED_RESPONSE_STRUCTURE.md b/EXPECTED_RESPONSE_STRUCTURE.md new file mode 100644 index 00000000..a64c3966 --- /dev/null +++ b/EXPECTED_RESPONSE_STRUCTURE.md @@ -0,0 +1,107 @@ +# Expected Response Structure for generate_images_from_prompts + +## Function Flow + +1. **Frontend calls**: `POST /v1/writer/images/generate_images/` with `{ ids: [1, 2, 3] }` +2. **Backend ViewSet** (`ImagesViewSet.generate_images`): + - Calls `run_ai_task` with `function_name='generate_images_from_prompts'` + - Returns response with `queued_prompts` if in TEST MODE + +3. **AIEngine.execute**: + - Validates, prepares, builds prompt (placeholder), parses (placeholder) + - Calls `save_output` which queues prompts (TEST MODE) + +4. **GenerateImagesFromPromptsFunction.save_output**: + - Returns dict with queued prompts + +## Response Structure (TEST MODE) + +```json +{ + "success": true, + "count": 3, + "images_generated": 0, + "images_failed": 0, + "total_images": 3, + "queued_prompts": [ + { + "image_id": 1, + "index": 1, + "image_type": "featured_image", + "content_title": "Your Content Title", + "provider": "openai", + "model": "dall-e-2", + "formatted_prompt": "Create a high-quality realistic image to use as a featured photo for a blog post titled \"Your Content Title\". The image should visually represent the theme, mood, and subject implied by the image prompt: [original prompt]. Focus on a realistic, well-composed scene that naturally communicates the topic without text or logos...", + "negative_prompt": null, + "prompt_length": 250 + }, + { + "image_id": 2, + "index": 2, + "image_type": "in_article_1", + "content_title": "Your Content Title", + "provider": "openai", + "model": "dall-e-2", + "formatted_prompt": "...", + "negative_prompt": null, + "prompt_length": 245 + } + ], + "test_mode": true, + "provider": "openai", + "model": "dall-e-2", + "errors": null +} +``` + +## Response Structure (Production Mode - when AI calls are enabled) + +```json +{ + "success": true, + "count": 3, + "images_generated": 3, + "images_failed": 0, + "total_images": 3, + "errors": null +} +``` + +## Key Points + +1. **TEST MODE** (current): + - `images_generated: 0` (no actual images generated) + - `queued_prompts` array contains all formatted prompts + - `test_mode: true` + - Each prompt includes the complete formatted prompt that would be sent to AI + +2. **Production MODE** (when AI calls are uncommented): + - `images_generated` will be > 0 + - `queued_prompts` will not be in response + - `test_mode: false` or not present + - Images will have `image_url` and `status='generated'` + +## Console Logging + +The function logs to console (if DEBUG_MODE=True): +- `[generate_images_from_prompts] [TEST MODE] Queued prompt X/Y` +- `[generate_images_from_prompts] [TEST MODE] Provider: openai, Model: dall-e-2` +- `[generate_images_from_prompts] [TEST MODE] Prompt length: 250 chars` +- `[generate_images_from_prompts] [TEST MODE] Prompt preview: ...` + +## Frontend Console Output + +When clicking "Generate Images", browser console will show: +``` +[Generate Images] Request: { imageIds: [1, 2, 3], count: 3 } +[Generate Images] Endpoint: /v1/writer/images/generate_images/ +[Generate Images] Full Response: { success: true, queued_prompts: [...], ... } +[Generate Images] Queued Prompts (TEST MODE - NOT sent to AI): [...] +[Generate Images] Provider: openai, Model: dall-e-2 +[Generate Images] Prompt 1/3: + - Image Type: featured_image + - Content: "Your Content Title" + - Prompt Length: 250 chars + - Full Prompt: "Create a high-quality realistic image..." +``` + diff --git a/backend/igny8_core/modules/writer/views.py b/backend/igny8_core/modules/writer/views.py index 62dd8f07..12fdc9d4 100644 --- a/backend/igny8_core/modules/writer/views.py +++ b/backend/igny8_core/modules/writer/views.py @@ -551,12 +551,24 @@ class ImagesViewSet(SiteSectorModelViewSet): account_id=account_id ) if result.get('success'): - return Response({ + # Include queued prompts in response for TEST MODE + response_data = { 'success': True, - 'images_generated': result.get('count', 0), + 'images_generated': result.get('images_generated', 0), 'images_failed': result.get('images_failed', 0), + 'count': result.get('count', 0), + 'total_images': result.get('total_images', 0), 'message': 'Images generated successfully' - }, status=status.HTTP_200_OK) + } + # Add test mode data if available + if result.get('queued_prompts'): + response_data['queued_prompts'] = result.get('queued_prompts') + response_data['test_mode'] = result.get('test_mode', False) + response_data['provider'] = result.get('provider') + response_data['model'] = result.get('model') + + logger.info(f"[generate_images] Response: {response_data}") + return Response(response_data, status=status.HTTP_200_OK) else: return Response({ 'error': result.get('error', 'Image generation failed'), diff --git a/frontend/src/pages/Writer/Images.tsx b/frontend/src/pages/Writer/Images.tsx index 043f8f72..5b771ba2 100644 --- a/frontend/src/pages/Writer/Images.tsx +++ b/frontend/src/pages/Writer/Images.tsx @@ -165,8 +165,31 @@ export default function Images() { return; } + console.log('[Generate Images] Request:', { imageIds, count: imageIds.length }); + console.log('[Generate Images] Endpoint: /v1/writer/images/generate_images/'); + const result = await generateImages(imageIds); + + console.log('[Generate Images] Full Response:', result); + console.log('[Generate Images] Response Keys:', Object.keys(result)); + if (result.success) { + // Log queued prompts if available (TEST MODE) + if (result.queued_prompts && result.queued_prompts.length > 0) { + console.log('[Generate Images] Queued Prompts (TEST MODE - NOT sent to AI):', result.queued_prompts); + console.log(`[Generate Images] Provider: ${result.provider}, Model: ${result.model}`); + result.queued_prompts.forEach((qp: any, idx: number) => { + console.log(`[Generate Images] Prompt ${idx + 1}/${result.queued_prompts.length}:`); + console.log(` - Image Type: ${qp.image_type}`); + console.log(` - Content: ${qp.content_title}`); + console.log(` - Prompt Length: ${qp.prompt_length} chars`); + console.log(` - Full Prompt:`, qp.formatted_prompt); + if (qp.negative_prompt) { + console.log(` - Negative Prompt:`, qp.negative_prompt); + } + }); + } + // Show toast message (no progress modal) const generated = result.images_generated || 0; const failed = result.images_failed || 0; @@ -179,9 +202,19 @@ export default function Images() { } loadImages(); // Reload to show new images } else { + console.error('[Generate Images] Error:', result.error); + console.error('[Generate Images] Full Error Response:', result); toast.error(result.error || 'Failed to generate images'); } } catch (error: any) { + console.error('[Generate Images] Exception:', error); + console.error('[Generate Images] Error Details:', { + message: error.message, + stack: error.stack, + response: error.response, + status: error.status, + statusText: error.statusText + }); toast.error(`Failed to generate images: ${error.message}`); } }, [toast, loadImages, images]); diff --git a/test_image_generation.py b/test_image_generation.py new file mode 100644 index 00000000..e2277c38 --- /dev/null +++ b/test_image_generation.py @@ -0,0 +1,184 @@ +#!/usr/bin/env python3 +""" +Test script for generate_images_from_prompts function +Run this to test the function and see the response +""" +import os +import sys +import django + +# Setup Django +sys.path.insert(0, os.path.join(os.path.dirname(__file__), 'backend')) +os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'igny8_core.settings') +django.setup() + +from igny8_core.ai.functions.generate_images_from_prompts import GenerateImagesFromPromptsFunction +from igny8_core.ai.engine import AIEngine +from igny8_core.modules.writer.models import Images +from igny8_core.auth.models import Account + +def test_generate_images_from_prompts(): + """Test the generate_images_from_prompts function""" + print("=" * 80) + print("Testing generate_images_from_prompts function") + print("=" * 80) + + # Get first account + try: + account = Account.objects.first() + if not account: + print("ERROR: No accounts found in database") + return + print(f"Using account: {account.name} (ID: {account.id})") + except Exception as e: + print(f"ERROR: Failed to get account: {e}") + return + + # Get pending images with prompts + try: + images = Images.objects.filter( + account=account, + status='pending' + ).exclude(prompt__isnull=True).exclude(prompt='')[:3] # Get first 3 + + if not images.exists(): + print("ERROR: No pending images with prompts found") + print("Please create some images with prompts first") + return + + image_ids = list(images.values_list('id', flat=True)) + print(f"\nFound {len(image_ids)} pending images: {image_ids}") + + # Show image details + for img in images: + content_title = "Unknown" + if img.content: + content_title = img.content.title or img.content.meta_title or "No title" + elif img.task: + content_title = img.task.title or "No title" + print(f" - Image ID {img.id}: {img.image_type} for '{content_title}'") + print(f" Prompt: {img.prompt[:100] if img.prompt else 'None'}...") + + except Exception as e: + print(f"ERROR: Failed to get images: {e}") + import traceback + traceback.print_exc() + return + + # Create function instance + try: + fn = GenerateImagesFromPromptsFunction() + print(f"\nFunction created: {fn.get_name()}") + except Exception as e: + print(f"ERROR: Failed to create function: {e}") + import traceback + traceback.print_exc() + return + + # Validate + try: + print("\n" + "=" * 80) + print("Step 1: VALIDATE") + print("=" * 80) + validation = fn.validate({'ids': image_ids}, account=account) + print(f"Validation result: {validation}") + if not validation.get('valid'): + print(f"ERROR: Validation failed: {validation.get('error')}") + return + except Exception as e: + print(f"ERROR: Validation failed: {e}") + import traceback + traceback.print_exc() + return + + # Prepare + try: + print("\n" + "=" * 80) + print("Step 2: PREPARE") + print("=" * 80) + prepared = fn.prepare({'ids': image_ids}, account=account) + print(f"Prepared data keys: {list(prepared.keys())}") + print(f"Provider: {prepared.get('provider')}") + print(f"Model: {prepared.get('model')}") + print(f"API Key: {'***' + prepared.get('api_key', '')[-4:] if prepared.get('api_key') and len(prepared.get('api_key', '')) > 4 else 'NOT SET'}") + print(f"Images count: {len(prepared.get('images', []))}") + print(f"Has prompt template: {bool(prepared.get('image_prompt_template'))}") + print(f"Has negative prompt: {bool(prepared.get('negative_prompt'))}") + except Exception as e: + print(f"ERROR: Prepare failed: {e}") + import traceback + traceback.print_exc() + return + + # Build prompt (placeholder for this function) + try: + print("\n" + "=" * 80) + print("Step 3: BUILD PROMPT") + print("=" * 80) + prompt = fn.build_prompt(prepared, account=account) + print(f"Prompt (placeholder): {prompt}") + except Exception as e: + print(f"ERROR: Build prompt failed: {e}") + import traceback + traceback.print_exc() + return + + # Execute via AIEngine (simulated, no actual Celery) + try: + print("\n" + "=" * 80) + print("Step 4: EXECUTE (via AIEngine - TEST MODE)") + print("=" * 80) + print("Note: This will queue prompts but NOT send to AI (TEST MODE)") + + engine = AIEngine(account=account) + result = engine.execute(fn, {'ids': image_ids}) + + print("\n" + "=" * 80) + print("EXECUTION RESULT") + print("=" * 80) + print(f"Success: {result.get('success')}") + print(f"Keys in result: {list(result.keys())}") + + if result.get('success'): + print(f"\nCount: {result.get('count', 0)}") + print(f"Images generated: {result.get('images_generated', 0)}") + print(f"Images failed: {result.get('images_failed', 0)}") + print(f"Total images: {result.get('total_images', 0)}") + print(f"Test mode: {result.get('test_mode', False)}") + print(f"Provider: {result.get('provider')}") + print(f"Model: {result.get('model')}") + + queued_prompts = result.get('queued_prompts', []) + if queued_prompts: + print(f"\nQueued Prompts ({len(queued_prompts)}):") + for idx, qp in enumerate(queued_prompts, 1): + print(f"\n Prompt {idx}/{len(queued_prompts)}:") + print(f" Image ID: {qp.get('image_id')}") + print(f" Image Type: {qp.get('image_type')}") + print(f" Content Title: {qp.get('content_title')}") + print(f" Provider: {qp.get('provider')}") + print(f" Model: {qp.get('model')}") + print(f" Prompt Length: {qp.get('prompt_length')} chars") + print(f" Full Prompt:") + print(f" {qp.get('formatted_prompt', '')[:200]}...") + if qp.get('negative_prompt'): + print(f" Negative Prompt: {qp.get('negative_prompt')}") + else: + print(f"\nError: {result.get('error')}") + print(f"Error type: {result.get('error_type')}") + + print("\n" + "=" * 80) + print("FULL RESULT JSON (for frontend)") + print("=" * 80) + import json + print(json.dumps(result, indent=2, default=str)) + + except Exception as e: + print(f"ERROR: Execution failed: {e}") + import traceback + traceback.print_exc() + return + +if __name__ == '__main__': + test_generate_images_from_prompts() +