Cyril Dupland
feat transcription: add dedicated meeting transcription endpoint with support for audio file uploads and language options. Implement validation for file formats and sizes, and update settings for the new transcription model. Enhance API documentation and examples for clarity.
3155a78 | """ | |
| CAPL Routeur IA API | |
| Main FastAPI application with AI agent routing. | |
| """ | |
| from dotenv import load_dotenv | |
| load_dotenv() # charge .env pour tout le process | |
| from pathlib import Path | |
| from fastapi import FastAPI, Request, status | |
| from fastapi.responses import JSONResponse, FileResponse | |
| from fastapi.middleware.cors import CORSMiddleware | |
| from fastapi.exceptions import RequestValidationError | |
| from contextlib import asynccontextmanager | |
| import logging | |
| from config import settings | |
| from api.routes import auth, completion, transcription, models, realtime | |
| from api.routes import documents | |
| from api.routes import voice | |
| # Configure logging | |
| logging.basicConfig( | |
| level=logging.INFO, | |
| format="%(asctime)s - %(name)s - %(levelname)s - %(message)s" | |
| ) | |
| logger = logging.getLogger(__name__) | |
| async def lifespan(app: FastAPI): | |
| """Lifespan event handler for startup and shutdown.""" | |
| # Startup | |
| logger.info(f"Starting {settings.api_title} v{settings.api_version}") | |
| logger.info(f"Environment: {settings.environment}") | |
| from services.voice.ice_servers import get_ice_servers | |
| from pipecat.transports.smallwebrtc.request_handler import SmallWebRTCRequestHandler | |
| raw_ice, rtc_ice = get_ice_servers() | |
| app.state.voice_ice_servers = raw_ice | |
| app.state.voice_handler = SmallWebRTCRequestHandler(ice_servers=rtc_ice) | |
| logger.info("Voice WebRTC handler initialized with %d ICE server(s)", len(rtc_ice)) | |
| yield | |
| # Shutdown | |
| logger.info("Shutting down API") | |
| if hasattr(app.state, "voice_handler") and app.state.voice_handler is not None: | |
| await app.state.voice_handler.close() | |
| # Create FastAPI app | |
| app = FastAPI( | |
| title=settings.api_title, | |
| version=settings.api_version, | |
| description=""" | |
| # CAPL Routeur IA API | |
| API sécurisée pour l'interaction avec des agents IA basés sur LangGraph. | |
| ## Fonctionnalités principales: | |
| - **Authentification JWT** pour sécuriser l'accès | |
| - **Completion texte** avec support du streaming (SSE) | |
| - **Multi-modèles**: OpenAI (GPT-4, GPT-3.5) et Mistral AI | |
| - **Multi-agents**: Architecture extensible pour différents types d'agents | |
| - **Transcription audio**: Conversion audio vers texte avec Whisper | |
| - **Conversation vocale**: WebRTC via Pipecat (STT + LangGraph + TTS) | |
| - **Temps réel**: Support WebSocket | |
| ## Authentification | |
| 1. Obtenez un token JWT via `POST /auth/token` | |
| 2. Incluez le token dans le header: `Authorization: Bearer <token>` | |
| 3. Utilisez le token pour toutes les requêtes protégées | |
| ## Architecture | |
| - **Clean Architecture** avec séparation domain/services/api | |
| - **SOLID principles** pour une extensibilité maximale | |
| - **LangGraph** pour l'orchestration des agents IA | |
| """, | |
| lifespan=lifespan, | |
| docs_url="/docs", | |
| redoc_url="/redoc" | |
| ) | |
| # CORS middleware | |
| app.add_middleware( | |
| CORSMiddleware, | |
| allow_origins=["*"], # À restreindre en production | |
| allow_credentials=True, | |
| allow_methods=["*"], | |
| allow_headers=["*"], | |
| ) | |
| # Exception handlers | |
| async def validation_exception_handler(request: Request, exc: RequestValidationError): | |
| """Handle validation errors with detailed messages.""" | |
| return JSONResponse( | |
| status_code=status.HTTP_422_UNPROCESSABLE_ENTITY, | |
| content={ | |
| "error": "Validation Error", | |
| "detail": exc.errors(), | |
| } | |
| ) | |
| async def general_exception_handler(request: Request, exc: Exception): | |
| """Handle unexpected exceptions.""" | |
| logger.error(f"Unexpected error: {str(exc)}", exc_info=True) | |
| return JSONResponse( | |
| status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, | |
| content={ | |
| "error": "Internal Server Error", | |
| "detail": str(exc) if settings.environment == "development" else "An unexpected error occurred" | |
| } | |
| ) | |
| # Root endpoint | |
| async def root(): | |
| """Root endpoint with API information.""" | |
| return { | |
| "name": settings.api_title, | |
| "version": settings.api_version, | |
| "status": "running", | |
| "environment": settings.environment, | |
| "docs": "/docs", | |
| "health": "/health" | |
| } | |
| # Include routers | |
| app.include_router(auth.router) | |
| app.include_router(models.router) | |
| app.include_router(completion.router) | |
| app.include_router(transcription.router) | |
| app.include_router(realtime.router) | |
| app.include_router(documents.router) | |
| app.include_router(voice.router) | |
| STATIC_DIR = Path(__file__).resolve().parent / "static" | |
| async def serve_voice_page(): | |
| """Serve the standalone WebRTC voice test page (SmallWebRTC).""" | |
| return FileResponse(str(STATIC_DIR / "voice.html")) | |
| async def serve_voice_daily_page(): | |
| """Serve the Daily.co voice test page with Prebuilt UI (works on HF Spaces).""" | |
| return FileResponse(str(STATIC_DIR / "voice_daily.html")) | |
| async def serve_voice_daily_minimal_page(): | |
| """Serve the Daily.co minimal page (no iframe, only bot audio in own UI).""" | |
| return FileResponse(str(STATIC_DIR / "voice_daily_minimal.html")) | |
| if __name__ == "__main__": | |
| import uvicorn | |
| uvicorn.run( | |
| "app:app", | |
| host="0.0.0.0", | |
| port=7860, | |
| reload=True if settings.environment == "development" else False | |
| ) | |