Add LLM token

This commit is contained in:
wangwei
2026-07-02 22:03:39 +08:00
parent e3afb8a07a
commit 52e67b0e7b
36 changed files with 2392 additions and 394 deletions
+81 -6
View File
@@ -526,10 +526,28 @@ class DocumentCommandService:
logger.warning("临时文件清理失败: {}", temp_path)
def delete(self, doc_id: str) -> bool:
"""Delete document record, binary file, and vector chunks."""
"""Delete document record, binary file, and vector chunks.
Handles two cases:
- Normal docs: have a metadata record in the document repository.
- Milvus-only (synthetic) docs: visible in management-list because they
have Milvus vectors but no JSON/PG metadata record. We still clean up
the Milvus chunks so the document disappears from the list.
"""
document = self.document_repository.get(doc_id)
if not document:
# No metadata record — might be a Milvus-only synthetic document.
# Attempt vector cleanup directly; treat as success if any chunks deleted.
try:
deleted_count = self.vector_index.delete_by_document(doc_id)
if deleted_count > 0:
logger.info("Deleted Milvus-only doc (no metadata record): doc_id={} chunks={}", doc_id, deleted_count)
return True
except Exception as exc:
logger.warning("Milvus-only delete failed for doc_id={}: {}", doc_id, exc)
return False
# Normal doc: clean up binary, vectors, artifacts, processing records, metadata.
try:
self.binary_store.delete(document.object_name)
except Exception:
@@ -627,13 +645,16 @@ class DocumentQueryService:
result.append(doc)
# Surface Milvus-only docs that have no metadata record at all.
# MinIO almost certainly has their binaries (they were uploaded), so
# set object_name to the sentinel "{doc_id}/" so the route marks
# has_file=True; the download endpoint will list MinIO to find the file.
for doc_id, row in milvus_by_id.items():
if doc_id not in meta_by_id:
synthetic = Document(
doc_id=doc_id,
doc_name=row.get("doc_title", doc_id),
file_name=row.get("doc_title", doc_id),
object_name="",
object_name=f"{doc_id}/", # sentinel: MinIO prefix exists
content_type="",
size_bytes=0,
status=DocumentStatus.INDEXED,
@@ -646,9 +667,63 @@ class DocumentQueryService:
result.sort(key=lambda d: d.updated_at, reverse=True)
return result[:limit] if limit is not None else result
def download(self, doc_id: str) -> tuple[Document, bytes]:
"""Handle download for the Document Query Service instance."""
def download(self, doc_id: str) -> tuple["Document", bytes]:
"""Return the document record and its binary content from MinIO.
Fallback strategy for Milvus-only docs (no JSON/PG metadata record):
1. Try metadata repository first (normal path).
2. If metadata is missing, list MinIO objects with prefix ``{doc_id}/``
and synthesise a minimal Document from the first object found.
This handles documents whose metadata records were lost but whose
binary files are still in object storage.
3. If neither source has the file, raise FileNotFoundError.
"""
from app.domain.documents import Document, DocumentStatus
document = self.document_repository.get(doc_id)
if not document:
raise FileNotFoundError(f"文档不存在: {doc_id}")
if document and document.object_name and not document.object_name.endswith("/"):
# Normal doc with a concrete object_name — read directly.
return document, self.binary_store.read(document.object_name)
if document and not document.object_name:
raise FileNotFoundError(f"该文档无原始文件(仅含索引数据,无法下载): {doc_id}")
if not document or document.object_name.endswith("/"):
# Metadata missing — try to find the file in MinIO by doc_id prefix.
try:
objects = self.binary_store.list_objects(prefix=f"{doc_id}/")
# Filter out artifact JSON files; prefer the source document.
candidates = [o for o in objects if not o.endswith(".json")]
if not candidates:
candidates = objects # fall back to all objects if only JSON found
if not candidates:
raise FileNotFoundError(f"文档不存在(MinIO 和元数据均无记录): {doc_id}")
object_name = candidates[0]
file_name = object_name.split("/", 1)[-1] if "/" in object_name else object_name
# Guess content type from extension.
ext = file_name.rsplit(".", 1)[-1].lower() if "." in file_name else ""
_ct_map = {
"pdf": "application/pdf",
"docx": "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
"doc": "application/msword",
"txt": "text/plain",
}
content_type = _ct_map.get(ext, "application/octet-stream")
# Synthesise a minimal Document so the route can build the response.
document = Document(
doc_id=doc_id,
doc_name=file_name,
file_name=file_name,
object_name=object_name,
content_type=content_type,
size_bytes=0,
status=DocumentStatus.INDEXED,
)
logger.info("MinIO fallback download: doc_id={} object={}", doc_id, object_name)
except FileNotFoundError:
raise
except Exception as exc:
raise FileNotFoundError(f"文档不存在: {doc_id}") from exc
return document, self.binary_store.read(document.object_name)