feat: multi-collection support — each project uses its own isolated collection
This commit is contained in:
+20
-12
@@ -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}
|
||||
|
||||
Reference in New Issue
Block a user