fix: use persistent platform-appropriate dirs instead of /tmp for vector store defaults (#4279)

Replace hardcoded /tmp/{provider} default paths with XDG-compliant persistent
directories (~/.local/share/mem0/{provider}), fixing failures on Windows,
macOS LaunchAgents, Linux systemd, and Docker. Adds MEM0_DATA_DIR env var
override, legacy /tmp data migration warning, and updates Qdrant/ChromaDB/FAISS
configs and docs to use the new defaults.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
utkarsh240799
2026-03-26 18:31:58 +05:30
co-authored by Claude Opus 4.6
parent 051551edb7
commit 0b397eaf06
9 changed files with 412 additions and 27 deletions
+10 -7
View File
@@ -2,6 +2,8 @@ from typing import Any, ClassVar, Dict, Optional
from pydantic import BaseModel, ConfigDict, Field, model_validator
from mem0.vector_stores.configs import get_default_vector_store_path
class ChromaDbConfig(BaseModel):
try:
@@ -23,24 +25,25 @@ class ChromaDbConfig(BaseModel):
def check_connection_config(cls, values):
host, port, path = values.get("host"), values.get("port"), values.get("path")
api_key, tenant = values.get("api_key"), values.get("tenant")
default_path = get_default_vector_store_path("chroma")
# Check if cloud configuration is provided
cloud_config = bool(api_key and tenant)
# If cloud configuration is provided, remove any default path that might have been added
if cloud_config and path == "/tmp/chroma":
if cloud_config and path == default_path:
values.pop("path", None)
return values
# Check if local/server configuration is provided
local_config = bool(path) or bool(host and port)
if not cloud_config and not local_config:
raise ValueError("Either ChromaDB Cloud configuration (api_key, tenant) or local configuration (path or host/port) must be provided.")
if cloud_config and local_config:
raise ValueError("Cannot specify both cloud configuration and local configuration. Choose one.")
return values
@model_validator(mode="before")
+3 -1
View File
@@ -2,6 +2,8 @@ from typing import Any, ClassVar, Dict, Optional
from pydantic import BaseModel, ConfigDict, Field, model_validator
from mem0.vector_stores.configs import get_default_vector_store_path
class QdrantConfig(BaseModel):
from qdrant_client import QdrantClient
@@ -13,7 +15,7 @@ class QdrantConfig(BaseModel):
client: Optional[QdrantClient] = Field(None, description="Existing Qdrant client instance")
host: Optional[str] = Field(None, description="Host address for Qdrant server")
port: Optional[int] = Field(None, description="Port for Qdrant server")
path: Optional[str] = Field("/tmp/qdrant", description="Path for local Qdrant database")
path: Optional[str] = Field(default_factory=lambda: get_default_vector_store_path("qdrant"), description="Path for local Qdrant database")
url: Optional[str] = Field(None, description="Full URL for Qdrant server")
api_key: Optional[str] = Field(None, description="API key for Qdrant server")
on_disk: Optional[bool] = Field(False,description="Enables persistent storage. Vectors are kept on disk (True) or in memory (False). Does not delete the local database path.")
+34 -13
View File
@@ -1,10 +1,33 @@
import logging
import os
from pathlib import Path
from typing import Dict, Optional
from pydantic import BaseModel, Field, model_validator
logger = logging.getLogger(__name__)
def get_default_vector_store_path(provider: str) -> str:
"""Return a platform-appropriate default path for a vector store provider.
Preference order: MEM0_DATA_DIR → XDG_DATA_HOME (Linux) → APPDATA (Windows) → ~/.local/share
"""
mem0_data_dir = os.environ.get("MEM0_DATA_DIR")
if mem0_data_dir:
base = Path(mem0_data_dir)
else:
xdg = os.environ.get("XDG_DATA_HOME")
app_data = os.environ.get("APPDATA")
if xdg:
base = Path(xdg)
elif app_data:
base = Path(app_data)
else:
base = Path.home() / ".local" / "share"
base = base / "mem0"
return str(base / provider)
class VectorStoreConfig(BaseModel):
provider: str = Field(
@@ -64,18 +87,16 @@ class VectorStoreConfig(BaseModel):
# 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__:
# Use a platform-appropriate user data directory instead of /tmp, which is
# ephemeral on many systems and may be unwritable in service/restricted environments.
# Preference order: XDG_DATA_HOME (Linux) → APPDATA (Windows) → ~/.local/share
xdg = os.environ.get("XDG_DATA_HOME")
app_data = os.environ.get("APPDATA")
if xdg:
base = Path(xdg)
elif app_data:
base = Path(app_data)
else:
base = Path.home() / ".local" / "share"
config["path"] = str(base / "mem0" / provider)
config["path"] = get_default_vector_store_path(provider)
legacy_path = Path(f"/tmp/{provider}")
if legacy_path.exists() and any(legacy_path.iterdir()):
logger.warning(
f"Found existing data at legacy path '{legacy_path}'. "
f"mem0 now defaults to '{config['path']}'. "
f"To keep using the old location, set path='/tmp/{provider}' in your config, "
f"or move the data to the new path."
)
self.config = config_class(**config)
return self
+3 -1
View File
@@ -8,6 +8,8 @@ from typing import Dict, List, Optional
import numpy as np
from pydantic import BaseModel
from mem0.vector_stores.configs import get_default_vector_store_path
import warnings
try:
@@ -58,7 +60,7 @@ class FAISS(VectorStoreBase):
Defaults to False.
"""
self.collection_name = collection_name
self.path = path or f"/tmp/faiss/{collection_name}"
self.path = path or f"{get_default_vector_store_path('faiss')}/{collection_name}"
self.distance_strategy = distance_strategy
self.normalize_L2 = normalize_L2
self.embedding_model_dims = embedding_model_dims