feat: implement Agent Chat Widget for entity pages
TASK_PHASE_AGENT_CHAT_WIDGET_MVP.md completed:
Backend:
- Add /api/v1/agents/{agent_id}/chat-room endpoint
- Add /api/v1/nodes/{node_id}/chat-room endpoint
- Add /api/v1/microdaos/{slug}/chat-room endpoint
Frontend:
- Create AgentChatWidget.tsx floating chat component
- Integrate into /agents/:agentId page
- Integrate into /nodes/:nodeId page
- Integrate into /microdao/:slug page
Ontology rule implemented:
'No page without agents' = ability to directly talk to agents on that page
This commit is contained in:
@@ -1255,6 +1255,158 @@ async def chat_bootstrap(
|
||||
}
|
||||
|
||||
|
||||
# =============================================================================
|
||||
# Chat Room API (TASK_PHASE_AGENT_CHAT_WIDGET_MVP)
|
||||
# =============================================================================
|
||||
|
||||
@api_router.get("/agents/{agent_id}/chat-room")
|
||||
async def get_agent_chat_room(agent_id: str):
|
||||
"""
|
||||
Отримати інформацію про кімнату чату для агента.
|
||||
Повертає room_id, agent info для ініціалізації чату.
|
||||
"""
|
||||
try:
|
||||
agent = await repo_city.get_agent_by_id(agent_id)
|
||||
if not agent:
|
||||
raise HTTPException(status_code=404, detail=f"Agent not found: {agent_id}")
|
||||
|
||||
# Get agent's primary room or create room slug
|
||||
room_slug = f"agent-console-{agent.get('public_slug') or agent_id}"
|
||||
room = await repo_city.get_room_by_slug(room_slug)
|
||||
|
||||
# If room doesn't exist, try to get agent's primary room from dashboard
|
||||
if not room:
|
||||
rooms = await repo_city.get_agent_rooms(agent_id)
|
||||
if rooms and len(rooms) > 0:
|
||||
room = rooms[0]
|
||||
room_slug = room.get("slug", room_slug)
|
||||
|
||||
return {
|
||||
"agent_id": agent_id,
|
||||
"agent_display_name": agent.get("display_name"),
|
||||
"agent_avatar_url": agent.get("avatar_url"),
|
||||
"agent_status": agent.get("status", "offline"),
|
||||
"agent_kind": agent.get("kind"),
|
||||
"room_slug": room_slug,
|
||||
"room_id": room.get("id") if room else None,
|
||||
"matrix_room_id": room.get("matrix_room_id") if room else None,
|
||||
"chat_available": room is not None and room.get("matrix_room_id") is not None
|
||||
}
|
||||
except HTTPException:
|
||||
raise
|
||||
except Exception as e:
|
||||
logger.error(f"Failed to get agent chat room for {agent_id}: {e}")
|
||||
raise HTTPException(status_code=500, detail="Failed to get agent chat room")
|
||||
|
||||
|
||||
@api_router.get("/nodes/{node_id}/chat-room")
|
||||
async def get_node_chat_room(node_id: str):
|
||||
"""
|
||||
Отримати інформацію про кімнату чату для ноди.
|
||||
Повертає room_id, guardian/steward agents info.
|
||||
"""
|
||||
try:
|
||||
node = await repo_city.get_node_by_id(node_id)
|
||||
if not node:
|
||||
raise HTTPException(status_code=404, detail=f"Node not found: {node_id}")
|
||||
|
||||
# Get node support room
|
||||
node_slug = node_id.replace("node-", "").replace("-", "_")
|
||||
room_slug = f"node-support-{node_slug}"
|
||||
room = await repo_city.get_room_by_slug(room_slug)
|
||||
|
||||
# Get guardian and steward agents
|
||||
guardian_agent = None
|
||||
steward_agent = None
|
||||
|
||||
if node.get("guardian_agent"):
|
||||
guardian_agent = {
|
||||
"id": node["guardian_agent"].get("id"),
|
||||
"display_name": node["guardian_agent"].get("name"),
|
||||
"kind": node["guardian_agent"].get("kind"),
|
||||
"role": "guardian"
|
||||
}
|
||||
|
||||
if node.get("steward_agent"):
|
||||
steward_agent = {
|
||||
"id": node["steward_agent"].get("id"),
|
||||
"display_name": node["steward_agent"].get("name"),
|
||||
"kind": node["steward_agent"].get("kind"),
|
||||
"role": "steward"
|
||||
}
|
||||
|
||||
return {
|
||||
"node_id": node_id,
|
||||
"node_name": node.get("name"),
|
||||
"node_status": node.get("status", "offline"),
|
||||
"room_slug": room_slug,
|
||||
"room_id": room.get("id") if room else None,
|
||||
"matrix_room_id": room.get("matrix_room_id") if room else None,
|
||||
"chat_available": room is not None and room.get("matrix_room_id") is not None,
|
||||
"agents": [a for a in [guardian_agent, steward_agent] if a is not None]
|
||||
}
|
||||
except HTTPException:
|
||||
raise
|
||||
except Exception as e:
|
||||
logger.error(f"Failed to get node chat room for {node_id}: {e}")
|
||||
raise HTTPException(status_code=500, detail="Failed to get node chat room")
|
||||
|
||||
|
||||
@api_router.get("/microdaos/{slug}/chat-room")
|
||||
async def get_microdao_chat_room(slug: str):
|
||||
"""
|
||||
Отримати інформацію про кімнату чату для MicroDAO.
|
||||
Повертає room_id, orchestrator agent info.
|
||||
"""
|
||||
try:
|
||||
dao = await repo_city.get_microdao_by_slug(slug)
|
||||
if not dao:
|
||||
raise HTTPException(status_code=404, detail=f"MicroDAO not found: {slug}")
|
||||
|
||||
# Get MicroDAO lobby room
|
||||
room_slug = f"microdao-lobby-{slug}"
|
||||
room = await repo_city.get_room_by_slug(room_slug)
|
||||
|
||||
# If no lobby room, try to get primary room
|
||||
if not room:
|
||||
rooms = await repo_city.get_microdao_rooms(dao["id"])
|
||||
if rooms and len(rooms) > 0:
|
||||
# Find primary room or first room
|
||||
primary = next((r for r in rooms if r.get("room_role") == "primary"), rooms[0])
|
||||
room = primary
|
||||
room_slug = room.get("slug", room_slug)
|
||||
|
||||
# Get orchestrator agent
|
||||
orchestrator = None
|
||||
orchestrator_id = dao.get("orchestrator_agent_id")
|
||||
if orchestrator_id:
|
||||
orch_agent = await repo_city.get_agent_by_id(orchestrator_id)
|
||||
if orch_agent:
|
||||
orchestrator = {
|
||||
"id": orchestrator_id,
|
||||
"display_name": orch_agent.get("display_name"),
|
||||
"avatar_url": orch_agent.get("avatar_url"),
|
||||
"kind": orch_agent.get("kind"),
|
||||
"role": "orchestrator"
|
||||
}
|
||||
|
||||
return {
|
||||
"microdao_id": dao["id"],
|
||||
"microdao_slug": slug,
|
||||
"microdao_name": dao.get("name"),
|
||||
"room_slug": room_slug,
|
||||
"room_id": room.get("id") if room else None,
|
||||
"matrix_room_id": room.get("matrix_room_id") if room else None,
|
||||
"chat_available": room is not None and room.get("matrix_room_id") is not None,
|
||||
"orchestrator": orchestrator
|
||||
}
|
||||
except HTTPException:
|
||||
raise
|
||||
except Exception as e:
|
||||
logger.error(f"Failed to get microdao chat room for {slug}: {e}")
|
||||
raise HTTPException(status_code=500, detail="Failed to get microdao chat room")
|
||||
|
||||
|
||||
# =============================================================================
|
||||
# City Feed API
|
||||
# =============================================================================
|
||||
|
||||
Reference in New Issue
Block a user