feat: multi-collection support — each project uses its own isolated collection

This commit is contained in:
2026-07-05 02:07:02 +08:00
parent 8042cf288b
commit 0aee167085
2 changed files with 59 additions and 18 deletions
+20 -12
View File
@@ -21,14 +21,20 @@ logger = logging.getLogger("md-vector-db")
class IngestRequest(BaseModel):
file_path: str | None = None
content: str | None = None
file_name: str | None = Field(
default=None, max_length=255
file_name: str | None = Field(default=None, max_length=255)
collection: str | None = Field(
default=None, max_length=128,
description="目标 collection(默认使用配置文件中的 collection_name",
)
class SearchRequest(BaseModel):
query: str = Field(..., min_length=1, max_length=2000)
top_k: int = Field(default=10, ge=1, le=100)
collection: str | None = Field(
default=None, max_length=128,
description="检索的 collection(默认使用配置文件中的 collection_name",
)
# -- 路径安全检查 --
@@ -64,9 +70,7 @@ def health(state: AppState = Depends(get_state)):
@app.get("/api/v1/collections")
def list_collections(state: AppState = Depends(get_state)):
info = state.searcher.get_collection_info()
sources = state.searcher.list_sources()
return {"collections": [info], "sources": sources}
return {"collections": state.list_collections_with_stats()}
@app.post("/api/v1/ingest")
@@ -75,6 +79,7 @@ def ingest_document(
state: AppState = Depends(get_state),
_: bool = Depends(verify_api_key),
):
ingestor = state.get_ingestor(req.collection)
try:
if req.file_path:
if not _is_safe_path(req.file_path):
@@ -85,14 +90,14 @@ def ingest_document(
raise HTTPException(status_code=400, detail="不允许访问当前目录外的路径")
if not path.exists():
raise HTTPException(status_code=404, detail=f"文件不存在: {path.name}")
count = state.ingestor.ingest_file(str(path))
count = ingestor.ingest_file(str(path))
file_name = path.name
elif req.content:
file_name = req.file_name or "untitled.md"
count = state.ingestor.ingest_content(req.content, file_name)
count = ingestor.ingest_content(req.content, file_name)
else:
raise HTTPException(status_code=400, detail="需要提供 file_path 或 content")
return {"status": "ok", "chunks": count, "file": file_name}
return {"status": "ok", "chunks": count, "file": file_name, "collection": ingestor.collection_name}
except HTTPException:
raise
except Exception:
@@ -106,8 +111,9 @@ def search_documents(
state: AppState = Depends(get_state),
_: bool = Depends(verify_api_key),
):
results = state.searcher.search(req.query, top_k=req.top_k)
return {"results": results}
searcher = state.get_searcher(req.collection)
results = searcher.search(req.query, top_k=req.top_k)
return {"results": results, "collection": searcher.collection_name}
@app.delete("/api/v1/documents/{file_name}")
@@ -115,8 +121,10 @@ def delete_document(
file_name: str,
state: AppState = Depends(get_state),
_: bool = Depends(verify_api_key),
collection: str | None = None,
):
deleted = state.searcher.delete_by_source(file_name)
searcher = state.get_searcher(collection)
deleted = searcher.delete_by_source(file_name)
if not deleted:
raise HTTPException(status_code=404, detail=f"文档不存在: {file_name}")
return {"status": "ok", "file": file_name}
return {"status": "ok", "file": file_name, "collection": searcher.collection_name}