fix: update Swapper endpoints (/health, /models), remove upload size limits, auto-convert images

This commit is contained in:
Apple
2025-12-01 03:03:27 -08:00
parent 9c79b6e526
commit b3e3c6417d
15 changed files with 61 additions and 46 deletions

View File

@@ -23,3 +23,4 @@ CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "7001"]

View File

@@ -346,3 +346,4 @@ Proprietary — DAARION Ecosystem

View File

@@ -69,41 +69,32 @@ AUTH_SERVICE_URL = os.getenv("AUTH_SERVICE_URL", "http://daarion-auth:7020")
MATRIX_GATEWAY_URL = os.getenv("MATRIX_GATEWAY_URL", "http://daarion-matrix-gateway:7025")
# Helper for image processing
def process_image(image_bytes: bytes, target_size: tuple = (256, 256)) -> tuple[bytes, bytes]:
def process_image(image_bytes: bytes, max_size: int = 1024, force_square: bool = False) -> tuple[bytes, bytes]:
"""
Process image:
1. Convert to PNG
2. Resize/Crop to target_size (default 256x256)
3. Generate thumbnail 128x128
1. Convert to PNG (any format accepted)
2. Resize to fit within max_size (preserving aspect ratio)
3. Optionally force square crop for avatars/logos
4. Generate thumbnail 128x128
Returns (processed_bytes, thumb_bytes)
"""
with Image.open(io.BytesIO(image_bytes)) as img:
# Convert to RGBA/RGB
if img.mode in ('P', 'CMYK'):
if img.mode in ('P', 'CMYK', 'LA'):
img = img.convert('RGBA')
elif img.mode != 'RGBA':
img = img.convert('RGB')
# Resize/Crop to target_size
img_ratio = img.width / img.height
target_ratio = target_size[0] / target_size[1]
if img_ratio > target_ratio:
# Wider than target
new_height = target_size[1]
new_width = int(new_height * img_ratio)
else:
# Taller than target
new_width = target_size[0]
new_height = int(new_width / img_ratio)
# Force square crop if needed (for avatars/logos)
if force_square:
min_dim = min(img.width, img.height)
left = (img.width - min_dim) / 2
top = (img.height - min_dim) / 2
img = img.crop((left, top, left + min_dim, top + min_dim))
img = img.resize((new_width, new_height), Image.Resampling.LANCZOS)
# Center crop
left = (new_width - target_size[0]) / 2
top = (new_height - target_size[1]) / 2
right = (new_width + target_size[0]) / 2
bottom = (new_height + target_size[1]) / 2
img = img.crop((left, top, right, bottom))
# Resize to fit within max_size (preserve aspect ratio)
if img.width > max_size or img.height > max_size:
img.thumbnail((max_size, max_size), Image.Resampling.LANCZOS)
# Save processed
processed_io = io.BytesIO()
@@ -318,25 +309,31 @@ async def update_agent_visibility_endpoint(
@router.post("/assets/upload")
async def upload_asset(
file: UploadFile = File(...),
type: str = Form(...) # microdao_logo, microdao_banner, room_logo, room_banner
type: str = Form(...) # microdao_logo, microdao_banner, room_logo, room_banner, agent_avatar
):
"""Upload asset (logo/banner) with auto-processing"""
"""Upload asset (logo/banner/avatar) with auto-processing. Accepts any image format."""
try:
# Validate type
if type not in ['microdao_logo', 'microdao_banner', 'room_logo', 'room_banner']:
raise HTTPException(status_code=400, detail="Invalid asset type")
valid_types = ['microdao_logo', 'microdao_banner', 'room_logo', 'room_banner', 'agent_avatar']
if type not in valid_types:
raise HTTPException(status_code=400, detail=f"Invalid asset type. Valid: {valid_types}")
# Validate file size (5MB limit) - done by reading content
# Validate file size (20MB limit)
content = await file.read()
if len(content) > 5 * 1024 * 1024:
raise HTTPException(status_code=400, detail="File too large (max 5MB)")
if len(content) > 20 * 1024 * 1024:
raise HTTPException(status_code=400, detail="File too large (max 20MB)")
# Process image
target_size = (256, 256)
# Process image based on type
# Logos and avatars: square, max 512px
# Banners: max 1920px width, preserve aspect ratio
if 'banner' in type:
target_size = (1200, 400) # Standard banner size
max_size = 1920
force_square = False
else:
max_size = 512
force_square = True # Square crop for logos/avatars
processed_bytes, thumb_bytes = process_image(content, target_size=target_size)
processed_bytes, thumb_bytes = process_image(content, max_size=max_size, force_square=force_square)
# Save to disk
filename = f"{uuid.uuid4()}.png"
@@ -4078,9 +4075,10 @@ async def get_node_swapper_detail(node_id: str):
models = [
SwapperModel(
name=m.get("name", "unknown"),
loaded=m.get("loaded", False),
# Swapper uses "status": "loaded" not "loaded": true
loaded=m.get("status") == "loaded" or m.get("loaded", False),
type=m.get("type"),
vram_gb=m.get("vram_gb")
vram_gb=m.get("size_gb") or m.get("vram_gb")
)
for m in models_data
]

View File

@@ -161,3 +161,4 @@ async def agents_presence_generator():