manifest.py
Ricky/RAG_project/ingestion/manifest.py
"""
Index manifest: detect when raw source files have changed so the vector
and keyword indexes can be rebuilt together.
The manifest stores a lightweight hash (filenames + mtime + size) of all
raw-data directories. If the hash changes between runs, load_or_build_database
will trigger a full rebuild even without RAG_FORCE_REBUILD=1.
"""
import os
import json
import hashlib
import logging
from pathlib import Path
logger = logging.getLogger(__name__)
_MANIFEST_FILENAME = "index_manifest.json"
def _manifest_path(chroma_db_path: str) -> Path:
return Path(chroma_db_path).parent / _MANIFEST_FILENAME
def compute_raw_data_hash(raw_dirs: list[str]) -> str:
"""
Return an MD5 hex digest computed from the sorted list of
(filename, mtime, size) tuples across all files in raw_dirs.
Ignores hidden files (starting with '.').
"""
h = hashlib.md5()
for raw_dir in sorted(raw_dirs):
if not os.path.isdir(raw_dir):
continue
for filename in sorted(os.listdir(raw_dir)):
if filename.startswith("."):
continue
filepath = os.path.join(raw_dir, filename)
if not os.path.isfile(filepath):
continue
stat = os.stat(filepath)
h.update(filename.encode())
h.update(str(int(stat.st_mtime)).encode())
h.update(str(stat.st_size).encode())
# Also hash the product summaries source file so adding curated
# knowledge triggers a rebuild.
summary_file = Path(__file__).resolve().parent.parent / "knowledge" / "product_summary.py"
if summary_file.is_file():
stat = summary_file.stat()
h.update(b"product_summary.py")
h.update(str(int(stat.st_mtime)).encode())
h.update(str(stat.st_size).encode())
return h.hexdigest()
def load_manifest(chroma_db_path: str) -> dict:
path = _manifest_path(chroma_db_path)
if not path.exists():
return {}
try:
return json.loads(path.read_text(encoding="utf-8"))
except Exception as e:
logger.warning("Could not read index manifest: %s", e)
return {}
def save_manifest(chroma_db_path: str, raw_dirs: list[str]) -> None:
path = _manifest_path(chroma_db_path)
path.parent.mkdir(parents=True, exist_ok=True)
data = {"raw_data_hash": compute_raw_data_hash(raw_dirs)}
try:
path.write_text(json.dumps(data, indent=2), encoding="utf-8")
except Exception as e:
logger.warning("Could not save index manifest: %s", e)
def needs_rebuild(chroma_db_path: str, raw_dirs: list[str]) -> bool:
"""
Return True when:
- No manifest file exists yet, OR
- The current raw-data hash differs from the stored hash.
"""
manifest = load_manifest(chroma_db_path)
if not manifest:
return True
current_hash = compute_raw_data_hash(raw_dirs)
stored_hash = manifest.get("raw_data_hash", "")
changed = current_hash != stored_hash
if changed:
logger.info("Raw data changed (hash mismatch) – index rebuild required.")
return changed
関連記事
__init__.py
__init__.py — python source code from the Ricky learning materials (Ricky/Cat Project Final/back_web-4/app/__init__.py).
記事を読む →auth.py
auth.py — python source code from the Ricky learning materials (Ricky/Cat Project Final/back_web-4/app/admin/auth.py).
記事を読む →routes.py
routes.py — python source code from the Ricky learning materials (Ricky/Cat Project Final/back_web-4/app/admin/routes.py).
記事を読む →config.py
config.py — python source code from the Ricky learning materials (Ricky/Cat Project Final/back_web-4/app/config.py).
記事を読む →admin_ingest.py
admin_ingest.py — python source code from the Ricky learning materials (Ricky/Cat Project Final/back_web-4/app/routes/admin_ingest.py).
記事を読む →admin_stats.py
admin_stats.py — python source code from the Ricky learning materials (Ricky/Cat Project Final/back_web-4/app/routes/admin_stats.py).
記事を読む →