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: if not images:
raise ValueError("No pending images found with prompts") raise ValueError("No pending images found with prompts")
# Get image generation settings # Get image generation settings - CHECK IF ENABLED
image_settings = {} image_settings = {}
image_generation_enabled = False
if account: if account:
try: try:
from igny8_core.modules.system.models import IntegrationSettings from igny8_core.modules.system.models import IntegrationSettings
integration = IntegrationSettings.objects.get( integration = IntegrationSettings.objects.get(
account=account, account=account,
integration_type='image_generation', integration_type='image_generation'
is_active=True
) )
image_generation_enabled = integration.is_active
image_settings = integration.config or {} 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: 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') 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') model = image_settings.get('model') or image_settings.get('runwareModel', 'runware:97@1')
else: 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 # Get prompt templates
image_prompt_template = PromptRegistry.get_image_prompt_template(account) image_prompt_template = PromptRegistry.get_image_prompt_template(account)
@@ -119,6 +182,7 @@ class GenerateImagesFromPromptsFunction(BaseAIFunction):
'account': account, 'account': account,
'provider': provider, 'provider': provider,
'model': model, 'model': model,
'api_key': provider_api_key, # Include API key
'image_type': image_settings.get('image_type', 'realistic'), 'image_type': image_settings.get('image_type', 'realistic'),
'image_format': image_settings.get('image_format', 'webp'), 'image_format': image_settings.get('image_format', 'webp'),
'image_prompt_template': image_prompt_template, 'image_prompt_template': image_prompt_template,
@@ -167,10 +231,18 @@ class GenerateImagesFromPromptsFunction(BaseAIFunction):
provider = original_data.get('provider', 'openai') provider = original_data.get('provider', 'openai')
model = original_data.get('model', 'dall-e-3') 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_type = original_data.get('image_type', 'realistic')
image_prompt_template = original_data.get('image_prompt_template', '') image_prompt_template = original_data.get('image_prompt_template', '')
negative_prompt = original_data.get('negative_prompt', '') 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')) ai_core = AICore(account=account or original_data.get('account'))
total_images = len(images) total_images = len(images)
@@ -212,6 +284,10 @@ class GenerateImagesFromPromptsFunction(BaseAIFunction):
if console_tracker: 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}] 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 # Process each image sequentially
for index, image in enumerate(images, 1): for index, image in enumerate(images, 1):
@@ -283,114 +359,60 @@ class GenerateImagesFromPromptsFunction(BaseAIFunction):
meta['image_queue'] = image_queue meta['image_queue'] = image_queue
progress_tracker.update("AI_CALL", progress_pct, ai_msg, meta=meta) progress_tracker.update("AI_CALL", progress_pct, ai_msg, meta=meta)
# Generate image (this is the actual API call) # Queue the complete prompt (TEST MODE - don't send to AI yet)
# Frontend will simulate smooth progress from 50% to 95% while waiting queued_prompts.append({
if console_tracker: 'image_id': image.id,
console_tracker.ai_call(f"[{function_name}] Calling {provider}/{model} API for image {index}/{total_images}") 'index': index,
'image_type': image.image_type,
result = ai_core.generate_image( 'content_title': content_title,
prompt=formatted_prompt, 'provider': provider,
provider=provider, 'model': model,
model=model, 'formatted_prompt': formatted_prompt,
size='1024x1024', 'negative_prompt': negative_prompt if provider == 'runware' else None,
negative_prompt=negative_prompt if provider == 'runware' else None, 'prompt_length': len(formatted_prompt)
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: if console_tracker:
console_tracker.error('ImageGenerationError', error_msg) 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]}...")
if progress_tracker and step_tracker: # TEMPORARY: Simulate result for testing (don't actually call AI)
parse_msg = f"Image {index} failed: {result['error']}" result = {
step_tracker.add_response_step("PARSE", "error", parse_msg) 'url': None,
meta = step_tracker.get_meta() 'error': None,
meta['image_queue'] = image_queue 'test_mode': True,
progress_pct = 70 + int((index - 1) / total_images * 15) # 70-85% for PARSE 'queued': True
progress_tracker.update("PARSE", progress_pct, parse_msg, meta=meta) }
continue # 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'
# )
image_url = result.get('url') # TEST MODE: Mark as queued (not actually generated)
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)
if console_tracker:
console_tracker.parse(f"[{function_name}] Image {index}/{total_images} generated successfully: {image_url[:50]}...")
# Update image record
with transaction.atomic():
image.image_url = image_url
image.status = 'generated'
image.save(update_fields=['image_url', 'status', 'updated_at'])
# Mark queue item as completed
queue_item['status'] = 'completed' queue_item['status'] = 'completed'
queue_item['progress'] = 100 queue_item['progress'] = 100
queue_item['image_url'] = image_url queue_item['image_url'] = None # No URL in test mode
images_generated += 1
logger.info(f"Image {image.id} ({image.image_type}) generated successfully: {image_url}")
if console_tracker: 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 # Update progress: SAVE phase
if progress_tracker and step_tracker: 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) step_tracker.add_request_step("SAVE", "success", save_msg)
meta = step_tracker.get_meta() meta = step_tracker.get_meta()
meta['image_queue'] = image_queue meta['image_queue'] = image_queue
@@ -416,29 +438,35 @@ class GenerateImagesFromPromptsFunction(BaseAIFunction):
continue 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 # Final progress update
if progress_tracker and step_tracker: 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) step_tracker.add_request_step("SAVE", "success", final_msg)
meta = step_tracker.get_meta() meta = step_tracker.get_meta()
meta['image_queue'] = image_queue meta['image_queue'] = image_queue
meta['queued_prompts'] = queued_prompts # Include queued prompts in meta
progress_tracker.update("SAVE", 98, final_msg, meta=meta) progress_tracker.update("SAVE", 98, final_msg, meta=meta)
if console_tracker: if console_tracker:
if images_generated > 0: console_tracker.done(f"[{function_name}] [TEST MODE] Queued {len(queued_prompts)}/{total_images} prompts successfully (NOT sent to AI)")
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")
return { return {
'count': images_generated, 'count': len(queued_prompts),
'images_generated': images_generated, 'images_generated': 0, # 0 because we're in test mode
'images_failed': images_failed, 'images_failed': 0,
'total_images': total_images, 'total_images': total_images,
'queued_prompts': queued_prompts, # Return queued prompts
'test_mode': True,
'provider': provider,
'model': model,
'errors': errors if errors else None 'errors': errors if errors else None
} }

