from typing import Annotated import asyncpg from dishka.integrations.fastapi import DishkaRoute, FromDishka from fastapi import APIRouter, Query from pydantic import BaseModel from utils.jobs import enqueue from utils.policy import repository from utils.read import chats from utils.read.models import ( DEFAULT_LIMIT, ChatListItem, MessageVersionView, MessageView, Page, PinnedView, ) from utils.read.pinned import get_pinned router = APIRouter(prefix="/api", tags=["chats"], route_class=DishkaRoute) class EnrichRequest(BaseModel): account_id: int AccountId = Annotated[int, Query()] Limit = Annotated[int, Query()] Offset = Annotated[int, Query()] @router.get("/chats") async def list_chats( pool: FromDishka[asyncpg.Pool], account_id: AccountId, limit: Limit = DEFAULT_LIMIT, offset: Offset = 0, folder_id: Annotated[int | None, Query()] = None, search: Annotated[str | None, Query()] = None, ) -> list[ChatListItem]: folder = ( await repository.get_folder(pool, account_id, folder_id) if folder_id is not None else None ) return await chats.list_chats( pool, account_id, Page(limit=limit, offset=offset), folder=folder, search=search ) @router.get("/chats/{chat_id}") async def get_chat( pool: FromDishka[asyncpg.Pool], chat_id: int, account_id: AccountId ) -> ChatListItem | None: return await chats.get_chat(pool, account_id, chat_id) @router.get("/chats/{chat_id}/messages") async def chat_history( pool: FromDishka[asyncpg.Pool], chat_id: int, account_id: AccountId, limit: Limit = DEFAULT_LIMIT, offset: Offset = 0, include_deleted: Annotated[bool, Query()] = True, before_id: Annotated[int | None, Query()] = None, after_id: Annotated[int | None, Query()] = None, ) -> list[MessageView]: return await chats.get_chat_history( pool, account_id, chat_id, Page(limit=limit, offset=offset), include_deleted=include_deleted, before_id=before_id, after_id=after_id, ) @router.get("/chats/{chat_id}/pinned") async def chat_pinned( pool: FromDishka[asyncpg.Pool], chat_id: int, account_id: AccountId ) -> PinnedView | None: return await get_pinned(pool, account_id, chat_id) @router.post("/chats/{chat_id}/enrich") async def enrich_chat( pool: FromDishka[asyncpg.Pool], chat_id: int, body: EnrichRequest ) -> dict[str, int]: job_id = await enqueue(pool, body.account_id, "enrich_chat", {"chat_id": chat_id}) return {"job_id": job_id} @router.get("/chats/{chat_id}/messages/{message_id}/versions") async def message_versions( pool: FromDishka[asyncpg.Pool], chat_id: int, message_id: int, account_id: AccountId ) -> list[MessageVersionView]: return await chats.get_message_versions(pool, account_id, chat_id, message_id) @router.get("/deleted") async def deleted_messages( pool: FromDishka[asyncpg.Pool], account_id: AccountId, chat_id: Annotated[int | None, Query()] = None, limit: Limit = DEFAULT_LIMIT, offset: Offset = 0, ) -> list[MessageView]: return await chats.get_deleted_messages( pool, account_id, Page(limit=limit, offset=offset), chat_id=chat_id )