"""Knowledge plugin routes — extraction, ask, review queue.""" from __future__ import annotations import uuid from fastapi import APIRouter, Depends, HTTPException, Query from pydantic import BaseModel, Field from sqlalchemy.ext.asyncio import AsyncSession from app.core.db import get_db from app.deps import require_permission from app.plugins.builtins.knowledge.services import ( ask_knowledge, extract_knowledge, get_review_queue, review_extraction, ) router = APIRouter(prefix="/api/v1/knowledge", tags=["knowledge"]) class KnowledgeExtractRequest(BaseModel): """Extract knowledge from a source.""" source_type: str = Field(..., min_length=1) source_id: uuid.UUID source_title: str | None = None source_text: str = Field(..., min_length=1) class KnowledgeAskRequest(BaseModel): """Ask a knowledge question.""" question: str = Field(..., min_length=1) class KnowledgeReviewRequest(BaseModel): """Approve or reject a knowledge extraction.""" approved: bool = False notes: str | None = None @router.post("/extract") async def extract( body: KnowledgeExtractRequest, db: AsyncSession = Depends(get_db), current_user: dict = Depends(require_permission("wiki:read")), ): """Extract knowledge from a source (wiki article, dms file, mail, communication).""" tenant_id = uuid.UUID(current_user["tenant_id"]) result = await extract_knowledge( db=db, tenant_id=tenant_id, source_type=body.source_type, source_id=body.source_id, source_title=body.source_title, source_text=body.source_text, user_id=uuid.UUID(current_user["user_id"]) if current_user.get("user_id") else None, ) return result @router.post("/ask") async def ask( body: KnowledgeAskRequest, db: AsyncSession = Depends(get_db), current_user: dict = Depends(require_permission("wiki:read")), ): """Ask a knowledge question — uses wiki + graph_rag as context.""" tenant_id = uuid.UUID(current_user["tenant_id"]) result = await ask_knowledge(db=db, tenant_id=tenant_id, question=body.question) return result @router.get("/review") async def review_queue( page: int = Query(1, ge=1), page_size: int = Query(20, ge=1, le=100), db: AsyncSession = Depends(get_db), current_user: dict = Depends(require_permission("wiki:read")), ): """Get pending knowledge extractions for review.""" tenant_id = uuid.UUID(current_user["tenant_id"]) return await get_review_queue(db=db, tenant_id=tenant_id, page=page, page_size=page_size) @router.post("/review/{extraction_id}") async def review( extraction_id: str, body: KnowledgeReviewRequest, db: AsyncSession = Depends(get_db), current_user: dict = Depends(require_permission("wiki:write")), ): """Approve or reject a knowledge extraction.""" tenant_id = uuid.UUID(current_user["tenant_id"]) try: eid = uuid.UUID(extraction_id) except ValueError: raise HTTPException(400, detail={"detail": "Invalid extraction_id", "code": "invalid_id"}) from None result = await review_extraction( db=db, tenant_id=tenant_id, extraction_id=eid, approved=body.approved, user_id=uuid.UUID(current_user["user_id"]) if current_user.get("user_id") else None, notes=body.notes, ) if "error" in result: raise HTTPException(404, detail={"detail": result["error"], "code": "not_found"}) return result