diff --git a/backend/igny8_core/ai/tasks.py b/backend/igny8_core/ai/tasks.py index f8a6a76d..11eccce6 100644 --- a/backend/igny8_core/ai/tasks.py +++ b/backend/igny8_core/ai/tasks.py @@ -165,6 +165,7 @@ def process_image_generation_queue(self, image_ids: list, account_id: int = None results = [] # Get image generation settings from IntegrationSettings + logger.info("[process_image_generation_queue] Step 1: Loading image generation settings") try: image_settings = IntegrationSettings.objects.get( account=account, @@ -172,34 +173,58 @@ def process_image_generation_queue(self, image_ids: list, account_id: int = None is_active=True ) config = image_settings.config or {} + logger.info(f"[process_image_generation_queue] Image generation settings found. Config keys: {list(config.keys())}") + logger.info(f"[process_image_generation_queue] Full config: {config}") + provider = config.get('provider', 'openai') model = config.get('model', 'dall-e-3') image_type = config.get('image_type', 'realistic') image_format = config.get('image_format', 'webp') desktop_enabled = config.get('desktop_enabled', True) mobile_enabled = config.get('mobile_enabled', True) + + logger.info(f"[process_image_generation_queue] Settings loaded:") + logger.info(f" - Provider: {provider}") + logger.info(f" - Model: {model}") + logger.info(f" - Image type: {image_type}") + logger.info(f" - Image format: {image_format}") + logger.info(f" - Desktop enabled: {desktop_enabled}") + logger.info(f" - Mobile enabled: {mobile_enabled}") except IntegrationSettings.DoesNotExist: - logger.error("Image generation settings not found") + logger.error("[process_image_generation_queue] ERROR: Image generation settings not found") + logger.error(f"[process_image_generation_queue] Account: {account.id if account else 'None'}, integration_type: 'image_generation'") return {'success': False, 'error': 'Image generation settings not found'} + except Exception as e: + logger.error(f"[process_image_generation_queue] ERROR loading image generation settings: {e}", exc_info=True) + return {'success': False, 'error': f'Error loading image generation settings: {str(e)}'} # Get provider API key (using same approach as test image generation) # Note: API key is stored as 'apiKey' (camelCase) in IntegrationSettings.config + logger.info(f"[process_image_generation_queue] Step 2: Loading {provider.upper()} API key") try: provider_settings = IntegrationSettings.objects.get( account=account, integration_type=provider, # 'openai' or 'runware' is_active=True ) + logger.info(f"[process_image_generation_queue] {provider.upper()} integration settings found") + logger.info(f"[process_image_generation_queue] {provider.upper()} config keys: {list(provider_settings.config.keys()) if provider_settings.config else 'None'}") + api_key = provider_settings.config.get('apiKey') if provider_settings.config else None if not api_key: logger.error(f"[process_image_generation_queue] {provider.upper()} API key not found in config") + logger.error(f"[process_image_generation_queue] {provider.upper()} config: {provider_settings.config}") return {'success': False, 'error': f'{provider.upper()} API key not configured'} - logger.info(f"[process_image_generation_queue] {provider.upper()} API key retrieved successfully") + + # Log API key presence (but not the actual key for security) + api_key_preview = f"{api_key[:10]}...{api_key[-4:]}" if len(api_key) > 14 else "***" + logger.info(f"[process_image_generation_queue] {provider.upper()} API key retrieved successfully (length: {len(api_key)}, preview: {api_key_preview})") except IntegrationSettings.DoesNotExist: - logger.error(f"[process_image_generation_queue] {provider.upper()} integration settings not found") + logger.error(f"[process_image_generation_queue] ERROR: {provider.upper()} integration settings not found") + logger.error(f"[process_image_generation_queue] Account: {account.id if account else 'None'}, integration_type: '{provider}'") return {'success': False, 'error': f'{provider.upper()} integration not found or not active'} except Exception as e: - logger.error(f"[process_image_generation_queue] Error getting {provider} API key: {e}", exc_info=True) + logger.error(f"[process_image_generation_queue] ERROR getting {provider} API key: {e}", exc_info=True) return {'success': False, 'error': f'Error retrieving {provider} API key: {str(e)}'} # Get image prompt template (has placeholders: {image_type}, {post_title}, {image_prompt}) @@ -239,10 +264,18 @@ def process_image_generation_queue(self, image_ids: list, account_id: int = None ) # Load image record + logger.info(f"[process_image_generation_queue] Image {index}/{total_images} (ID: {image_id}): Loading from database") try: image = Images.objects.get(id=image_id, account=account) + logger.info(f"[process_image_generation_queue] Image {image_id} loaded:") + logger.info(f" - Type: {image.image_type}") + logger.info(f" - Status: {image.status}") + logger.info(f" - Prompt length: {len(image.prompt) if image.prompt else 0} chars") + logger.info(f" - Prompt preview: {image.prompt[:100] if image.prompt else 'None'}...") + logger.info(f" - Content ID: {image.content.id if image.content else 'None'}") except Images.DoesNotExist: - logger.error(f"Image {image_id} not found") + logger.error(f"[process_image_generation_queue] Image {image_id} not found in database") + logger.error(f"[process_image_generation_queue] Account: {account.id if account else 'None'}") results.append({ 'image_id': image_id, 'status': 'failed', @@ -250,6 +283,15 @@ def process_image_generation_queue(self, image_ids: list, account_id: int = None }) failed += 1 continue + except Exception as e: + logger.error(f"[process_image_generation_queue] ERROR loading image {image_id}: {e}", exc_info=True) + results.append({ + 'image_id': image_id, + 'status': 'failed', + 'error': f'Error loading image: {str(e)[:180]}' + }) + failed += 1 + continue # Check if prompt exists if not image.prompt: @@ -419,17 +461,45 @@ def process_image_generation_queue(self, image_ids: list, account_id: int = None else: logger.info(f"Image generation successful for {image_id}") # Update image record: success + image_url = result.get('url') + logger.info(f"[process_image_generation_queue] Image {image_id} - URL received: {image_url[:100] if image_url else 'None'}...") + logger.info(f"[process_image_generation_queue] Image {image_id} - URL length: {len(image_url) if image_url else 0} characters") + + # Log URL length for debugging (model field now supports up to 500 chars) + if image_url and len(image_url) > 500: + logger.error(f"[process_image_generation_queue] Image {image_id} - URL TOO LONG: {len(image_url)} chars (max 500). URL: {image_url[:150]}...") + logger.error(f"[process_image_generation_queue] Image {image_id} - CharField max_length=500 is too short! URL will be truncated.") + # Truncate to 500 chars if somehow longer (shouldn't happen, but safety check) + image_url = image_url[:500] + logger.warning(f"[process_image_generation_queue] Image {image_id} - Truncated URL length: {len(image_url)} chars") + elif image_url and len(image_url) > 200: + logger.info(f"[process_image_generation_queue] Image {image_id} - URL length {len(image_url)} chars (was limited to 200, now supports 500)") + try: - image.image_url = result.get('url') + # Try to save - if URL is too long, this will fail + image.image_url = image_url image.status = 'generated' + logger.info(f"[process_image_generation_queue] Image {image_id} - Attempting to save to database with URL length: {len(image_url) if image_url else 0}") image.save(update_fields=['image_url', 'status']) + logger.info(f"[process_image_generation_queue] Image {image_id} - Successfully saved to database") except Exception as save_error: - logger.error(f"Failed to save image URL/status to database: {save_error}", exc_info=True) + error_str = str(save_error) + logger.error(f"[process_image_generation_queue] Image {image_id} - Database save FAILED: {error_str}", exc_info=True) + logger.error(f"[process_image_generation_queue] Image {image_id} - Error type: {type(save_error).__name__}") + logger.error(f"[process_image_generation_queue] Image {image_id} - URL that failed: {image_url[:200] if image_url else 'None'}...") + logger.error(f"[process_image_generation_queue] Image {image_id} - URL length: {len(image_url) if image_url else 0}") + + # Check if it's a length-related error + if 'too long' in error_str.lower() or 'varying' in error_str.lower() or '200' in error_str: + logger.error(f"[process_image_generation_queue] Image {image_id} - CONFIRMED: URL length issue. URLField max_length=200, but URL is {len(image_url) if image_url else 0} chars") + # Continue even if save fails, but mark as failed in results + # Truncate error message to 180 chars to avoid same issue when saving error + truncated_error = error_str[:180] if len(error_str) > 180 else error_str results.append({ 'image_id': image_id, 'status': 'failed', - 'error': f'Database save error: {str(save_error)[:200]}' + 'error': f'Database save error: {truncated_error}' }) failed += 1 else: diff --git a/backend/igny8_core/modules/writer/models.py b/backend/igny8_core/modules/writer/models.py index c8c525cd..75dbfa5e 100644 --- a/backend/igny8_core/modules/writer/models.py +++ b/backend/igny8_core/modules/writer/models.py @@ -164,7 +164,7 @@ class Images(SiteSectorBaseModel): help_text="The task this image belongs to (legacy, use content instead)" ) image_type = models.CharField(max_length=50, choices=IMAGE_TYPE_CHOICES, default='featured') - image_url = models.URLField(blank=True, null=True, help_text="URL of the generated/stored image") + image_url = models.CharField(max_length=500, blank=True, null=True, help_text="URL of the generated/stored image") image_path = models.CharField(max_length=500, blank=True, null=True, help_text="Local path if stored locally") prompt = models.TextField(blank=True, null=True, help_text="Image generation prompt used") status = models.CharField(max_length=50, default='pending', help_text="Status: pending, generated, failed")