from typing import Optional from fastapi import APIRouter, Depends, HTTPException from pydantic import BaseModel from sqlalchemy.ext.asyncio import AsyncSession from deps.db import get_db from services import classifier, storage router = APIRouter(prefix="/api/topics", tags=["topics"]) class TopicCreate(BaseModel): name: str description: str = "" color: str = "#6366f1" class TopicUpdate(BaseModel): name: Optional[str] = None description: Optional[str] = None color: Optional[str] = None class SuggestRequest(BaseModel): document_id: str @router.get("") async def list_topics(session: AsyncSession = Depends(get_db)): topics = await storage.load_topics(session) counts = await storage.topic_doc_counts(session) for t in topics: t["doc_count"] = counts.get(t["name"], 0) return {"topics": topics} @router.post("") async def create_topic(body: TopicCreate, session: AsyncSession = Depends(get_db)): topic = await storage.create_topic(session, body.name, body.description, body.color) topic["doc_count"] = 0 return topic @router.patch("/{topic_id}") async def update_topic( topic_id: str, body: TopicUpdate, session: AsyncSession = Depends(get_db), ): topic = await storage.update_topic( session, topic_id, name=body.name, description=body.description, color=body.color, ) if topic is None: raise HTTPException(404, "Topic not found") counts = await storage.topic_doc_counts(session) topic["doc_count"] = counts.get(topic["name"], 0) return topic @router.delete("/{topic_id}") async def delete_topic(topic_id: str, session: AsyncSession = Depends(get_db)): name = await storage.delete_topic(session, topic_id) if name is None: raise HTTPException(404, "Topic not found") return {"success": True, "removed_from_documents": True} @router.post("/suggest") async def suggest_topics(body: SuggestRequest, session: AsyncSession = Depends(get_db)): meta = await storage.get_metadata(session, body.document_id) if meta is None: raise HTTPException(404, "Document not found") try: suggestions = await classifier.suggest_topics_for_document(session, body.document_id) except Exception as e: raise HTTPException(500, f"Suggestion failed: {e}") return {"suggested": suggestions}