feat: add API endpoints
This commit is contained in:
180
app/main.py
Normal file
180
app/main.py
Normal file
@@ -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
|
||||||
|
)
|
||||||
Reference in New Issue
Block a user