Files
wehub-resource-sync e4dcfc49aa
Tests / Import Check (Python 3.13) (push) Has been cancelled
Tests / Import Check (Python 3.14) (push) Has been cancelled
Tests / Python Tests (Python 3.11) (push) Has been cancelled
Tests / Python Tests (Python 3.12) (push) Has been cancelled
Tests / Python Tests (Python 3.14) (push) Has been cancelled
Tests / Test Summary (push) Has been cancelled
Tests / Lint and Format (push) Has been cancelled
Tests / Web Node Tests (push) Has been cancelled
Tests / Import Check (Python 3.11) (push) Has been cancelled
Tests / Import Check (Python 3.12) (push) Has been cancelled
Tests / Python Tests (Python 3.13) (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 13:00:43 +08:00

274 lines
8.6 KiB
Python

"""Provider-owned index readiness probes.
DeepTutor owns KB lifecycle/status, but each RAG provider owns the shape of its
persisted index. This module is the narrow read-only seam between those worlds:
callers ask "is this provider index really queryable?" and get a structured
answer without knowing provider-specific filenames.
"""
from __future__ import annotations
from dataclasses import dataclass, field
import json
from pathlib import Path
from typing import Any
from deeptutor.services.rag.factory import (
DEFAULT_PROVIDER,
GRAPHRAG_PROVIDER,
LIGHTRAG_PROVIDER,
PAGEINDEX_PROVIDER,
normalize_provider_name,
version_matches_provider,
)
@dataclass(frozen=True)
class ProviderIndexProbe:
"""Read-only verdict for one provider storage directory."""
provider: str
storage_dir: str | None
ready: bool
failure_summary: str = ""
doc_count: int | None = None
diagnostics: dict[str, Any] = field(default_factory=dict)
def inspect_provider_index(
provider: str | None, storage_dir: str | Path | None
) -> ProviderIndexProbe:
"""Inspect one provider storage directory using real provider artifacts."""
resolved = normalize_provider_name(provider)
path = Path(storage_dir) if storage_dir is not None else None
if path is None:
return ProviderIndexProbe(resolved, None, False, "No storage path recorded.")
if resolved == PAGEINDEX_PROVIDER:
return _inspect_pageindex(path)
if resolved == GRAPHRAG_PROVIDER:
return _inspect_graphrag(path)
if resolved == LIGHTRAG_PROVIDER:
return _inspect_lightrag(path)
return _inspect_llamaindex(path)
def inspect_provider_version(entry: dict[str, Any], provider: str | None) -> ProviderIndexProbe:
"""Inspect a version-list entry for ``provider``."""
resolved = normalize_provider_name(provider)
storage_path = entry.get("storage_path")
if not storage_path:
return ProviderIndexProbe(resolved, None, False, "No storage path recorded.")
if not version_matches_provider(entry, resolved):
return ProviderIndexProbe(
resolved,
str(storage_path),
False,
diagnostics={
"version_provider": entry.get("provider"),
"version_signature": entry.get("signature"),
"provider_mismatch": True,
},
)
return inspect_provider_index(resolved, Path(str(storage_path)))
def inspect_kb_versions(kb_dir: str | Path, provider: str | None) -> list[dict[str, Any]]:
"""Return version entries annotated with provider-probe readiness."""
from deeptutor.services.rag.index_versioning import list_kb_versions
versions: list[dict[str, Any]] = []
for entry in list_kb_versions(Path(kb_dir)):
adjusted = dict(entry)
probe = inspect_provider_version(adjusted, provider)
adjusted["ready"] = probe.ready
if probe.failure_summary:
adjusted["failure_summary"] = probe.failure_summary
if probe.doc_count is not None:
adjusted["doc_count"] = probe.doc_count
if probe.diagnostics:
adjusted["probe_diagnostics"] = probe.diagnostics
versions.append(adjusted)
return versions
def latest_ready_provider_version(
kb_dir: str | Path, provider: str | None
) -> dict[str, Any] | None:
"""Return the newest provider-ready version, if any."""
for entry in inspect_kb_versions(kb_dir, provider):
if entry.get("ready"):
return entry
return None
def has_ready_provider_index(kb_dir: str | Path, provider: str | None) -> bool:
"""Return whether ``kb_dir`` has a genuinely ready index for ``provider``."""
return latest_ready_provider_version(kb_dir, provider) is not None
def provider_failure_summary(
kb_dir: str | Path,
provider: str | None,
*,
limit: int = 3,
) -> str:
"""Return the first provider-specific failure summary under ``kb_dir``."""
failures: list[str] = []
for entry in inspect_kb_versions(kb_dir, provider):
summary = str(entry.get("failure_summary") or "").strip()
if summary:
failures.append(summary)
if len(failures) >= limit:
break
return "; ".join(failures[:limit])
def _inspect_llamaindex(storage_dir: Path) -> ProviderIndexProbe:
diagnostics: dict[str, Any] = {}
if not storage_dir.is_dir():
return ProviderIndexProbe(
DEFAULT_PROVIDER,
str(storage_dir),
False,
"LlamaIndex storage directory does not exist.",
)
docstore = storage_dir / "docstore.json"
index_store = storage_dir / "index_store.json"
vector_stores = sorted(path.name for path in storage_dir.glob("*vector_store.json"))
diagnostics["vector_stores"] = vector_stores
if not docstore.exists():
return ProviderIndexProbe(
DEFAULT_PROVIDER,
str(storage_dir),
False,
"Missing LlamaIndex docstore.json.",
diagnostics=diagnostics,
)
if not index_store.exists():
return ProviderIndexProbe(
DEFAULT_PROVIDER,
str(storage_dir),
False,
"Missing LlamaIndex index_store.json.",
doc_count=_llamaindex_doc_count(docstore),
diagnostics=diagnostics,
)
return ProviderIndexProbe(
DEFAULT_PROVIDER,
str(storage_dir),
True,
doc_count=_llamaindex_doc_count(docstore),
diagnostics=diagnostics,
)
def _inspect_pageindex(storage_dir: Path) -> ProviderIndexProbe:
from deeptutor.services.rag.pipelines.pageindex import storage
manifest = storage.read_manifest(storage_dir)
ids = storage.doc_ids(manifest)
if not ids:
return ProviderIndexProbe(
PAGEINDEX_PROVIDER,
str(storage_dir),
False,
"PageIndex manifest has no document ids.",
doc_count=0,
)
return ProviderIndexProbe(
PAGEINDEX_PROVIDER,
str(storage_dir),
True,
doc_count=len(ids),
diagnostics={"manifest": str(storage.manifest_path(storage_dir))},
)
def _inspect_graphrag(storage_dir: Path) -> ProviderIndexProbe:
from deeptutor.services.rag.pipelines.graphrag import storage
out = storage.output_dir(storage_dir)
tables = [name for name in storage.OUTPUT_TABLES if (out / f"{name}.parquet").exists()]
if not storage.has_output(storage_dir):
return ProviderIndexProbe(
GRAPHRAG_PROVIDER,
str(storage_dir),
False,
"GraphRAG output has no core parquet tables.",
diagnostics={"output_tables": tables},
)
return ProviderIndexProbe(
GRAPHRAG_PROVIDER,
str(storage_dir),
True,
diagnostics={"output_tables": tables},
)
def _inspect_lightrag(storage_dir: Path) -> ProviderIndexProbe:
from deeptutor.services.rag.pipelines.lightrag import storage
failure = storage.failure_summary(storage_dir)
ready = storage.has_output(storage_dir)
return ProviderIndexProbe(
LIGHTRAG_PROVIDER,
str(storage_dir),
ready,
failure_summary="" if ready else failure,
doc_count=_lightrag_doc_count(storage_dir),
)
def _llamaindex_doc_count(docstore_path: Path) -> int | None:
payload = _read_json(docstore_path)
if not isinstance(payload, dict):
return None
data = payload.get("docstore/data")
return len(data) if isinstance(data, dict) else None
def _lightrag_doc_count(storage_dir: Path) -> int | None:
payload = _read_json(storage_dir / "kv_store_doc_status.json")
if not isinstance(payload, dict):
return None
count = 0
for item in payload.values():
if not isinstance(item, dict):
continue
chunks = item.get("chunks_list")
status = str(item.get("status") or "").lower()
if (isinstance(chunks, list) and chunks) or status in {
"processed",
"completed",
"done",
"success",
"indexed",
}:
count += 1
return count
def _read_json(path: Path) -> dict[str, Any] | None:
if not path.exists():
return None
try:
with open(path, encoding="utf-8") as handle:
payload = json.load(handle)
return payload if isinstance(payload, dict) else None
except Exception:
return None
__all__ = [
"ProviderIndexProbe",
"inspect_provider_index",
"inspect_provider_version",
"inspect_kb_versions",
"latest_ready_provider_version",
"has_ready_provider_index",
"provider_failure_summary",
]