Files
moreminimore-marketing/backend/config/stability_config.py
Kunthawat Greethong c35fa52117 Base code
2026-01-08 22:39:53 +07:00

656 lines
20 KiB
Python

"""Configuration settings for Stability AI integration."""
import os
from typing import Dict, Any, List, Optional
from dataclasses import dataclass
from enum import Enum
class StabilityEndpoint(Enum):
"""Stability AI API endpoints."""
# Generate endpoints
GENERATE_ULTRA = "/v2beta/stable-image/generate/ultra"
GENERATE_CORE = "/v2beta/stable-image/generate/core"
GENERATE_SD3 = "/v2beta/stable-image/generate/sd3"
# Edit endpoints
EDIT_ERASE = "/v2beta/stable-image/edit/erase"
EDIT_INPAINT = "/v2beta/stable-image/edit/inpaint"
EDIT_OUTPAINT = "/v2beta/stable-image/edit/outpaint"
EDIT_SEARCH_REPLACE = "/v2beta/stable-image/edit/search-and-replace"
EDIT_SEARCH_RECOLOR = "/v2beta/stable-image/edit/search-and-recolor"
EDIT_REMOVE_BACKGROUND = "/v2beta/stable-image/edit/remove-background"
EDIT_REPLACE_BACKGROUND = "/v2beta/stable-image/edit/replace-background-and-relight"
# Upscale endpoints
UPSCALE_FAST = "/v2beta/stable-image/upscale/fast"
UPSCALE_CONSERVATIVE = "/v2beta/stable-image/upscale/conservative"
UPSCALE_CREATIVE = "/v2beta/stable-image/upscale/creative"
# Control endpoints
CONTROL_SKETCH = "/v2beta/stable-image/control/sketch"
CONTROL_STRUCTURE = "/v2beta/stable-image/control/structure"
CONTROL_STYLE = "/v2beta/stable-image/control/style"
CONTROL_STYLE_TRANSFER = "/v2beta/stable-image/control/style-transfer"
# 3D endpoints
STABLE_FAST_3D = "/v2beta/3d/stable-fast-3d"
STABLE_POINT_AWARE_3D = "/v2beta/3d/stable-point-aware-3d"
# Audio endpoints
AUDIO_TEXT_TO_AUDIO = "/v2beta/audio/stable-audio-2/text-to-audio"
AUDIO_AUDIO_TO_AUDIO = "/v2beta/audio/stable-audio-2/audio-to-audio"
AUDIO_INPAINT = "/v2beta/audio/stable-audio-2/inpaint"
# Results endpoint
RESULTS = "/v2beta/results/{id}"
# Legacy V1 endpoints
V1_TEXT_TO_IMAGE = "/v1/generation/{engine_id}/text-to-image"
V1_IMAGE_TO_IMAGE = "/v1/generation/{engine_id}/image-to-image"
V1_MASKING = "/v1/generation/{engine_id}/image-to-image/masking"
# User endpoints
USER_ACCOUNT = "/v1/user/account"
USER_BALANCE = "/v1/user/balance"
ENGINES_LIST = "/v1/engines/list"
@dataclass
class StabilityConfig:
"""Configuration for Stability AI service."""
api_key: str
base_url: str = "https://api.stability.ai"
timeout: int = 300
max_retries: int = 3
rate_limit_requests: int = 150
rate_limit_window: int = 10 # seconds
max_file_size: int = 10 * 1024 * 1024 # 10MB
supported_image_formats: List[str] = None
supported_audio_formats: List[str] = None
def __post_init__(self):
if self.supported_image_formats is None:
self.supported_image_formats = ["jpeg", "jpg", "png", "webp"]
if self.supported_audio_formats is None:
self.supported_audio_formats = ["mp3", "wav"]
# Model pricing information
MODEL_PRICING = {
"generate": {
"ultra": 8,
"core": 3,
"sd3.5-large": 6.5,
"sd3.5-large-turbo": 4,
"sd3.5-medium": 3.5,
"sd3.5-flash": 2.5
},
"edit": {
"erase": 5,
"inpaint": 5,
"outpaint": 4,
"search_and_replace": 5,
"search_and_recolor": 5,
"remove_background": 5,
"replace_background_and_relight": 8
},
"upscale": {
"fast": 2,
"conservative": 40,
"creative": 60
},
"control": {
"sketch": 5,
"structure": 5,
"style": 5,
"style_transfer": 8
},
"3d": {
"stable_fast_3d": 10,
"stable_point_aware_3d": 4
},
"audio": {
"text_to_audio": 20,
"audio_to_audio": 20,
"inpaint": 20
}
}
# Image dimension limits
IMAGE_LIMITS = {
"generate": {
"min_pixels": 4096,
"max_pixels": 16777216, # 16MP
"min_dimension": 64,
"max_dimension": 16384
},
"edit": {
"min_pixels": 4096,
"max_pixels": 9437184, # ~9.4MP
"min_dimension": 64,
"aspect_ratio_min": 0.4, # 1:2.5
"aspect_ratio_max": 2.5 # 2.5:1
},
"upscale": {
"fast": {
"min_width": 32,
"max_width": 1536,
"min_height": 32,
"max_height": 1536,
"min_pixels": 1024,
"max_pixels": 1048576
},
"conservative": {
"min_pixels": 4096,
"max_pixels": 9437184,
"min_dimension": 64
},
"creative": {
"min_pixels": 4096,
"max_pixels": 1048576,
"min_dimension": 64
}
},
"control": {
"min_pixels": 4096,
"max_pixels": 9437184,
"min_dimension": 64,
"aspect_ratio_min": 0.4,
"aspect_ratio_max": 2.5
},
"3d": {
"min_pixels": 4096,
"max_pixels": 4194304, # 4MP
"min_dimension": 64
}
}
# Audio limits
AUDIO_LIMITS = {
"min_duration": 6,
"max_duration": 190,
"max_file_size": 50 * 1024 * 1024, # 50MB
"supported_formats": ["mp3", "wav"]
}
# Style preset descriptions
STYLE_PRESET_DESCRIPTIONS = {
"enhance": "Enhance the natural qualities of the image",
"anime": "Japanese animation style",
"photographic": "Realistic photographic style",
"digital-art": "Digital artwork style",
"comic-book": "Comic book illustration style",
"fantasy-art": "Fantasy and magical themes",
"line-art": "Clean line art style",
"analog-film": "Vintage film photography style",
"neon-punk": "Cyberpunk with neon lighting",
"isometric": "Isometric 3D perspective",
"low-poly": "Low polygon 3D style",
"origami": "Paper folding art style",
"modeling-compound": "Clay or modeling compound style",
"cinematic": "Movie-like cinematic style",
"3d-model": "3D rendered model style",
"pixel-art": "Retro pixel art style",
"tile-texture": "Seamless tile texture style"
}
# Default parameters for different operations
DEFAULT_PARAMETERS = {
"generate": {
"ultra": {
"aspect_ratio": "1:1",
"output_format": "png"
},
"core": {
"aspect_ratio": "1:1",
"output_format": "png"
},
"sd3": {
"model": "sd3.5-large",
"mode": "text-to-image",
"aspect_ratio": "1:1",
"output_format": "png"
}
},
"edit": {
"erase": {
"grow_mask": 5,
"output_format": "png"
},
"inpaint": {
"grow_mask": 5,
"output_format": "png"
},
"outpaint": {
"creativity": 0.5,
"output_format": "png"
}
},
"upscale": {
"fast": {
"output_format": "png"
},
"conservative": {
"creativity": 0.35,
"output_format": "png"
},
"creative": {
"creativity": 0.3,
"output_format": "png"
}
},
"control": {
"sketch": {
"control_strength": 0.7,
"output_format": "png"
},
"structure": {
"control_strength": 0.7,
"output_format": "png"
},
"style": {
"aspect_ratio": "1:1",
"fidelity": 0.5,
"output_format": "png"
}
},
"3d": {
"stable_fast_3d": {
"texture_resolution": "1024",
"foreground_ratio": 0.85,
"remesh": "none",
"vertex_count": -1
},
"stable_point_aware_3d": {
"texture_resolution": "1024",
"foreground_ratio": 1.3,
"remesh": "none",
"target_type": "none",
"target_count": 1000,
"guidance_scale": 3
}
},
"audio": {
"text_to_audio": {
"duration": 190,
"model": "stable-audio-2",
"output_format": "mp3"
},
"audio_to_audio": {
"duration": 190,
"model": "stable-audio-2",
"output_format": "mp3",
"strength": 1
},
"inpaint": {
"duration": 190,
"steps": 8,
"output_format": "mp3",
"mask_start": 30,
"mask_end": 190
}
}
}
# Rate limiting configuration
RATE_LIMIT_CONFIG = {
"requests_per_window": 150,
"window_seconds": 10,
"timeout_seconds": 60,
"burst_allowance": 10 # Allow brief bursts above limit
}
# Content moderation settings
CONTENT_MODERATION = {
"enabled": True,
"blocked_keywords": [
# This would contain actual blocked keywords in production
],
"warning_keywords": [
# Keywords that trigger warnings but don't block
]
}
# Quality settings for different use cases
QUALITY_PRESETS = {
"draft": {
"model": "core",
"steps": None, # Use model defaults
"cfg_scale": None,
"description": "Fast generation for drafts and iterations"
},
"standard": {
"model": "sd3.5-medium",
"steps": None,
"cfg_scale": 4,
"description": "Balanced quality and speed"
},
"premium": {
"model": "ultra",
"steps": None,
"cfg_scale": None,
"description": "Highest quality for final outputs"
},
"professional": {
"model": "sd3.5-large",
"steps": None,
"cfg_scale": 4,
"style_preset": "photographic",
"description": "Professional photography style"
}
}
# Workflow templates
WORKFLOW_TEMPLATES = {
"portrait_enhancement": {
"description": "Enhance portrait photos with professional quality",
"steps": [
{"operation": "upscale_conservative", "params": {"creativity": 0.2}},
{"operation": "inpaint", "params": {"prompt": "professional portrait, high quality"}}
]
},
"art_creation": {
"description": "Create artistic images from sketches",
"steps": [
{"operation": "control_sketch", "params": {"control_strength": 0.8}},
{"operation": "upscale_fast", "params": {}}
]
},
"product_photography": {
"description": "Create professional product images",
"steps": [
{"operation": "remove_background", "params": {}},
{"operation": "replace_background_and_relight", "params": {"background_prompt": "professional studio lighting, white background"}}
]
},
"creative_exploration": {
"description": "Explore different creative interpretations",
"steps": [
{"operation": "generate_core", "params": {}},
{"operation": "control_style", "params": {"fidelity": 0.7}},
{"operation": "upscale_creative", "params": {"creativity": 0.4}}
]
}
}
def get_stability_config() -> StabilityConfig:
"""Get Stability AI configuration from environment variables.
Returns:
StabilityConfig instance
"""
api_key = os.getenv("STABILITY_API_KEY")
if not api_key:
raise ValueError("STABILITY_API_KEY environment variable is required")
return StabilityConfig(
api_key=api_key,
base_url=os.getenv("STABILITY_BASE_URL", "https://api.stability.ai"),
timeout=int(os.getenv("STABILITY_TIMEOUT", "300")),
max_retries=int(os.getenv("STABILITY_MAX_RETRIES", "3")),
max_file_size=int(os.getenv("STABILITY_MAX_FILE_SIZE", str(10 * 1024 * 1024)))
)
def validate_image_requirements(
width: int,
height: int,
operation: str
) -> Dict[str, Any]:
"""Validate image requirements for specific operations.
Args:
width: Image width
height: Image height
operation: Operation type (generate, edit, upscale, etc.)
Returns:
Validation result with success status and any issues
"""
issues = []
limits = IMAGE_LIMITS.get(operation, IMAGE_LIMITS["generate"])
total_pixels = width * height
# Check minimum requirements
if "min_pixels" in limits and total_pixels < limits["min_pixels"]:
issues.append(f"Image must have at least {limits['min_pixels']} pixels")
if "max_pixels" in limits and total_pixels > limits["max_pixels"]:
issues.append(f"Image must have at most {limits['max_pixels']} pixels")
if "min_dimension" in limits:
if width < limits["min_dimension"] or height < limits["min_dimension"]:
issues.append(f"Both dimensions must be at least {limits['min_dimension']} pixels")
# Check aspect ratio for operations that require it
if "aspect_ratio_min" in limits and "aspect_ratio_max" in limits:
aspect_ratio = width / height
if aspect_ratio < limits["aspect_ratio_min"] or aspect_ratio > limits["aspect_ratio_max"]:
issues.append(f"Aspect ratio must be between {limits['aspect_ratio_min']}:1 and {limits['aspect_ratio_max']}:1")
return {
"is_valid": len(issues) == 0,
"issues": issues,
"total_pixels": total_pixels,
"aspect_ratio": round(width / height, 3)
}
def get_model_recommendations(
use_case: str,
quality_preference: str = "standard",
speed_preference: str = "balanced"
) -> Dict[str, Any]:
"""Get model recommendations based on use case and preferences.
Args:
use_case: Type of use case (portrait, landscape, art, product, etc.)
quality_preference: Quality preference (draft, standard, premium)
speed_preference: Speed preference (fast, balanced, quality)
Returns:
Model recommendations with explanations
"""
recommendations = {}
# Base recommendations by use case
if use_case == "portrait":
recommendations["primary"] = "ultra"
recommendations["alternative"] = "sd3.5-large"
recommendations["style_preset"] = "photographic"
elif use_case == "art":
recommendations["primary"] = "sd3.5-large"
recommendations["alternative"] = "ultra"
recommendations["style_preset"] = "digital-art"
elif use_case == "product":
recommendations["primary"] = "ultra"
recommendations["alternative"] = "sd3.5-large"
recommendations["style_preset"] = "photographic"
elif use_case == "concept":
recommendations["primary"] = "core"
recommendations["alternative"] = "sd3.5-medium"
recommendations["style_preset"] = "enhance"
else:
recommendations["primary"] = "core"
recommendations["alternative"] = "sd3.5-medium"
# Adjust based on preferences
if speed_preference == "fast":
if recommendations["primary"] == "ultra":
recommendations["primary"] = "core"
elif recommendations["primary"] == "sd3.5-large":
recommendations["primary"] = "sd3.5-medium"
elif speed_preference == "quality":
if recommendations["primary"] == "core":
recommendations["primary"] = "ultra"
elif recommendations["primary"] == "sd3.5-medium":
recommendations["primary"] = "sd3.5-large"
# Add quality preset
if quality_preference in QUALITY_PRESETS:
recommendations.update(QUALITY_PRESETS[quality_preference])
return recommendations
def get_optimal_parameters(
operation: str,
image_info: Optional[Dict[str, Any]] = None,
user_preferences: Optional[Dict[str, Any]] = None
) -> Dict[str, Any]:
"""Get optimal parameters for a specific operation.
Args:
operation: Operation type
image_info: Information about input image
user_preferences: User preferences
Returns:
Optimal parameters for the operation
"""
# Start with defaults
params = DEFAULT_PARAMETERS.get(operation, {}).copy()
# Adjust based on image characteristics
if image_info:
total_pixels = image_info.get("total_pixels", 0)
# Adjust creativity based on image quality
if "creativity" in params:
if total_pixels < 100000: # Very low res
params["creativity"] = min(params["creativity"] + 0.1, 0.5)
elif total_pixels > 2000000: # High res
params["creativity"] = max(params["creativity"] - 0.1, 0.1)
# Apply user preferences
if user_preferences:
for key, value in user_preferences.items():
if key in params:
params[key] = value
return params
def calculate_estimated_cost(
operation: str,
model: Optional[str] = None,
steps: Optional[int] = None
) -> float:
"""Calculate estimated cost in credits for an operation.
Args:
operation: Operation type
model: Model name (if applicable)
steps: Number of steps (for step-based pricing)
Returns:
Estimated cost in credits
"""
if operation in MODEL_PRICING:
if isinstance(MODEL_PRICING[operation], dict):
if model and model in MODEL_PRICING[operation]:
base_cost = MODEL_PRICING[operation][model]
else:
# Use default model cost
base_cost = list(MODEL_PRICING[operation].values())[0]
else:
base_cost = MODEL_PRICING[operation]
else:
base_cost = 5 # Default cost
# Adjust for steps if applicable (mainly for audio)
if steps and operation.startswith("audio") and model == "stable-audio-2":
# Audio 2.0 uses formula: 17 + 0.06 * steps
return 17 + 0.06 * steps
return base_cost
def get_operation_limits(operation: str) -> Dict[str, Any]:
"""Get limits and constraints for a specific operation.
Args:
operation: Operation type
Returns:
Limits and constraints
"""
limits = {
"file_size_limit": 10 * 1024 * 1024, # 10MB default
"timeout": 300,
"rate_limit": True
}
# Add operation-specific limits
if operation in IMAGE_LIMITS:
limits.update(IMAGE_LIMITS[operation])
if operation.startswith("audio"):
limits.update(AUDIO_LIMITS)
limits["file_size_limit"] = 50 * 1024 * 1024 # 50MB for audio
if operation.startswith("3d"):
limits["file_size_limit"] = 10 * 1024 * 1024 # 10MB for 3D
return limits
# Environment-specific configurations
def get_environment_config() -> Dict[str, Any]:
"""Get environment-specific configuration.
Returns:
Environment configuration
"""
env = os.getenv("ENVIRONMENT", "development")
configs = {
"development": {
"debug_mode": True,
"log_level": "DEBUG",
"cache_results": False,
"mock_responses": False
},
"staging": {
"debug_mode": True,
"log_level": "INFO",
"cache_results": True,
"mock_responses": False
},
"production": {
"debug_mode": False,
"log_level": "WARNING",
"cache_results": True,
"mock_responses": False
}
}
return configs.get(env, configs["development"])
# Feature flags
FEATURE_FLAGS = {
"enable_batch_processing": True,
"enable_webhooks": True,
"enable_caching": True,
"enable_analytics": True,
"enable_experimental_endpoints": True,
"enable_quality_analysis": True,
"enable_prompt_optimization": True,
"enable_workflow_templates": True
}
def is_feature_enabled(feature: str) -> bool:
"""Check if a feature is enabled.
Args:
feature: Feature name
Returns:
True if feature is enabled
"""
return FEATURE_FLAGS.get(feature, False)