feat: add /public/agents endpoint for Agent Console with home_node
This commit is contained in:
@@ -161,6 +161,21 @@ class AgentRead(BaseModel):
|
||||
capabilities: List[str] = []
|
||||
|
||||
|
||||
class AgentSummary(BaseModel):
|
||||
"""Agent summary for Agent Console"""
|
||||
id: str
|
||||
display_name: str
|
||||
kind: str = "assistant"
|
||||
avatar_url: Optional[str] = None
|
||||
status: str = "offline"
|
||||
is_public: bool = False
|
||||
public_slug: Optional[str] = None
|
||||
public_title: Optional[str] = None
|
||||
district: Optional[str] = None
|
||||
home_node: Optional[HomeNodeView] = None
|
||||
microdao_memberships: List[Dict[str, Any]] = []
|
||||
|
||||
|
||||
class AgentPresence(BaseModel):
|
||||
"""Agent presence in a room"""
|
||||
agent_id: str
|
||||
|
||||
@@ -307,6 +307,87 @@ async def get_all_agents() -> List[dict]:
|
||||
return [dict(row) for row in rows]
|
||||
|
||||
|
||||
async def get_agents_with_home_node(
|
||||
kind: Optional[str] = None,
|
||||
node_id: Optional[str] = None,
|
||||
limit: int = 100,
|
||||
offset: int = 0
|
||||
) -> Tuple[List[dict], int]:
|
||||
"""Отримати агентів з інформацією про home_node"""
|
||||
pool = await get_pool()
|
||||
|
||||
params: List[Any] = []
|
||||
where_clauses = ["1=1"]
|
||||
|
||||
if kind:
|
||||
params.append(kind)
|
||||
where_clauses.append(f"a.kind = ${len(params)}")
|
||||
|
||||
if node_id:
|
||||
params.append(node_id)
|
||||
where_clauses.append(f"a.node_id = ${len(params)}")
|
||||
|
||||
where_sql = " AND ".join(where_clauses)
|
||||
|
||||
query = f"""
|
||||
SELECT
|
||||
a.id,
|
||||
a.display_name,
|
||||
a.kind,
|
||||
a.avatar_url,
|
||||
a.status,
|
||||
a.is_public,
|
||||
a.public_slug,
|
||||
a.public_title,
|
||||
a.public_district,
|
||||
a.node_id,
|
||||
nc.node_name AS home_node_name,
|
||||
nc.hostname AS home_node_hostname,
|
||||
nc.roles AS home_node_roles,
|
||||
nc.environment AS home_node_environment,
|
||||
COUNT(*) OVER() AS total_count
|
||||
FROM agents a
|
||||
LEFT JOIN node_cache nc ON a.node_id = nc.node_id
|
||||
WHERE {where_sql}
|
||||
ORDER BY a.display_name
|
||||
LIMIT ${len(params) + 1} OFFSET ${len(params) + 2}
|
||||
"""
|
||||
|
||||
params.append(limit)
|
||||
params.append(offset)
|
||||
|
||||
rows = await pool.fetch(query, *params)
|
||||
if not rows:
|
||||
return [], 0
|
||||
|
||||
total = rows[0]["total_count"]
|
||||
items = []
|
||||
|
||||
for row in rows:
|
||||
data = dict(row)
|
||||
data.pop("total_count", None)
|
||||
|
||||
# Build home_node object
|
||||
if data.get("node_id"):
|
||||
data["home_node"] = {
|
||||
"id": data.get("node_id"),
|
||||
"name": data.get("home_node_name"),
|
||||
"hostname": data.get("home_node_hostname"),
|
||||
"roles": list(data.get("home_node_roles") or []),
|
||||
"environment": data.get("home_node_environment")
|
||||
}
|
||||
else:
|
||||
data["home_node"] = None
|
||||
|
||||
# Clean up intermediate fields
|
||||
for key in ["home_node_name", "home_node_hostname", "home_node_roles", "home_node_environment"]:
|
||||
data.pop(key, None)
|
||||
|
||||
items.append(data)
|
||||
|
||||
return items, total
|
||||
|
||||
|
||||
async def get_agents_by_room(room_id: str) -> List[dict]:
|
||||
"""Отримати агентів у конкретній кімнаті"""
|
||||
pool = await get_pool()
|
||||
|
||||
@@ -21,6 +21,7 @@ from models_city import (
|
||||
CityMapResponse,
|
||||
AgentRead,
|
||||
AgentPresence,
|
||||
AgentSummary,
|
||||
HomeNodeView,
|
||||
PublicCitizenSummary,
|
||||
PublicCitizenProfile,
|
||||
@@ -57,6 +58,63 @@ class MicrodaoMembershipPayload(BaseModel):
|
||||
is_core: bool = False
|
||||
|
||||
|
||||
# =============================================================================
|
||||
# Agents API (for Agent Console)
|
||||
# =============================================================================
|
||||
|
||||
@public_router.get("/agents")
|
||||
async def list_agents(
|
||||
kind: Optional[str] = Query(None, description="Filter by agent kind"),
|
||||
node_id: Optional[str] = Query(None, description="Filter by node_id"),
|
||||
limit: int = Query(100, le=200),
|
||||
offset: int = Query(0, ge=0)
|
||||
):
|
||||
"""Список всіх агентів для Agent Console"""
|
||||
try:
|
||||
agents, total = await repo_city.get_agents_with_home_node(
|
||||
kind=kind,
|
||||
node_id=node_id,
|
||||
limit=limit,
|
||||
offset=offset
|
||||
)
|
||||
|
||||
items: List[AgentSummary] = []
|
||||
for agent in agents:
|
||||
# Build home_node if available
|
||||
home_node_data = agent.get("home_node")
|
||||
home_node = None
|
||||
if home_node_data:
|
||||
home_node = HomeNodeView(
|
||||
id=home_node_data.get("id"),
|
||||
name=home_node_data.get("name"),
|
||||
hostname=home_node_data.get("hostname"),
|
||||
roles=home_node_data.get("roles", []),
|
||||
environment=home_node_data.get("environment")
|
||||
)
|
||||
|
||||
# Get microdao memberships
|
||||
memberships = await repo_city.get_agent_microdao_memberships(agent["id"])
|
||||
|
||||
items.append(AgentSummary(
|
||||
id=agent["id"],
|
||||
display_name=agent["display_name"],
|
||||
kind=agent.get("kind", "assistant"),
|
||||
avatar_url=agent.get("avatar_url"),
|
||||
status=agent.get("status", "offline"),
|
||||
is_public=agent.get("is_public", False),
|
||||
public_slug=agent.get("public_slug"),
|
||||
public_title=agent.get("public_title"),
|
||||
district=agent.get("public_district"),
|
||||
home_node=home_node,
|
||||
microdao_memberships=memberships
|
||||
))
|
||||
|
||||
return {"items": items, "total": total}
|
||||
except Exception as e:
|
||||
logger.error(f"Failed to list agents: {e}")
|
||||
raise HTTPException(status_code=500, detail="Failed to list agents")
|
||||
|
||||
|
||||
# =============================================================================
|
||||
# Public Citizens API
|
||||
# =============================================================================
|
||||
|
||||
Reference in New Issue
Block a user