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

@@ -158,7 +158,7 @@ export function AgentAvatarUpload({
{canEdit && (
<div className="flex-1">
<p className="text-sm text-white/50 mb-3">
Upload a custom avatar for this agent. Recommended size: 256x256px.
Upload a custom avatar for this agent. Any image format accepted.
</p>
<input
ref={inputRef}

View File

@@ -200,21 +200,26 @@ class NodeGuardian:
# Collect Swapper Metrics
swapper_url = os.getenv("SWAPPER_URL", "http://swapper-service:8890")
try:
# Check healthz
# Check health (Swapper uses /health, not /healthz)
try:
r = await self.client.get(f"{swapper_url}/healthz", timeout=3.0)
metrics["swapper_healthy"] = (r.status_code == 200)
r = await self.client.get(f"{swapper_url}/health", timeout=3.0)
if r.status_code == 200:
health_data = r.json()
metrics["swapper_healthy"] = health_data.get("status") == "healthy"
else:
metrics["swapper_healthy"] = False
except Exception:
metrics["swapper_healthy"] = False
# Check models
# Check models (Swapper uses /models, not /v1/models)
try:
r = await self.client.get(f"{swapper_url}/v1/models", timeout=5.0)
r = await self.client.get(f"{swapper_url}/models", timeout=5.0)
if r.status_code == 200:
data = r.json()
models = data.get("models", [])
metrics["swapper_models_total"] = len(models)
metrics["swapper_models_loaded"] = sum(1 for m in models if m.get("loaded") is True)
# Swapper uses "status": "loaded" not "loaded": true
metrics["swapper_models_loaded"] = sum(1 for m in models if m.get("status") == "loaded")
metrics["swapper_state"] = data
except Exception as e:
# logger.warning(f"Failed to fetch Swapper models: {e}")

View File

@@ -39,3 +39,4 @@ echo " docker-compose -f docker-compose.city-space.yml logs -f"

View File

@@ -42,3 +42,4 @@ echo " ./scripts/test-phase2-e2e.sh"

View File

@@ -51,3 +51,4 @@ echo ""

View File

@@ -55,3 +55,4 @@ echo "📚 Documentation: docs/PHASE4_READY.md"

View File

@@ -14,3 +14,4 @@ echo "✅ Services stopped!"

View File

@@ -17,3 +17,4 @@ echo " docker-compose -f docker-compose.agents.yml down -v"

View File

@@ -16,3 +16,4 @@ echo ""

View File

@@ -11,3 +11,4 @@ echo "✅ Phase 4 services stopped"

View File

@@ -204,3 +204,4 @@ fi

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():