1
This commit is contained in:
@@ -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
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user