Skip to content

feat(embedding): route embeddings through OpenRouter via EMBEDDING_MODEL - #176

Merged
murrayju merged 1 commit into
mainfrom
murrayju/age-547-openrouter-embeddings
Jul 29, 2026
Merged

feat(embedding): route embeddings through OpenRouter via EMBEDDING_MODEL#176
murrayju merged 1 commit into
mainfrom
murrayju/age-547-openrouter-embeddings

Conversation

@murrayju

Copy link
Copy Markdown
Member

Summary

Routes embedding requests through OpenRouter (or any OpenAI-compatible gateway) via deploy-time environment variables, so it can be controlled from our Helm charts.

Most of the plumbing already existed: EMBEDDING_BASE_URL and EMBEDDING_API_KEY are already wired through to the AI SDK's createOpenAI({ apiKey, baseURL }). The missing piece was the model name, which was hardcoded to text-embedding-3-small in packages/server/config.ts.

OpenAI-compatible gateways namespace models differently — OpenRouter serves this one as openai/text-embedding-3-small — so base URL + key alone would have failed with an unknown-model error.

This adds EMBEDDING_MODEL, defaulting to the existing constant, so current deploys are unaffected.

Helm configuration

EMBEDDING_BASE_URL=https://openrouter.ai/api/v1
EMBEDDING_MODEL=openai/text-embedding-3-small
EMBEDDING_API_KEY=<OpenRouter key>

Existing embeddings stay valid — no re-index

The main risk was a vector-space mismatch, which would silently degrade search quality rather than error. Verified against the live OpenRouter API that its vectors for this model are bit-identical to OpenAI's:

  • cosine similarity: 1.0
  • max absolute elementwise difference: 0.0

So stored vectors remain compatible; no re-embedding or re-index is needed.

Worth noting: OpenRouter's /models catalog omits embedding models entirely (they live on a separate /embeddings/models endpoint), which initially made it look like embeddings weren't supported at all. They are.

Deliberate non-changes

  • dimensions stays hardcoded at 1536. The column is halfvec(1536), so exposing this would only allow misconfiguration into a runtime failure. Documented in embeddingConstants.
  • Tokenizer untouched. It's pinned to cl100k_base, which is correct for text-embedding-3-* regardless of the name prefix.

Also fixed

scripts/embed-query.ts hardcoded the same model. Left as-is, it would have generated query vectors from a different model than the stored ones once EMBEDDING_MODEL was set — a subtle correctness bug. It now mirrors the same override.

Testing

  • Exercised the real code path (buildEmbeddingConfiggenerateEmbedding / generateEmbeddings) live against OpenRouter: 1536 dims, single and batch, no errors.
  • Added 5 unit tests covering the default, the override, the empty-string fallback, the fixed dimensions, and base-URL forwarding.
  • ./bun run check:full fully green: 1173 + 1551 + 45 pass, 0 fail.

Note for reviewers: if integration tests fail locally with password authentication failed for user "postgres", check that me-postgres actually owns port 5432 — an unrelated project's Postgres container can claim it, sending the suite at the wrong database. Unrelated to this change.

Refs: https://linear.app/tigerdata/issue/AGE-547/route-memory-engine-embeddings-through-openrouter

EMBEDDING_BASE_URL and EMBEDDING_API_KEY were already wired through to the
AI SDK's createOpenAI({ apiKey, baseURL }), but the model name was hardcoded
to "text-embedding-3-small". OpenAI-compatible gateways namespace models
differently -- OpenRouter serves it as "openai/text-embedding-3-small" -- so
base URL + key alone fail with an unknown-model error.

Add EMBEDDING_MODEL, defaulting to the current constant so existing deploys
are unaffected. Routing through OpenRouter is then purely deploy-time config:

  EMBEDDING_BASE_URL=https://openrouter.ai/api/v1
  EMBEDDING_MODEL=openai/text-embedding-3-small
  EMBEDDING_API_KEY=<OpenRouter key>

Verified against the live OpenRouter API that its vectors are bit-identical
to OpenAI's for this model (cosine similarity 1.0, max elementwise diff 0.0),
so stored embeddings stay valid -- no re-embedding or re-index needed.

dimensions stays hardcoded at 1536 on purpose: the column is halfvec(1536),
so making it configurable would only permit misconfiguration. The tokenizer
needs no change either -- it is pinned to cl100k_base, correct for
text-embedding-3-* regardless of the name prefix.

Also fix scripts/embed-query.ts, which hardcoded the same model and would
otherwise generate query vectors from a different model than the stored ones.

Refs: https://linear.app/tigerdata/issue/AGE-547/route-memory-engine-embeddings-through-openrouter
@murrayju
murrayju requested review from cevian and jgpruitt as code owners July 29, 2026 15:52
Copilot AI review requested due to automatic review settings July 29, 2026 15:52

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds deploy-time configurability for the embedding model name so embeddings can be routed through OpenRouter (or any OpenAI-compatible gateway) using environment variables, without changing existing deployments’ behavior.

Changes:

  • Introduces EMBEDDING_MODEL (defaulting to text-embedding-3-small) in buildEmbeddingConfig, alongside the existing EMBEDDING_BASE_URL/EMBEDDING_API_KEY plumbing.
  • Updates the scripts/embed-query.ts helper to respect EMBEDDING_MODEL to avoid query-vector/model mismatch.
  • Expands docs and adds unit tests covering defaulting, override behavior, empty-string fallback, fixed dimensions, and base URL forwarding.

Reviewed changes

Copilot reviewed 6 out of 6 changed files in this pull request and generated no comments.

Show a summary per file
File Description
SELF_HOST.md Documents gateway routing via EMBEDDING_BASE_URL + EMBEDDING_MODEL and warns about model/vector-space compatibility.
scripts/embed-query.ts Uses EMBEDDING_MODEL override to keep generated query embeddings aligned with stored embeddings.
packages/server/start.ts Reads EMBEDDING_MODEL from env with a safe default via embeddingConstants.model.
packages/server/start.test.ts Adds unit tests for model default/override behavior and base URL forwarding.
packages/server/config.ts Clarifies embedding configuration constraints (model override, fixed 1536 dims, vector-space compatibility).
.env.sample Documents EMBEDDING_MODEL and provides OpenRouter example configuration.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@murrayju
murrayju merged commit 979d2a1 into main Jul 29, 2026
6 checks passed
@murrayju
murrayju deleted the murrayju/age-547-openrouter-embeddings branch July 29, 2026 15:59
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants