refactor: fix ruff bugbear and silent-except findings
- B904: chain OSError/collection errors with "raise ... from e" in
normalize.py and searcher.py so the original traceback is preserved.
- B007: rename unused loop variables to _name in dedup, dialect, layers,
and room_detector_local.
- S110/S112: replace bare "try/except/pass" and "try/except/continue"
with logger.debug(..., exc_info=True) in mcp_server, searcher,
palace, palace_graph, miner, convo_miner, and fact_checker so
background failures are observable without changing behaviour.
A module-level logger ("mempalace_mcp", matching mcp_server/searcher)
is added to the five files that didn't already have one. Configured
ruff checks (E/F/W/C901) and ruff --select B, S110, S112 all pass.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
committed by
Igor Lins e Silva
parent
b68485dfd4
commit
ca5899e361
@@ -11,6 +11,7 @@ Same palace as project mining. Different ingest strategy.
|
|||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
import hashlib
|
import hashlib
|
||||||
|
import logging
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from collections import defaultdict
|
from collections import defaultdict
|
||||||
@@ -24,6 +25,8 @@ from .palace import (
|
|||||||
mine_lock,
|
mine_lock,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
logger = logging.getLogger("mempalace_mcp")
|
||||||
|
|
||||||
|
|
||||||
# Cached hall keywords — avoids re-reading config per drawer
|
# Cached hall keywords — avoids re-reading config per drawer
|
||||||
_HALL_KEYWORDS_CACHE = None
|
_HALL_KEYWORDS_CACHE = None
|
||||||
@@ -331,7 +334,7 @@ def _file_chunks_locked(collection, source_file, chunks, wing, room, agent, extr
|
|||||||
try:
|
try:
|
||||||
collection.delete(where={"source_file": source_file})
|
collection.delete(where={"source_file": source_file})
|
||||||
except Exception:
|
except Exception:
|
||||||
pass
|
logger.debug("Stale-drawer purge failed for %s", source_file, exc_info=True)
|
||||||
|
|
||||||
# Batch chunks into bounded upserts so large transcripts keep most of
|
# Batch chunks into bounded upserts so large transcripts keep most of
|
||||||
# the embedding speedup without one huge Chroma/SQLite request. Keep
|
# the embedding speedup without one huge Chroma/SQLite request. Keep
|
||||||
|
|||||||
+1
-1
@@ -89,7 +89,7 @@ def dedup_source_group(col, drawer_ids, threshold=DEFAULT_THRESHOLD, dry_run=Tru
|
|||||||
kept = []
|
kept = []
|
||||||
to_delete = []
|
to_delete = []
|
||||||
|
|
||||||
for did, doc, meta in items:
|
for did, doc, _meta in items:
|
||||||
if not doc or len(doc) < 20:
|
if not doc or len(doc) < 20:
|
||||||
to_delete.append(did)
|
to_delete.append(did)
|
||||||
continue
|
continue
|
||||||
|
|||||||
@@ -873,7 +873,7 @@ class Dialect:
|
|||||||
|
|
||||||
for date_key in sorted(by_date.keys()):
|
for date_key in sorted(by_date.keys()):
|
||||||
lines.append(f"=MOMENTS[{date_key}]=")
|
lines.append(f"=MOMENTS[{date_key}]=")
|
||||||
for z, fnum in by_date[date_key]:
|
for z, _fnum in by_date[date_key]:
|
||||||
entities = []
|
entities = []
|
||||||
for p in z.get("people", []):
|
for p in z.get("people", []):
|
||||||
code = self.encode_entity(p)
|
code = self.encode_entity(p)
|
||||||
|
|||||||
@@ -27,6 +27,7 @@ Usage:
|
|||||||
|
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import logging
|
||||||
import os
|
import os
|
||||||
import re
|
import re
|
||||||
from datetime import datetime, timezone
|
from datetime import datetime, timezone
|
||||||
@@ -35,6 +36,8 @@ from datetime import datetime, timezone
|
|||||||
# ~/.mempalace/known_entities.json on every check_text call.
|
# ~/.mempalace/known_entities.json on every check_text call.
|
||||||
from .miner import _load_known_entities_raw
|
from .miner import _load_known_entities_raw
|
||||||
|
|
||||||
|
logger = logging.getLogger("mempalace_mcp")
|
||||||
|
|
||||||
|
|
||||||
# Narrow detection patterns — parse "X is Y's Z" and "X's Z is Y".
|
# Narrow detection patterns — parse "X is Y's Z" and "X's Z is Y".
|
||||||
# Names are captured greedily as word sequences (letters + optional
|
# Names are captured greedily as word sequences (letters + optional
|
||||||
@@ -214,6 +217,7 @@ def _check_kg_contradictions(text: str, palace_path: str) -> list:
|
|||||||
try:
|
try:
|
||||||
facts = kg.query_entity(subject, direction="outgoing")
|
facts = kg.query_entity(subject, direction="outgoing")
|
||||||
except Exception:
|
except Exception:
|
||||||
|
logger.debug("KG lookup failed for subject %r", subject, exc_info=True)
|
||||||
continue
|
continue
|
||||||
if not facts:
|
if not facts:
|
||||||
continue
|
continue
|
||||||
|
|||||||
+1
-1
@@ -157,7 +157,7 @@ class Layer1:
|
|||||||
lines.append(room_line)
|
lines.append(room_line)
|
||||||
total_len += len(room_line)
|
total_len += len(room_line)
|
||||||
|
|
||||||
for imp, meta, doc in entries:
|
for _imp, meta, doc in entries:
|
||||||
source = Path(meta.get("source_file", "")).name if meta.get("source_file") else ""
|
source = Path(meta.get("source_file", "")).name if meta.get("source_file") else ""
|
||||||
|
|
||||||
# Truncate doc to keep L1 compact
|
# Truncate doc to keep L1 compact
|
||||||
|
|||||||
@@ -900,7 +900,7 @@ def tool_add_drawer(
|
|||||||
if existing and existing["ids"]:
|
if existing and existing["ids"]:
|
||||||
return {"success": True, "reason": "already_exists", "drawer_id": drawer_id}
|
return {"success": True, "reason": "already_exists", "drawer_id": drawer_id}
|
||||||
except Exception:
|
except Exception:
|
||||||
pass
|
logger.debug("Idempotency pre-check failed for %s", drawer_id, exc_info=True)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
col.upsert(
|
col.upsert(
|
||||||
@@ -1418,7 +1418,7 @@ def tool_hook_settings(silent_save: bool = None, desktop_toast: bool = None):
|
|||||||
try:
|
try:
|
||||||
config = MempalaceConfig()
|
config = MempalaceConfig()
|
||||||
except Exception:
|
except Exception:
|
||||||
pass
|
logger.debug("Could not re-read config after update", exc_info=True)
|
||||||
|
|
||||||
result = {
|
result = {
|
||||||
"success": True,
|
"success": True,
|
||||||
|
|||||||
+4
-1
@@ -12,6 +12,7 @@ import sys
|
|||||||
import shlex
|
import shlex
|
||||||
import hashlib
|
import hashlib
|
||||||
import fnmatch
|
import fnmatch
|
||||||
|
import logging
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from collections import defaultdict
|
from collections import defaultdict
|
||||||
@@ -31,6 +32,8 @@ from .palace import (
|
|||||||
upsert_closet_lines,
|
upsert_closet_lines,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
logger = logging.getLogger("mempalace_mcp")
|
||||||
|
|
||||||
READABLE_EXTENSIONS = {
|
READABLE_EXTENSIONS = {
|
||||||
".txt",
|
".txt",
|
||||||
".md",
|
".md",
|
||||||
@@ -842,7 +845,7 @@ def process_file(
|
|||||||
try:
|
try:
|
||||||
collection.delete(where={"source_file": source_file})
|
collection.delete(where={"source_file": source_file})
|
||||||
except Exception:
|
except Exception:
|
||||||
pass
|
logger.debug("Stale-drawer purge failed for %s", source_file, exc_info=True)
|
||||||
|
|
||||||
# Batch chunks into bounded upserts so the embedding model sees many
|
# Batch chunks into bounded upserts so the embedding model sees many
|
||||||
# chunks per forward pass without building one huge Chroma/SQLite
|
# chunks per forward pass without building one huge Chroma/SQLite
|
||||||
|
|||||||
@@ -118,14 +118,14 @@ def normalize(filepath: str) -> str:
|
|||||||
try:
|
try:
|
||||||
file_size = os.path.getsize(filepath)
|
file_size = os.path.getsize(filepath)
|
||||||
except OSError as e:
|
except OSError as e:
|
||||||
raise IOError(f"Could not read {filepath}: {e}")
|
raise IOError(f"Could not read {filepath}: {e}") from e
|
||||||
if file_size > 500 * 1024 * 1024: # 500 MB safety limit
|
if file_size > 500 * 1024 * 1024: # 500 MB safety limit
|
||||||
raise IOError(f"File too large ({file_size // (1024 * 1024)} MB): {filepath}")
|
raise IOError(f"File too large ({file_size // (1024 * 1024)} MB): {filepath}")
|
||||||
try:
|
try:
|
||||||
with open(filepath, "r", encoding="utf-8", errors="replace") as f:
|
with open(filepath, "r", encoding="utf-8", errors="replace") as f:
|
||||||
content = f.read()
|
content = f.read()
|
||||||
except OSError as e:
|
except OSError as e:
|
||||||
raise IOError(f"Could not read {filepath}: {e}")
|
raise IOError(f"Could not read {filepath}: {e}") from e
|
||||||
|
|
||||||
if not content.strip():
|
if not content.strip():
|
||||||
return content
|
return content
|
||||||
|
|||||||
+5
-2
@@ -6,12 +6,15 @@ Consolidates collection access patterns used by both miners and the MCP server.
|
|||||||
|
|
||||||
import contextlib
|
import contextlib
|
||||||
import hashlib
|
import hashlib
|
||||||
|
import logging
|
||||||
import os
|
import os
|
||||||
import re
|
import re
|
||||||
import threading
|
import threading
|
||||||
|
|
||||||
from .backends.chroma import ChromaBackend
|
from .backends.chroma import ChromaBackend
|
||||||
|
|
||||||
|
logger = logging.getLogger("mempalace_mcp")
|
||||||
|
|
||||||
SKIP_DIRS = {
|
SKIP_DIRS = {
|
||||||
".git",
|
".git",
|
||||||
"node_modules",
|
"node_modules",
|
||||||
@@ -229,7 +232,7 @@ def purge_file_closets(closets_col, source_file: str) -> None:
|
|||||||
try:
|
try:
|
||||||
closets_col.delete(where={"source_file": source_file})
|
closets_col.delete(where={"source_file": source_file})
|
||||||
except Exception:
|
except Exception:
|
||||||
pass
|
logger.debug("Closet purge failed for %s", source_file, exc_info=True)
|
||||||
|
|
||||||
|
|
||||||
def upsert_closet_lines(closets_col, closet_id_base, lines, metadata):
|
def upsert_closet_lines(closets_col, closet_id_base, lines, metadata):
|
||||||
@@ -307,7 +310,7 @@ def mine_lock(source_file: str):
|
|||||||
|
|
||||||
fcntl.flock(lf, fcntl.LOCK_UN)
|
fcntl.flock(lf, fcntl.LOCK_UN)
|
||||||
except Exception:
|
except Exception:
|
||||||
pass
|
logger.debug("Mine-lock release failed", exc_info=True)
|
||||||
lf.close()
|
lf.close()
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -575,7 +575,7 @@ def follow_tunnels(wing: str, room: str, col=None, config=None):
|
|||||||
if did and did in drawer_map:
|
if did and did in drawer_map:
|
||||||
c["drawer_preview"] = drawer_map[did][:300]
|
c["drawer_preview"] = drawer_map[did][:300]
|
||||||
except Exception:
|
except Exception:
|
||||||
pass
|
logger.debug("Drawer preview hydration failed", exc_info=True)
|
||||||
|
|
||||||
return connections
|
return connections
|
||||||
|
|
||||||
|
|||||||
@@ -202,7 +202,7 @@ def detect_rooms_from_files(project_dir: str) -> list:
|
|||||||
|
|
||||||
SKIP_DIRS = {".git", "node_modules", "__pycache__", ".venv", "venv", "dist", "build"}
|
SKIP_DIRS = {".git", "node_modules", "__pycache__", ".venv", "venv", "dist", "build"}
|
||||||
|
|
||||||
for root, dirs, filenames in os.walk(project_path):
|
for _root, dirs, filenames in os.walk(project_path):
|
||||||
dirs[:] = [d for d in dirs if d not in SKIP_DIRS]
|
dirs[:] = [d for d in dirs if d not in SKIP_DIRS]
|
||||||
for filename in filenames:
|
for filename in filenames:
|
||||||
name_lower = filename.lower().replace("-", "_").replace(" ", "_")
|
name_lower = filename.lower().replace("-", "_").replace(" ", "_")
|
||||||
|
|||||||
@@ -245,7 +245,7 @@ def _expand_with_neighbors(drawers_col, matched_doc: str, matched_meta: dict, ra
|
|||||||
all_meta = drawers_col.get(where={"source_file": src}, include=["metadatas"])
|
all_meta = drawers_col.get(where={"source_file": src}, include=["metadatas"])
|
||||||
total_drawers = len(all_meta.ids) if all_meta.ids else None
|
total_drawers = len(all_meta.ids) if all_meta.ids else None
|
||||||
except Exception:
|
except Exception:
|
||||||
pass
|
logger.debug("total_drawers lookup failed for %s", src, exc_info=True)
|
||||||
|
|
||||||
return {
|
return {
|
||||||
"text": combined_text,
|
"text": combined_text,
|
||||||
@@ -297,10 +297,10 @@ def search(query: str, palace_path: str, wing: str = None, room: str = None, n_r
|
|||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
col = get_collection(palace_path, create=False)
|
col = get_collection(palace_path, create=False)
|
||||||
except Exception:
|
except Exception as e:
|
||||||
print(f"\n No palace found at {palace_path}")
|
print(f"\n No palace found at {palace_path}")
|
||||||
print(" Run: mempalace init <dir> then mempalace mine <dir>")
|
print(" Run: mempalace init <dir> then mempalace mine <dir>")
|
||||||
raise SearchError(f"No palace found at {palace_path}")
|
raise SearchError(f"No palace found at {palace_path}") from e
|
||||||
|
|
||||||
# Alert the user if this palace predates hnsw:space=cosine being set on
|
# Alert the user if this palace predates hnsw:space=cosine being set on
|
||||||
# creation — their similarity scores will be junk until they run repair.
|
# creation — their similarity scores will be junk until they run repair.
|
||||||
@@ -795,7 +795,8 @@ def search_memories(
|
|||||||
if source and source not in closet_boost_by_source:
|
if source and source not in closet_boost_by_source:
|
||||||
closet_boost_by_source[source] = (rank, cdist, cdoc[:200])
|
closet_boost_by_source[source] = (rank, cdist, cdoc[:200])
|
||||||
except Exception:
|
except Exception:
|
||||||
pass # no closets yet — hybrid degrades to pure drawer search
|
# No closets yet — hybrid degrades to pure drawer search.
|
||||||
|
logger.debug("Closet collection unavailable; using drawer-only search", exc_info=True)
|
||||||
|
|
||||||
# Rank-based boost. The ordinal signal ("which closet matched best") is
|
# Rank-based boost. The ordinal signal ("which closet matched best") is
|
||||||
# more reliable than absolute distance on narrative content, where
|
# more reliable than absolute distance on narrative content, where
|
||||||
@@ -877,6 +878,7 @@ def search_memories(
|
|||||||
include=["documents", "metadatas"],
|
include=["documents", "metadatas"],
|
||||||
)
|
)
|
||||||
except Exception:
|
except Exception:
|
||||||
|
logger.debug("Neighbor fetch failed for %s", full_source, exc_info=True)
|
||||||
continue
|
continue
|
||||||
docs = source_drawers.documents
|
docs = source_drawers.documents
|
||||||
metas_ = source_drawers.metadatas
|
metas_ = source_drawers.metadatas
|
||||||
|
|||||||
Reference in New Issue
Block a user