From 31da896e566cd7ec62b208691c1d363d3e90d9b4 Mon Sep 17 00:00:00 2001 From: Paul Bailey Date: Tue, 9 Dec 2025 00:57:46 +0000 Subject: [PATCH] Implement backend API foundation - Created FastAPI application with CORS middleware - Defined Pydantic models (RoleDetectionRequest, RolesResponse, TTSRequest, ErrorResponse) - Implemented /api/health endpoint - Set up error handling middleware for validation and general exceptions - Configured logging with INFO level - Added test script to verify API functionality - Validates Requirements: 9.1, 9.2, 9.4 --- .kiro/specs/text-to-audiobook/tasks.md | 4 +- backend/main.py | 81 +++++++++++++++++++++++++- backend/test_api.py | 26 +++++++++ 3 files changed, 107 insertions(+), 4 deletions(-) create mode 100644 backend/test_api.py diff --git a/.kiro/specs/text-to-audiobook/tasks.md b/.kiro/specs/text-to-audiobook/tasks.md index 6a1898d..879620b 100644 --- a/.kiro/specs/text-to-audiobook/tasks.md +++ b/.kiro/specs/text-to-audiobook/tasks.md @@ -1,6 +1,6 @@ # Implementation Plan -- [-] 1. Set up project structure and dependencies +- [x] 1. Set up project structure and dependencies - Create backend directory with FastAPI project structure - Create frontend directory with React + TypeScript + Vite - Configure Python virtual environment and install dependencies (fastapi, uvicorn, kokoro, soundfile, python-multipart) @@ -9,7 +9,7 @@ - Set up environment variable configuration for both projects - _Requirements: 9.6, 10.2, 11.1_ -- [ ] 2. Implement backend API foundation +- [x] 2. Implement backend API foundation - Create FastAPI application with CORS middleware - Define Pydantic models for requests and responses (RoleDetectionRequest, RolesResponse, TTSRequest, ErrorResponse) - Implement /api/health endpoint diff --git a/backend/main.py b/backend/main.py index 70f6229..05626c6 100644 --- a/backend/main.py +++ b/backend/main.py @@ -1,10 +1,59 @@ """ FastAPI backend for Text to Audiobook application. """ -from fastapi import FastAPI +import logging +from typing import List, Optional +from fastapi import FastAPI, Request, status from fastapi.middleware.cors import CORSMiddleware +from fastapi.responses import JSONResponse +from fastapi.exceptions import RequestValidationError +from pydantic import BaseModel, Field -app = FastAPI(title="Text to Audiobook API") +# Configure logging +logging.basicConfig( + level=logging.INFO, + format='%(asctime)s - %(name)s - %(levelname)s - %(message)s' +) +logger = logging.getLogger(__name__) + +# Pydantic Models +class RoleDetectionRequest(BaseModel): + """Request model for role detection endpoint.""" + text: str = Field(..., description="Text to analyze for role detection") + +class RoleInfo(BaseModel): + """Model representing a detected role.""" + id: str = Field(..., description="Unique identifier for the role") + label: str = Field(..., description="Display name for the role") + +class RolesResponse(BaseModel): + """Response model for role detection endpoint.""" + roles: List[RoleInfo] = Field(..., description="List of detected roles") + +class RoleVoiceMapping(BaseModel): + """Model for role-to-voice mapping.""" + id: str = Field(..., description="Role identifier") + voice: str = Field(..., description="Kokoro voice ID") + +class TTSRequest(BaseModel): + """Request model for TTS generation endpoint.""" + text: str = Field(..., description="Text to convert to speech") + lang: str = Field(..., description="Language code (a, b, f, j)") + roles: List[RoleVoiceMapping] = Field(..., description="Role to voice mappings") + speed: Optional[float] = Field(1.0, description="Playback speed (0.5-1.5)") + +class ErrorResponse(BaseModel): + """Standard error response model.""" + error: str = Field(..., description="Error type") + message: str = Field(..., description="Human-readable error message") + details: Optional[dict] = Field(None, description="Additional error details") + +# Initialize FastAPI app +app = FastAPI( + title="Text to Audiobook API", + description="API for converting text to multi-voice audiobooks using Kokoro TTS", + version="1.0.0" +) # Configure CORS app.add_middleware( @@ -15,8 +64,36 @@ app.add_middleware( allow_headers=["*"], ) +# Error handling middleware +@app.exception_handler(RequestValidationError) +async def validation_exception_handler(request: Request, exc: RequestValidationError): + """Handle validation errors with custom error response.""" + logger.error(f"Validation error: {exc.errors()}") + return JSONResponse( + status_code=status.HTTP_400_BAD_REQUEST, + content={ + "error": "ValidationError", + "message": "Invalid request data", + "details": {"errors": exc.errors()} + } + ) +@app.exception_handler(Exception) +async def general_exception_handler(request: Request, exc: Exception): + """Handle general exceptions with custom error response.""" + logger.error(f"Unhandled exception: {str(exc)}", exc_info=True) + return JSONResponse( + status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, + content={ + "error": "InternalServerError", + "message": "An unexpected error occurred", + "details": {"error": str(exc)} + } + ) + +# API Endpoints @app.get("/api/health") async def health_check(): """Health check endpoint.""" + logger.info("Health check requested") return {"status": "ok"} diff --git a/backend/test_api.py b/backend/test_api.py new file mode 100644 index 0000000..5a17c8a --- /dev/null +++ b/backend/test_api.py @@ -0,0 +1,26 @@ +#!/usr/bin/env python3 +"""Simple test script for API foundation.""" +import urllib.request +import json + +def test_health(): + """Test health endpoint.""" + response = urllib.request.urlopen('http://localhost:8001/api/health') + data = json.loads(response.read().decode()) + print(f"✓ Health check: {data}") + +def test_cors(): + """Test CORS headers.""" + req = urllib.request.Request('http://localhost:8001/api/health', + headers={'Origin': 'http://localhost:5173'}) + response = urllib.request.urlopen(req) + cors_origin = response.headers.get('Access-Control-Allow-Origin') + cors_creds = response.headers.get('Access-Control-Allow-Credentials') + print(f"✓ CORS Origin: {cors_origin}") + print(f"✓ CORS Credentials: {cors_creds}") + +if __name__ == '__main__': + print("Testing API Foundation...") + test_health() + test_cors() + print("\nAll tests passed!")