deeptutor-knowledge 1.7.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- deeptutor/api/routers/knowledge.py +5225 -0
- deeptutor/api/routers/marginnote4.py +278 -0
- deeptutor/capabilities/ima/__init__.py +6 -0
- deeptutor/capabilities/ima/binding.py +118 -0
- deeptutor/capabilities/ima/capability.py +92 -0
- deeptutor/capabilities/ima/prompts/en/system.md +33 -0
- deeptutor/capabilities/ima/prompts/zh/system.md +24 -0
- deeptutor/capabilities/ima/tools.py +476 -0
- deeptutor/capabilities/marginnote4/__init__.py +13 -0
- deeptutor/capabilities/marginnote4/binding.py +66 -0
- deeptutor/capabilities/marginnote4/capability.py +80 -0
- deeptutor/capabilities/marginnote4/models.py +173 -0
- deeptutor/capabilities/marginnote4/prompts/en/system.md +54 -0
- deeptutor/capabilities/marginnote4/prompts/zh/system.md +44 -0
- deeptutor/capabilities/marginnote4/store.py +562 -0
- deeptutor/capabilities/marginnote4/tools.py +329 -0
- deeptutor/capabilities/obsidian/__init__.py +6 -0
- deeptutor/capabilities/obsidian/binding.py +75 -0
- deeptutor/capabilities/obsidian/capability.py +82 -0
- deeptutor/capabilities/obsidian/prompts/en/system.md +32 -0
- deeptutor/capabilities/obsidian/prompts/zh/system.md +28 -0
- deeptutor/capabilities/obsidian/tools.py +399 -0
- deeptutor/capabilities/obsidian/vault.py +341 -0
- deeptutor/knowledge/__init__.py +28 -0
- deeptutor/knowledge/add_documents.py +734 -0
- deeptutor/knowledge/indexing_run.py +432 -0
- deeptutor/knowledge/initializer.py +448 -0
- deeptutor/knowledge/manager.py +2570 -0
- deeptutor/knowledge/module_service.py +29 -0
- deeptutor/knowledge/naming.py +40 -0
- deeptutor/knowledge/progress_events.py +45 -0
- deeptutor/knowledge/progress_tracker.py +355 -0
- deeptutor/knowledge/staging.py +69 -0
- deeptutor/services/config/knowledge_base_config.py +239 -0
- deeptutor/services/embedding/__init__.py +46 -0
- deeptutor/services/embedding/adapters/__init__.py +40 -0
- deeptutor/services/embedding/adapters/base.py +196 -0
- deeptutor/services/embedding/adapters/cohere.py +176 -0
- deeptutor/services/embedding/adapters/dashscope_native.py +242 -0
- deeptutor/services/embedding/adapters/gemini.py +462 -0
- deeptutor/services/embedding/adapters/jina.py +161 -0
- deeptutor/services/embedding/adapters/ollama.py +145 -0
- deeptutor/services/embedding/adapters/openai_compatible.py +406 -0
- deeptutor/services/embedding/adapters/openai_sdk.py +150 -0
- deeptutor/services/embedding/client.py +323 -0
- deeptutor/services/embedding/config.py +114 -0
- deeptutor/services/embedding/request_options.py +46 -0
- deeptutor/services/embedding/validation.py +128 -0
- deeptutor/services/github_source/__init__.py +5 -0
- deeptutor/services/github_source/client.py +164 -0
- deeptutor/services/github_source/sync.py +384 -0
- deeptutor/services/github_source/sync_service.py +72 -0
- deeptutor/services/rag/__init__.py +21 -0
- deeptutor/services/rag/embedding_binding.py +331 -0
- deeptutor/services/rag/embedding_signature.py +63 -0
- deeptutor/services/rag/eval/__init__.py +92 -0
- deeptutor/services/rag/eval/dataset.py +244 -0
- deeptutor/services/rag/eval/diff.py +312 -0
- deeptutor/services/rag/eval/matching.py +218 -0
- deeptutor/services/rag/eval/metrics.py +213 -0
- deeptutor/services/rag/eval/report.py +111 -0
- deeptutor/services/rag/eval/runner.py +309 -0
- deeptutor/services/rag/factory.py +369 -0
- deeptutor/services/rag/file_routing.py +5 -0
- deeptutor/services/rag/index_probe.py +327 -0
- deeptutor/services/rag/index_versioning.py +387 -0
- deeptutor/services/rag/kb_paths.py +50 -0
- deeptutor/services/rag/linked_kb.py +237 -0
- deeptutor/services/rag/pipelines/__init__.py +7 -0
- deeptutor/services/rag/pipelines/base.py +41 -0
- deeptutor/services/rag/pipelines/graphrag/__init__.py +18 -0
- deeptutor/services/rag/pipelines/graphrag/compatibility.py +42 -0
- deeptutor/services/rag/pipelines/graphrag/completion_adapter.py +251 -0
- deeptutor/services/rag/pipelines/graphrag/config.py +357 -0
- deeptutor/services/rag/pipelines/graphrag/engine.py +479 -0
- deeptutor/services/rag/pipelines/graphrag/errors.py +328 -0
- deeptutor/services/rag/pipelines/graphrag/ingestion.py +135 -0
- deeptutor/services/rag/pipelines/graphrag/pandas_compat.py +68 -0
- deeptutor/services/rag/pipelines/graphrag/pipeline.py +284 -0
- deeptutor/services/rag/pipelines/graphrag/provider.py +134 -0
- deeptutor/services/rag/pipelines/graphrag/storage.py +127 -0
- deeptutor/services/rag/pipelines/ima/__init__.py +28 -0
- deeptutor/services/rag/pipelines/ima/client.py +364 -0
- deeptutor/services/rag/pipelines/ima/config.py +140 -0
- deeptutor/services/rag/pipelines/ima/envelope.py +109 -0
- deeptutor/services/rag/pipelines/ima/inventory.py +209 -0
- deeptutor/services/rag/pipelines/ima/media.py +190 -0
- deeptutor/services/rag/pipelines/ima/models.py +348 -0
- deeptutor/services/rag/pipelines/ima/notes.py +240 -0
- deeptutor/services/rag/pipelines/ima/pipeline.py +238 -0
- deeptutor/services/rag/pipelines/ima/probe.py +105 -0
- deeptutor/services/rag/pipelines/ima/sources.py +105 -0
- deeptutor/services/rag/pipelines/ima/transport.py +130 -0
- deeptutor/services/rag/pipelines/kiwix/__init__.py +1 -0
- deeptutor/services/rag/pipelines/kiwix/client.py +369 -0
- deeptutor/services/rag/pipelines/kiwix/pipeline.py +75 -0
- deeptutor/services/rag/pipelines/lightrag/README.md +78 -0
- deeptutor/services/rag/pipelines/lightrag/__init__.py +15 -0
- deeptutor/services/rag/pipelines/lightrag/block_policy.py +266 -0
- deeptutor/services/rag/pipelines/lightrag/cache_reuse.py +137 -0
- deeptutor/services/rag/pipelines/lightrag/config.py +453 -0
- deeptutor/services/rag/pipelines/lightrag/engine.py +536 -0
- deeptutor/services/rag/pipelines/lightrag/indexing_policy.py +696 -0
- deeptutor/services/rag/pipelines/lightrag/ingress.py +364 -0
- deeptutor/services/rag/pipelines/lightrag/parser.py +99 -0
- deeptutor/services/rag/pipelines/lightrag/pipeline.py +692 -0
- deeptutor/services/rag/pipelines/lightrag/roles.py +132 -0
- deeptutor/services/rag/pipelines/lightrag/sidecar.py +320 -0
- deeptutor/services/rag/pipelines/lightrag/storage.py +338 -0
- deeptutor/services/rag/pipelines/lightrag/worker.py +259 -0
- deeptutor/services/rag/pipelines/lightrag/write_lock.py +55 -0
- deeptutor/services/rag/pipelines/lightrag_server/__init__.py +15 -0
- deeptutor/services/rag/pipelines/lightrag_server/client.py +149 -0
- deeptutor/services/rag/pipelines/lightrag_server/config.py +68 -0
- deeptutor/services/rag/pipelines/lightrag_server/pipeline.py +120 -0
- deeptutor/services/rag/pipelines/lightrag_server/probe.py +110 -0
- deeptutor/services/rag/pipelines/llamaindex/__init__.py +3 -0
- deeptutor/services/rag/pipelines/llamaindex/config.py +199 -0
- deeptutor/services/rag/pipelines/llamaindex/document_loader.py +648 -0
- deeptutor/services/rag/pipelines/llamaindex/embedding_adapter.py +264 -0
- deeptutor/services/rag/pipelines/llamaindex/errors.py +76 -0
- deeptutor/services/rag/pipelines/llamaindex/exercise_lookup.py +373 -0
- deeptutor/services/rag/pipelines/llamaindex/ingestion.py +129 -0
- deeptutor/services/rag/pipelines/llamaindex/pipeline.py +556 -0
- deeptutor/services/rag/pipelines/llamaindex/rerank.py +130 -0
- deeptutor/services/rag/pipelines/llamaindex/retrievers.py +360 -0
- deeptutor/services/rag/pipelines/llamaindex/storage.py +655 -0
- deeptutor/services/rag/pipelines/llamaindex/vector_store.py +288 -0
- deeptutor/services/rag/pipelines/modes.py +48 -0
- deeptutor/services/rag/pipelines/pageindex/__init__.py +47 -0
- deeptutor/services/rag/pipelines/pageindex/client.py +146 -0
- deeptutor/services/rag/pipelines/pageindex/config.py +49 -0
- deeptutor/services/rag/pipelines/pageindex/pipeline.py +313 -0
- deeptutor/services/rag/pipelines/pageindex/reasoning.py +262 -0
- deeptutor/services/rag/pipelines/pageindex/storage.py +133 -0
- deeptutor/services/rag/pipelines/pageindex/tools.py +221 -0
- deeptutor/services/rag/pipelines/weknora/__init__.py +13 -0
- deeptutor/services/rag/pipelines/weknora/client.py +106 -0
- deeptutor/services/rag/pipelines/weknora/config.py +56 -0
- deeptutor/services/rag/pipelines/weknora/pipeline.py +74 -0
- deeptutor/services/rag/pipelines/weknora/probe.py +80 -0
- deeptutor/services/rag/preflight.py +274 -0
- deeptutor/services/rag/provider_binding.py +70 -0
- deeptutor/services/rag/query_rewrite.py +186 -0
- deeptutor/services/rag/service.py +379 -0
- deeptutor/services/rag/smart_retriever.py +81 -0
- deeptutor/services/rag/source_pages.py +292 -0
- deeptutor/services/rag/source_visuals.py +434 -0
- deeptutor/services/rag/visual_assets.py +430 -0
- deeptutor/services/rag/visual_coverage.py +165 -0
- deeptutor/services/web_source/__init__.py +1 -0
- deeptutor/services/web_source/bilingual.py +255 -0
- deeptutor/services/web_source/crawler.py +681 -0
- deeptutor/services/web_source/html_extractor.py +643 -0
- deeptutor/services/web_source/markdown.py +28 -0
- deeptutor/services/web_source/navigation.py +72 -0
- deeptutor/services/web_source/repository.py +640 -0
- deeptutor/services/web_source/robots.py +182 -0
- deeptutor/services/web_source/scheduler.py +336 -0
- deeptutor/services/web_source/snapshot_assets.py +171 -0
- deeptutor/services/web_source/sync.py +253 -0
- deeptutor/services/workspace/kb_move.py +446 -0
- deeptutor/tools/rag_tool.py +110 -0
- deeptutor_cli/kb.py +756 -0
- deeptutor_knowledge-1.7.0.dist-info/METADATA +15 -0
- deeptutor_knowledge-1.7.0.dist-info/RECORD +168 -0
- deeptutor_knowledge-1.7.0.dist-info/WHEEL +5 -0
- deeptutor_knowledge-1.7.0.dist-info/top_level.txt +2 -0
|
@@ -0,0 +1,327 @@
|
|
|
1
|
+
"""Provider-owned index readiness probes.
|
|
2
|
+
|
|
3
|
+
DeepTutor owns KB lifecycle/status, but each RAG provider owns the shape of its
|
|
4
|
+
persisted index. This module is the narrow read-only seam between those worlds:
|
|
5
|
+
callers ask "is this provider index really queryable?" and get a structured
|
|
6
|
+
answer without knowing provider-specific filenames.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from __future__ import annotations
|
|
10
|
+
|
|
11
|
+
from dataclasses import dataclass, field
|
|
12
|
+
from functools import lru_cache
|
|
13
|
+
import json
|
|
14
|
+
from pathlib import Path
|
|
15
|
+
from typing import Any
|
|
16
|
+
|
|
17
|
+
from deeptutor.services.rag.factory import (
|
|
18
|
+
DEFAULT_PROVIDER,
|
|
19
|
+
GRAPHRAG_PROVIDER,
|
|
20
|
+
LIGHTRAG_PROVIDER,
|
|
21
|
+
PAGEINDEX_OSS_PROVIDER,
|
|
22
|
+
PAGEINDEX_PROVIDER,
|
|
23
|
+
normalize_provider_name,
|
|
24
|
+
version_matches_provider,
|
|
25
|
+
)
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
@dataclass(frozen=True)
|
|
29
|
+
class ProviderIndexProbe:
|
|
30
|
+
"""Read-only verdict for one provider storage directory."""
|
|
31
|
+
|
|
32
|
+
provider: str
|
|
33
|
+
storage_dir: str | None
|
|
34
|
+
ready: bool
|
|
35
|
+
failure_summary: str = ""
|
|
36
|
+
doc_count: int | None = None
|
|
37
|
+
diagnostics: dict[str, Any] = field(default_factory=dict)
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def inspect_provider_index(
|
|
41
|
+
provider: str | None, storage_dir: str | Path | None
|
|
42
|
+
) -> ProviderIndexProbe:
|
|
43
|
+
"""Inspect one provider storage directory using real provider artifacts."""
|
|
44
|
+
resolved = normalize_provider_name(provider)
|
|
45
|
+
path = Path(storage_dir) if storage_dir is not None else None
|
|
46
|
+
if path is None:
|
|
47
|
+
return ProviderIndexProbe(resolved, None, False, "No storage path recorded.")
|
|
48
|
+
if resolved in {PAGEINDEX_PROVIDER, PAGEINDEX_OSS_PROVIDER}:
|
|
49
|
+
return _inspect_pageindex(path, resolved)
|
|
50
|
+
if resolved == GRAPHRAG_PROVIDER:
|
|
51
|
+
return _inspect_graphrag(path)
|
|
52
|
+
if resolved == LIGHTRAG_PROVIDER:
|
|
53
|
+
return _inspect_lightrag(path)
|
|
54
|
+
return _inspect_llamaindex(path)
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def inspect_provider_version(entry: dict[str, Any], provider: str | None) -> ProviderIndexProbe:
|
|
58
|
+
"""Inspect a version-list entry for ``provider``."""
|
|
59
|
+
resolved = normalize_provider_name(provider)
|
|
60
|
+
storage_path = entry.get("storage_path")
|
|
61
|
+
if not storage_path:
|
|
62
|
+
return ProviderIndexProbe(resolved, None, False, "No storage path recorded.")
|
|
63
|
+
if not version_matches_provider(entry, resolved):
|
|
64
|
+
return ProviderIndexProbe(
|
|
65
|
+
resolved,
|
|
66
|
+
str(storage_path),
|
|
67
|
+
False,
|
|
68
|
+
diagnostics={
|
|
69
|
+
"version_provider": entry.get("provider"),
|
|
70
|
+
"version_signature": entry.get("signature"),
|
|
71
|
+
"provider_mismatch": True,
|
|
72
|
+
},
|
|
73
|
+
)
|
|
74
|
+
return inspect_provider_index(resolved, Path(str(storage_path)))
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
def inspect_kb_versions(kb_dir: str | Path, provider: str | None) -> list[dict[str, Any]]:
|
|
78
|
+
"""Return version entries annotated with provider-probe readiness."""
|
|
79
|
+
from deeptutor.services.rag.index_versioning import list_kb_versions
|
|
80
|
+
|
|
81
|
+
versions: list[dict[str, Any]] = []
|
|
82
|
+
for entry in list_kb_versions(Path(kb_dir)):
|
|
83
|
+
adjusted = dict(entry)
|
|
84
|
+
probe = inspect_provider_version(adjusted, provider)
|
|
85
|
+
adjusted["ready"] = probe.ready
|
|
86
|
+
if probe.failure_summary:
|
|
87
|
+
adjusted["failure_summary"] = probe.failure_summary
|
|
88
|
+
if probe.doc_count is not None:
|
|
89
|
+
adjusted["doc_count"] = probe.doc_count
|
|
90
|
+
if probe.diagnostics:
|
|
91
|
+
adjusted["probe_diagnostics"] = probe.diagnostics
|
|
92
|
+
versions.append(adjusted)
|
|
93
|
+
return versions
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
def latest_ready_provider_version(
|
|
97
|
+
kb_dir: str | Path, provider: str | None
|
|
98
|
+
) -> dict[str, Any] | None:
|
|
99
|
+
"""Return the newest provider-ready version, if any."""
|
|
100
|
+
for entry in inspect_kb_versions(kb_dir, provider):
|
|
101
|
+
if entry.get("ready"):
|
|
102
|
+
return entry
|
|
103
|
+
return None
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
def has_ready_provider_index(kb_dir: str | Path, provider: str | None) -> bool:
|
|
107
|
+
"""Return whether ``kb_dir`` has a genuinely ready index for ``provider``."""
|
|
108
|
+
return latest_ready_provider_version(kb_dir, provider) is not None
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
def provider_failure_summary(
|
|
112
|
+
kb_dir: str | Path,
|
|
113
|
+
provider: str | None,
|
|
114
|
+
*,
|
|
115
|
+
limit: int = 3,
|
|
116
|
+
versions: list[dict[str, Any]] | None = None,
|
|
117
|
+
) -> str:
|
|
118
|
+
"""Return the first provider-specific failure summary under ``kb_dir``.
|
|
119
|
+
|
|
120
|
+
``versions`` may carry a pre-computed :func:`inspect_kb_versions` result so
|
|
121
|
+
bulk callers (e.g. ``KnowledgeBaseManager.get_info``) do not rescan and
|
|
122
|
+
re-parse every index version just to collect failure text.
|
|
123
|
+
|
|
124
|
+
"""
|
|
125
|
+
entries = versions if versions is not None else inspect_kb_versions(kb_dir, provider)
|
|
126
|
+
failures: list[str] = []
|
|
127
|
+
for entry in entries:
|
|
128
|
+
summary = str(entry.get("failure_summary") or "").strip()
|
|
129
|
+
if summary:
|
|
130
|
+
failures.append(summary)
|
|
131
|
+
if len(failures) >= limit:
|
|
132
|
+
break
|
|
133
|
+
return "; ".join(failures[:limit])
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
def _inspect_llamaindex(storage_dir: Path) -> ProviderIndexProbe:
|
|
137
|
+
diagnostics: dict[str, Any] = {}
|
|
138
|
+
if not storage_dir.is_dir():
|
|
139
|
+
return ProviderIndexProbe(
|
|
140
|
+
DEFAULT_PROVIDER,
|
|
141
|
+
str(storage_dir),
|
|
142
|
+
False,
|
|
143
|
+
"LlamaIndex storage directory does not exist.",
|
|
144
|
+
)
|
|
145
|
+
|
|
146
|
+
if (storage_dir / ".building.json").exists():
|
|
147
|
+
meta = _read_json(storage_dir / "meta.json")
|
|
148
|
+
if not meta or meta.get("state") != "published" or not meta.get("readiness_verified"):
|
|
149
|
+
return ProviderIndexProbe(
|
|
150
|
+
DEFAULT_PROVIDER,
|
|
151
|
+
str(storage_dir),
|
|
152
|
+
False,
|
|
153
|
+
"Candidate index has not passed persistence verification.",
|
|
154
|
+
)
|
|
155
|
+
docstore = storage_dir / "docstore.json"
|
|
156
|
+
index_store = storage_dir / "index_store.json"
|
|
157
|
+
vector_stores = sorted(path.name for path in storage_dir.glob("*vector_store.json"))
|
|
158
|
+
diagnostics["vector_stores"] = vector_stores
|
|
159
|
+
|
|
160
|
+
if not docstore.exists():
|
|
161
|
+
return ProviderIndexProbe(
|
|
162
|
+
DEFAULT_PROVIDER,
|
|
163
|
+
str(storage_dir),
|
|
164
|
+
False,
|
|
165
|
+
"Missing LlamaIndex docstore.json.",
|
|
166
|
+
diagnostics=diagnostics,
|
|
167
|
+
)
|
|
168
|
+
if not index_store.exists():
|
|
169
|
+
return ProviderIndexProbe(
|
|
170
|
+
DEFAULT_PROVIDER,
|
|
171
|
+
str(storage_dir),
|
|
172
|
+
False,
|
|
173
|
+
"Missing LlamaIndex index_store.json.",
|
|
174
|
+
doc_count=_llamaindex_doc_count(docstore),
|
|
175
|
+
diagnostics=diagnostics,
|
|
176
|
+
)
|
|
177
|
+
|
|
178
|
+
return ProviderIndexProbe(
|
|
179
|
+
DEFAULT_PROVIDER,
|
|
180
|
+
str(storage_dir),
|
|
181
|
+
True,
|
|
182
|
+
doc_count=_llamaindex_doc_count(docstore),
|
|
183
|
+
diagnostics=diagnostics,
|
|
184
|
+
)
|
|
185
|
+
|
|
186
|
+
|
|
187
|
+
def _inspect_pageindex(storage_dir: Path, provider: str) -> ProviderIndexProbe:
|
|
188
|
+
from deeptutor.services.rag.pipelines.pageindex import storage
|
|
189
|
+
|
|
190
|
+
manifest = storage.read_manifest(storage_dir, provider=provider)
|
|
191
|
+
ids = storage.doc_ids(manifest)
|
|
192
|
+
if not ids:
|
|
193
|
+
return ProviderIndexProbe(
|
|
194
|
+
provider,
|
|
195
|
+
str(storage_dir),
|
|
196
|
+
False,
|
|
197
|
+
"PageIndex manifest has no document ids.",
|
|
198
|
+
doc_count=0,
|
|
199
|
+
)
|
|
200
|
+
if provider == PAGEINDEX_OSS_PROVIDER:
|
|
201
|
+
docs_dir = storage.sdk_storage_path(storage_dir) / "docs"
|
|
202
|
+
missing = [
|
|
203
|
+
doc_id
|
|
204
|
+
for doc_id in ids
|
|
205
|
+
if not all(
|
|
206
|
+
(docs_dir / doc_id / name).is_file()
|
|
207
|
+
for name in ("doc.json", "tree.json", "pages.json")
|
|
208
|
+
)
|
|
209
|
+
]
|
|
210
|
+
if missing:
|
|
211
|
+
return ProviderIndexProbe(
|
|
212
|
+
provider,
|
|
213
|
+
str(storage_dir),
|
|
214
|
+
False,
|
|
215
|
+
"PageIndex OSS Local Library is missing document artifacts.",
|
|
216
|
+
doc_count=len(ids) - len(missing),
|
|
217
|
+
diagnostics={"missing_doc_ids": missing[:10]},
|
|
218
|
+
)
|
|
219
|
+
return ProviderIndexProbe(
|
|
220
|
+
provider,
|
|
221
|
+
str(storage_dir),
|
|
222
|
+
True,
|
|
223
|
+
doc_count=len(ids),
|
|
224
|
+
diagnostics={"manifest": str(storage.manifest_path(storage_dir))},
|
|
225
|
+
)
|
|
226
|
+
|
|
227
|
+
|
|
228
|
+
def _inspect_graphrag(storage_dir: Path) -> ProviderIndexProbe:
|
|
229
|
+
from deeptutor.services.rag.pipelines.graphrag import storage
|
|
230
|
+
|
|
231
|
+
out = storage.output_dir(storage_dir)
|
|
232
|
+
tables = [name for name in storage.OUTPUT_TABLES if (out / f"{name}.parquet").exists()]
|
|
233
|
+
if not storage.has_output(storage_dir):
|
|
234
|
+
return ProviderIndexProbe(
|
|
235
|
+
GRAPHRAG_PROVIDER,
|
|
236
|
+
str(storage_dir),
|
|
237
|
+
False,
|
|
238
|
+
"GraphRAG output has no core parquet tables.",
|
|
239
|
+
diagnostics={"output_tables": tables},
|
|
240
|
+
)
|
|
241
|
+
return ProviderIndexProbe(
|
|
242
|
+
GRAPHRAG_PROVIDER,
|
|
243
|
+
str(storage_dir),
|
|
244
|
+
True,
|
|
245
|
+
diagnostics={"output_tables": tables},
|
|
246
|
+
)
|
|
247
|
+
|
|
248
|
+
|
|
249
|
+
def _inspect_lightrag(storage_dir: Path) -> ProviderIndexProbe:
|
|
250
|
+
from deeptutor.services.rag.pipelines.lightrag import storage
|
|
251
|
+
|
|
252
|
+
failure = storage.failure_summary(storage_dir)
|
|
253
|
+
ready = storage.has_output(storage_dir)
|
|
254
|
+
return ProviderIndexProbe(
|
|
255
|
+
LIGHTRAG_PROVIDER,
|
|
256
|
+
str(storage_dir),
|
|
257
|
+
ready,
|
|
258
|
+
failure_summary="" if ready else failure,
|
|
259
|
+
doc_count=_lightrag_doc_count(storage_dir),
|
|
260
|
+
)
|
|
261
|
+
|
|
262
|
+
|
|
263
|
+
# docstore.json holds one entry per chunk/node and can be multi-MB, so parsing
|
|
264
|
+
# it is the dominant cost of a LlamaIndex readiness probe. The count only
|
|
265
|
+
# changes when the file itself changes, so it is cached keyed on size +
|
|
266
|
+
# mtime_ns (same freshness convention as storage._freshness_token).
|
|
267
|
+
_DOCSTORE_COUNT_CACHE_SIZE = 128
|
|
268
|
+
|
|
269
|
+
|
|
270
|
+
@lru_cache(maxsize=_DOCSTORE_COUNT_CACHE_SIZE)
|
|
271
|
+
def _llamaindex_doc_count_cached(path: str, size: int, mtime_ns: int) -> int | None:
|
|
272
|
+
payload = _read_json(Path(path))
|
|
273
|
+
if not isinstance(payload, dict):
|
|
274
|
+
return None
|
|
275
|
+
data = payload.get("docstore/data")
|
|
276
|
+
return len(data) if isinstance(data, dict) else None
|
|
277
|
+
|
|
278
|
+
|
|
279
|
+
def _llamaindex_doc_count(docstore_path: Path) -> int | None:
|
|
280
|
+
try:
|
|
281
|
+
stat = docstore_path.stat()
|
|
282
|
+
except OSError:
|
|
283
|
+
return None
|
|
284
|
+
return _llamaindex_doc_count_cached(str(docstore_path), stat.st_size, stat.st_mtime_ns)
|
|
285
|
+
|
|
286
|
+
|
|
287
|
+
def _lightrag_doc_count(storage_dir: Path) -> int | None:
|
|
288
|
+
payload = _read_json(storage_dir / "kv_store_doc_status.json")
|
|
289
|
+
if not isinstance(payload, dict):
|
|
290
|
+
return None
|
|
291
|
+
count = 0
|
|
292
|
+
for item in payload.values():
|
|
293
|
+
if not isinstance(item, dict):
|
|
294
|
+
continue
|
|
295
|
+
chunks = item.get("chunks_list")
|
|
296
|
+
status = str(item.get("status") or "").lower()
|
|
297
|
+
if (isinstance(chunks, list) and chunks) or status in {
|
|
298
|
+
"processed",
|
|
299
|
+
"completed",
|
|
300
|
+
"done",
|
|
301
|
+
"success",
|
|
302
|
+
"indexed",
|
|
303
|
+
}:
|
|
304
|
+
count += 1
|
|
305
|
+
return count
|
|
306
|
+
|
|
307
|
+
|
|
308
|
+
def _read_json(path: Path) -> dict[str, Any] | None:
|
|
309
|
+
if not path.exists():
|
|
310
|
+
return None
|
|
311
|
+
try:
|
|
312
|
+
with open(path, encoding="utf-8") as handle:
|
|
313
|
+
payload = json.load(handle)
|
|
314
|
+
return payload if isinstance(payload, dict) else None
|
|
315
|
+
except Exception:
|
|
316
|
+
return None
|
|
317
|
+
|
|
318
|
+
|
|
319
|
+
__all__ = [
|
|
320
|
+
"ProviderIndexProbe",
|
|
321
|
+
"inspect_provider_index",
|
|
322
|
+
"inspect_provider_version",
|
|
323
|
+
"inspect_kb_versions",
|
|
324
|
+
"latest_ready_provider_version",
|
|
325
|
+
"has_ready_provider_index",
|
|
326
|
+
"provider_failure_summary",
|
|
327
|
+
]
|