This commit is contained in:
IGNY8 VPS (Salman)
2025-11-11 22:19:22 +00:00
parent ce9663438b
commit c84a02c757
2 changed files with 154 additions and 184 deletions

View File

@@ -89,26 +89,89 @@ class GenerateImagesFromPromptsFunction(BaseAIFunction):
if not images:
raise ValueError("No pending images found with prompts")
# Get image generation settings
# Get image generation settings - CHECK IF ENABLED
image_settings = {}
image_generation_enabled = False
if account:
try:
from igny8_core.modules.system.models import IntegrationSettings
integration = IntegrationSettings.objects.get(
account=account,
integration_type='image_generation',
is_active=True
integration_type='image_generation'
)
image_generation_enabled = integration.is_active
image_settings = integration.config or {}
logger.info(f"[generate_images_from_prompts] Image generation settings: enabled={image_generation_enabled}, config_keys={list(image_settings.keys())}")
except IntegrationSettings.DoesNotExist:
logger.warning(f"[generate_images_from_prompts] Image generation integration not found for account {account.id}")
raise ValueError("Image generation integration not configured")
except Exception as e:
logger.warning(f"Failed to load image generation settings: {e}")
logger.error(f"[generate_images_from_prompts] Failed to load image generation settings: {e}")
raise ValueError(f"Failed to load image generation settings: {str(e)}")
# Extract settings with defaults
if not image_generation_enabled:
raise ValueError("Image generation is not enabled in settings")
# Get provider from image_generation settings
provider = image_settings.get('provider') or image_settings.get('service', 'openai')
if provider == 'runware':
logger.info(f"[generate_images_from_prompts] Provider from settings: {provider}")
# Get provider-specific settings (OpenAI or Runware) - CHECK IF ENABLED
provider_api_key = None
provider_enabled = False
provider_model = None
if provider == 'openai':
try:
openai_settings = IntegrationSettings.objects.get(
account=account,
integration_type='openai'
)
provider_enabled = openai_settings.is_active
provider_api_key = openai_settings.config.get('apiKey') if openai_settings.config else None
provider_model = openai_settings.config.get('model') if openai_settings.config else None
logger.info(f"[generate_images_from_prompts] OpenAI settings: enabled={provider_enabled}, has_key={bool(provider_api_key)}, model={provider_model}")
except IntegrationSettings.DoesNotExist:
logger.error(f"[generate_images_from_prompts] OpenAI integration not found")
raise ValueError("OpenAI integration not configured")
except Exception as e:
logger.error(f"[generate_images_from_prompts] Error getting OpenAI settings: {e}")
raise ValueError(f"Failed to load OpenAI settings: {str(e)}")
elif provider == 'runware':
try:
runware_settings = IntegrationSettings.objects.get(
account=account,
integration_type='runware'
)
provider_enabled = runware_settings.is_active
provider_api_key = runware_settings.config.get('apiKey') if runware_settings.config else None
provider_model = runware_settings.config.get('model') if runware_settings.config else None
logger.info(f"[generate_images_from_prompts] Runware settings: enabled={provider_enabled}, has_key={bool(provider_api_key)}, model={provider_model}")
except IntegrationSettings.DoesNotExist:
logger.error(f"[generate_images_from_prompts] Runware integration not found")
raise ValueError("Runware integration not configured")
except Exception as e:
logger.error(f"[generate_images_from_prompts] Error getting Runware settings: {e}")
raise ValueError(f"Failed to load Runware settings: {str(e)}")
else:
raise ValueError(f"Invalid provider: {provider}")
# Validate provider is enabled and has API key
if not provider_enabled:
raise ValueError(f"{provider.capitalize()} integration is not enabled")
if not provider_api_key:
raise ValueError(f"{provider.capitalize()} API key not configured")
# Determine model: from provider settings, or image_generation settings, or default
if provider_model:
model = provider_model
elif provider == 'runware':
model = image_settings.get('model') or image_settings.get('runwareModel', 'runware:97@1')
else:
model = image_settings.get('model', 'dall-e-3')
model = image_settings.get('model') or image_settings.get('imageModel', 'dall-e-3')
logger.info(f"[generate_images_from_prompts] Final settings: provider={provider}, model={model}, enabled={provider_enabled}, has_api_key={bool(provider_api_key)}")
# Get prompt templates
image_prompt_template = PromptRegistry.get_image_prompt_template(account)
@@ -119,6 +182,7 @@ class GenerateImagesFromPromptsFunction(BaseAIFunction):
'account': account,
'provider': provider,
'model': model,
'api_key': provider_api_key, # Include API key
'image_type': image_settings.get('image_type', 'realistic'),
'image_format': image_settings.get('image_format', 'webp'),
'image_prompt_template': image_prompt_template,
@@ -167,10 +231,18 @@ class GenerateImagesFromPromptsFunction(BaseAIFunction):
provider = original_data.get('provider', 'openai')
model = original_data.get('model', 'dall-e-3')
api_key = original_data.get('api_key') # Get API key from prepare
image_type = original_data.get('image_type', 'realistic')
image_prompt_template = original_data.get('image_prompt_template', '')
negative_prompt = original_data.get('negative_prompt', '')
# Validate API key is present
if not api_key:
error_msg = f"[{function_name}] API key not found for provider {provider}"
if console_tracker:
console_tracker.error('ConfigurationError', error_msg)
raise ValueError(error_msg)
ai_core = AICore(account=account or original_data.get('account'))
total_images = len(images)
@@ -212,6 +284,10 @@ class GenerateImagesFromPromptsFunction(BaseAIFunction):
if console_tracker:
console_tracker.prep(f"[{function_name}] Image queue initialized with {total_images} image{'s' if total_images != 1 else ''}")
console_tracker.prep(f"[{function_name}] Provider: {provider}, Model: {model}, API Key: {'***' + api_key[-4:] if api_key and len(api_key) > 4 else 'NOT SET'}")
# Queue all prompts first (TEST MODE - don't send to AI)
queued_prompts = []
# Process each image sequentially
for index, image in enumerate(images, 1):
@@ -283,114 +359,60 @@ class GenerateImagesFromPromptsFunction(BaseAIFunction):
meta['image_queue'] = image_queue
progress_tracker.update("AI_CALL", progress_pct, ai_msg, meta=meta)
# Generate image (this is the actual API call)
# Frontend will simulate smooth progress from 50% to 95% while waiting
if console_tracker:
console_tracker.ai_call(f"[{function_name}] Calling {provider}/{model} API for image {index}/{total_images}")
result = ai_core.generate_image(
prompt=formatted_prompt,
provider=provider,
model=model,
size='1024x1024',
negative_prompt=negative_prompt if provider == 'runware' else None,
function_name='generate_images_from_prompts'
)
# Update progress to 90% (API call completed, processing response)
queue_item['progress'] = 90
if progress_tracker and step_tracker:
meta = step_tracker.get_meta()
meta['image_queue'] = image_queue
progress_tracker.update("AI_CALL", progress_pct, ai_msg, meta=meta)
if result.get('error'):
# Mark as failed
queue_item['status'] = 'failed'
queue_item['progress'] = 100
queue_item['error'] = result['error']
with transaction.atomic():
image.status = 'failed'
image.save(update_fields=['status', 'updated_at'])
error_msg = f"[{function_name}] Image {index}/{total_images} failed: {result['error']}"
errors.append(error_msg)
images_failed += 1
logger.error(f"Image generation failed for image {image.id}: {result['error']}")
if console_tracker:
console_tracker.error('ImageGenerationError', error_msg)
if progress_tracker and step_tracker:
parse_msg = f"Image {index} failed: {result['error']}"
step_tracker.add_response_step("PARSE", "error", parse_msg)
meta = step_tracker.get_meta()
meta['image_queue'] = image_queue
progress_pct = 70 + int((index - 1) / total_images * 15) # 70-85% for PARSE
progress_tracker.update("PARSE", progress_pct, parse_msg, meta=meta)
continue
image_url = result.get('url')
if not image_url:
# Mark as failed
queue_item['status'] = 'failed'
queue_item['progress'] = 100
queue_item['error'] = 'No URL returned'
with transaction.atomic():
image.status = 'failed'
image.save(update_fields=['status', 'updated_at'])
error_msg = f"[{function_name}] Image {index}/{total_images} failed: No URL returned"
errors.append(error_msg)
images_failed += 1
logger.error(f"No image URL returned for image {image.id}")
if console_tracker:
console_tracker.error('ImageGenerationError', error_msg)
if progress_tracker and step_tracker:
parse_msg = f"Image {index} failed: No URL returned"
step_tracker.add_response_step("PARSE", "error", parse_msg)
meta = step_tracker.get_meta()
meta['image_queue'] = image_queue
progress_pct = 70 + int((index - 1) / total_images * 15)
progress_tracker.update("PARSE", progress_pct, parse_msg, meta=meta)
continue
# Update progress: PARSE phase (90%)
queue_item['progress'] = 90
if progress_tracker and step_tracker:
parse_msg = f"Image {index} of {total_images} generated successfully"
step_tracker.add_response_step("PARSE", "success", parse_msg)
meta = step_tracker.get_meta()
meta['image_queue'] = image_queue
progress_pct = 70 + int((index - 1) / total_images * 15) # 70-85% for PARSE
progress_tracker.update("PARSE", progress_pct, parse_msg, meta=meta)
# Queue the complete prompt (TEST MODE - don't send to AI yet)
queued_prompts.append({
'image_id': image.id,
'index': index,
'image_type': image.image_type,
'content_title': content_title,
'provider': provider,
'model': model,
'formatted_prompt': formatted_prompt,
'negative_prompt': negative_prompt if provider == 'runware' else None,
'prompt_length': len(formatted_prompt)
})
if console_tracker:
console_tracker.parse(f"[{function_name}] Image {index}/{total_images} generated successfully: {image_url[:50]}...")
console_tracker.ai_call(f"[{function_name}] [TEST MODE] Queued prompt {index}/{total_images}: {image.image_type} for '{content_title}'")
console_tracker.ai_call(f"[{function_name}] [TEST MODE] Provider: {provider}, Model: {model}")
console_tracker.ai_call(f"[{function_name}] [TEST MODE] Prompt length: {len(formatted_prompt)} chars")
console_tracker.ai_call(f"[{function_name}] [TEST MODE] Prompt preview: {formatted_prompt[:150]}...")
# Update image record
with transaction.atomic():
image.image_url = image_url
image.status = 'generated'
image.save(update_fields=['image_url', 'status', 'updated_at'])
# TEMPORARY: Simulate result for testing (don't actually call AI)
result = {
'url': None,
'error': None,
'test_mode': True,
'queued': True
}
# Mark queue item as completed
# ACTUAL AI CALL (COMMENTED OUT FOR TESTING)
# if console_tracker:
# console_tracker.ai_call(f"[{function_name}] Calling {provider}/{model} API for image {index}/{total_images}")
#
# result = ai_core.generate_image(
# prompt=formatted_prompt,
# provider=provider,
# model=model,
# size='1024x1024',
# n=1,
# api_key=api_key, # Pass API key explicitly
# negative_prompt=negative_prompt if provider == 'runware' else None,
# function_name='generate_images_from_prompts'
# )
# TEST MODE: Mark as queued (not actually generated)
queue_item['status'] = 'completed'
queue_item['progress'] = 100
queue_item['image_url'] = image_url
images_generated += 1
logger.info(f"Image {image.id} ({image.image_type}) generated successfully: {image_url}")
queue_item['image_url'] = None # No URL in test mode
if console_tracker:
console_tracker.save(f"[{function_name}] Saved image {index}/{total_images} to database (ID: {image.id})")
console_tracker.parse(f"[{function_name}] [TEST MODE] Prompt queued for image {index}/{total_images}")
console_tracker.save(f"[{function_name}] [TEST MODE] Queued image {index}/{total_images} (ID: {image.id})")
# Update progress: SAVE phase
if progress_tracker and step_tracker:
save_msg = f"Saved image {index} of {total_images}"
save_msg = f"Queued prompt {index} of {total_images} (TEST MODE)"
step_tracker.add_request_step("SAVE", "success", save_msg)
meta = step_tracker.get_meta()
meta['image_queue'] = image_queue
@@ -416,29 +438,35 @@ class GenerateImagesFromPromptsFunction(BaseAIFunction):
continue
# Log all queued prompts (TEST MODE)
if console_tracker:
console_tracker.save(f"[{function_name}] [TEST MODE] All prompts queued. Total: {len(queued_prompts)} prompts")
console_tracker.save(f"[{function_name}] [TEST MODE] Provider: {provider}, Model: {model}")
for qp in queued_prompts:
console_tracker.save(f"[{function_name}] [TEST MODE] Image {qp['index']}: {qp['image_type']} - '{qp['content_title']}'")
console_tracker.save(f"[{function_name}] [TEST MODE] Prompt ({qp['prompt_length']} chars): {qp['formatted_prompt'][:100]}...")
# Final progress update
if progress_tracker and step_tracker:
final_msg = f"Generated {images_generated} of {total_images} images"
final_msg = f"Queued {len(queued_prompts)} prompts (TEST MODE - not sent to AI)"
step_tracker.add_request_step("SAVE", "success", final_msg)
meta = step_tracker.get_meta()
meta['image_queue'] = image_queue
meta['queued_prompts'] = queued_prompts # Include queued prompts in meta
progress_tracker.update("SAVE", 98, final_msg, meta=meta)
if console_tracker:
if images_generated > 0:
console_tracker.save(f"[{function_name}] SUCCESS: Generated {images_generated}/{total_images} image{'s' if images_generated != 1 else ''} successfully")
if images_failed > 0:
console_tracker.error('ImageGenerationError', f"[{function_name}] FAILED: {images_failed}/{total_images} image{'s' if images_failed != 1 else ''} failed")
if images_generated == total_images:
console_tracker.done(f"[{function_name}] All {total_images} image{'s' if total_images != 1 else ''} generated successfully")
else:
console_tracker.done(f"[{function_name}] Completed: {images_generated} succeeded, {images_failed} failed out of {total_images} total")
console_tracker.done(f"[{function_name}] [TEST MODE] Queued {len(queued_prompts)}/{total_images} prompts successfully (NOT sent to AI)")
return {
'count': images_generated,
'images_generated': images_generated,
'images_failed': images_failed,
'count': len(queued_prompts),
'images_generated': 0, # 0 because we're in test mode
'images_failed': 0,
'total_images': total_images,
'queued_prompts': queued_prompts, # Return queued prompts
'test_mode': True,
'provider': provider,
'model': model,
'errors': errors if errors else None
}

