"""Multi-model AI API endpoints.""" from fastapi import APIRouter, Depends, HTTPException, Query from typing import Dict, Any, Optional from enum import Enum from ..services.multi_model_service import MultiModelService, get_multi_model_service from ..services.ai_model_registry import ModelProvider, ModelSelectionStrategy from ..services.ai_service import SummaryRequest, SummaryLength from ..models.api_models import BaseResponse router = APIRouter(prefix="/api/models", tags=["models"]) class ModelProviderEnum(str, Enum): """API enum for model providers.""" OPENAI = "openai" ANTHROPIC = "anthropic" DEEPSEEK = "deepseek" class ModelStrategyEnum(str, Enum): """API enum for selection strategies.""" COST_OPTIMIZED = "cost_optimized" QUALITY_OPTIMIZED = "quality_optimized" SPEED_OPTIMIZED = "speed_optimized" BALANCED = "balanced" @router.get("/available", response_model=Dict[str, Any]) async def get_available_models( service: MultiModelService = Depends(get_multi_model_service) ) -> Dict[str, Any]: """Get list of available AI models and their configurations. Returns information about all configured models including capabilities, pricing, and current availability status. """ try: models = [] for provider_name in service.get_available_models(): provider = ModelProvider(provider_name) config = service.registry.get_model_config(provider) if config: models.append({ "provider": provider_name, "model": config.model_name, "display_name": config.display_name, "available": config.is_available, "context_window": config.context_window, "max_tokens": config.max_tokens, "pricing": { "input_per_1k": config.input_cost_per_1k, "output_per_1k": config.output_cost_per_1k }, "performance": { "latency_ms": config.average_latency_ms, "reliability": config.reliability_score, "quality": config.quality_score }, "capabilities": [cap.value for cap in config.capabilities], "languages": config.supported_languages }) return { "status": "success", "models": models, "active_count": len([m for m in models if m["available"]]) } except Exception as e: raise HTTPException(status_code=500, detail=f"Failed to get models: {str(e)}") @router.post("/summarize", response_model=Dict[str, Any]) async def generate_multi_model_summary( request: SummaryRequest, provider: Optional[ModelProviderEnum] = Query(None, description="Preferred model provider"), strategy: Optional[ModelStrategyEnum] = Query(ModelStrategyEnum.BALANCED, description="Model selection strategy"), max_cost: Optional[float] = Query(None, description="Maximum cost in USD"), service: MultiModelService = Depends(get_multi_model_service) ) -> Dict[str, Any]: """Generate summary using multi-model system with intelligent selection. Args: request: Summary request with transcript and options provider: Optional preferred provider strategy: Model selection strategy max_cost: Optional maximum cost constraint Returns: Summary result with model used and cost information """ try: # Convert enums model_provider = ModelProvider(provider.value) if provider else None model_strategy = ModelSelectionStrategy(strategy.value) # Generate summary result, used_provider = await service.generate_summary( request=request, strategy=model_strategy, preferred_provider=model_provider, max_cost=max_cost ) return { "status": "success", "summary": result.summary, "key_points": result.key_points, "main_themes": result.main_themes, "actionable_insights": result.actionable_insights, "confidence_score": result.confidence_score, "model_used": used_provider.value, "usage": { "input_tokens": result.usage.input_tokens, "output_tokens": result.usage.output_tokens, "total_tokens": result.usage.total_tokens }, "cost": result.cost_data, "metadata": result.processing_metadata } except ValueError as e: raise HTTPException(status_code=400, detail=str(e)) except Exception as e: raise HTTPException(status_code=500, detail=f"Summarization failed: {str(e)}") @router.post("/compare", response_model=Dict[str, Any]) async def compare_models( request: SummaryRequest, service: MultiModelService = Depends(get_multi_model_service) ) -> Dict[str, Any]: """Compare summary results across different models. Generates summaries using all available models and provides a comparison of results, costs, and performance. Args: request: Summary request Returns: Comparison of results from different models """ try: results = {} # Generate summary with each available provider for provider_name in service.get_available_models(): provider = ModelProvider(provider_name) try: result, _ = await service.generate_summary( request=request, preferred_provider=provider ) results[provider_name] = { "success": True, "summary": result.summary[:500] + "..." if len(result.summary) > 500 else result.summary, "key_points_count": len(result.key_points), "confidence": result.confidence_score, "cost": result.cost_data["total_cost"], "processing_time": result.processing_metadata.get("processing_time", 0), "tokens": result.usage.total_tokens } except Exception as e: results[provider_name] = { "success": False, "error": str(e) } # Calculate statistics successful = [r for r in results.values() if r.get("success")] if successful: avg_cost = sum(r["cost"] for r in successful) / len(successful) avg_confidence = sum(r["confidence"] for r in successful) / len(successful) return { "status": "success", "comparisons": results, "statistics": { "models_tested": len(results), "successful": len(successful), "average_cost": avg_cost, "average_confidence": avg_confidence, "cheapest": min(successful, key=lambda x: x["cost"])["cost"] if successful else 0, "fastest": min(successful, key=lambda x: x["processing_time"])["processing_time"] if successful else 0 } } else: return { "status": "partial", "comparisons": results, "message": "No models succeeded" } except Exception as e: raise HTTPException(status_code=500, detail=f"Comparison failed: {str(e)}") @router.get("/metrics", response_model=Dict[str, Any]) async def get_model_metrics( provider: Optional[ModelProviderEnum] = Query(None, description="Specific provider or all"), service: MultiModelService = Depends(get_multi_model_service) ) -> Dict[str, Any]: """Get performance metrics for AI models. Returns usage statistics, success rates, costs, and performance metrics for the specified provider or all providers. Args: provider: Optional specific provider Returns: Metrics and statistics """ try: if provider: model_provider = ModelProvider(provider.value) metrics = service.get_provider_metrics(model_provider) else: metrics = service.get_metrics() return { "status": "success", "metrics": metrics } except Exception as e: raise HTTPException(status_code=500, detail=f"Failed to get metrics: {str(e)}") @router.post("/estimate-cost", response_model=Dict[str, Any]) async def estimate_cost( transcript_length: int = Query(..., description="Transcript length in characters"), service: MultiModelService = Depends(get_multi_model_service) ) -> Dict[str, Any]: """Estimate cost for summarization across different models. Provides cost estimates and recommendations for model selection based on transcript length. Args: transcript_length: Length of transcript in characters Returns: Cost estimates and recommendations """ try: if transcript_length <= 0: raise ValueError("Transcript length must be positive") estimates = service.estimate_cost(transcript_length) return { "status": "success", "data": estimates } except ValueError as e: raise HTTPException(status_code=400, detail=str(e)) except Exception as e: raise HTTPException(status_code=500, detail=f"Failed to estimate cost: {str(e)}") @router.post("/reset-availability", response_model=Dict[str, Any]) async def reset_model_availability( provider: Optional[ModelProviderEnum] = Query(None, description="Specific provider or all"), service: MultiModelService = Depends(get_multi_model_service) ) -> Dict[str, Any]: """Reset model availability after errors. Clears error states and marks models as available again. Args: provider: Optional specific provider to reset Returns: Reset confirmation """ try: if provider: model_provider = ModelProvider(provider.value) service.reset_model_availability(model_provider) message = f"Reset availability for {provider.value}" else: service.reset_model_availability() message = "Reset availability for all models" return { "status": "success", "message": message } except Exception as e: raise HTTPException(status_code=500, detail=f"Failed to reset availability: {str(e)}") @router.put("/strategy", response_model=Dict[str, Any]) async def set_default_strategy( strategy: ModelStrategyEnum, service: MultiModelService = Depends(get_multi_model_service) ) -> Dict[str, Any]: """Set default model selection strategy. Args: strategy: New default strategy Returns: Confirmation of strategy change """ try: model_strategy = ModelSelectionStrategy(strategy.value) service.set_default_strategy(model_strategy) return { "status": "success", "message": f"Default strategy set to {strategy.value}", "strategy": strategy.value } except Exception as e: raise HTTPException(status_code=500, detail=f"Failed to set strategy: {str(e)}")