"""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)