From e59d5eea7316befded594a877cb710557500fd02 Mon Sep 17 00:00:00 2001 From: Albert Date: Sun, 17 Aug 2025 01:53:57 +0000 Subject: [PATCH] feat: add API endpoints --- app/main.py | 180 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 180 insertions(+) create mode 100644 app/main.py diff --git a/app/main.py b/app/main.py new file mode 100644 index 0000000..b318ce9 --- /dev/null +++ b/app/main.py @@ -0,0 +1,180 @@ +import uuid +import logging +from contextlib import asynccontextmanager +from typing import Annotated + +from fastapi import FastAPI, Depends, HTTPException, BackgroundTasks +from fastapi.middleware.cors import CORSMiddleware + +from app.core.config import settings +from app.data.database import init_db +from app.data.models import ( + StartSessionRequest, + SendMessageRequest, + SessionResponse, + SessionStatusResponse +) +from app.services.session_service import SessionService + +# Configure logging +logging.basicConfig(level=logging.INFO) +logger = logging.getLogger(__name__) + + +@asynccontextmanager +async def lifespan(app: FastAPI): + """Initialize database on startup.""" + logger.info("Initializing database...") + await init_db() + logger.info("Database initialized successfully") + yield + logger.info("Shutting down...") + + +app = FastAPI( + title="SkyTalk API", + description="AI-powered backend service for conversational idea exploration and knowledge synthesis", + version="1.0.0", + lifespan=lifespan +) + +# Add CORS middleware for development +app.add_middleware( + CORSMiddleware, + allow_origins=["*"], # Configure appropriately for production + allow_credentials=True, + allow_methods=["*"], + allow_headers=["*"], +) + + +def get_session_service() -> SessionService: + """Dependency to get SessionService instance.""" + return SessionService() + + +@app.post("/sessions/start", response_model=SessionResponse) +async def start_session( + request: StartSessionRequest, + session_service: Annotated[SessionService, Depends(get_session_service)] +) -> SessionResponse: + """Start a new interview session. + + Args: + request: StartSessionRequest containing the topic + session_service: Injected SessionService instance + + Returns: + SessionResponse with session_id, status, and initial message + """ + try: + result = await session_service.start_session(request.topic) + return SessionResponse( + session_id=result["session_id"], + status=result["status"], + message=result["message"] + ) + except Exception as e: + logger.error(f"Error starting session: {e}") + raise HTTPException(status_code=500, detail="Failed to start session") + + +@app.post("/sessions/sendMessage", response_model=SessionResponse) +async def send_message( + request: SendMessageRequest, + background_tasks: BackgroundTasks, + session_service: Annotated[SessionService, Depends(get_session_service)] +) -> SessionResponse: + """Send a message to an active session. + + Args: + request: SendMessageRequest containing session_id and message + background_tasks: FastAPI background tasks for async processing + session_service: Injected SessionService instance + + Returns: + SessionResponse with the AI's response + """ + try: + result = await session_service.handle_message( + session_id=request.session_id, + message=request.message + ) + + # If session is ending, trigger background synthesis + if result.get("session_ending", False): + background_tasks.add_task( + session_service.process_session_background_task, + request.session_id + ) + logger.info(f"Triggered background synthesis for session {request.session_id}") + + return SessionResponse( + session_id=result["session_id"], + status=result["status"], + message=result["message"] + ) + + except ValueError as e: + logger.error(f"Session error: {e}") + raise HTTPException(status_code=404, detail=str(e)) + except Exception as e: + logger.error(f"Error handling message: {e}") + raise HTTPException(status_code=500, detail="Failed to process message") + + +@app.get("/sessions/getStatus", response_model=SessionStatusResponse) +async def get_session_status( + session_id: uuid.UUID, + session_service: Annotated[SessionService, Depends(get_session_service)] +) -> SessionStatusResponse: + """Get the current status of a session. + + Args: + session_id: UUID of the session + session_service: Injected SessionService instance + + Returns: + SessionStatusResponse with session status and metadata + """ + try: + result = await session_service.get_session_status(session_id) + return SessionStatusResponse( + session_id=result["session_id"], + status=result["status"], + notes_count=result["notes_count"], + created_at=result["created_at"] + ) + except ValueError as e: + logger.error(f"Session not found: {e}") + raise HTTPException(status_code=404, detail=str(e)) + except Exception as e: + logger.error(f"Error getting session status: {e}") + raise HTTPException(status_code=500, detail="Failed to get session status") + + +@app.get("/health") +async def health_check(): + """Health check endpoint.""" + return {"status": "healthy", "service": "SkyTalk API"} + + +@app.get("/") +async def root(): + """Root endpoint with API information.""" + return { + "message": "Welcome to SkyTalk API", + "description": "AI-powered conversational idea exploration and knowledge synthesis", + "version": "1.0.0", + "docs": "/docs" + } + + +if __name__ == "__main__": + import uvicorn + uvicorn.run( + "app.main:app", + host=settings.API_HOST, + port=settings.API_PORT, + reload=True + ) \ No newline at end of file