Skip to content

[FEATURE] Let the openai embedding provider use semantic_embedding_api_base / api_key #1336

Description

@oliver-mee

Feature Description

Let the openai embedding provider use semantic_embedding_api_base and semantic_embedding_api_key, so it can be pointed at any OpenAI-compatible embeddings endpoint. Right now only the litellm provider reads those two config fields.

Problem This Feature Solves

If you run a local OpenAI-compatible embedding server (llama.cpp's llama-server --embedding, vLLM, Text Embeddings Inference, LM Studio, Ollama's OpenAI shim), the only way to reach it from Basic Memory today is the litellm provider, which the docs mark as "Experimental - advanced users only".

That forces extra requirements that have nothing to do with the endpoint itself: semantic_embedding_dimensions becomes mandatory for any non-default model, the model string has to be written in LiteLLM's openai/... form, and there is the per-model input_type handling to think about. Meanwhile the plain openai provider is a thin wrapper over the OpenAI client, which already speaks to any compatible base URL.

Nothing is broken here. It is a small wiring gap that pushes people onto the experimental path for a case the stable path could already handle.

Proposed Solution

OpenAIEmbeddingProvider.__init__ already accepts both parameters (src/basic_memory/repository/openai_provider.py):

def __init__(
    self,
    model_name: str = "text-embedding-3-small",
    *,
    batch_size: int = 64,
    request_concurrency: int = 4,
    dimensions: int = 1536,
    api_key: str | None = None,
    base_url: str | None = None,
    timeout: float = 30.0,
) -> None:

They are just never passed from config. In src/basic_memory/repository/embedding_provider_factory.py, the openai branch constructs the provider with only the model, batch size, concurrency, and **extra_kwargs (which carries dimensions and, for FastEmbed, cache/thread knobs):

provider = OpenAIEmbeddingProvider(
    model_name=model_name,
    batch_size=app_config.semantic_embedding_batch_size,
    request_concurrency=app_config.semantic_embedding_request_concurrency,
    **extra_kwargs,
)

The litellm branch immediately below it does pass api_key=app_config.semantic_embedding_api_key and api_base=app_config.semantic_embedding_api_base.

So the change looks like:

  1. Pass api_key=app_config.semantic_embedding_api_key and base_url=app_config.semantic_embedding_api_base in the openai branch.
  2. Include those two values in _provider_cache_key, which currently digests them only when provider_name == "litellm". Without this, switching endpoints in-process would keep returning the cached provider.
  3. Update docs/semantic-search.md, which describes semantic_embedding_api_base as being for the LiteLLM provider only.

Config would then look like:

{
  "semantic_embedding_provider": "openai",
  "semantic_embedding_model": "my-embedding-model",
  "semantic_embedding_api_base": "http://localhost:8080/v1",
  "semantic_embedding_api_key": "not-used-but-required-by-client",
  "semantic_embedding_dimensions": 1024
}

One design question for the maintainers: whether the two fields should stay shared across providers, or whether the openai provider deserves its own pair. Shared is less config surface and matches how they are named now, but it does mean one field with two meanings.

Alternative Solutions

  • Use the litellm provider. This works, and it is what [FEATURE] Add api_base support for LiteLLM semantic embedding providers #1005 and feat(core): add LiteLLM API base and API key config #1043 added api_base for. It just means running on a provider the docs flag as experimental, and carrying its extra requirements, for a setup the stable provider could serve.
  • Set OPENAI_BASE_URL in the environment. The OpenAI Python client reads it, so this may work by accident depending on how the client is constructed, but it is not a documented Basic Memory setting and it applies process-wide rather than per-provider. Not something to rely on.
  • Leave it alone and document it. A note in the semantic search docs saying "to use a custom endpoint, use the litellm provider" would at least stop people looking for a setting that is not there.

Additional Context

Verified against v0.23.2 as installed, and re-checked against current main on GitHub: the openai branch of the factory still omits both values, and the provider constructor still accepts them.

Related prior work: #1005 and #1043 added api_base and api_key for the LiteLLM provider. #899 marked that provider experimental. This issue is the equivalent for the non-experimental openai provider.

Impact

It makes local and self-hosted embeddings a supported path on the stable provider rather than the experimental one. That matters most for people indexing a large knowledge base, where a local server can be much faster and cheaper than a hosted API, and for anyone who cannot send note content to a third-party endpoint at all.

Happy to open a PR if this is wanted. It looks like passing two existing config values into a constructor that already accepts them, plus the cache-key and docs updates above. Say the word on the shared-versus-separate config question and I will follow whichever you prefer.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions