- Created routers/image_studio/models.py with all 40 Pydantic models - Created routers/image_studio/deps.py with get_studio_manager() and _require_user_id() - Renamed old monolithic image_studio.py -> image_studio_router.py - Updated __init__.py to re-export the router for backward compatibility - Old file now imports models and deps from new modules (no inline definitions) Backward compatibility: from routers.image_studio import router still works. Route count unchanged: 33 routes, prefix /api/image-studio.
373 lines
15 KiB
Python
373 lines
15 KiB
Python
"""Pydantic request/response models for Image Studio API."""
|
|
|
|
from typing import Optional, List, Dict, Any, Literal
|
|
from pydantic import BaseModel, Field
|
|
|
|
|
|
# ==================== Create Studio ====================
|
|
|
|
class CreateImageRequest(BaseModel):
|
|
prompt: str = Field(..., description="Image generation prompt")
|
|
template_id: Optional[str] = Field(None, description="Template ID to use")
|
|
provider: Optional[str] = Field("auto", description="Provider: auto, stability, wavespeed, huggingface, gemini")
|
|
model: Optional[str] = Field(None, description="Specific model to use")
|
|
width: Optional[int] = Field(None, description="Image width in pixels")
|
|
height: Optional[int] = Field(None, description="Image height in pixels")
|
|
aspect_ratio: Optional[str] = Field(None, description="Aspect ratio (e.g., '1:1', '16:9')")
|
|
style_preset: Optional[str] = Field(None, description="Style preset")
|
|
quality: str = Field("standard", description="Quality: draft, standard, premium")
|
|
negative_prompt: Optional[str] = Field(None, description="Negative prompt")
|
|
guidance_scale: Optional[float] = Field(None, description="Guidance scale")
|
|
steps: Optional[int] = Field(None, description="Number of inference steps")
|
|
seed: Optional[int] = Field(None, description="Random seed")
|
|
num_variations: int = Field(1, ge=1, le=10, description="Number of variations (1-10)")
|
|
enhance_prompt: bool = Field(True, description="Enhance prompt with AI")
|
|
use_persona: bool = Field(False, description="Use persona for brand consistency")
|
|
persona_id: Optional[str] = Field(None, description="Persona ID")
|
|
|
|
|
|
class CostEstimationRequest(BaseModel):
|
|
provider: str = Field(..., description="Provider name")
|
|
model: Optional[str] = Field(None, description="Model name")
|
|
operation: str = Field("generate", description="Operation type")
|
|
num_images: int = Field(1, ge=1, description="Number of images")
|
|
width: Optional[int] = Field(None, description="Image width")
|
|
height: Optional[int] = Field(None, description="Image height")
|
|
|
|
|
|
# ==================== Edit Studio ====================
|
|
|
|
class EditImageRequest(BaseModel):
|
|
image_base64: str = Field(..., description="Primary image payload (base64 or data URL)")
|
|
operation: Literal[
|
|
"remove_background",
|
|
"inpaint",
|
|
"outpaint",
|
|
"search_replace",
|
|
"search_recolor",
|
|
"general_edit",
|
|
] = Field(..., description="Edit operation to perform")
|
|
prompt: Optional[str] = Field(None, description="Primary prompt/instruction")
|
|
negative_prompt: Optional[str] = Field(None, description="Negative prompt for providers that support it")
|
|
mask_base64: Optional[str] = Field(None, description="Optional mask image in base64")
|
|
search_prompt: Optional[str] = Field(None, description="Search prompt for replace operations")
|
|
select_prompt: Optional[str] = Field(None, description="Select prompt for recolor operations")
|
|
background_image_base64: Optional[str] = Field(None, description="Reference background image")
|
|
lighting_image_base64: Optional[str] = Field(None, description="Reference lighting image")
|
|
expand_left: Optional[int] = Field(0, description="Outpaint expansion in pixels (left)")
|
|
expand_right: Optional[int] = Field(0, description="Outpaint expansion in pixels (right)")
|
|
expand_up: Optional[int] = Field(0, description="Outpaint expansion in pixels (up)")
|
|
expand_down: Optional[int] = Field(0, description="Outpaint expansion in pixels (down)")
|
|
provider: Optional[str] = Field(None, description="Explicit provider override")
|
|
model: Optional[str] = Field(None, description="Explicit model override")
|
|
style_preset: Optional[str] = Field(None, description="Style preset for Stability helpers")
|
|
guidance_scale: Optional[float] = Field(None, description="Guidance scale for general edits")
|
|
steps: Optional[int] = Field(None, description="Inference steps")
|
|
seed: Optional[int] = Field(None, description="Random seed for reproducibility")
|
|
output_format: str = Field("png", description="Output format for edited image")
|
|
options: Optional[Dict[str, Any]] = Field(None, description="Advanced provider-specific options (e.g., grow_mask)")
|
|
|
|
|
|
class EditImageResponse(BaseModel):
|
|
success: bool
|
|
operation: str
|
|
provider: str
|
|
image_base64: str
|
|
width: int
|
|
height: int
|
|
metadata: Dict[str, Any]
|
|
|
|
|
|
class EditOperationsResponse(BaseModel):
|
|
operations: Dict[str, Dict[str, Any]]
|
|
|
|
|
|
class EditModelsResponse(BaseModel):
|
|
models: List[Dict[str, Any]]
|
|
total: int
|
|
|
|
|
|
class EditModelRecommendationRequest(BaseModel):
|
|
operation: str
|
|
image_resolution: Optional[Dict[str, int]] = None
|
|
user_tier: Optional[str] = None
|
|
preferences: Optional[Dict[str, Any]] = None
|
|
|
|
|
|
class EditModelRecommendationResponse(BaseModel):
|
|
recommended_model: str
|
|
reason: str
|
|
alternatives: List[Dict[str, Any]]
|
|
|
|
|
|
# ==================== Face Swap Studio ====================
|
|
|
|
class FaceSwapRequest(BaseModel):
|
|
base_image_base64: str
|
|
face_image_base64: str
|
|
model: Optional[str] = None
|
|
target_face_index: Optional[int] = None
|
|
target_gender: Optional[str] = None
|
|
options: Optional[Dict[str, Any]] = None
|
|
|
|
|
|
class FaceSwapResponse(BaseModel):
|
|
success: bool
|
|
image_base64: str
|
|
width: int
|
|
height: int
|
|
provider: str
|
|
model: str
|
|
metadata: Dict[str, Any]
|
|
|
|
|
|
class FaceSwapModelsResponse(BaseModel):
|
|
models: List[Dict[str, Any]]
|
|
total: int
|
|
|
|
|
|
class FaceSwapModelRecommendationRequest(BaseModel):
|
|
base_image_resolution: Optional[Dict[str, int]] = None
|
|
face_image_resolution: Optional[Dict[str, int]] = None
|
|
user_tier: Optional[str] = None
|
|
preferences: Optional[Dict[str, Any]] = None
|
|
|
|
|
|
class FaceSwapModelRecommendationResponse(BaseModel):
|
|
recommended_model: str
|
|
reason: str
|
|
alternatives: List[Dict[str, Any]]
|
|
|
|
|
|
# ==================== Upscale Studio ====================
|
|
|
|
class UpscaleImageRequest(BaseModel):
|
|
image_base64: str
|
|
mode: Literal["fast", "conservative", "creative", "auto"] = "auto"
|
|
target_width: Optional[int] = Field(None, description="Target width in pixels")
|
|
target_height: Optional[int] = Field(None, description="Target height in pixels")
|
|
preset: Optional[str] = Field(None, description="Named preset (web, print, social)")
|
|
prompt: Optional[str] = Field(None, description="Prompt for conservative/creative modes")
|
|
|
|
|
|
class UpscaleImageResponse(BaseModel):
|
|
success: bool
|
|
mode: str
|
|
image_base64: str
|
|
width: int
|
|
height: int
|
|
metadata: Dict[str, Any]
|
|
|
|
|
|
# ==================== Control Studio ====================
|
|
|
|
class ControlImageRequest(BaseModel):
|
|
control_image_base64: str = Field(..., description="Control image (sketch/structure/style) in base64")
|
|
operation: Literal["sketch", "structure", "style", "style_transfer"] = Field(..., description="Control operation")
|
|
prompt: str = Field(..., description="Text prompt for generation")
|
|
style_image_base64: Optional[str] = Field(None, description="Style reference image (for style_transfer only)")
|
|
negative_prompt: Optional[str] = Field(None, description="Negative prompt")
|
|
control_strength: Optional[float] = Field(None, ge=0.0, le=1.0, description="Control strength (sketch/structure)")
|
|
fidelity: Optional[float] = Field(None, ge=0.0, le=1.0, description="Style fidelity (style operation)")
|
|
style_strength: Optional[float] = Field(None, ge=0.0, le=1.0, description="Style strength (style_transfer)")
|
|
composition_fidelity: Optional[float] = Field(None, ge=0.0, le=1.0, description="Composition fidelity (style_transfer)")
|
|
change_strength: Optional[float] = Field(None, ge=0.0, le=1.0, description="Change strength (style_transfer)")
|
|
aspect_ratio: Optional[str] = Field(None, description="Aspect ratio (style operation)")
|
|
style_preset: Optional[str] = Field(None, description="Style preset")
|
|
seed: Optional[int] = Field(None, description="Random seed")
|
|
output_format: str = Field("png", description="Output format")
|
|
|
|
|
|
class ControlImageResponse(BaseModel):
|
|
success: bool
|
|
operation: str
|
|
provider: str
|
|
image_base64: str
|
|
width: int
|
|
height: int
|
|
metadata: Dict[str, Any]
|
|
|
|
|
|
class ControlOperationsResponse(BaseModel):
|
|
operations: Dict[str, Dict[str, Any]]
|
|
|
|
|
|
# ==================== Social Optimizer ====================
|
|
|
|
class SocialOptimizeRequest(BaseModel):
|
|
image_base64: str = Field(..., description="Source image in base64 or data URL")
|
|
platforms: List[str] = Field(..., description="List of platforms to optimize for")
|
|
format_names: Optional[Dict[str, str]] = Field(None, description="Specific format per platform")
|
|
show_safe_zones: bool = Field(False, description="Include safe zone overlay in output")
|
|
crop_mode: str = Field("smart", description="Crop mode: smart, center, or fit")
|
|
focal_point: Optional[Dict[str, float]] = Field(None, description="Focal point for smart crop (x, y as 0-1)")
|
|
output_format: str = Field("png", description="Output format (png or jpg)")
|
|
|
|
|
|
class SocialOptimizeResponse(BaseModel):
|
|
success: bool
|
|
results: List[Dict[str, Any]]
|
|
total_optimized: int
|
|
|
|
|
|
class PlatformFormatsResponse(BaseModel):
|
|
formats: List[Dict[str, Any]]
|
|
|
|
|
|
# ==================== Transform Studio ====================
|
|
|
|
class TransformImageToVideoRequestModel(BaseModel):
|
|
image_base64: str = Field(..., description="Image in base64 or data URL format")
|
|
prompt: str = Field(..., description="Text prompt describing the video")
|
|
audio_base64: Optional[str] = Field(None, description="Optional audio file (wav/mp3, 3-30s, ≤15MB)")
|
|
resolution: Literal["480p", "720p", "1080p"] = Field("720p", description="Output resolution")
|
|
duration: Literal[5, 10] = Field(5, description="Video duration in seconds")
|
|
negative_prompt: Optional[str] = Field(None, description="Negative prompt")
|
|
seed: Optional[int] = Field(None, description="Random seed for reproducibility")
|
|
enable_prompt_expansion: bool = Field(True, description="Enable prompt optimizer")
|
|
|
|
|
|
class TalkingAvatarRequestModel(BaseModel):
|
|
image_base64: str = Field(..., description="Person image in base64 or data URL")
|
|
audio_base64: str = Field(..., description="Audio file in base64 or data URL (wav/mp3, max 10 minutes)")
|
|
resolution: Literal["480p", "720p"] = Field("720p", description="Output resolution")
|
|
prompt: Optional[str] = Field(None, description="Optional prompt for expression/style")
|
|
mask_image_base64: Optional[str] = Field(None, description="Optional mask for animatable regions")
|
|
seed: Optional[int] = Field(None, description="Random seed")
|
|
|
|
|
|
class TransformVideoResponse(BaseModel):
|
|
success: bool
|
|
video_url: Optional[str] = None
|
|
video_base64: Optional[str] = None
|
|
duration: float
|
|
resolution: str
|
|
width: int
|
|
height: int
|
|
file_size: int
|
|
cost: float
|
|
provider: str
|
|
model: str
|
|
metadata: Dict[str, Any]
|
|
|
|
|
|
class TransformCostEstimateRequest(BaseModel):
|
|
operation: Literal["image-to-video", "talking-avatar"] = Field(..., description="Operation type")
|
|
resolution: str = Field(..., description="Output resolution")
|
|
duration: Optional[int] = Field(None, description="Video duration in seconds (for image-to-video)")
|
|
|
|
|
|
class TransformCostEstimateResponse(BaseModel):
|
|
estimated_cost: float
|
|
breakdown: Dict[str, Any]
|
|
currency: str
|
|
provider: str
|
|
model: str
|
|
|
|
|
|
# ==================== Compression ====================
|
|
|
|
class CompressImageRequest(BaseModel):
|
|
image_base64: str = Field(..., description="Image in base64 or data URL format")
|
|
quality: int = Field(85, ge=1, le=100, description="Compression quality (1-100)")
|
|
format: str = Field("jpeg", description="Output format: jpeg, png, webp")
|
|
target_size_kb: Optional[int] = Field(None, ge=10, description="Target file size in KB")
|
|
strip_metadata: bool = Field(True, description="Remove EXIF metadata")
|
|
progressive: bool = Field(True, description="Progressive JPEG encoding")
|
|
optimize: bool = Field(True, description="Optimize encoding")
|
|
|
|
|
|
class CompressImageResponse(BaseModel):
|
|
success: bool
|
|
image_base64: str
|
|
original_size_kb: float
|
|
compressed_size_kb: float
|
|
compression_ratio: float
|
|
format: str
|
|
width: int
|
|
height: int
|
|
quality_used: int
|
|
metadata_stripped: bool
|
|
|
|
|
|
class CompressBatchRequest(BaseModel):
|
|
images: List[CompressImageRequest] = Field(..., description="List of images to compress")
|
|
|
|
|
|
class CompressBatchResponse(BaseModel):
|
|
success: bool
|
|
results: List[CompressImageResponse]
|
|
total_images: int
|
|
successful: int
|
|
failed: int
|
|
|
|
|
|
class CompressionEstimateRequest(BaseModel):
|
|
image_base64: str = Field(..., description="Image in base64 or data URL format")
|
|
format: str = Field("jpeg", description="Output format")
|
|
quality: int = Field(85, ge=1, le=100, description="Quality level")
|
|
|
|
|
|
class CompressionEstimateResponse(BaseModel):
|
|
original_size_kb: float
|
|
estimated_size_kb: float
|
|
estimated_reduction_percent: float
|
|
width: int
|
|
height: int
|
|
format: str
|
|
|
|
|
|
class CompressionFormatsResponse(BaseModel):
|
|
formats: List[Dict[str, Any]]
|
|
|
|
|
|
class CompressionPresetsResponse(BaseModel):
|
|
presets: List[Dict[str, Any]]
|
|
|
|
|
|
# ==================== Format Converter ====================
|
|
|
|
class ConvertFormatRequest(BaseModel):
|
|
image_base64: str = Field(..., description="Image in base64 or data URL format")
|
|
target_format: str = Field(..., description="Target format: png, jpeg, jpg, webp, gif, bmp, tiff")
|
|
preserve_transparency: bool = Field(True, description="Preserve transparency when possible")
|
|
quality: Optional[int] = Field(None, ge=1, le=100, description="Quality for lossy formats (1-100)")
|
|
color_space: Optional[str] = Field(None, description="Color space: sRGB, Adobe RGB")
|
|
strip_metadata: bool = Field(False, description="Remove EXIF metadata")
|
|
optimize: bool = Field(True, description="Optimize encoding")
|
|
progressive: bool = Field(True, description="Progressive JPEG encoding")
|
|
|
|
|
|
class ConvertFormatResponse(BaseModel):
|
|
success: bool
|
|
image_base64: str
|
|
original_format: str
|
|
target_format: str
|
|
original_size_kb: float
|
|
converted_size_kb: float
|
|
width: int
|
|
height: int
|
|
transparency_preserved: bool
|
|
metadata_preserved: bool
|
|
color_space: Optional[str] = None
|
|
|
|
|
|
class ConvertFormatBatchRequest(BaseModel):
|
|
images: List[ConvertFormatRequest] = Field(..., description="List of images to convert")
|
|
|
|
|
|
class ConvertFormatBatchResponse(BaseModel):
|
|
success: bool
|
|
results: List[ConvertFormatResponse]
|
|
total_images: int
|
|
successful: int
|
|
failed: int
|
|
|
|
|
|
class SupportedFormatsResponse(BaseModel):
|
|
formats: List[Dict[str, Any]]
|
|
|
|
|
|
class FormatRecommendationsResponse(BaseModel):
|
|
recommendations: List[Dict[str, Any]]
|