d
This commit is contained in:
@@ -488,8 +488,18 @@ class AICore:
|
||||
# CRITICAL: Truncate prompt to OpenAI's 1000 character limit BEFORE any processing
|
||||
if len(prompt) > 1000:
|
||||
print(f"[AI][{function_name}][Warning] Prompt too long ({len(prompt)} chars), truncating to 1000")
|
||||
prompt = prompt[:997].rsplit(' ', 1)[0] + "..."
|
||||
# Try word-aware truncation, but fallback to hard truncate if no space found
|
||||
truncated = prompt[:997]
|
||||
last_space = truncated.rfind(' ')
|
||||
if last_space > 900: # Only use word-aware if we have a reasonable space
|
||||
prompt = truncated[:last_space] + "..."
|
||||
else:
|
||||
prompt = prompt[:1000] # Hard truncate if no good space found
|
||||
print(f"[AI][{function_name}] Truncated prompt length: {len(prompt)}")
|
||||
# Final safety check
|
||||
if len(prompt) > 1000:
|
||||
prompt = prompt[:1000]
|
||||
print(f"[AI][{function_name}][Error] Had to hard truncate to exactly 1000 chars")
|
||||
|
||||
api_key = api_key or self._openai_api_key
|
||||
if not api_key:
|
||||
|
||||
@@ -275,57 +275,110 @@ def process_image_generation_queue(self, image_ids: list, account_id: int = None
|
||||
continue
|
||||
|
||||
# Format template with image prompt from database
|
||||
# Template has placeholders: {image_type}, {post_title}, {image_prompt}
|
||||
# For DALL-E 2: Use image prompt directly (no template)
|
||||
# For DALL-E 3 and others: Use template with placeholders
|
||||
# CRITICAL: OpenAI has strict 1000 character limit for prompts
|
||||
try:
|
||||
# Get template length to calculate available space
|
||||
template_placeholder_length = len(image_prompt_template.replace('{image_type}', '').replace('{post_title}', '').replace('{image_prompt}', ''))
|
||||
image_prompt = image.prompt or ""
|
||||
|
||||
# Truncate post_title aggressively (max 100 chars to leave room)
|
||||
post_title = content.title or content.meta_title or f"Content #{content.id}"
|
||||
if len(post_title) > 100:
|
||||
post_title = post_title[:97] + "..."
|
||||
if model == 'dall-e-2':
|
||||
# DALL-E 2: Use image prompt directly, no template
|
||||
logger.info(f"[process_image_generation_queue] Using DALL-E 2 - skipping template, using image prompt directly")
|
||||
formatted_prompt = image_prompt
|
||||
|
||||
# Calculate max image_prompt length: 1000 - template_text - post_title - safety margin
|
||||
# Assume template adds ~200 chars, post_title max 100, safety margin 50 = ~650 chars for image_prompt
|
||||
image_prompt = image.prompt or ""
|
||||
max_image_prompt_length = 650
|
||||
if len(image_prompt) > max_image_prompt_length:
|
||||
logger.warning(f"Image prompt too long ({len(image_prompt)} chars), truncating to {max_image_prompt_length}")
|
||||
image_prompt = image_prompt[:max_image_prompt_length].rsplit(' ', 1)[0] + "..."
|
||||
|
||||
formatted_prompt = image_prompt_template.format(
|
||||
image_type=image_type,
|
||||
post_title=post_title,
|
||||
image_prompt=image_prompt
|
||||
)
|
||||
|
||||
# CRITICAL: Final safety check - ALWAYS truncate to 1000 chars max
|
||||
# Truncate to 1000 chars if needed
|
||||
if len(formatted_prompt) > 1000:
|
||||
logger.warning(f"Formatted prompt too long ({len(formatted_prompt)} chars), truncating to 1000")
|
||||
formatted_prompt = formatted_prompt[:997].rsplit(' ', 1)[0] + "..."
|
||||
logger.warning(f"DALL-E 2 prompt too long ({len(formatted_prompt)} chars), truncating to 1000")
|
||||
truncated = formatted_prompt[:997]
|
||||
last_space = truncated.rfind(' ')
|
||||
if last_space > 900:
|
||||
formatted_prompt = truncated[:last_space] + "..."
|
||||
else:
|
||||
formatted_prompt = formatted_prompt[:1000]
|
||||
else:
|
||||
# DALL-E 3 and others: Use template
|
||||
try:
|
||||
# Truncate post_title aggressively (max 80 chars to leave more room for image_prompt)
|
||||
post_title = content.title or content.meta_title or f"Content #{content.id}"
|
||||
if len(post_title) > 80:
|
||||
post_title = post_title[:77] + "..."
|
||||
|
||||
# Double-check after truncation
|
||||
if len(formatted_prompt) > 1000:
|
||||
logger.error(f"Prompt still too long after truncation ({len(formatted_prompt)} chars), forcing hard truncate")
|
||||
formatted_prompt = formatted_prompt[:1000]
|
||||
# Calculate actual template length with placeholders filled
|
||||
# Format template with dummy values to measure actual length
|
||||
template_with_dummies = image_prompt_template.format(
|
||||
image_type=image_type,
|
||||
post_title='X' * len(post_title), # Use same length as actual post_title
|
||||
image_prompt='' # Empty to measure template overhead
|
||||
)
|
||||
template_overhead = len(template_with_dummies)
|
||||
|
||||
except Exception as e:
|
||||
# Fallback if template formatting fails
|
||||
logger.warning(f"Prompt template formatting failed: {e}, using image prompt directly")
|
||||
formatted_prompt = image.prompt or ""
|
||||
# CRITICAL: Truncate to 1000 chars even in fallback
|
||||
if len(formatted_prompt) > 1000:
|
||||
logger.warning(f"Fallback prompt too long ({len(formatted_prompt)} chars), truncating to 1000")
|
||||
formatted_prompt = formatted_prompt[:997].rsplit(' ', 1)[0] + "..."
|
||||
# Final hard truncate if still too long
|
||||
if len(formatted_prompt) > 1000:
|
||||
formatted_prompt = formatted_prompt[:1000]
|
||||
# Calculate max image_prompt length: 1000 - template_overhead - safety margin (20)
|
||||
max_image_prompt_length = 1000 - template_overhead - 20
|
||||
if max_image_prompt_length < 50:
|
||||
# If template is too long, use minimum 50 chars for image_prompt
|
||||
max_image_prompt_length = 50
|
||||
logger.warning(f"Template is very long ({template_overhead} chars), limiting image_prompt to {max_image_prompt_length}")
|
||||
|
||||
logger.info(f"[process_image_generation_queue] Template overhead: {template_overhead} chars, max image_prompt: {max_image_prompt_length} chars")
|
||||
|
||||
# Truncate image_prompt to calculated max
|
||||
if len(image_prompt) > max_image_prompt_length:
|
||||
logger.warning(f"Image prompt too long ({len(image_prompt)} chars), truncating to {max_image_prompt_length}")
|
||||
# Word-aware truncation
|
||||
truncated = image_prompt[:max_image_prompt_length - 3]
|
||||
last_space = truncated.rfind(' ')
|
||||
if last_space > max_image_prompt_length * 0.8: # Only if we have a reasonable space
|
||||
image_prompt = truncated[:last_space] + "..."
|
||||
else:
|
||||
image_prompt = image_prompt[:max_image_prompt_length - 3] + "..."
|
||||
|
||||
formatted_prompt = image_prompt_template.format(
|
||||
image_type=image_type,
|
||||
post_title=post_title,
|
||||
image_prompt=image_prompt
|
||||
)
|
||||
|
||||
# CRITICAL: Final safety check - ALWAYS truncate to 1000 chars max
|
||||
if len(formatted_prompt) > 1000:
|
||||
logger.warning(f"Formatted prompt too long ({len(formatted_prompt)} chars), truncating to 1000")
|
||||
# Try word-aware truncation
|
||||
truncated = formatted_prompt[:997]
|
||||
last_space = truncated.rfind(' ')
|
||||
if last_space > 900: # Only use word-aware if we have a reasonable space
|
||||
formatted_prompt = truncated[:last_space] + "..."
|
||||
else:
|
||||
formatted_prompt = formatted_prompt[:1000] # Hard truncate
|
||||
|
||||
# Double-check after truncation - MUST be <= 1000
|
||||
if len(formatted_prompt) > 1000:
|
||||
logger.error(f"Prompt still too long after truncation ({len(formatted_prompt)} chars), forcing hard truncate")
|
||||
formatted_prompt = formatted_prompt[:1000]
|
||||
|
||||
except Exception as e:
|
||||
# Fallback if template formatting fails
|
||||
logger.warning(f"Prompt template formatting failed: {e}, using image prompt directly")
|
||||
formatted_prompt = image_prompt
|
||||
# CRITICAL: Truncate to 1000 chars even in fallback
|
||||
if len(formatted_prompt) > 1000:
|
||||
logger.warning(f"Fallback prompt too long ({len(formatted_prompt)} chars), truncating to 1000")
|
||||
# Try word-aware truncation
|
||||
truncated = formatted_prompt[:997]
|
||||
last_space = truncated.rfind(' ')
|
||||
if last_space > 900:
|
||||
formatted_prompt = truncated[:last_space] + "..."
|
||||
else:
|
||||
formatted_prompt = formatted_prompt[:1000] # Hard truncate
|
||||
# Final hard truncate if still too long - MUST be <= 1000
|
||||
if len(formatted_prompt) > 1000:
|
||||
formatted_prompt = formatted_prompt[:1000]
|
||||
|
||||
# Generate image (using same approach as test image generation)
|
||||
logger.info(f"[process_image_generation_queue] Generating image {index}/{total_images} (ID: {image_id})")
|
||||
logger.info(f"[process_image_generation_queue] Provider: {provider}, Model: {model}")
|
||||
logger.info(f"[process_image_generation_queue] Prompt length: {len(formatted_prompt)}")
|
||||
logger.info(f"[process_image_generation_queue] Prompt length: {len(formatted_prompt)} (MUST be <= 1000)")
|
||||
if len(formatted_prompt) > 1000:
|
||||
logger.error(f"[process_image_generation_queue] ERROR: Prompt is {len(formatted_prompt)} chars, truncating NOW!")
|
||||
formatted_prompt = formatted_prompt[:1000]
|
||||
logger.info(f"[process_image_generation_queue] Final prompt length: {len(formatted_prompt)}")
|
||||
logger.info(f"[process_image_generation_queue] Image type: {image_type}")
|
||||
|
||||
result = ai_core.generate_image(
|
||||
|
||||
@@ -28,6 +28,8 @@ interface ImageQueueModalProps {
|
||||
queue: ImageQueueItem[];
|
||||
totalImages: number;
|
||||
taskId?: string | null;
|
||||
model?: string;
|
||||
provider?: string;
|
||||
onUpdateQueue?: (queue: ImageQueueItem[]) => void;
|
||||
onLog?: (log: {
|
||||
timestamp: string;
|
||||
@@ -45,6 +47,8 @@ export default function ImageQueueModal({
|
||||
queue,
|
||||
totalImages,
|
||||
taskId,
|
||||
model,
|
||||
provider,
|
||||
onUpdateQueue,
|
||||
onLog,
|
||||
}: ImageQueueModalProps) {
|
||||
@@ -306,6 +310,11 @@ export default function ImageQueueModal({
|
||||
<p className="text-sm text-gray-500 dark:text-gray-400 mt-1">
|
||||
Total: {totalImages} image{totalImages !== 1 ? 's' : ''} in queue
|
||||
</p>
|
||||
{model && (
|
||||
<p className="text-xs text-gray-400 dark:text-gray-500 mt-1">
|
||||
Model: {provider === 'openai' ? 'OpenAI' : provider === 'runware' ? 'Runware' : provider || 'Unknown'} {model === 'dall-e-2' ? 'DALL·E 2' : model === 'dall-e-3' ? 'DALL·E 3' : model}
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -73,6 +73,8 @@ export default function Images() {
|
||||
const [imageQueue, setImageQueue] = useState<ImageQueueItem[]>([]);
|
||||
const [currentContentId, setCurrentContentId] = useState<number | null>(null);
|
||||
const [taskId, setTaskId] = useState<string | null>(null);
|
||||
const [imageModel, setImageModel] = useState<string | null>(null);
|
||||
const [imageProvider, setImageProvider] = useState<string | null>(null);
|
||||
|
||||
// Load images - wrapped in useCallback
|
||||
const loadImages = useCallback(async () => {
|
||||
@@ -231,17 +233,25 @@ export default function Images() {
|
||||
return;
|
||||
}
|
||||
|
||||
// Fetch image generation settings to get max_in_article_images
|
||||
// Fetch image generation settings to get max_in_article_images, model, and provider
|
||||
let maxInArticleImages = 2; // Default
|
||||
let model = null;
|
||||
let provider = null;
|
||||
try {
|
||||
const settings = await fetchImageGenerationSettings();
|
||||
if (settings.success && settings.config) {
|
||||
maxInArticleImages = settings.config.max_in_article_images || 2;
|
||||
model = settings.config.model || null;
|
||||
provider = settings.config.provider || null;
|
||||
}
|
||||
} catch (error) {
|
||||
console.warn('Failed to fetch image settings, using default:', error);
|
||||
}
|
||||
|
||||
// Store model and provider for modal display
|
||||
setImageModel(model);
|
||||
setImageProvider(provider);
|
||||
|
||||
// Build image queue
|
||||
const queue = buildImageQueue(contentId, maxInArticleImages);
|
||||
|
||||
@@ -394,12 +404,16 @@ export default function Images() {
|
||||
setImageQueue([]);
|
||||
setCurrentContentId(null);
|
||||
setTaskId(null);
|
||||
setImageModel(null);
|
||||
setImageProvider(null);
|
||||
// Reload images after closing if generation completed
|
||||
loadImages();
|
||||
}}
|
||||
queue={imageQueue}
|
||||
totalImages={imageQueue.length}
|
||||
taskId={taskId}
|
||||
model={imageModel || undefined}
|
||||
provider={imageProvider || undefined}
|
||||
onUpdateQueue={setImageQueue}
|
||||
onLog={addAiLog}
|
||||
/>
|
||||
|
||||
Reference in New Issue
Block a user