from fastapi import APIRouter, HTTPException, Query, Body, Request from fastapi.responses import StreamingResponse from typing import Optional, Dict, Any, List from pydantic import BaseModel from app.chat_service import ( list_universe_conversations, create_universe_conversation, get_conversation_messages, stream_chat_messages, rename_universe_conversation, pin_universe_conversation, delete_universe_conversation ) router = APIRouter(prefix="/api/chat", tags=["Chat"]) class CreateConversationRequest(BaseModel): title: Optional[str] = "Nouvelle conversation" class UpdateConversationRequest(BaseModel): title: Optional[str] = None pinned: Optional[bool] = None class SendMessageRequest(BaseModel): message: str @router.get("/{universe_id}/conversations") async def get_conversations(universe_id: str): """Returns the list of conversations for a universe.""" convs = await list_universe_conversations(universe_id) return {"ok": True, "universe_id": universe_id, "conversations": convs} @router.post("/{universe_id}/conversations") async def create_conversation(universe_id: str, body: CreateConversationRequest = Body(default_factory=CreateConversationRequest)): """Creates a new conversation in a universe.""" conv = await create_universe_conversation(universe_id, title=body.title) return {"ok": True, "conversation": conv} @router.get("/{universe_id}/conversations/{session_key}/messages") async def get_messages(universe_id: str, session_key: str): """Retrieves full message history for a conversation.""" messages = await get_conversation_messages(universe_id, session_key) return {"ok": True, "session_key": session_key, "messages": messages} @router.post("/{universe_id}/conversations/{session_key}/send") async def send_message(universe_id: str, session_key: str, body: SendMessageRequest): """Sends a message and streams SSE chunks back.""" if not body.message or not body.message.strip(): raise HTTPException(status_code=400, detail="Message cannot be empty") stream = stream_chat_messages(universe_id, session_key, body.message.strip()) return StreamingResponse( stream, media_type="text/event-stream", headers={ "Cache-Control": "no-cache", "Connection": "keep-alive", "X-Accel-Buffering": "no" } ) @router.patch("/{universe_id}/conversations/{session_key}") async def update_conv(universe_id: str, session_key: str, body: UpdateConversationRequest): """Updates conversation title or pinned status.""" if body.title is not None: await rename_universe_conversation(universe_id, session_key, body.title) if body.pinned is not None: await pin_universe_conversation(universe_id, session_key, body.pinned) return {"ok": True} @router.delete("/{universe_id}/conversations/{session_key}") async def delete_conv(universe_id: str, session_key: str): """Deletes a conversation.""" await delete_universe_conversation(universe_id, session_key) return {"ok": True}