Files
simple-chat-api/app.py
T
2026-07-03 20:33:22 +08:00

110 lines
3.8 KiB
Python

from contextlib import asynccontextmanager
from collections.abc import AsyncGenerator
from fastapi import FastAPI, HTTPException, Request, status
from openai import AsyncOpenAI
from config import Settings
from models import (
CreateSessionRequest,
CreateSessionResponse,
SendMessageRequest,
SendMessageResponse,
SessionHistoryResponse,
)
from service import ChatProviderError, ChatService
from storage import JsonSessionStorage, SessionNotFoundError, SessionStorageError
from tools import ToolRegistry
def create_app(
settings: Settings | None = None,
client: AsyncOpenAI | None = None,
) -> FastAPI:
resolved_settings = settings or Settings.from_env()
storage = JsonSessionStorage(resolved_settings.data_dir)
@asynccontextmanager
async def lifespan(app: FastAPI) -> AsyncGenerator[None, None]:
resolved_client = client
if resolved_client is None:
if not resolved_settings.api_key:
raise RuntimeError("DEEPSEEK_API_KEY is required")
resolved_client = AsyncOpenAI(
api_key=resolved_settings.api_key,
base_url=resolved_settings.base_url,
)
app.state.chat_service = ChatService(
storage=storage,
client=resolved_client,
model=resolved_settings.model,
tool_registry=ToolRegistry(resolved_settings.tool_timeout_seconds),
max_tool_rounds=resolved_settings.max_tool_rounds,
max_tool_calls_per_turn=resolved_settings.max_tool_calls_per_turn,
)
yield
if client is None:
await resolved_client.close()
app = FastAPI(title="Simple Chat API", version="0.1.0", lifespan=lifespan)
@app.post(
"/sessions",
response_model=CreateSessionResponse,
status_code=status.HTTP_201_CREATED,
)
async def create_session(
body: CreateSessionRequest | None = None,
) -> CreateSessionResponse:
system_prompt = (
body.system_prompt
if body is not None and body.system_prompt is not None
else resolved_settings.default_system_prompt
)
try:
session = await storage.create(system_prompt)
except SessionStorageError as exc:
raise HTTPException(status_code=500, detail=str(exc)) from exc
return CreateSessionResponse(
session_id=session.session_id,
system_prompt=session.system_prompt,
created_at=session.created_at,
)
@app.post(
"/sessions/{session_id}/messages",
response_model=SendMessageResponse,
)
async def send_message(
session_id: str, body: SendMessageRequest, request: Request
) -> SendMessageResponse:
service: ChatService = request.app.state.chat_service
try:
return await service.generate_response(session_id, body.content)
except SessionNotFoundError as exc:
raise HTTPException(status_code=404, detail="session not found") from exc
except ChatProviderError as exc:
raise HTTPException(status_code=502, detail=str(exc)) from exc
except SessionStorageError as exc:
raise HTTPException(status_code=500, detail=str(exc)) from exc
@app.get(
"/sessions/{session_id}/messages",
response_model=SessionHistoryResponse,
)
async def get_session_history(
session_id: str, request: Request
) -> SessionHistoryResponse:
service: ChatService = request.app.state.chat_service
try:
return await service.get_session_history(session_id)
except SessionNotFoundError as exc:
raise HTTPException(status_code=404, detail="session not found") from exc
except SessionStorageError as exc:
raise HTTPException(status_code=500, detail=str(exc)) from exc
return app
app = create_app()