- SDXL image generation using RealVisXL_V4.0 - IP-Adapter FaceID integration for consistent face generation - Simplified API (removed client_id requirement) - New params: face_image, face_strength - 'vixy' shortcut for face-locked generation - Queue-based async job processing - FastAPI with proper error handling Co-authored-by: Alex <alex@k4zka.online>
74 lines
2.9 KiB
Python
Executable File
74 lines
2.9 KiB
Python
Executable File
"""
|
|
Pydantic models for API requests and responses.
|
|
"""
|
|
|
|
from typing import Optional, Dict, Any, Literal
|
|
from pydantic import BaseModel, Field, validator
|
|
from datetime import datetime
|
|
import config
|
|
|
|
|
|
class GenerationParams(BaseModel):
|
|
"""Optional generation parameters."""
|
|
width: int = Field(default=config.DEFAULT_WIDTH, ge=512, le=2048)
|
|
height: int = Field(default=config.DEFAULT_HEIGHT, ge=512, le=2048)
|
|
num_inference_steps: int = Field(default=config.DEFAULT_STEPS, ge=config.MIN_STEPS, le=config.MAX_STEPS)
|
|
guidance_scale: float = Field(default=config.DEFAULT_GUIDANCE_SCALE, ge=config.MIN_GUIDANCE, le=config.MAX_GUIDANCE)
|
|
seed: Optional[int] = Field(default=None, description="Random seed for reproducibility")
|
|
face_image: Optional[str] = Field(default=None, description="Face reference image name (from faces directory) or 'vixy' for default")
|
|
face_strength: float = Field(default=config.DEFAULT_FACE_STRENGTH, ge=0.0, le=1.0, description="Face conditioning strength (0.0-1.0)")
|
|
|
|
@validator('width', 'height')
|
|
def must_be_multiple_of_8(cls, v):
|
|
if v % 8 != 0:
|
|
raise ValueError('Width and height must be multiples of 8')
|
|
return v
|
|
|
|
|
|
class GenerateRequest(BaseModel):
|
|
"""Request to generate an image."""
|
|
prompt: str = Field(..., min_length=1, max_length=2000, description="Text prompt for image generation")
|
|
negative_prompt: Optional[str] = Field(default=None, max_length=2000, description="Negative prompt to avoid certain features")
|
|
params: Optional[GenerationParams] = Field(default_factory=GenerationParams)
|
|
|
|
|
|
class JobResponse(BaseModel):
|
|
"""Response when submitting a generation job."""
|
|
job_id: str = Field(..., description="Unique job identifier")
|
|
status: Literal["queued", "processing", "completed", "failed"] = Field(..., description="Current job status")
|
|
created_at: datetime = Field(..., description="Job creation timestamp")
|
|
message: Optional[str] = Field(default=None, description="Optional message")
|
|
|
|
|
|
class JobStatus(BaseModel):
|
|
"""Detailed job status information."""
|
|
job_id: str
|
|
status: Literal["queued", "processing", "completed", "failed"]
|
|
progress: int = Field(..., ge=0, le=100, description="Progress percentage (0-100)")
|
|
created_at: datetime
|
|
started_at: Optional[datetime] = None
|
|
completed_at: Optional[datetime] = None
|
|
error: Optional[str] = None
|
|
prompt: str
|
|
|
|
|
|
class HealthResponse(BaseModel):
|
|
"""Health check response."""
|
|
model_config = {"protected_namespaces": ()} # Allow "model_" prefix
|
|
|
|
status: Literal["healthy", "unhealthy"]
|
|
version: str
|
|
model_loaded: bool
|
|
queue_size: int
|
|
active_jobs: int
|
|
uptime_seconds: float
|
|
|
|
|
|
class ModelsResponse(BaseModel):
|
|
"""Available models information."""
|
|
base_model: str
|
|
refiner_model: Optional[str] = None
|
|
refiner_enabled: bool
|
|
device: str
|
|
fp16_enabled: bool
|