from enum import Enum from typing import Optional, Dict, Any from fastapi import status class ErrorCode(str, Enum): INVALID_URL = "INVALID_URL" UNSUPPORTED_FORMAT = "UNSUPPORTED_FORMAT" VIDEO_NOT_FOUND = "VIDEO_NOT_FOUND" TRANSCRIPT_NOT_AVAILABLE = "TRANSCRIPT_NOT_AVAILABLE" TRANSCRIPT_UNAVAILABLE = "TRANSCRIPT_UNAVAILABLE" AI_SERVICE_ERROR = "AI_SERVICE_ERROR" RATE_LIMIT_EXCEEDED = "RATE_LIMIT_EXCEEDED" INTERNAL_ERROR = "INTERNAL_ERROR" TOKEN_LIMIT_EXCEEDED = "TOKEN_LIMIT_EXCEEDED" COST_LIMIT_EXCEEDED = "COST_LIMIT_EXCEEDED" AI_SERVICE_UNAVAILABLE = "AI_SERVICE_UNAVAILABLE" class BaseAPIException(Exception): def __init__( self, message: str, error_code: ErrorCode, status_code: int = status.HTTP_500_INTERNAL_SERVER_ERROR, details: Optional[Dict[str, Any]] = None, recoverable: bool = False ): self.message = message self.error_code = error_code self.status_code = status_code self.details = details or {} self.recoverable = recoverable super().__init__(message) class UserInputError(BaseAPIException): def __init__( self, message: str, error_code: ErrorCode, details: Optional[Dict[str, Any]] = None ): super().__init__( message=message, error_code=error_code, status_code=status.HTTP_400_BAD_REQUEST, details=details, recoverable=True ) class ValidationError(UserInputError): def __init__(self, message: str, details: Optional[Dict[str, Any]] = None): super().__init__( message=message, error_code=ErrorCode.INVALID_URL, details=details ) class UnsupportedFormatError(UserInputError): def __init__(self, message: str, details: Optional[Dict[str, Any]] = None): super().__init__( message=message, error_code=ErrorCode.UNSUPPORTED_FORMAT, details=details ) class YouTubeError(BaseAPIException): """Base exception for YouTube-related errors""" def __init__(self, message: str, details: Optional[Dict[str, Any]] = None): super().__init__( message=message, error_code=ErrorCode.VIDEO_NOT_FOUND, status_code=status.HTTP_400_BAD_REQUEST, details=details ) class TranscriptExtractionError(BaseAPIException): """Base exception for transcript extraction failures""" pass class AIServiceError(BaseAPIException): """Base exception for AI service errors""" pass class TokenLimitExceededError(AIServiceError): """Raised when content exceeds model token limit""" def __init__(self, token_count: int, max_tokens: int): super().__init__( message=f"Content ({token_count} tokens) exceeds model limit ({max_tokens} tokens)", error_code=ErrorCode.TOKEN_LIMIT_EXCEEDED, status_code=status.HTTP_400_BAD_REQUEST, details={ "token_count": token_count, "max_tokens": max_tokens, "suggestions": [ "Use chunked processing for long content", "Choose a briefer summary length", "Split content into smaller sections" ] } ) class CostLimitExceededError(AIServiceError): """Raised when processing cost exceeds limits""" def __init__(self, estimated_cost: float, cost_limit: float): super().__init__( message=f"Estimated cost ${estimated_cost:.3f} exceeds limit ${cost_limit:.2f}", error_code=ErrorCode.COST_LIMIT_EXCEEDED, status_code=status.HTTP_400_BAD_REQUEST, details={ "estimated_cost": estimated_cost, "cost_limit": cost_limit, "cost_reduction_tips": [ "Choose 'brief' summary length", "Remove less important content from transcript", "Process content in smaller segments" ] } ) class AIServiceUnavailableError(AIServiceError): """Raised when AI service is temporarily unavailable""" def __init__(self, message: str = "AI service is temporarily unavailable"): super().__init__( message=message, error_code=ErrorCode.AI_SERVICE_UNAVAILABLE, status_code=status.HTTP_503_SERVICE_UNAVAILABLE, details={ "suggestions": [ "Please try again in a few moments", "Check API status page for any ongoing issues" ] }, recoverable=True ) class PipelineError(BaseAPIException): """Base exception for pipeline processing errors""" def __init__( self, message: str, stage: str = "unknown", recoverable: bool = True, details: Optional[Dict[str, Any]] = None ): super().__init__( message=message, error_code=ErrorCode.INTERNAL_ERROR, status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, details={ "stage": stage, **(details or {}) }, recoverable=recoverable ) class ServiceError(BaseAPIException): """General service error for business logic failures""" def __init__( self, message: str, service: str = "unknown", recoverable: bool = True, details: Optional[Dict[str, Any]] = None ): super().__init__( message=message, error_code=ErrorCode.INTERNAL_ERROR, status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, details={ "service": service, **(details or {}) }, recoverable=recoverable )