Skip to content

Commit fe92c06

Browse files
groksrcclaude
andcommitted
fix(core): address review feedback on FastEmbed cache fix
- Use explicit ``is not None`` instead of ``or`` in _resolve_cache_dir so an empty-string override doesn't silently fall through to the default. Empty string is an invalid path; letting FastEmbed surface the error is clearer than swapping it out for ~/.basic-memory/fastembed_cache. - Drop the ``os.getenv("HOME", Path.home())`` dance in resolve_data_dir. ``Path.home()`` already reads $HOME on POSIX and %USERPROFILE% on Windows, so the env check was redundant and made tests that patched os.name to simulate Windows on non-Windows hosts fragile. - Switch default_fastembed_cache_dir to ``os.getenv`` for consistency with the rest of config.py. - Fix test_default_fastembed_cache_dir_never_falls_back_to_tmp so it no longer mis-fires on Linux CI. ``tempfile.gettempdir()`` is ``/tmp`` there and pytest's ``tmp_path`` lives under it, so the previous ``startswith(tempfile.gettempdir())`` guard flagged legitimate paths. Compare exact paths against FastEmbed's concrete ``<tempdir>/fastembed_cache`` fallback instead. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> Signed-off-by: Drew Cain <groksrc@gmail.com>
1 parent 1a8fbd5 commit fe92c06

File tree

3 files changed

+38
-14
lines changed

3 files changed

+38
-14
lines changed

src/basic_memory/config.py

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -54,16 +54,16 @@ def resolve_data_dir() -> Path:
5454
"""Resolve the Basic Memory data directory.
5555
5656
Single source of truth for the per-user state directory. Honors
57-
BASIC_MEMORY_CONFIG_DIR so each process/worktree can isolate config
58-
and database state; otherwise falls back to ``~/.basic-memory``.
57+
``BASIC_MEMORY_CONFIG_DIR`` so each process/worktree can isolate config
58+
and database state; otherwise falls back to ``<user home>/.basic-memory``.
5959
60-
Works cross-platform: ``Path.home()`` returns ``%USERPROFILE%`` on
61-
Windows and ``$HOME`` on macOS/Linux.
60+
Cross-platform: ``Path.home()`` reads ``$HOME`` on POSIX and
61+
``%USERPROFILE%`` on Windows, so there's no need to check ``$HOME``
62+
explicitly here.
6263
"""
6364
if config_dir := os.getenv("BASIC_MEMORY_CONFIG_DIR"):
6465
return Path(config_dir)
65-
home = os.getenv("HOME", Path.home())
66-
return Path(home) / DATA_DIR_NAME
66+
return Path.home() / DATA_DIR_NAME
6767

6868

6969
def default_fastembed_cache_dir() -> str:
@@ -83,7 +83,7 @@ def default_fastembed_cache_dir() -> str:
8383
ONNX load raises ``NO_SUCHFILE``. Persisting the cache under the
8484
per-user data directory works identically on macOS, Linux, and Windows.
8585
"""
86-
if env_override := os.environ.get("FASTEMBED_CACHE_PATH"):
86+
if env_override := os.getenv("FASTEMBED_CACHE_PATH"):
8787
return env_override
8888
return str(resolve_data_dir() / "fastembed_cache")
8989

src/basic_memory/repository/embedding_provider_factory.py

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,17 @@
2323

2424

2525
def _resolve_cache_dir(app_config: BasicMemoryConfig) -> str:
26-
"""Resolve the effective FastEmbed cache dir for this config."""
27-
return app_config.semantic_embedding_cache_dir or default_fastembed_cache_dir()
26+
"""Resolve the effective FastEmbed cache dir for this config.
27+
28+
Uses an explicit ``is not None`` check — an empty string override from
29+
config or ``BASIC_MEMORY_SEMANTIC_EMBEDDING_CACHE_DIR`` is an invalid
30+
path, not a request to fall back to the default, and FastEmbed's error
31+
message is clearer than silently swapping in a different directory.
32+
"""
33+
configured = app_config.semantic_embedding_cache_dir
34+
if configured is not None:
35+
return configured
36+
return default_fastembed_cache_dir()
2837

2938

3039
def _available_cpu_count() -> int | None:

tests/test_config.py

Lines changed: 20 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -335,15 +335,30 @@ def test_default_fastembed_cache_dir_env_override(self, tmp_path, monkeypatch):
335335

336336
assert default_fastembed_cache_dir() == str(custom)
337337

338-
def test_default_fastembed_cache_dir_never_falls_back_to_tmp(self, config_home, monkeypatch):
339-
"""Regression guard for #741: default must not point at /tmp/fastembed_cache."""
338+
def test_default_fastembed_cache_dir_never_falls_back_to_fastembed_tmp_default(
339+
self, config_home, monkeypatch
340+
):
341+
"""Regression guard for #741.
342+
343+
FastEmbed's own fallback when ``cache_dir`` is ``None`` is
344+
``<system tmp>/fastembed_cache`` — the exact path that disappears in
345+
sandboxed MCP runtimes (Codex CLI). Ensure Basic Memory's resolver
346+
never lands on that path.
347+
348+
Compared as exact paths rather than ``startswith(tempfile.gettempdir())``
349+
because the test runner itself can legitimately live under ``/tmp``
350+
(pytest's ``tmp_path`` does on Linux CI), and that's not the bug we
351+
care about here.
352+
"""
340353
monkeypatch.delenv("BASIC_MEMORY_CONFIG_DIR", raising=False)
341354
monkeypatch.delenv("FASTEMBED_CACHE_PATH", raising=False)
342355

343-
resolved = default_fastembed_cache_dir()
356+
resolved = Path(default_fastembed_cache_dir())
344357

345-
assert "/tmp/fastembed_cache" not in resolved
346-
assert not resolved.startswith(tempfile.gettempdir())
358+
# Must equal the data-dir-relative default.
359+
assert resolved == config_home / ".basic-memory" / "fastembed_cache"
360+
# And must not equal FastEmbed's own <tempdir>/fastembed_cache fallback.
361+
assert resolved != Path(tempfile.gettempdir()) / "fastembed_cache"
347362

348363

349364
class TestConfigManager:

0 commit comments

Comments
 (0)