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
This commit is contained in:
@@ -1,6 +1,6 @@
|
|||||||
# Implementation Plan
|
# 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 backend directory with FastAPI project structure
|
||||||
- Create frontend directory with React + TypeScript + Vite
|
- Create frontend directory with React + TypeScript + Vite
|
||||||
- Configure Python virtual environment and install dependencies (fastapi, uvicorn, kokoro, soundfile, python-multipart)
|
- 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
|
- Set up environment variable configuration for both projects
|
||||||
- _Requirements: 9.6, 10.2, 11.1_
|
- _Requirements: 9.6, 10.2, 11.1_
|
||||||
|
|
||||||
- [ ] 2. Implement backend API foundation
|
- [x] 2. Implement backend API foundation
|
||||||
- Create FastAPI application with CORS middleware
|
- Create FastAPI application with CORS middleware
|
||||||
- Define Pydantic models for requests and responses (RoleDetectionRequest, RolesResponse, TTSRequest, ErrorResponse)
|
- Define Pydantic models for requests and responses (RoleDetectionRequest, RolesResponse, TTSRequest, ErrorResponse)
|
||||||
- Implement /api/health endpoint
|
- Implement /api/health endpoint
|
||||||
|
|||||||
@@ -1,10 +1,59 @@
|
|||||||
"""
|
"""
|
||||||
FastAPI backend for Text to Audiobook application.
|
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.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
|
# Configure CORS
|
||||||
app.add_middleware(
|
app.add_middleware(
|
||||||
@@ -15,8 +64,36 @@ app.add_middleware(
|
|||||||
allow_headers=["*"],
|
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")
|
@app.get("/api/health")
|
||||||
async def health_check():
|
async def health_check():
|
||||||
"""Health check endpoint."""
|
"""Health check endpoint."""
|
||||||
|
logger.info("Health check requested")
|
||||||
return {"status": "ok"}
|
return {"status": "ok"}
|
||||||
|
|||||||
26
backend/test_api.py
Normal file
26
backend/test_api.py
Normal file
@@ -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!")
|
||||||
Reference in New Issue
Block a user