Skip to content

Commit 9455c50

Browse files
author
alvinttang
committed
fix(vector_stores): use platform user-data dir instead of /tmp/{provider}
The default vector store path was /tmp/{provider}, which breaks in a few real deployments: - macOS LaunchAgents (sandbox often blocks /tmp writes; /tmp is periodically cleaned) - systemd services with PrivateTmp= or noexec /tmp - Windows (no /tmp at all) - Docker (/tmp is ephemeral unless mounted) Resolution order is now: 1. MEM0_DATA_DIR env var (explicit override) 2. Platform convention via stdlib: - macOS: ~/Library/Application Support/mem0 - Windows: %LOCALAPPDATA%/mem0 - Linux/BSD: $XDG_DATA_HOME/mem0 or ~/.local/share/mem0 3. provider name appended as the subdir Explicit path in user config still wins over both the env var and the default, so behavior for anyone already setting path= is unchanged. Refs #4279
1 parent 74771b4 commit 9455c50

2 files changed

Lines changed: 82 additions & 1 deletion

File tree

mem0/vector_stores/configs.py

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,40 @@
1+
import os
2+
import sys
13
from typing import Dict, Optional
24

35
from pydantic import BaseModel, Field, model_validator
46

57

8+
def _default_data_dir() -> str:
9+
"""Return a writable user-data directory suitable for embedded vector stores.
10+
11+
Resolution order:
12+
1. ``MEM0_DATA_DIR`` environment variable (explicit override).
13+
2. Platform convention:
14+
- macOS: ``~/Library/Application Support/mem0``
15+
- Windows: ``%LOCALAPPDATA%/mem0``
16+
- Linux/BSD: ``$XDG_DATA_HOME/mem0`` or ``~/.local/share/mem0``
17+
18+
The previous default of ``/tmp/{provider}`` broke macOS LaunchAgents, systemd
19+
services with ``noexec`` ``/tmp``, Windows (no ``/tmp``), and Docker (ephemeral
20+
``/tmp``). See #4279.
21+
"""
22+
env_dir = os.environ.get("MEM0_DATA_DIR")
23+
if env_dir:
24+
return env_dir
25+
if sys.platform == "darwin":
26+
return os.path.expanduser("~/Library/Application Support/mem0")
27+
if sys.platform == "win32":
28+
return os.path.join(
29+
os.environ.get("LOCALAPPDATA") or os.path.expanduser("~/AppData/Local"),
30+
"mem0",
31+
)
32+
return os.path.join(
33+
os.environ.get("XDG_DATA_HOME") or os.path.expanduser("~/.local/share"),
34+
"mem0",
35+
)
36+
37+
638
class VectorStoreConfig(BaseModel):
739
provider: str = Field(
840
description="Provider of the vector store (e.g., 'qdrant', 'chroma', 'upstash_vector')",
@@ -61,7 +93,7 @@ def validate_and_create_config(self) -> "VectorStoreConfig":
6193

6294
# also check if path in allowed kays for pydantic model, and whether config extra fields are allowed
6395
if "path" not in config and "path" in config_class.__annotations__:
64-
config["path"] = f"/tmp/{provider}"
96+
config["path"] = os.path.join(_default_data_dir(), provider)
6597

6698
self.config = config_class(**config)
6799
return self
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
"""Regression test for #4279: vector store path defaults to /tmp/{provider}, which
2+
fails or silently loses data in macOS LaunchAgents, systemd services, Docker, etc.
3+
"""
4+
import os
5+
import sys
6+
from unittest.mock import patch
7+
8+
import pytest
9+
10+
from mem0.vector_stores.configs import VectorStoreConfig
11+
12+
13+
def test_default_path_is_not_tmp():
14+
"""The default path must not be /tmp/{provider} on any platform."""
15+
cfg = VectorStoreConfig(provider="faiss", config={})
16+
path = cfg.config.path
17+
assert path is not None
18+
assert not path.startswith("/tmp/"), f"default path still falls back to /tmp: {path}"
19+
assert path.endswith("faiss")
20+
21+
22+
def test_env_var_override():
23+
"""MEM0_DATA_DIR must override the platform default."""
24+
with patch.dict(os.environ, {"MEM0_DATA_DIR": "/var/lib/mem0_test"}):
25+
cfg = VectorStoreConfig(provider="faiss", config={})
26+
assert cfg.config.path == os.path.join("/var/lib/mem0_test", "faiss")
27+
28+
29+
def test_explicit_path_wins():
30+
"""An explicit path in the user config must beat both the env var and the default."""
31+
with patch.dict(os.environ, {"MEM0_DATA_DIR": "/should/not/leak"}):
32+
cfg = VectorStoreConfig(provider="faiss", config={"path": "/explicit/user/path"})
33+
assert cfg.config.path == "/explicit/user/path"
34+
35+
36+
@pytest.mark.skipif(sys.platform != "darwin", reason="macOS-specific default")
37+
def test_macos_default_uses_application_support():
38+
with patch.dict(os.environ, {}, clear=False):
39+
os.environ.pop("MEM0_DATA_DIR", None)
40+
cfg = VectorStoreConfig(provider="faiss", config={})
41+
assert "Application Support/mem0" in cfg.config.path
42+
43+
44+
@pytest.mark.skipif(sys.platform != "linux", reason="Linux-specific default")
45+
def test_linux_default_respects_xdg_data_home():
46+
with patch.dict(os.environ, {"XDG_DATA_HOME": "/custom/xdg"}):
47+
os.environ.pop("MEM0_DATA_DIR", None)
48+
cfg = VectorStoreConfig(provider="faiss", config={})
49+
assert cfg.config.path == os.path.join("/custom/xdg", "mem0", "faiss")

0 commit comments

Comments
 (0)