Compare commits
3 Commits
abeede5f04
...
826ad89a3e
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
826ad89a3e | ||
|
|
504d0174f7 | ||
|
|
5299fd82eb |
@@ -99,7 +99,6 @@ class GenerateImagePromptsFunction(BaseAIFunction):
|
||||
content_text = self._format_content_for_prompt(extracted)
|
||||
|
||||
# Get prompt from PromptRegistry - same as other functions
|
||||
# Provide multiple context key variations for compatibility with different prompt templates
|
||||
prompt = PromptRegistry.get_prompt(
|
||||
function_name='generate_image_prompts',
|
||||
account=account,
|
||||
@@ -107,11 +106,6 @@ class GenerateImagePromptsFunction(BaseAIFunction):
|
||||
'title': extracted['title'],
|
||||
'content': content_text,
|
||||
'max_images': max_images,
|
||||
'count': max_images, # Alias for backward compatibility
|
||||
'TITLE': extracted['title'], # Uppercase variants
|
||||
'CONTENT': content_text,
|
||||
'MAX_IMAGES': max_images,
|
||||
'COUNT': max_images,
|
||||
}
|
||||
)
|
||||
|
||||
@@ -224,17 +218,34 @@ class GenerateImagePromptsFunction(BaseAIFunction):
|
||||
|
||||
# Helper methods
|
||||
def _get_max_in_article_images(self, account) -> int:
|
||||
"""Get max_in_article_images from global settings with optional account override"""
|
||||
from igny8_core.ai.settings import get_image_generation_config
|
||||
"""
|
||||
Get max_in_article_images from settings.
|
||||
Uses account's IntegrationSettings override, or GlobalIntegrationSettings.
|
||||
"""
|
||||
from igny8_core.modules.system.models import IntegrationSettings
|
||||
from igny8_core.modules.system.global_settings_models import GlobalIntegrationSettings
|
||||
|
||||
# Try account-specific override first
|
||||
try:
|
||||
config = get_image_generation_config(account)
|
||||
max_images = config.get('max_in_article_images', 2) # Default to 2 if not set
|
||||
logger.info(f"Using max_in_article_images={max_images} for account {account.id}")
|
||||
return max_images
|
||||
except Exception as e:
|
||||
logger.warning(f"Failed to get max_in_article_images from settings: {e}. Using default of 2")
|
||||
return 2 # Fallback default
|
||||
settings = IntegrationSettings.objects.get(
|
||||
account=account,
|
||||
integration_type='image_generation',
|
||||
is_active=True
|
||||
)
|
||||
max_images = settings.config.get('max_in_article_images')
|
||||
|
||||
if max_images is not None:
|
||||
max_images = int(max_images)
|
||||
logger.info(f"Using max_in_article_images={max_images} from account {account.id} IntegrationSettings override")
|
||||
return max_images
|
||||
except IntegrationSettings.DoesNotExist:
|
||||
logger.debug(f"No IntegrationSettings override for account {account.id}, using GlobalIntegrationSettings")
|
||||
|
||||
# Use GlobalIntegrationSettings default
|
||||
global_settings = GlobalIntegrationSettings.get_instance()
|
||||
max_images = global_settings.max_in_article_images
|
||||
logger.info(f"Using max_in_article_images={max_images} from GlobalIntegrationSettings (account {account.id})")
|
||||
return max_images
|
||||
|
||||
def _extract_content_elements(self, content: Content, max_images: int) -> Dict:
|
||||
"""Extract title, intro paragraphs, and H2 headings from content HTML"""
|
||||
|
||||
@@ -67,23 +67,42 @@ class GenerateImagesFunction(BaseAIFunction):
|
||||
if not tasks:
|
||||
raise ValueError("No tasks found")
|
||||
|
||||
# Get image generation settings from global settings with optional account override
|
||||
from igny8_core.ai.settings import get_image_generation_config
|
||||
# Get image generation settings
|
||||
# Try account-specific override, otherwise use GlobalIntegrationSettings
|
||||
from igny8_core.modules.system.models import IntegrationSettings
|
||||
from igny8_core.modules.system.global_settings_models import GlobalIntegrationSettings
|
||||
|
||||
image_config = get_image_generation_config(account)
|
||||
provider = image_config.get('provider', 'openai')
|
||||
model = image_config.get('model', 'dall-e-3')
|
||||
max_in_article_images = image_config.get('max_in_article_images', 2)
|
||||
image_settings = {}
|
||||
try:
|
||||
integration = IntegrationSettings.objects.get(
|
||||
account=account,
|
||||
integration_type='image_generation',
|
||||
is_active=True
|
||||
)
|
||||
image_settings = integration.config or {}
|
||||
logger.info(f"Using image settings from account {account.id} IntegrationSettings override")
|
||||
except IntegrationSettings.DoesNotExist:
|
||||
logger.info(f"No IntegrationSettings override for account {account.id}, using GlobalIntegrationSettings")
|
||||
|
||||
# Use GlobalIntegrationSettings for missing values
|
||||
global_settings = GlobalIntegrationSettings.get_instance()
|
||||
|
||||
# Extract settings with defaults from global settings
|
||||
provider = image_settings.get('provider') or image_settings.get('service') or global_settings.default_image_service
|
||||
if provider == 'runware':
|
||||
model = image_settings.get('model') or image_settings.get('runwareModel') or global_settings.runware_model
|
||||
else:
|
||||
model = image_settings.get('model') or global_settings.dalle_model
|
||||
|
||||
return {
|
||||
'tasks': tasks,
|
||||
'account': account,
|
||||
'provider': provider,
|
||||
'model': model,
|
||||
'image_type': image_config.get('style', 'realistic'),
|
||||
'max_in_article_images': max_in_article_images,
|
||||
'desktop_enabled': True, # Always enabled
|
||||
'mobile_enabled': True, # Always enabled
|
||||
'image_type': image_settings.get('image_type') or global_settings.image_style,
|
||||
'max_in_article_images': int(image_settings.get('max_in_article_images') or global_settings.max_in_article_images),
|
||||
'desktop_enabled': image_settings.get('desktop_enabled', True),
|
||||
'mobile_enabled': image_settings.get('mobile_enabled', True),
|
||||
}
|
||||
|
||||
def build_prompt(self, data: Dict, account=None) -> Dict:
|
||||
|
||||
@@ -130,24 +130,20 @@ class PromptRegistry:
|
||||
logger.debug(f"Replaced placeholder {placeholder} with {len(str(value))} characters")
|
||||
|
||||
# Step 2: Try .format() style for {variable} placeholders (if any remain)
|
||||
# Normalize context keys - provide both original case, lowercase, and uppercase
|
||||
# Normalize context keys - convert UPPER to lowercase for .format()
|
||||
normalized_context = {}
|
||||
for key, value in context.items():
|
||||
# Add original key
|
||||
# Try both original key and lowercase version
|
||||
normalized_context[key] = value
|
||||
# Add lowercase version
|
||||
normalized_context[key.lower()] = value
|
||||
# Add uppercase version
|
||||
normalized_context[key.upper()] = value
|
||||
|
||||
# Only try .format() if there are {variable} placeholders
|
||||
if '{' in rendered and '}' in rendered:
|
||||
try:
|
||||
rendered = rendered.format(**normalized_context)
|
||||
logger.debug(f"Successfully formatted prompt with context keys: {list(context.keys())}")
|
||||
except (KeyError, ValueError, IndexError) as e:
|
||||
# If .format() fails, log warning but keep the [IGNY8_*] replacements
|
||||
logger.warning(f"Failed to format prompt with .format(): {e}. Context keys: {list(context.keys())}. Using [IGNY8_*] replacements only.")
|
||||
logger.warning(f"Failed to format prompt with .format(): {e}. Using [IGNY8_*] replacements only.")
|
||||
|
||||
return rendered
|
||||
|
||||
|
||||
@@ -123,120 +123,3 @@ def get_model_config(function_name: str, account) -> Dict[str, Any]:
|
||||
'response_format': response_format,
|
||||
}
|
||||
|
||||
|
||||
def get_image_generation_config(account) -> Dict[str, Any]:
|
||||
"""
|
||||
Get image generation configuration for AI functions.
|
||||
|
||||
Architecture:
|
||||
- API keys: ALWAYS from GlobalIntegrationSettings (platform-wide)
|
||||
- Model/params: From IntegrationSettings if account has override, else from global
|
||||
- Supports both OpenAI (DALL-E) and Runware providers
|
||||
|
||||
Args:
|
||||
account: Account instance (required)
|
||||
|
||||
Returns:
|
||||
dict: Image generation configuration with 'provider', 'model', 'api_key',
|
||||
'size', 'quality', 'style', 'max_in_article_images'
|
||||
|
||||
Raises:
|
||||
ValueError: If account not provided or settings not configured
|
||||
"""
|
||||
if not account:
|
||||
raise ValueError("Account is required for image generation configuration")
|
||||
|
||||
try:
|
||||
from igny8_core.modules.system.global_settings_models import GlobalIntegrationSettings
|
||||
from igny8_core.modules.system.models import IntegrationSettings
|
||||
|
||||
# Get global settings (for API keys and defaults)
|
||||
global_settings = GlobalIntegrationSettings.get_instance()
|
||||
|
||||
# Start with global defaults
|
||||
provider = global_settings.default_image_service # 'openai' or 'runware'
|
||||
max_in_article_images = global_settings.max_in_article_images
|
||||
|
||||
if provider == 'runware':
|
||||
api_key = global_settings.runware_api_key
|
||||
model = global_settings.runware_model
|
||||
size = global_settings.desktop_image_size
|
||||
quality = global_settings.image_quality
|
||||
style = global_settings.image_style
|
||||
|
||||
if not api_key:
|
||||
raise ValueError(
|
||||
"Platform Runware API key not configured. "
|
||||
"Please configure GlobalIntegrationSettings in Django admin."
|
||||
)
|
||||
else: # openai/dalle
|
||||
api_key = global_settings.dalle_api_key or global_settings.openai_api_key
|
||||
model = global_settings.dalle_model
|
||||
size = global_settings.dalle_size
|
||||
quality = global_settings.image_quality
|
||||
style = global_settings.image_style
|
||||
|
||||
if not api_key:
|
||||
raise ValueError(
|
||||
"Platform OpenAI/DALL-E API key not configured. "
|
||||
"Please configure GlobalIntegrationSettings in Django admin."
|
||||
)
|
||||
|
||||
# Check if account has overrides
|
||||
# Try both 'image_generation' and provider-specific types for backward compatibility
|
||||
account_settings = None
|
||||
for integration_type in ['image_generation', provider, 'dalle', 'runware']:
|
||||
try:
|
||||
account_settings = IntegrationSettings.objects.get(
|
||||
account=account,
|
||||
integration_type=integration_type,
|
||||
is_active=True
|
||||
)
|
||||
break
|
||||
except IntegrationSettings.DoesNotExist:
|
||||
continue
|
||||
|
||||
if account_settings:
|
||||
config = account_settings.config or {}
|
||||
|
||||
# Override provider if specified
|
||||
if config.get('provider') or config.get('service'):
|
||||
provider = config.get('provider') or config.get('service')
|
||||
|
||||
# Override model if specified
|
||||
if config.get('model'):
|
||||
model = config['model']
|
||||
|
||||
# Override size if specified
|
||||
if config.get('size') or config.get('image_size'):
|
||||
size = config.get('size') or config.get('image_size')
|
||||
|
||||
# Override quality if specified
|
||||
if config.get('quality') or config.get('image_quality'):
|
||||
quality = config.get('quality') or config.get('image_quality')
|
||||
|
||||
# Override style if specified
|
||||
if config.get('style') or config.get('image_style'):
|
||||
style = config.get('style') or config.get('image_style')
|
||||
|
||||
# Override max_in_article_images if specified
|
||||
if config.get('max_in_article_images'):
|
||||
max_in_article_images = int(config['max_in_article_images'])
|
||||
|
||||
return {
|
||||
'provider': provider,
|
||||
'model': model,
|
||||
'api_key': api_key, # ALWAYS from global
|
||||
'size': size,
|
||||
'quality': quality,
|
||||
'style': style,
|
||||
'max_in_article_images': max_in_article_images,
|
||||
}
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Could not load image generation settings for account {account.id}: {e}")
|
||||
raise ValueError(
|
||||
f"Could not load image generation configuration for account {account.id}. "
|
||||
f"Please configure GlobalIntegrationSettings."
|
||||
)
|
||||
|
||||
|
||||
@@ -181,41 +181,47 @@ def process_image_generation_queue(self, image_ids: list, account_id: int = None
|
||||
failed = 0
|
||||
results = []
|
||||
|
||||
# Get image generation settings from IntegrationSettings
|
||||
# Always use system account settings (aws-admin) for global configuration
|
||||
logger.info("[process_image_generation_queue] Step 1: Loading image generation settings from aws-admin")
|
||||
from igny8_core.auth.models import Account
|
||||
# Get image generation settings
|
||||
# Try account-specific override, otherwise use GlobalIntegrationSettings
|
||||
logger.info("[process_image_generation_queue] Step 1: Loading image generation settings")
|
||||
from igny8_core.modules.system.global_settings_models import GlobalIntegrationSettings
|
||||
|
||||
config = {}
|
||||
try:
|
||||
system_account = Account.objects.get(slug='aws-admin')
|
||||
image_settings = IntegrationSettings.objects.get(
|
||||
account=system_account,
|
||||
account=account,
|
||||
integration_type='image_generation',
|
||||
is_active=True
|
||||
)
|
||||
logger.info(f"[process_image_generation_queue] Using system account (aws-admin) settings")
|
||||
logger.info(f"[process_image_generation_queue] Using account {account.id} IntegrationSettings override")
|
||||
config = image_settings.config or {}
|
||||
except (Account.DoesNotExist, IntegrationSettings.DoesNotExist):
|
||||
logger.error("[process_image_generation_queue] ERROR: Image generation settings not found in aws-admin account")
|
||||
return {'success': False, 'error': 'Image generation settings not found in aws-admin account'}
|
||||
except IntegrationSettings.DoesNotExist:
|
||||
logger.info(f"[process_image_generation_queue] No IntegrationSettings override for account {account.id}, using GlobalIntegrationSettings")
|
||||
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)}'}
|
||||
|
||||
# Use GlobalIntegrationSettings for missing values
|
||||
global_settings = GlobalIntegrationSettings.get_instance()
|
||||
|
||||
logger.info(f"[process_image_generation_queue] Image generation settings loaded. Config keys: {list(config.keys())}")
|
||||
logger.info(f"[process_image_generation_queue] Full config: {config}")
|
||||
|
||||
# Get provider and model from config (respect user settings)
|
||||
provider = config.get('provider', 'openai')
|
||||
# Get model - try 'model' first, then 'imageModel' as fallback
|
||||
model = config.get('model') or config.get('imageModel') or 'dall-e-3'
|
||||
# Get provider and model from config with global fallbacks
|
||||
provider = config.get('provider') or global_settings.default_image_service
|
||||
if provider == 'runware':
|
||||
model = config.get('model') or config.get('imageModel') or global_settings.runware_model
|
||||
else:
|
||||
model = config.get('model') or config.get('imageModel') or global_settings.dalle_model
|
||||
|
||||
logger.info(f"[process_image_generation_queue] Using PROVIDER: {provider}, MODEL: {model} from settings")
|
||||
image_type = config.get('image_type', 'realistic')
|
||||
image_type = config.get('image_type') or global_settings.image_style
|
||||
image_format = config.get('image_format', 'webp')
|
||||
desktop_enabled = config.get('desktop_enabled', True)
|
||||
mobile_enabled = config.get('mobile_enabled', True)
|
||||
# Get image sizes from config, with fallback defaults
|
||||
featured_image_size = config.get('featured_image_size') or ('1280x832' if provider == 'runware' else '1024x1024')
|
||||
desktop_image_size = config.get('desktop_image_size') or '1024x1024'
|
||||
desktop_image_size = config.get('desktop_image_size') or global_settings.desktop_image_size
|
||||
in_article_image_size = config.get('in_article_image_size') or '512x512' # Default to 512x512
|
||||
|
||||
logger.info(f"[process_image_generation_queue] Settings loaded:")
|
||||
@@ -226,44 +232,22 @@ def process_image_generation_queue(self, image_ids: list, account_id: int = None
|
||||
logger.info(f" - Desktop enabled: {desktop_enabled}")
|
||||
logger.info(f" - Mobile enabled: {mobile_enabled}")
|
||||
|
||||
# Get provider API key (using same approach as test image generation)
|
||||
# Note: API key is stored as 'apiKey' (camelCase) in IntegrationSettings.config
|
||||
# Normal users use system account settings (aws-admin) via fallback
|
||||
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, # Use the provider from settings
|
||||
is_active=True
|
||||
)
|
||||
logger.info(f"[process_image_generation_queue] {provider.upper()} integration settings found for account {account.id}")
|
||||
except IntegrationSettings.DoesNotExist:
|
||||
# Fallback to system account (aws-admin) settings
|
||||
logger.info(f"[process_image_generation_queue] No {provider.upper()} settings for account {account.id}, falling back to system account")
|
||||
from igny8_core.auth.models import Account
|
||||
try:
|
||||
system_account = Account.objects.get(slug='aws-admin')
|
||||
provider_settings = IntegrationSettings.objects.get(
|
||||
account=system_account,
|
||||
integration_type=provider,
|
||||
is_active=True
|
||||
)
|
||||
logger.info(f"[process_image_generation_queue] Using system account (aws-admin) {provider.upper()} settings")
|
||||
except (Account.DoesNotExist, IntegrationSettings.DoesNotExist):
|
||||
logger.error(f"[process_image_generation_queue] ERROR: {provider.upper()} integration settings not found in system account either")
|
||||
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.upper()} API key: {e}", exc_info=True)
|
||||
return {'success': False, 'error': f'Error retrieving {provider.upper()} API key: {str(e)}'}
|
||||
# Get provider API key
|
||||
# API keys are ALWAYS from GlobalIntegrationSettings (accounts cannot override API keys)
|
||||
# Account IntegrationSettings only store provider preference, NOT API keys
|
||||
logger.info(f"[process_image_generation_queue] Step 2: Loading {provider.upper()} API key from GlobalIntegrationSettings")
|
||||
|
||||
# Extract API key from provider settings
|
||||
logger.info(f"[process_image_generation_queue] {provider.upper()} config keys: {list(provider_settings.config.keys()) if provider_settings.config else 'None'}")
|
||||
# Get API key from GlobalIntegrationSettings
|
||||
if provider == 'runware':
|
||||
api_key = global_settings.runware_api_key
|
||||
elif provider == 'openai':
|
||||
api_key = global_settings.dalle_api_key or global_settings.openai_api_key
|
||||
else:
|
||||
api_key = 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.error(f"[process_image_generation_queue] {provider.upper()} API key not configured in GlobalIntegrationSettings")
|
||||
return {'success': False, 'error': f'{provider.upper()} API key not configured in GlobalIntegrationSettings'}
|
||||
|
||||
# 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 "***"
|
||||
|
||||
Reference in New Issue
Block a user