View File

@@ -14,16 +14,9 @@ import {
import { useToast } from '../../components/ui/toast/ToastContainer';
import { FileIcon, DownloadIcon, BoltIcon } from '../../icons';
import { createImagesPageConfig } from '../../config/pages/images.config';
import ProgressModal from '../../components/common/ProgressModal';
import ImageQueueModal from '../../components/common/ImageQueueModal';
import { useProgressModal } from '../../hooks/useProgressModal';
export default function Images() {
const toast = useToast();
// Progress modal for AI functions
const progressModal = useProgressModal();
const hasReloadedRef = useRef(false);
// Data state
const [images, setImages] = useState<ContentImagesGroup[]>([]);
@@ -174,31 +167,24 @@ export default function Images() {
const result = await generateImages(imageIds);
if (result.success) {
if (result.task_id) {
// Open progress modal for async task
progressModal.openModal(
result.task_id,
'Generate Images',
'ai-generate-images-from-prompts-01-desktop'
);
// Show toast message (no progress modal)
const generated = result.images_generated || 0;
const failed = result.images_failed || 0;
if (generated > 0) {
toast.success(`Images generated: ${generated} image${generated !== 1 ? 's' : ''} created${failed > 0 ? `, ${failed} failed` : ''}`);
} else if (failed > 0) {
toast.error(`Image generation failed: ${failed} image${failed !== 1 ? 's' : ''} failed`);
} else {
// Synchronous completion
const generated = result.images_generated || 0;
const failed = result.images_failed || 0;
if (generated > 0) {
toast.success(`Images generated: ${generated} image${generated !== 1 ? 's' : ''} created${failed > 0 ? `, ${failed} failed` : ''}`);
} else {
toast.error(`Image generation failed: ${failed} image${failed !== 1 ? 's' : ''} failed`);
}
loadImages(); // Reload to show new images
toast.success('Image generation completed');
}
loadImages(); // Reload to show new images
} else {
toast.error(result.error || 'Failed to generate images');
}
} catch (error: any) {
toast.error(`Failed to generate images: ${error.message}`);
}
}, [toast, progressModal, loadImages, images]);
}, [toast, loadImages, images]);
// Get max in-article images from the data (to determine column count)
const maxInArticleImages = useMemo(() => {
@@ -284,50 +270,6 @@ export default function Images() {
setCurrentPage(1);
}}
/>
{/* Image Queue Modal for Image Generation */}
{progressModal.imageQueue && progressModal.imageQueue.length > 0 ? (
<ImageQueueModal
isOpen={progressModal.isOpen}
queue={progressModal.imageQueue}
onClose={() => {
const wasCompleted = progressModal.progress.status === 'completed';
progressModal.closeModal();
// Reload data after modal closes (if completed)
if (wasCompleted && !hasReloadedRef.current) {
hasReloadedRef.current = true;
loadImages();
setTimeout(() => {
hasReloadedRef.current = false;
}, 1000);
}
}}
/>
) : (
/* Progress Modal for other AI Functions */
<ProgressModal
isOpen={progressModal.isOpen}
title={progressModal.title}
percentage={progressModal.progress.percentage}
status={progressModal.progress.status}
message={progressModal.progress.message}
details={progressModal.progress.details}
taskId={progressModal.taskId || undefined}
functionId={progressModal.functionId}
onClose={() => {
const wasCompleted = progressModal.progress.status === 'completed';
progressModal.closeModal();
// Reload data after modal closes (if completed)
if (wasCompleted && !hasReloadedRef.current) {
hasReloadedRef.current = true;
loadImages();
setTimeout(() => {
hasReloadedRef.current = false;
}, 1000);
}
}}
/>
)}
</>
);
}