|
|
@@ -6,15 +6,16 @@
|
|
|
import os
|
|
|
import json
|
|
|
import uuid
|
|
|
+import asyncio
|
|
|
import tempfile
|
|
|
from typing import Optional
|
|
|
|
|
|
from fastapi import APIRouter, HTTPException, UploadFile, File, Form
|
|
|
from pydantic import BaseModel
|
|
|
|
|
|
-from ..core.viking_client import upload_to_tos, add_doc_to_knowledge
|
|
|
+from ..core.viking_client import upload_to_tos, add_doc_to_knowledge, delete_from_tos, delete_doc_from_knowledge
|
|
|
from ..db.ai_config import get_config_by_app_name
|
|
|
-from ..db.mongo import save_knowledge_doc, get_knowledge_docs
|
|
|
+from ..db.mongo import save_knowledge_doc, get_knowledge_docs, get_knowledge_doc_by_doc_id, delete_knowledge_doc
|
|
|
|
|
|
router = APIRouter()
|
|
|
|
|
|
@@ -24,6 +25,11 @@ class KnowledgeDocsQuery(BaseModel):
|
|
|
page: int = 1
|
|
|
page_size: int = 20
|
|
|
|
|
|
+
|
|
|
+class KnowledgeDeleteRequest(BaseModel):
|
|
|
+ doc_id: str
|
|
|
+ app_name: str
|
|
|
+
|
|
|
# 支持的文档类型:后缀 → Viking doc_type 值
|
|
|
ALLOWED_DOC_EXTENSIONS: dict[str, str] = {
|
|
|
".txt": "txt",
|
|
|
@@ -45,6 +51,14 @@ def _generate_doc_id() -> str:
|
|
|
return f"doc_{uuid.uuid4().hex}"
|
|
|
|
|
|
|
|
|
+async def _cleanup_tos(object_key: str) -> None:
|
|
|
+ """上传成功后异步清理 TOS 临时文件,失败仅打印日志不影响主流程。"""
|
|
|
+ try:
|
|
|
+ await delete_from_tos(object_key)
|
|
|
+ except Exception as e:
|
|
|
+ print(f"TOS 临时文件清理失败(key={object_key}): {e}")
|
|
|
+
|
|
|
+
|
|
|
@router.post("/upload", summary="上传本地文档到知识库")
|
|
|
async def upload_knowledge_doc(
|
|
|
file: UploadFile = File(..., description="待上传文档,支持 txt/doc/docx/pdf/markdown/pptx/ppt"),
|
|
|
@@ -152,18 +166,20 @@ async def upload_knowledge_doc(
|
|
|
except Exception as e:
|
|
|
raise HTTPException(status_code=500, detail=f"导入知识库失败: {str(e)}")
|
|
|
|
|
|
+ # ── 9. 异步删除 TOS 临时文件(不阻塞响应)─────────────────
|
|
|
+ asyncio.create_task(_cleanup_tos(object_key))
|
|
|
+
|
|
|
finally:
|
|
|
- # ── 9. 清理临时文件 ──────────────────────────────────────
|
|
|
+ # ── 10. 清理本地临时文件 ─────────────────────────────────
|
|
|
if tmp_path and os.path.exists(tmp_path):
|
|
|
os.unlink(tmp_path)
|
|
|
|
|
|
- # ── 10. 构造返回结果并存储到 MongoDB ──────────────────────────
|
|
|
+ # ── 11. 构造返回结果并存储到 MongoDB ─────────────────────────
|
|
|
result = {
|
|
|
"doc_id": kb_data.get("doc_id", final_doc_id),
|
|
|
"resource_id": kb_data.get("resource_id", resource_id),
|
|
|
"collection_name": kb_data.get("collection_name", ""),
|
|
|
"project": kb_data.get("project", "default"),
|
|
|
- "tos_path": tos_uri,
|
|
|
"doc_name": final_doc_name,
|
|
|
"doc_type": doc_type,
|
|
|
"app_name": app_name,
|
|
|
@@ -182,4 +198,37 @@ async def list_knowledge_docs(request: KnowledgeDocsQuery):
|
|
|
"""
|
|
|
page = max(1, request.page)
|
|
|
page_size = request.page_size if 1 <= request.page_size <= 100 else 20
|
|
|
- return get_knowledge_docs(app_name=request.app_name, page=page, page_size=page_size)
|
|
|
+ return get_knowledge_docs(app_name=request.app_name, page=page, page_size=page_size)
|
|
|
+
|
|
|
+
|
|
|
+@router.post("/delete", summary="删除知识库文档")
|
|
|
+async def delete_knowledge_doc_api(request: KnowledgeDeleteRequest):
|
|
|
+ """
|
|
|
+ 删除指定文档,依次执行:
|
|
|
+ 1. 按 doc_id 查询 MongoDB,获取 resource_id
|
|
|
+ 2. 调用 Viking /api/knowledge/doc/delete 删除知识库文档
|
|
|
+ 3. 删除 MongoDB 中的上传记录
|
|
|
+ 注:TOS 临时文件由桶生命周期规则自动过期清理,此处不处理。
|
|
|
+ """
|
|
|
+ # ── 1. 查询 MongoDB 记录 ────────────────────────────────────
|
|
|
+ doc = get_knowledge_doc_by_doc_id(request.doc_id)
|
|
|
+ if not doc:
|
|
|
+ raise HTTPException(status_code=404, detail=f"未找到 doc_id '{request.doc_id}' 的文档记录")
|
|
|
+
|
|
|
+ if doc.get("app_name") != request.app_name:
|
|
|
+ raise HTTPException(status_code=403, detail="doc_id 与 app_name 不匹配")
|
|
|
+
|
|
|
+ resource_id = doc.get("resource_id", "")
|
|
|
+
|
|
|
+ # ── 2. 删除 Viking 知识库文档 ───────────────────────────────
|
|
|
+ try:
|
|
|
+ await delete_doc_from_knowledge(resource_id=resource_id, doc_id=request.doc_id)
|
|
|
+ except ValueError as e:
|
|
|
+ raise HTTPException(status_code=400, detail=str(e))
|
|
|
+ except Exception as e:
|
|
|
+ raise HTTPException(status_code=500, detail=f"删除 Viking 知识库文档失败: {str(e)}")
|
|
|
+
|
|
|
+ # ── 3. 删除 MongoDB 记录 ────────────────────────────────────
|
|
|
+ delete_knowledge_doc(request.doc_id)
|
|
|
+
|
|
|
+ return {"doc_id": request.doc_id, "message": "删除成功"}
|