"""API endpoints for AI summarization.""" import uuid import os from fastapi import APIRouter, HTTPException, BackgroundTasks, Depends from pydantic import BaseModel, Field from typing import Optional, List, Dict, Any from ..services.ai_service import SummaryRequest, SummaryLength from ..services.deepseek_summarizer import DeepSeekSummarizer from ..core.exceptions import AIServiceError, CostLimitExceededError router = APIRouter(prefix="/api", tags=["summarization"]) # In-memory storage for async job results (replace with Redis/DB in production) job_results: Dict[str, Any] = {} class SummarizeRequest(BaseModel): """Request model for summarization endpoint.""" transcript: str = Field(..., description="Video transcript to summarize") length: SummaryLength = Field(SummaryLength.STANDARD, description="Summary length preference") focus_areas: Optional[List[str]] = Field(None, description="Areas to focus on") language: str = Field("en", description="Content language") async_processing: bool = Field(False, description="Process asynchronously") class SummarizeResponse(BaseModel): """Response model for summarization endpoint.""" summary_id: Optional[str] = None # For async processing summary: Optional[str] = None # For sync processing key_points: Optional[List[str]] = None main_themes: Optional[List[str]] = None actionable_insights: Optional[List[str]] = None confidence_score: Optional[float] = None processing_metadata: Optional[dict] = None cost_data: Optional[dict] = None status: str = "completed" # "processing", "completed", "failed" async def get_ai_service() -> DeepSeekSummarizer: """Dependency to get AI service instance.""" api_key = os.getenv("DEEPSEEK_API_KEY") if not api_key: raise HTTPException( status_code=500, detail="DeepSeek API key not configured" ) # Create and initialize service using BaseAIService pattern service = DeepSeekSummarizer(api_key=api_key) if not service.is_initialized: await service.initialize() return service @router.post("/summarize", response_model=SummarizeResponse) async def summarize_transcript( request: SummarizeRequest, background_tasks: BackgroundTasks, ai_service: DeepSeekSummarizer = Depends(get_ai_service) ): """Generate AI summary from transcript.""" # Validate transcript length if len(request.transcript.strip()) < 50: raise HTTPException( status_code=400, detail="Transcript too short for meaningful summarization" ) if len(request.transcript) > 100000: # ~100k characters request.async_processing = True # Force async for very long transcripts try: # Estimate cost before processing estimated_cost = ai_service.estimate_cost(request.transcript, request.length) if estimated_cost > 1.00: # Cost limit check raise CostLimitExceededError(estimated_cost, 1.00) summary_request = SummaryRequest( transcript=request.transcript, length=request.length, focus_areas=request.focus_areas, language=request.language ) if request.async_processing: # Process asynchronously summary_id = str(uuid.uuid4()) background_tasks.add_task( process_summary_async, summary_id=summary_id, request=summary_request, ai_service=ai_service ) # Store initial status job_results[summary_id] = { "status": "processing", "summary_id": summary_id } return SummarizeResponse( summary_id=summary_id, status="processing" ) else: # Process synchronously result = await ai_service.generate_summary(summary_request) return SummarizeResponse( summary=result.summary, key_points=result.key_points, main_themes=result.main_themes, actionable_insights=result.actionable_insights, confidence_score=result.confidence_score, processing_metadata=result.processing_metadata, cost_data=result.cost_data, status="completed" ) except CostLimitExceededError as e: raise HTTPException( status_code=e.status_code, detail={ "error": "Cost limit exceeded", "message": e.message, "details": e.details } ) except AIServiceError as e: raise HTTPException( status_code=500, detail={ "error": "AI service error", "message": e.message, "code": e.error_code, "details": e.details } ) except Exception as e: raise HTTPException( status_code=500, detail={ "error": "Internal server error", "message": str(e) } ) async def process_summary_async( summary_id: str, request: SummaryRequest, ai_service: DeepSeekSummarizer ): """Background task for async summary processing.""" try: result = await ai_service.generate_summary(request) # Store result in memory (replace with proper storage) job_results[summary_id] = { "status": "completed", "summary": result.summary, "key_points": result.key_points, "main_themes": result.main_themes, "actionable_insights": result.actionable_insights, "confidence_score": result.confidence_score, "processing_metadata": result.processing_metadata, "cost_data": result.cost_data } except Exception as e: job_results[summary_id] = { "status": "failed", "error": str(e) } @router.get("/summaries/{summary_id}", response_model=SummarizeResponse) async def get_summary(summary_id: str): """Get async summary result by ID.""" # Retrieve from memory (replace with proper storage) result = job_results.get(summary_id) if not result: raise HTTPException(status_code=404, detail="Summary not found") return SummarizeResponse(**result)