mirror of
https://github.com/safishamsi/graphify.git
synced 2026-10-15 16:39:43 +00:00
fix(cache): surface corrupt semantic cache entries instead of silently missing (#2683)
This commit is contained in:
1 parent
a30b56e2df
commit
5d8d6113db
2 files changed
+59
-2
No files matched your search
+28
-2
@@ -80,6 +80,12 @@ _PROMPT_FP_LEN = 12
|
||||
# check_semantic_cache can report N to the user (#1939).
|
||||
_legacy_semantic_hits = 0
|
||||
|
||||
# Count of cache entries that failed to parse as JSON this process. A corrupt
|
||||
# entry is not a miss: left in place it fails on every future run, silently
|
||||
# re-extracting (and, for semantic kinds, re-billing) the file forever. The
|
||||
# counter lets check_semantic_cache surface one aggregate warning (#2405).
|
||||
_corrupt_cache_entries = 0
|
||||
|
||||
# Prompt-file fingerprints already computed, keyed by (path, size, mtime_ns) —
|
||||
# the same stat signature the hash index uses. check_semantic_cache resolves the
|
||||
# prompt once per FILE in the corpus, so without this a 500-doc run re-reads and
|
||||
@@ -896,7 +902,7 @@ def load_cached(path: Path, root: Path = Path("."), kind: str = "ast",
|
||||
``merge_existing``) pass allow_legacy=False.
|
||||
Returns None if no cache entry or file has changed.
|
||||
"""
|
||||
global _legacy_semantic_hits
|
||||
global _legacy_semantic_hits, _corrupt_cache_entries
|
||||
location = cache_root if cache_root is not None else root
|
||||
try:
|
||||
h = file_hash(path, root, cache_root=cache_root)
|
||||
@@ -912,7 +918,14 @@ def load_cached(path: Path, root: Path = Path("."), kind: str = "ast",
|
||||
if entry.exists():
|
||||
try:
|
||||
result = json.loads(entry.read_text(encoding="utf-8"))
|
||||
except (json.JSONDecodeError, OSError):
|
||||
except json.JSONDecodeError:
|
||||
# Corrupt entry, not a miss: a truncated write or a bad producer
|
||||
# (e.g. unescaped Windows backslashes in source_file) leaves JSON
|
||||
# that fails to parse on every future run, so the file is silently
|
||||
# re-extracted forever. Count it so the run can report it (#2405).
|
||||
_corrupt_cache_entries += 1
|
||||
return None
|
||||
except OSError:
|
||||
return None
|
||||
# A ``partial`` entry was produced from a truncated LLM response and
|
||||
# covers only part of the file's symbols. Serving it as authoritative
|
||||
@@ -1154,6 +1167,7 @@ def check_semantic_cache(
|
||||
cached_hyperedges: list[dict] = []
|
||||
uncached: list[str] = []
|
||||
legacy_before = _legacy_semantic_hits
|
||||
corrupt_before = _corrupt_cache_entries
|
||||
|
||||
for fpath in files:
|
||||
p = Path(fpath)
|
||||
@@ -1180,6 +1194,18 @@ def check_semantic_cache(
|
||||
stacklevel=2,
|
||||
)
|
||||
|
||||
corrupt = _corrupt_cache_entries - corrupt_before
|
||||
if corrupt:
|
||||
warnings.warn(
|
||||
f"{corrupt} semantic cache entr{'y' if corrupt == 1 else 'ies'} could "
|
||||
"not be parsed as JSON and were treated as misses, so those files were "
|
||||
"re-extracted. A corrupt entry stays on disk and fails again every run; "
|
||||
"run with --force (or GRAPHIFY_FORCE=1) to rewrite them, or clear the "
|
||||
"cache to stop paying for the re-extraction (#2405).",
|
||||
RuntimeWarning,
|
||||
stacklevel=2,
|
||||
)
|
||||
|
||||
return cached_nodes, cached_edges, cached_hyperedges, uncached
|
||||
|
||||
|
||||
|
||||
Reference in new issue
Block a user