youtube-summarizer/backend/api/models.py

327 lines
12 KiB
Python

"""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)}")