""" Chat completion router. OpenAI-compatible /v1/chat/completions endpoint. """ import json import logging from typing import AsyncGenerator from fastapi import APIRouter from starlette.responses import StreamingResponse from src.chat import service from src.chat.schemas import ( ChatCompletionRequest, ChatCompletionResponse, ) logger = logging.getLogger(__name__) router = APIRouter(prefix="/chat", tags=["chat"]) async def _stream_response( request: ChatCompletionRequest, ) -> AsyncGenerator[str, None]: """ Generate SSE stream for chat completion. Yields raw SSE-formatted strings matching OpenAI's format exactly: data: {json}\n\n """ try: async for chunk in service.create_chat_completion_stream(request): yield f"data: {chunk.model_dump_json(exclude_none=True)}\n\n" yield "data: [DONE]\n\n" except Exception as e: logger.error(f"Error in streaming response: {e}") error_data = json.dumps({"error": {"message": str(e), "type": "internal_error"}}) yield f"data: {error_data}\n\n" @router.post("/completions", response_model=ChatCompletionResponse) async def create_chat_completion( request: ChatCompletionRequest, ) -> ChatCompletionResponse | StreamingResponse: """ Create chat completion (OpenAI-compatible). Supports both regular and streaming responses. Args: request: Chat completion request Returns: Chat completion response or SSE stream """ logger.info(f"Chat completion request for model: {request.model}") if request.stream: logger.info("Streaming response requested") return StreamingResponse( _stream_response(request), media_type="text/event-stream", headers={ "Cache-Control": "no-store", "X-Accel-Buffering": "no", }, ) return await service.create_chat_completion(request)