""" Hybrid API Endpoints for SAAP OpenRouter Integration Additional endpoints to support multi-provider functionality """ from fastapi import APIRouter, HTTPException, Depends from typing import Dict, Optional, Any import logging from datetime import datetime from services.agent_manager_hybrid import HybridAgentManagerService logger = logging.getLogger(__name__) # Router for hybrid endpoints hybrid_router = APIRouter(prefix="/api/v1/hybrid", tags=["hybrid"]) def get_hybrid_manager() -> HybridAgentManagerService: """Dependency to get hybrid agent manager (if available)""" # This will be injected by main.py if hybrid mode is enabled return None # ===================================================== # PROVIDER COMPARISON & PERFORMANCE ENDPOINTS # ===================================================== @hybrid_router.post("/agents/{agent_id}/compare") async def compare_providers( agent_id: str, message_data: Dict[str, str], hybrid_manager: HybridAgentManagerService = Depends(get_hybrid_manager) ): """ 🆚 Send same message to both colossus and OpenRouter for comparison Useful for benchmarking performance and cost analysis """ if not hybrid_manager: raise HTTPException(status_code=503, detail="Hybrid mode not enabled") try: message = message_data.get("message", "") if not message: raise HTTPException(status_code=400, detail="Message content required") logger.info(f"📊 Provider comparison requested for {agent_id}") # Send to both providers comparison = await hybrid_manager.compare_providers(agent_id, message) if "error" in comparison: return { "success": False, "error": comparison["error"], "timestamp": datetime.utcnow().isoformat() } logger.info(f"✅ Provider comparison completed for {agent_id}") return { "success": True, "comparison": comparison, "timestamp": datetime.utcnow().isoformat() } except Exception as e: logger.error(f"❌ Provider comparison error: {e}") raise HTTPException(status_code=500, detail=f"Comparison failed: {str(e)}") @hybrid_router.get("/stats/providers") async def get_provider_statistics( hybrid_manager: HybridAgentManagerService = Depends(get_hybrid_manager) ): """ 📊 Get comprehensive provider performance statistics Returns success rates, response times, and cost data """ if not hybrid_manager: raise HTTPException(status_code=503, detail="Hybrid mode not enabled") try: stats = hybrid_manager.get_provider_stats() return { "success": True, "statistics": stats, "timestamp": datetime.utcnow().isoformat() } except Exception as e: logger.error(f"❌ Provider stats error: {e}") raise HTTPException(status_code=500, detail=f"Statistics failed: {str(e)}") @hybrid_router.get("/costs/openrouter") async def get_openrouter_costs( hybrid_manager: HybridAgentManagerService = Depends(get_hybrid_manager) ): """ 💰 Get OpenRouter cost summary and budget status """ if not hybrid_manager: raise HTTPException(status_code=503, detail="Hybrid mode not enabled") if not hybrid_manager.openrouter_client: raise HTTPException(status_code=503, detail="OpenRouter client not available") try: cost_summary = hybrid_manager.openrouter_client.get_cost_summary() return { "success": True, "costs": cost_summary, "timestamp": datetime.utcnow().isoformat() } except Exception as e: logger.error(f"❌ OpenRouter cost error: {e}") raise HTTPException(status_code=500, detail=f"Cost summary failed: {str(e)}") # ===================================================== # PROVIDER SWITCHING & CONFIGURATION # ===================================================== @hybrid_router.post("/config/primary-provider") async def set_primary_provider( config_data: Dict[str, str], hybrid_manager: HybridAgentManagerService = Depends(get_hybrid_manager) ): """ 🔄 Switch primary provider (colossus/openrouter) """ if not hybrid_manager: raise HTTPException(status_code=503, detail="Hybrid mode not enabled") try: provider = config_data.get("provider", "") if provider not in ["colossus", "openrouter"]: raise HTTPException(status_code=400, detail="Provider must be 'colossus' or 'openrouter'") success = await hybrid_manager.set_primary_provider(provider) if success: return { "success": True, "message": f"Primary provider set to {provider}", "provider": provider, "timestamp": datetime.utcnow().isoformat() } else: raise HTTPException(status_code=400, detail=f"Failed to switch to {provider}") except HTTPException: raise except Exception as e: logger.error(f"❌ Provider switch error: {e}") raise HTTPException(status_code=500, detail=f"Provider switch failed: {str(e)}") @hybrid_router.get("/config/status") async def get_hybrid_status( hybrid_manager: HybridAgentManagerService = Depends(get_hybrid_manager) ): """ â„šī¸ Get hybrid system configuration and status """ if not hybrid_manager: return { "hybrid_enabled": False, "message": "Hybrid mode not available", "timestamp": datetime.utcnow().isoformat() } try: # Check provider availability providers_status = { "colossus": { "available": hybrid_manager.colossus_client is not None, "status": hybrid_manager.colossus_connection_status if hasattr(hybrid_manager, 'colossus_connection_status') else "unknown" }, "openrouter": { "available": hybrid_manager.openrouter_client is not None, "status": "unknown" } } # Test OpenRouter if available if hybrid_manager.openrouter_client: try: or_health = await hybrid_manager.openrouter_client.health_check() providers_status["openrouter"]["status"] = or_health.get("status", "unknown") providers_status["openrouter"]["daily_cost"] = or_health.get("daily_cost", 0) providers_status["openrouter"]["budget_remaining"] = or_health.get("budget_remaining", 0) except Exception as e: providers_status["openrouter"]["status"] = f"error: {e}" return { "hybrid_enabled": True, "primary_provider": hybrid_manager.primary_provider, "failover_enabled": hybrid_manager.enable_failover, "cost_comparison_enabled": hybrid_manager.enable_cost_comparison, "providers": providers_status, "loaded_agents": len(hybrid_manager.agents), "timestamp": datetime.utcnow().isoformat() } except Exception as e: logger.error(f"❌ Hybrid status error: {e}") raise HTTPException(status_code=500, detail=f"Status check failed: {str(e)}") # ===================================================== # OPTIONAL: DIRECT PROVIDER ENDPOINTS # ===================================================== @hybrid_router.post("/agents/{agent_id}/chat/colossus") async def chat_with_colossus( agent_id: str, message_data: Dict[str, str], hybrid_manager: HybridAgentManagerService = Depends(get_hybrid_manager) ): """ 🤖 Direct chat with agent via colossus (bypass primary provider setting) """ if not hybrid_manager: raise HTTPException(status_code=503, detail="Hybrid mode not enabled") try: message = message_data.get("message", "") if not message: raise HTTPException(status_code=400, detail="Message content required") # Force colossus provider response = await hybrid_manager.send_message_to_agent(agent_id, message, "colossus") if "error" in response: return { "success": False, "error": response["error"], "provider": "colossus", "timestamp": datetime.utcnow().isoformat() } return { "success": True, "agent_id": agent_id, "message": message, "response": response, "provider": "colossus", "timestamp": datetime.utcnow().isoformat() } except Exception as e: logger.error(f"❌ colossus chat error: {e}") raise HTTPException(status_code=500, detail=f"colossus chat failed: {str(e)}") @hybrid_router.post("/agents/{agent_id}/chat/openrouter") async def chat_with_openrouter( agent_id: str, message_data: Dict[str, str], hybrid_manager: HybridAgentManagerService = Depends(get_hybrid_manager) ): """ 🌐 Direct chat with agent via OpenRouter (bypass primary provider setting) """ if not hybrid_manager: raise HTTPException(status_code=503, detail="Hybrid mode not enabled") if not hybrid_manager.openrouter_client: raise HTTPException(status_code=503, detail="OpenRouter client not available") try: message = message_data.get("message", "") if not message: raise HTTPException(status_code=400, detail="Message content required") # Force OpenRouter provider response = await hybrid_manager.send_message_to_agent(agent_id, message, "openrouter") if "error" in response: return { "success": False, "error": response["error"], "provider": "openrouter", "timestamp": datetime.utcnow().isoformat() } return { "success": True, "agent_id": agent_id, "message": message, "response": response, "provider": "openrouter", "timestamp": datetime.utcnow().isoformat() } except Exception as e: logger.error(f"❌ OpenRouter chat error: {e}") raise HTTPException(status_code=500, detail=f"OpenRouter chat failed: {str(e)}") # ===================================================== # HEALTH CHECK FOR HYBRID SYSTEM # ===================================================== @hybrid_router.get("/health") async def hybrid_health_check( hybrid_manager: HybridAgentManagerService = Depends(get_hybrid_manager) ): """ đŸĨ Comprehensive health check for hybrid system """ if not hybrid_manager: return { "status": "hybrid_disabled", "message": "Hybrid mode not enabled - using standard mode", "timestamp": datetime.utcnow().isoformat() } try: health_status = { "status": "healthy", "providers": {}, "agents_loaded": len(hybrid_manager.agents), "primary_provider": hybrid_manager.primary_provider, "timestamp": datetime.utcnow().isoformat() } # Check colossus if hybrid_manager.colossus_client: try: # Test colossus connection if method available if hasattr(hybrid_manager, '_test_colossus_connection'): await hybrid_manager._test_colossus_connection() health_status["providers"]["colossus"] = { "status": getattr(hybrid_manager, 'colossus_connection_status', 'unknown'), "available": True } except Exception as e: health_status["providers"]["colossus"] = { "status": f"error: {e}", "available": False } else: health_status["providers"]["colossus"] = { "status": "not_configured", "available": False } # Check OpenRouter if hybrid_manager.openrouter_client: try: or_health = await hybrid_manager.openrouter_client.health_check() health_status["providers"]["openrouter"] = { "status": or_health.get("status", "unknown"), "available": or_health.get("status") == "healthy", "daily_cost": or_health.get("daily_cost", 0), "budget_remaining": or_health.get("budget_remaining", 0) } except Exception as e: health_status["providers"]["openrouter"] = { "status": f"error: {e}", "available": False } else: health_status["providers"]["openrouter"] = { "status": "not_configured", "available": False } # Overall health status provider_count = sum(1 for p in health_status["providers"].values() if p["available"]) if provider_count == 0: health_status["status"] = "unhealthy" health_status["message"] = "No providers available" elif provider_count == 1: health_status["status"] = "degraded" health_status["message"] = "Only one provider available" else: health_status["status"] = "healthy" health_status["message"] = "All providers operational" return health_status except Exception as e: logger.error(f"❌ Hybrid health check error: {e}") return { "status": "error", "error": str(e), "timestamp": datetime.utcnow().isoformat() } # Export router for main.py integration __all__ = ["hybrid_router", "get_hybrid_manager"]