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:
co-authored by
Claude Opus 4.6
parent
051551edb7
commit
0b397eaf06
@@ -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")
|
||||
|
||||
@@ -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.")
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user