Base code
This commit is contained in:
656
backend/config/stability_config.py
Normal file
656
backend/config/stability_config.py
Normal file
@@ -0,0 +1,656 @@
|
||||
"""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)
|
||||
Reference in New Issue
Block a user