diff --git a/mem0/configs/embeddings/base.py b/mem0/configs/embeddings/base.py index de2f4324f..2cd03dd68 100644 --- a/mem0/configs/embeddings/base.py +++ b/mem0/configs/embeddings/base.py @@ -34,7 +34,7 @@ class BaseEmbedderConfig(ABC): # Gemini specific output_dimensionality: Optional[str] = None, # LM Studio specific - lmstudio_base_url: Optional[str] = "http://localhost:1234/v1", + lmstudio_base_url: Optional[str] = None, # AWS Bedrock specific aws_access_key_id: Optional[str] = None, aws_secret_access_key: Optional[str] = None, @@ -70,7 +70,8 @@ class BaseEmbedderConfig(ABC): :type memory_update_embedding_type: Optional[str], optional :param memory_search_embedding_type: The type of embedding to use for the search memory action, defaults to None :type memory_search_embedding_type: Optional[str], optional - :param lmstudio_base_url: LM Studio base URL to be use, defaults to "http://localhost:1234/v1" + :param lmstudio_base_url: LM Studio base URL to be use, defaults to None + (resolved by the provider from LMSTUDIO_BASE_URL, then "http://localhost:1234/v1") :type lmstudio_base_url: Optional[str], optional """ diff --git a/mem0/embeddings/lmstudio.py b/mem0/embeddings/lmstudio.py index f8943e430..606d39da4 100644 --- a/mem0/embeddings/lmstudio.py +++ b/mem0/embeddings/lmstudio.py @@ -1,3 +1,4 @@ +import os from typing import Literal, Optional from openai import OpenAI @@ -13,6 +14,9 @@ class LMStudioEmbedding(EmbeddingBase): self.config.model = self.config.model or "nomic-ai/nomic-embed-text-v1.5-GGUF/nomic-embed-text-v1.5.f16.gguf" self.config.embedding_dims = self.config.embedding_dims or 1536 self.config.api_key = self.config.api_key or "lm-studio" + self.config.lmstudio_base_url = ( + self.config.lmstudio_base_url or os.getenv("LMSTUDIO_BASE_URL") or "http://localhost:1234/v1" + ) self.client = OpenAI(base_url=self.config.lmstudio_base_url, api_key=self.config.api_key) diff --git a/tests/embeddings/test_lm_studio_embeddings.py b/tests/embeddings/test_lm_studio_embeddings.py index c391fc389..6f33384a5 100644 --- a/tests/embeddings/test_lm_studio_embeddings.py +++ b/tests/embeddings/test_lm_studio_embeddings.py @@ -6,7 +6,6 @@ from mem0.configs.embeddings.base import BaseEmbedderConfig from mem0.embeddings.lmstudio import LMStudioEmbedding - @pytest.fixture def mock_lm_studio_client(): with patch("mem0.embeddings.lmstudio.OpenAI") as mock_openai: @@ -80,3 +79,31 @@ def test_embed_batch_count_mismatch_raises(mock_lm_studio_client): with pytest.raises(ValueError, match="returned 1 embeddings for 2 texts"): embedder.embed_batch(["first text", "second text"]) + + +def test_base_url_honors_lmstudio_base_url_env(mock_lm_studio_client, monkeypatch): + monkeypatch.setenv("LMSTUDIO_BASE_URL", "http://lmstudio.internal:9000/v1") + + with patch("mem0.embeddings.lmstudio.OpenAI") as mock_openai: + LMStudioEmbedding(BaseEmbedderConfig()) + + assert mock_openai.call_args.kwargs["base_url"] == "http://lmstudio.internal:9000/v1" + + +def test_base_url_prefers_explicit_config_over_env(mock_lm_studio_client, monkeypatch): + monkeypatch.setenv("LMSTUDIO_BASE_URL", "http://lmstudio.internal:9000/v1") + + with patch("mem0.embeddings.lmstudio.OpenAI") as mock_openai: + LMStudioEmbedding(BaseEmbedderConfig(lmstudio_base_url="http://explicit:1234/v1")) + + assert mock_openai.call_args.kwargs["base_url"] == "http://explicit:1234/v1" + + +def test_base_url_falls_back_to_localhost_default(mock_lm_studio_client, monkeypatch): + monkeypatch.delenv("LMSTUDIO_BASE_URL", raising=False) + + with patch("mem0.embeddings.lmstudio.OpenAI") as mock_openai: + embedder = LMStudioEmbedding(BaseEmbedderConfig()) + + assert mock_openai.call_args.kwargs["base_url"] == "http://localhost:1234/v1" + assert embedder.config.lmstudio_base_url == "http://localhost:1234/v1"