#!/usr/bin/env python3 """ BZZZ Integration API for WHOOSH API endpoints for team collaboration, decision publishing, and consensus mechanisms """ from fastapi import APIRouter, HTTPException, Depends, Query from typing import Dict, List, Optional, Any from pydantic import BaseModel, Field from datetime import datetime from ..services.bzzz_integration_service import bzzz_service, AgentRole from ..core.auth_deps import get_current_user from ..models.user import User router = APIRouter(prefix="/api/bzzz", tags=["BZZZ Integration"]) # Pydantic models for API requests/responses class DecisionRequest(BaseModel): title: str = Field(..., description="Decision title") description: str = Field(..., description="Detailed decision description") context: Dict[str, Any] = Field(default_factory=dict, description="Decision context data") ucxl_address: Optional[str] = Field(None, description="Related UCXL address") class DecisionResponse(BaseModel): decision_id: str title: str description: str author_role: str timestamp: datetime ucxl_address: Optional[str] = None class TaskAssignmentRequest(BaseModel): task_description: str = Field(..., description="Task description") required_capabilities: List[str] = Field(..., description="Required capabilities") priority: str = Field("medium", description="Task priority (low, medium, high, urgent)") class TaskAssignmentResponse(BaseModel): decision_id: Optional[str] assigned_to: str assignment_score: float alternatives: List[Dict[str, Any]] class TeamMemberInfo(BaseModel): agent_id: str role: str endpoint: str capabilities: List[str] status: str class TeamStatusResponse(BaseModel): total_members: int online_members: int offline_members: int role_distribution: Dict[str, int] active_decisions: int recent_decisions: List[Dict[str, Any]] network_health: float class ConsensusResponse(BaseModel): decision_id: str total_votes: int approvals: int approval_rate: float consensus_reached: bool details: Dict[str, Any] @router.get("/status", response_model=TeamStatusResponse) async def get_team_status( current_user: User = Depends(get_current_user) ) -> TeamStatusResponse: """Get current BZZZ team status and network health""" try: status = await bzzz_service.get_team_status() return TeamStatusResponse(**status) except Exception as e: raise HTTPException(status_code=500, detail=f"Failed to get team status: {str(e)}") @router.get("/members", response_model=List[TeamMemberInfo]) async def get_team_members( current_user: User = Depends(get_current_user) ) -> List[TeamMemberInfo]: """Get list of active team members in BZZZ network""" try: members = [] for member in bzzz_service.team_members.values(): members.append(TeamMemberInfo( agent_id=member.agent_id, role=member.role.value, endpoint=member.endpoint, capabilities=member.capabilities, status=member.status )) return members except Exception as e: raise HTTPException(status_code=500, detail=f"Failed to get team members: {str(e)}") @router.post("/decisions", response_model=Dict[str, str]) async def publish_decision( decision: DecisionRequest, current_user: User = Depends(get_current_user) ) -> Dict[str, str]: """ Publish a decision to the BZZZ network for team consensus """ try: decision_id = await bzzz_service.publish_decision( title=decision.title, description=decision.description, context=decision.context, ucxl_address=decision.ucxl_address ) if decision_id: return {"decision_id": decision_id, "status": "published"} else: raise HTTPException(status_code=500, detail="Failed to publish decision") except Exception as e: raise HTTPException(status_code=500, detail=f"Failed to publish decision: {str(e)}") @router.get("/decisions", response_model=List[DecisionResponse]) async def get_recent_decisions( limit: int = Query(10, ge=1, le=100), current_user: User = Depends(get_current_user) ) -> List[DecisionResponse]: """Get recent decisions from BZZZ network""" try: decisions = sorted( bzzz_service.active_decisions.values(), key=lambda d: d.timestamp, reverse=True )[:limit] return [ DecisionResponse( decision_id=decision.id, title=decision.title, description=decision.description, author_role=decision.author_role, timestamp=decision.timestamp, ucxl_address=decision.ucxl_address ) for decision in decisions ] except Exception as e: raise HTTPException(status_code=500, detail=f"Failed to get decisions: {str(e)}") @router.get("/decisions/{decision_id}/consensus", response_model=Optional[ConsensusResponse]) async def get_decision_consensus( decision_id: str, current_user: User = Depends(get_current_user) ) -> Optional[ConsensusResponse]: """Get consensus status for a specific decision""" try: consensus = await bzzz_service.get_team_consensus(decision_id) if consensus: return ConsensusResponse(**consensus) else: raise HTTPException(status_code=404, detail="Decision not found or no consensus data available") except HTTPException: raise except Exception as e: raise HTTPException(status_code=500, detail=f"Failed to get consensus: {str(e)}") @router.post("/tasks/assign", response_model=TaskAssignmentResponse) async def coordinate_task_assignment( task: TaskAssignmentRequest, current_user: User = Depends(get_current_user) ) -> TaskAssignmentResponse: """ Coordinate task assignment across team members based on capabilities and availability """ try: assignment = await bzzz_service.coordinate_task_assignment( task_description=task.task_description, required_capabilities=task.required_capabilities, priority=task.priority ) if assignment: return TaskAssignmentResponse(**assignment) else: raise HTTPException(status_code=404, detail="No suitable team members found for task") except HTTPException: raise except Exception as e: raise HTTPException(status_code=500, detail=f"Failed to coordinate task assignment: {str(e)}") @router.post("/network/discover") async def rediscover_network( current_user: User = Depends(get_current_user) ) -> Dict[str, Any]: """Manually trigger team member discovery""" try: await bzzz_service._discover_team_members() return { "status": "success", "members_discovered": len(bzzz_service.team_members), "timestamp": datetime.utcnow().isoformat() } except Exception as e: raise HTTPException(status_code=500, detail=f"Failed to rediscover network: {str(e)}") @router.get("/roles", response_model=List[str]) async def get_available_roles() -> List[str]: """Get list of available agent roles in BZZZ system""" return [role.value for role in AgentRole] @router.get("/capabilities/{agent_id}", response_model=Dict[str, Any]) async def get_agent_capabilities( agent_id: str, current_user: User = Depends(get_current_user) ) -> Dict[str, Any]: """Get detailed capabilities of a specific team member""" try: if agent_id not in bzzz_service.team_members: raise HTTPException(status_code=404, detail=f"Agent {agent_id} not found") member = bzzz_service.team_members[agent_id] return { "agent_id": member.agent_id, "role": member.role.value, "capabilities": member.capabilities, "status": member.status, "endpoint": member.endpoint, "last_seen": datetime.utcnow().isoformat() # Placeholder } except HTTPException: raise except Exception as e: raise HTTPException(status_code=500, detail=f"Failed to get agent capabilities: {str(e)}") @router.get("/health") async def bzzz_health_check() -> Dict[str, Any]: """BZZZ integration health check endpoint""" try: total_members = len(bzzz_service.team_members) online_members = sum(1 for m in bzzz_service.team_members.values() if m.status == "online") health_status = "healthy" if online_members >= total_members * 0.5 else "degraded" if online_members == 0: health_status = "offline" return { "status": health_status, "bzzz_endpoints": len(bzzz_service.bzzz_endpoints), "team_members": total_members, "online_members": online_members, "active_decisions": len(bzzz_service.active_decisions), "timestamp": datetime.utcnow().isoformat() } except Exception as e: return { "status": "error", "error": str(e), "timestamp": datetime.utcnow().isoformat() } # Note: Exception handlers are registered at the app level, not router level