View File

@@ -14,17 +14,10 @@ import {
import { useToast } from '../../components/ui/toast/ToastContainer'; import { useToast } from '../../components/ui/toast/ToastContainer';
import { FileIcon, DownloadIcon, BoltIcon } from '../../icons'; import { FileIcon, DownloadIcon, BoltIcon } from '../../icons';
import { createImagesPageConfig } from '../../config/pages/images.config'; 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() { export default function Images() {
const toast = useToast(); const toast = useToast();
// Progress modal for AI functions
const progressModal = useProgressModal();
const hasReloadedRef = useRef(false);
// Data state // Data state
const [images, setImages] = useState<ContentImagesGroup[]>([]); const [images, setImages] = useState<ContentImagesGroup[]>([]);
const [loading, setLoading] = useState(true); const [loading, setLoading] = useState(true);
@@ -174,31 +167,24 @@ export default function Images() {
const result = await generateImages(imageIds); const result = await generateImages(imageIds);
if (result.success) { if (result.success) {
if (result.task_id) { // Show toast message (no progress modal)
// Open progress modal for async task
progressModal.openModal(
result.task_id,
'Generate Images',
'ai-generate-images-from-prompts-01-desktop'
);
} else {
// Synchronous completion
const generated = result.images_generated || 0; const generated = result.images_generated || 0;
const failed = result.images_failed || 0; const failed = result.images_failed || 0;
if (generated > 0) { if (generated > 0) {
toast.success(`Images generated: ${generated} image${generated !== 1 ? 's' : ''} created${failed > 0 ? `, ${failed} failed` : ''}`); toast.success(`Images generated: ${generated} image${generated !== 1 ? 's' : ''} created${failed > 0 ? `, ${failed} failed` : ''}`);
} else { } else if (failed > 0) {
toast.error(`Image generation failed: ${failed} image${failed !== 1 ? 's' : ''} failed`); toast.error(`Image generation failed: ${failed} image${failed !== 1 ? 's' : ''} failed`);
} else {
toast.success('Image generation completed');
} }
loadImages(); // Reload to show new images loadImages(); // Reload to show new images
}
} else { } else {
toast.error(result.error || 'Failed to generate images'); toast.error(result.error || 'Failed to generate images');
} }
} catch (error: any) { } catch (error: any) {
toast.error(`Failed to generate images: ${error.message}`); 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) // Get max in-article images from the data (to determine column count)
const maxInArticleImages = useMemo(() => { const maxInArticleImages = useMemo(() => {
@@ -284,50 +270,6 @@ export default function Images() {
setCurrentPage(1); 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);
}
}}
/>
)}
</> </>
); );
} }