PMFlow/backend/app/api/rag_api/routes_knowledge.py

142 lines
5.2 KiB
Python

from __future__ import annotations
from .shared import *
from .helpers import _extract_text_from_upload, _resolve_tenant_id
@router.get("/knowledge-base/docs")
async def list_knowledge_docs(
current_user: User = Depends(get_current_user),
db: AsyncSession = Depends(get_db),
tenant_id: int = Depends(get_tenant_id),
):
_ = current_user
service = AdaptiveRAGService(db, tenant_id=_resolve_tenant_id(current_user, tenant_id))
return success_response(await service.list_knowledge_docs())
@router.post("/knowledge-base/docs")
async def create_knowledge_doc(
request: KnowledgeDocCreateRequest,
current_user: User = Depends(get_current_user),
db: AsyncSession = Depends(get_db),
tenant_id: int = Depends(get_tenant_id),
):
service = AdaptiveRAGService(db, tenant_id=_resolve_tenant_id(current_user, tenant_id))
result = await service.create_knowledge_doc(
title=request.title,
file_type=request.file_type,
raw_content=request.content,
uploaded_by=int(current_user.id),
source_type=request.source_type,
file_url=request.file_url,
project_id=request.project_id,
)
return success_response(result)
@router.post("/knowledge-base/upload")
async def upload_knowledge_doc(
file: UploadFile = File(...),
project_id: int | None = Form(default=None),
current_user: User = Depends(get_current_user),
db: AsyncSession = Depends(get_db),
tenant_id: int = Depends(get_tenant_id),
):
raw = await file.read()
if not raw:
raise HTTPException(status_code=400, detail="上传文件为空")
if len(raw) > 15 * 1024 * 1024:
raise HTTPException(status_code=400, detail="文件过大,限制 15MB")
file_type, text = _extract_text_from_upload(file.filename or "uploaded.txt", raw)
if not str(text or "").strip():
raise HTTPException(status_code=400, detail="无法提取有效文本内容")
effective_tenant_id = _resolve_tenant_id(current_user, tenant_id)
service = AdaptiveRAGService(db, tenant_id=effective_tenant_id)
doc = await service.create_knowledge_doc_placeholder(
title=(file.filename or "uploaded.txt")[:255],
file_type=file_type,
uploaded_by=int(current_user.id),
source_type="file",
file_url=None,
)
asyncio.create_task(
AdaptiveRAGService.process_knowledge_doc_content_async(
tenant_id=effective_tenant_id,
doc_id=int(doc.id),
raw_content=text,
project_id=project_id,
)
)
return success_response(service._serialize_doc(doc))
@router.post("/knowledge-base/urls")
async def create_knowledge_url(
request: KnowledgeURLCreateRequest,
current_user: User = Depends(get_current_user),
db: AsyncSession = Depends(get_db),
tenant_id: int = Depends(get_tenant_id),
):
url = str(request.url or "").strip()
if not re.match(r"^https?://", url, flags=re.IGNORECASE):
raise HTTPException(status_code=400, detail="URL 必须以 http:// 或 https:// 开头")
effective_tenant_id = _resolve_tenant_id(current_user, tenant_id)
service = AdaptiveRAGService(db, tenant_id=effective_tenant_id)
doc = await service.create_knowledge_doc_placeholder(
title=(request.title or url)[:255],
file_type="url",
uploaded_by=int(current_user.id),
source_type="url",
file_url=url,
)
asyncio.create_task(
AdaptiveRAGService.refresh_url_doc_async(
tenant_id=effective_tenant_id,
doc_id=int(doc.id),
project_id=request.project_id,
)
)
return success_response(service._serialize_doc(doc))
@router.post("/knowledge-base/docs/{doc_id}/refresh")
async def refresh_knowledge_doc(
doc_id: int,
project_id: int | None = Query(default=None),
current_user: User = Depends(get_current_user),
db: AsyncSession = Depends(get_db),
tenant_id: int = Depends(get_tenant_id),
):
effective_tenant_id = _resolve_tenant_id(current_user, tenant_id)
service = AdaptiveRAGService(db, tenant_id=effective_tenant_id)
try:
doc = await service.get_knowledge_doc(doc_id=doc_id)
if str(doc.get("source_type") or "").lower() != "url":
raise HTTPException(status_code=400, detail="仅 URL 类型条目支持 refresh")
marked = await service.mark_doc_processing(doc_id=doc_id)
except ValueError as exc:
raise HTTPException(status_code=404, detail=str(exc)) from exc
asyncio.create_task(
AdaptiveRAGService.refresh_url_doc_async(
tenant_id=effective_tenant_id,
doc_id=doc_id,
project_id=project_id,
)
)
return success_response(marked)
@router.delete("/knowledge-base/docs/{doc_id}")
async def delete_knowledge_doc(
doc_id: int,
current_user: User = Depends(get_current_user),
db: AsyncSession = Depends(get_db),
tenant_id: int = Depends(get_tenant_id),
):
_ = current_user
service = AdaptiveRAGService(db, tenant_id=_resolve_tenant_id(current_user, tenant_id))
ok = await service.delete_knowledge_doc(doc_id=doc_id)
if not ok:
raise HTTPException(status_code=404, detail="文档不存在")
return success_response({"deleted": True, "doc_id": doc_id})