my-rag / app /api /v1 /admin.py
voice-rag's picture
try to connect llm to this rag
6bb0235
Raw
History Blame Contribute Delete
4.75 kB
"""
Admin API β€” /api/v1/collections, /api/v1/delete
Management endpoints for tenant collections.
"""
from fastapi import APIRouter, Depends, HTTPException
from pydantic import BaseModel, Field
from loguru import logger
from app.core.qdrant_client import qdrant_manager
from app.core.security import verify_api_key
router = APIRouter()
# ─── List All Collections ─────────────────────────────────────────────────────
@router.get("/collections")
async def list_collections(_: str = Depends(verify_api_key)):
"""
List all Qdrant collections currently in the instance.
Returns both kb_* tenant collections and any other collections.
**Required Header:** `X-RAG-API-Key`
"""
try:
collections = await qdrant_manager.list_all_collections()
tenant_collections = [c for c in collections if c.startswith("kb_")]
return {
"total": len(collections),
"tenant_collections": tenant_collections,
"all_collections": collections,
}
except Exception as e:
raise HTTPException(status_code=500, detail=str(e))
# ─── Collection Info ──────────────────────────────────────────────────────────
@router.get("/collections/{company_id}")
async def get_collection_info(
company_id: str,
_: str = Depends(verify_api_key),
):
"""
Get stats for a specific tenant's collection.
Returns vector count, point count, and status.
**Required Header:** `X-RAG-API-Key`
"""
try:
info = await qdrant_manager.get_collection_info(company_id)
return info
except Exception as e:
raise HTTPException(status_code=404, detail=f"Collection not found: {str(e)}")
# ─── Delete Tenant Collection ─────────────────────────────────────────────────
class DeleteRequest(BaseModel):
company_id: str = Field(..., description="Tenant to permanently remove.")
confirm: bool = Field(
...,
description="Must be `true` to confirm deletion. Irreversible.",
)
@router.post("/delete")
async def delete_tenant(
body: DeleteRequest,
_: str = Depends(verify_api_key),
):
"""
Permanently delete a tenant's entire knowledge base collection.
**This is irreversible.** All embedded documents are lost.
Requires `confirm: true` in the request body as a safety check.
Uses POST instead of DELETE to avoid request body stripping by proxies/CDNs.
**Required Header:** `X-RAG-API-Key`
"""
if not body.confirm:
raise HTTPException(
status_code=400,
detail="Deletion not confirmed. Set 'confirm': true to proceed.",
)
try:
result = await qdrant_manager.delete_tenant_collection(body.company_id)
logger.info(f"Admin deleted collection for '{body.company_id}'")
return {
"status": "deleted",
"company_id": body.company_id,
"collection": result["collection"],
"message": f"Collection '{result['collection']}' permanently deleted.",
}
except Exception as e:
logger.error(f"Delete failed for '{body.company_id}': {e}")
raise HTTPException(status_code=500, detail=str(e))
# ─── Delete Specific Source from a Collection ─────────────────────────────────
class DeleteSourceRequest(BaseModel):
company_id: str = Field(...)
source: str = Field(..., description="Filename or URL to remove from the collection.")
@router.post("/delete/source")
async def delete_source(
body: DeleteSourceRequest,
_: str = Depends(verify_api_key),
):
"""
Delete all chunks belonging to a specific source (file or URL)
from a tenant's collection, without deleting the entire collection.
Use this when a company updates a document β€” delete old version, re-ingest new.
Uses POST instead of DELETE to avoid request body stripping by proxies/CDNs.
**Required Header:** `X-RAG-API-Key`
"""
try:
await qdrant_manager.delete_chunks_by_source(body.company_id, body.source)
return {
"status": "deleted",
"company_id": body.company_id,
"source": body.source,
"message": f"All chunks for source '{body.source}' deleted from 'kb_{body.company_id}'.",
}
except Exception as e:
raise HTTPException(status_code=500, detail=str(e))