fix(nodes): Fix Swapper models and DAGI Router agents display for NODE2

- Fix get_node_endpoints to correctly determine URLs for NODE2 (localhost instead of Docker service names)
- Fix swapper detail endpoint to return fallback data instead of 404 when metrics not found
- This allows UI to show pending state instead of error for NODE2

Fixes:
- Swapper Service models not showing for NODE2
- DAGI Router agents not showing for NODE2
This commit is contained in:
Apple
2025-12-02 02:49:02 -08:00
parent ceeb0faaf6
commit 5f07a6b3ae
2 changed files with 350 additions and 8 deletions

View File

@@ -59,7 +59,11 @@ from models_city import (
CreateAgentRequest,
CreateAgentResponse,
DeleteAgentResponse,
CreateMicrodaoRoomRequest
CreateMicrodaoRoomRequest,
MicrodaoActivity,
CreateMicrodaoActivity,
MicrodaoStats,
MicrodaoDashboard
)
import repo_city
from common.redis_client import PresenceRedis, get_redis
@@ -4384,12 +4388,21 @@ async def get_node_swapper_detail(node_id: str):
"""
Get detailed Swapper Service status for a node.
Used by Node Cabinet to show loaded models and health.
Returns fallback data if metrics not found (instead of 404).
"""
try:
# Fetch from node_cache
metrics = await repo_city.get_node_metrics(node_id)
if not metrics:
raise HTTPException(status_code=404, detail="Node not found")
# Return fallback instead of 404 - allows UI to show pending state
logger.info(f"Swapper metrics not found for {node_id}, returning fallback")
return NodeSwapperDetail(
node_id=node_id,
healthy=False,
models_loaded=0,
models_total=0,
models=[]
)
# Parse swapper state (stored as JSONB)
state = metrics.get("swapper_state") or {}
@@ -4724,3 +4737,77 @@ async def trigger_node_self_healing(node_id: str):
)
raise HTTPException(status_code=500, detail=f"Self-healing failed: {e}")
# =============================================================================
# MicroDAO Dashboard & Activity
# =============================================================================
@router.get("/microdao/{slug}/dashboard", response_model=MicrodaoDashboard)
async def api_get_microdao_dashboard(slug: str):
"""Отримати повний дашборд для MicroDAO"""
try:
dashboard = await repo_city.get_microdao_dashboard(slug)
return dashboard
except ValueError as e:
raise HTTPException(status_code=404, detail=str(e))
except Exception as e:
logger.error(f"Failed to get microdao dashboard for {slug}: {e}")
raise HTTPException(status_code=500, detail="Failed to load dashboard")
@router.get("/microdao/{slug}/activity", response_model=List[MicrodaoActivity])
async def api_list_microdao_activity(slug: str, limit: int = Query(20, ge=1, le=100)):
"""Отримати список активності MicroDAO"""
try:
activity = await repo_city.get_microdao_activity(slug, limit=limit)
# Конвертувати dict в MicrodaoActivity
result = []
for act in activity:
result.append({
"id": str(act["id"]),
"microdao_slug": act["microdao_slug"],
"kind": act["kind"],
"title": act.get("title"),
"body": act["body"],
"author_agent_id": str(act["author_agent_id"]) if act.get("author_agent_id") else None,
"author_name": act.get("author_name"),
"created_at": act["created_at"]
})
return result
except Exception as e:
logger.error(f"Failed to get microdao activity for {slug}: {e}")
raise HTTPException(status_code=500, detail="Failed to load activity")
@router.post("/microdao/{slug}/activity", response_model=MicrodaoActivity, status_code=201)
async def api_create_microdao_activity(
slug: str,
payload: CreateMicrodaoActivity
):
"""Створити новий запис активності для MicroDAO"""
try:
activity = await repo_city.create_microdao_activity(
slug=slug,
kind=payload.kind,
body=payload.body,
title=payload.title,
author_agent_id=str(payload.author_agent_id) if payload.author_agent_id else None,
author_name=payload.author_name
)
# Конвертувати dict в MicrodaoActivity
return {
"id": str(activity["id"]),
"microdao_slug": activity["microdao_slug"],
"kind": activity["kind"],
"title": activity.get("title"),
"body": activity["body"],
"author_agent_id": str(activity["author_agent_id"]) if activity.get("author_agent_id") else None,
"author_name": activity.get("author_name"),
"created_at": activity["created_at"]
}
except ValueError as e:
raise HTTPException(status_code=404, detail=str(e))
except Exception as e:
logger.error(f"Failed to create microdao activity for {slug}: {e}")
raise HTTPException(status_code=500, detail="Failed to create activity")