Skip to content
Open
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
34 changes: 33 additions & 1 deletion mem0/vector_stores/configs.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,40 @@
import os
import sys
from typing import Dict, Optional

from pydantic import BaseModel, Field, model_validator


def _default_data_dir() -> str:
"""Return a writable user-data directory suitable for embedded vector stores.

Resolution order:
1. ``MEM0_DATA_DIR`` environment variable (explicit override).
2. Platform convention:
- macOS: ``~/Library/Application Support/mem0``
- Windows: ``%LOCALAPPDATA%/mem0``
- Linux/BSD: ``$XDG_DATA_HOME/mem0`` or ``~/.local/share/mem0``

The previous default of ``/tmp/{provider}`` broke macOS LaunchAgents, systemd
services with ``noexec`` ``/tmp``, Windows (no ``/tmp``), and Docker (ephemeral
``/tmp``). See #4279.
"""
env_dir = os.environ.get("MEM0_DATA_DIR")
if env_dir:
return env_dir
if sys.platform == "darwin":
return os.path.expanduser("~/Library/Application Support/mem0")
if sys.platform == "win32":
return os.path.join(
os.environ.get("LOCALAPPDATA") or os.path.expanduser("~/AppData/Local"),
"mem0",
)
return os.path.join(
os.environ.get("XDG_DATA_HOME") or os.path.expanduser("~/.local/share"),
"mem0",
)


class VectorStoreConfig(BaseModel):
provider: str = Field(
description="Provider of the vector store (e.g., 'qdrant', 'chroma', 'upstash_vector')",
Expand Down Expand Up @@ -61,7 +93,7 @@ def validate_and_create_config(self) -> "VectorStoreConfig":

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

self.config = config_class(**config)
return self
49 changes: 49 additions & 0 deletions tests/vector_stores/test_default_path.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
"""Regression test for #4279: vector store path defaults to /tmp/{provider}, which
fails or silently loses data in macOS LaunchAgents, systemd services, Docker, etc.
"""
import os
import sys
from unittest.mock import patch

import pytest

from mem0.vector_stores.configs import VectorStoreConfig


def test_default_path_is_not_tmp():
"""The default path must not be /tmp/{provider} on any platform."""
cfg = VectorStoreConfig(provider="faiss", config={})
path = cfg.config.path
assert path is not None
assert not path.startswith("/tmp/"), f"default path still falls back to /tmp: {path}"
assert path.endswith("faiss")


def test_env_var_override():
"""MEM0_DATA_DIR must override the platform default."""
with patch.dict(os.environ, {"MEM0_DATA_DIR": "/var/lib/mem0_test"}):
cfg = VectorStoreConfig(provider="faiss", config={})
assert cfg.config.path == os.path.join("/var/lib/mem0_test", "faiss")


def test_explicit_path_wins():
"""An explicit path in the user config must beat both the env var and the default."""
with patch.dict(os.environ, {"MEM0_DATA_DIR": "/should/not/leak"}):
cfg = VectorStoreConfig(provider="faiss", config={"path": "/explicit/user/path"})
assert cfg.config.path == "/explicit/user/path"


@pytest.mark.skipif(sys.platform != "darwin", reason="macOS-specific default")
def test_macos_default_uses_application_support():
with patch.dict(os.environ, {}, clear=False):
os.environ.pop("MEM0_DATA_DIR", None)
cfg = VectorStoreConfig(provider="faiss", config={})
assert "Application Support/mem0" in cfg.config.path


@pytest.mark.skipif(sys.platform != "linux", reason="Linux-specific default")
def test_linux_default_respects_xdg_data_home():
with patch.dict(os.environ, {"XDG_DATA_HOME": "/custom/xdg"}):
os.environ.pop("MEM0_DATA_DIR", None)
cfg = VectorStoreConfig(provider="faiss", config={})
assert cfg.config.path == os.path.join("/custom/xdg", "mem0", "faiss")