Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions clematis/memory/index.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,11 @@ def __init__(self) -> None:
self._eps: List[Dict[str, Any]] = []
self._ver: int = 0

def clear(self) -> None:
"""Remove all episodes and reset version counter."""
self._eps.clear()
self._ver = 0

def add(self, ep: Dict[str, Any]) -> None:
self._eps.append(ep)
self._ver += 1
Expand Down
38 changes: 38 additions & 0 deletions clematis/memory/lance_index.py
Original file line number Diff line number Diff line change
Expand Up @@ -205,6 +205,44 @@ def add(self, ep: Dict[str, Any]) -> None:
# Bump version counter
self._bump_version()

def clear(self) -> None:
"""Drop all stored episodes and reset the version counter."""
# Drop the episodes table if it exists (ignore failures).
try:
drop = getattr(self._db, "drop_table", None)
if callable(drop) and self._episodes is not None:
drop(self._table_name)
except Exception:
# As a fallback, delete every row individually.
try:
rows = [row.get("id") for row in self._read_all_rows()]
for eid in rows:
if eid is None:
continue
try:
self._episodes.delete(f"id == '{eid}'") # type: ignore[union-attr]
except Exception:
pass
except Exception:
pass
finally:
self._episodes = None

# Reset meta table back to the initial counter state.
try:
drop = getattr(self._db, "drop_table", None)
if callable(drop):
drop(self._meta_name)
except Exception:
try:
if self._meta is not None:
self._meta.delete("key == 'counter'") # type: ignore[union-attr]
except Exception:
pass

self._meta = self._open_or_create_meta()
self._version_cache = 0

def search_tiered(
self,
owner: Optional[str],
Expand Down
1 change: 1 addition & 0 deletions healthcheck.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
- Identity marker suite verified (`pytest -q -m identity`), and full test suite passes locally (`pytest -q`).
- Built wheels into `dist_local/` and captured deterministic hashes via `shasum -a 256 dist_local/* | sort` as part of the packaging parity check.
- Removed `ValueError` inheritance from typed errors, updated validators/scripts/tests to catch `ConfigError`/`SnapshotError`, and refreshed docs to match the cleaned taxonomy.
- Chat CLI `[wipe]` now purges the configured LanceDB store (when enabled) and reinitialises the memory index so subsequent `[seed]` runs repopulate the persistent backend with fresh embeddings.

## Recommended Next Actions
- Re-run the identity and packaging matrices once the version/doc alignment is settled, ensuring the refreshed defaults keep byte-identical outputs.
Expand Down
23 changes: 19 additions & 4 deletions scripts/chat.py
Original file line number Diff line number Diff line change
Expand Up @@ -592,9 +592,17 @@ def _seed_memories(
state["_chat_seeded_memories"] = True


def _wipe_memories(state: Dict[str, Any]) -> None:
state["mem_index"] = InMemoryIndex()
state["mem_backend"] = "inmemory"
def _wipe_memories(state: Dict[str, Any], cfg_t2: Dict[str, Any]) -> None:
idx = state.get("mem_index")
if idx is not None:
clear = getattr(idx, "clear", None)
if callable(clear):
try:
clear()
except Exception as exc: # pragma: no cover - defensive logging path
print(f"[chat] WARNING: failed to clear memory index: {exc}", file=sys.stderr)
state.pop("mem_index", None)
state.pop("mem_backend", None)
state.pop("mem_backend_fallback_reason", None)
state["_chat_seeded_memories"] = False
state["_chat_memory_ids"] = []
Expand All @@ -613,6 +621,13 @@ def _wipe_memories(state: Dict[str, Any]) -> None:
state["active_graphs"] = [DEFAULT_GRAPH_ID]
state["_chat_seeded_graph"] = False
_ensure_store(state)
# Recreate the configured index so subsequent seeds write to the persistent store.
try:
_ensure_index(state, cfg_t2)
except Exception as exc: # pragma: no cover - defensive logging
print(f"[chat] WARNING: failed to reinitialise memory index after wipe: {exc}", file=sys.stderr)
state["mem_index"] = InMemoryIndex()
state["mem_backend"] = "inmemory"


def _apply_llm_mode(cfg: Dict[str, Any], args: argparse.Namespace) -> None:
Expand Down Expand Up @@ -785,7 +800,7 @@ def main(argv: Optional[list[str]] = None) -> int:
print("[reset] state reloaded.")
continue
if lowered in {"[wipe]", "/wipe"}:
_wipe_memories(state)
_wipe_memories(state, cfg_t2)
print("[wipe] memories cleared.")
if not args.no_seed:
print(" (use [seed] to restore demo memories.)")
Expand Down