Skip to content

feat: support OPENAI_BASE_URL and OPENAI_EMBEDDING_MODEL for OpenAI-compatible endpoints#186

Merged
rohitg00 merged 1 commit intorohitg00:mainfrom
Edison-A-N:feat/openai-base-url
Apr 22, 2026
Merged

feat: support OPENAI_BASE_URL and OPENAI_EMBEDDING_MODEL for OpenAI-compatible endpoints#186
rohitg00 merged 1 commit intorohitg00:mainfrom
Edison-A-N:feat/openai-base-url

Conversation

@Edison-A-N
Copy link
Copy Markdown
Contributor

@Edison-A-N Edison-A-N commented Apr 22, 2026

Summary

  • Add OPENAI_BASE_URL and OPENAI_EMBEDDING_MODEL env vars to OpenAIEmbeddingProvider, enabling use with OpenAI-compatible proxies (Azure OpenAI, vLLM, LM Studio, domestic relays, etc.)
  • Follows the same pattern as MinimaxProvider (MINIMAX_BASE_URL) — env var with sensible default, zero breaking changes

Details

Problem: OpenAIEmbeddingProvider hardcodes https://api.openai.com/v1/embeddings and text-embedding-3-small. Users behind proxies or using compatible APIs cannot configure the endpoint.

Fix: Two optional env vars with backward-compatible defaults:

Env Var Default Description
OPENAI_BASE_URL https://api.openai.com Base URL without path
OPENAI_EMBEDDING_MODEL text-embedding-3-small Embedding model name

Files changed: 2 (provider + tests)
Tests added: 4 (default behavior, error case, base URL override, model override)

Related

See also #185 which takes a broader approach (full OpenAI-compatible provider + local runtime detection). This PR is intentionally minimal and focused on the embedding provider only.

How to verify

OPENAI_API_KEY=sk-test OPENAI_BASE_URL=https://your-proxy.com npm test -- test/embedding-provider.test.ts

Summary by CodeRabbit

Release Notes

  • New Features
    • OpenAI embedding provider now supports custom OpenAI-compatible API endpoints
    • Configurable embedding model selection via environment variables
    • Default settings preserved for backward compatibility

Signed-off-by: Edison-A-N <zhangn661@gmail.com>
@vercel
Copy link
Copy Markdown

vercel Bot commented Apr 22, 2026

@Edison-A-N is attempting to deploy a commit to the rohitg00's projects Team on Vercel.

A member of the Team first needs to authorize it.

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 22, 2026

📝 Walkthrough

Walkthrough

The OpenAI embedding provider was extended to support configurable OpenAI-compatible endpoints and embedding models. It now dynamically constructs the request URL using an optional baseUrl environment variable and allows the embedding model to be configured, replacing hardcoded values with configurable defaults.

Changes

Cohort / File(s) Summary
OpenAI Embedding Provider Configuration
src/providers/embedding/openai.ts
Added baseUrl and model private fields with environment variable support (OPENAI_BASE_URL, OPENAI_EMBEDDING_MODEL). Replaced hardcoded embedding URL and model with dynamic construction using these values and sensible defaults.
Provider Test Suite
test/embedding-provider.test.ts
New comprehensive test suite for OpenAIEmbeddingProvider covering default behavior, missing API key error handling, fetch call validation with correct base URL and model in request payload, and environment variable configuration.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Poem

🐰 With carrots of config and endpoints so fine,
The OpenAI provider now lets settings align!
Base URLs and models, no more hardcoded chains,
Flexibility flourishes through environmental veins! 🌟

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'feat: support OPENAI_BASE_URL and OPENAI_EMBEDDING_MODEL for OpenAI-compatible endpoints' accurately and concisely describes the main change: adding two environment variables for OpenAI-compatible endpoint configuration.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/providers/embedding/openai.ts (1)

27-39: ⚠️ Potential issue | 🟡 Minor

Normalize trailing slash on OPENAI_BASE_URL.

If a user sets OPENAI_BASE_URL=https://my-proxy.example.com/ (a very common habit), the constructed URL becomes https://my-proxy.example.com//v1/embeddings, which some proxies reject or route differently.

Proposed fix
-    this.baseUrl =
-      getEnvVar("OPENAI_BASE_URL") || DEFAULT_BASE_URL;
+    this.baseUrl = (
+      getEnvVar("OPENAI_BASE_URL") || DEFAULT_BASE_URL
+    ).replace(/\/+$/, "");
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/providers/embedding/openai.ts` around lines 27 - 39, Normalize
OPENAI_BASE_URL when initializing baseUrl: strip any trailing slash from the
value returned by getEnvVar("OPENAI_BASE_URL") (falling back to
DEFAULT_BASE_URL) so that baseUrl never ends with "/", preventing double slashes
when building endpoints in methods like embedBatch which constructs
`${this.baseUrl}/v1/embeddings`. Update the constructor logic that sets
this.baseUrl to trim a trailing "/" (but preserve the host when value is empty
or undefined by using DEFAULT_BASE_URL).
🧹 Nitpick comments (2)
test/embedding-provider.test.ts (2)

51-73: Ensure OPENAI_API_KEY is cleared in the new suite's beforeEach.

The new OpenAIEmbeddingProvider suite only clears OPENAI_BASE_URL and OPENAI_EMBEDDING_MODEL, but not OPENAI_API_KEY. The "throws when no API key is provided" test at Line 70-73 deletes it inline, but relies on the absence of OPENAI_API_KEY in the ambient environment for correctness of ordering — if tests run in a shell where OPENAI_API_KEY is exported, the other tests are fine (they pass "test-key" explicitly), but deleting it consistently in beforeEach would make the suite more self-contained and match the pattern of the createEmbeddingProvider suite above.

   beforeEach(() => {
     process.env = { ...originalEnv };
+    delete process.env["OPENAI_API_KEY"];
     delete process.env["OPENAI_BASE_URL"];
     delete process.env["OPENAI_EMBEDDING_MODEL"];
   });
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@test/embedding-provider.test.ts` around lines 51 - 73, The suite's beforeEach
currently deletes OPENAI_BASE_URL and OPENAI_EMBEDDING_MODEL but not
OPENAI_API_KEY, making the "throws when no API key is provided" test brittle;
update the beforeEach that runs for OpenAIEmbeddingProvider to also delete
process.env["OPENAI_API_KEY"] so the OpenAIEmbeddingProvider constructor tests
run in a clean environment (refer to the beforeEach block and the
OpenAIEmbeddingProvider constructor and the test that deletes OPENAI_API_KEY).

101-101: Guard against missing fetch call arguments.

fetchSpy.mock.calls[0][1] will throw an unhelpful TypeError if fetch was never called or called without a second argument. A small assertion improves failure diagnostics:

-    const body = JSON.parse((fetchSpy.mock.calls[0][1] as RequestInit).body as string);
+    expect(fetchSpy).toHaveBeenCalledTimes(1);
+    const init = fetchSpy.mock.calls[0]?.[1] as RequestInit | undefined;
+    const body = JSON.parse(init?.body as string);
     expect(body.model).toBe("text-embedding-3-large");
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@test/embedding-provider.test.ts` at line 101, Add a guard that verifies fetch
was called with a second argument before accessing fetchSpy.mock.calls[0][1] to
avoid a TypeError; e.g., assert fetchSpy.mock.calls.length > 0 and that
fetchSpy.mock.calls[0].length > 1 (or use expect(fetchSpy).toHaveBeenCalled())
prior to the line that builds body (the const body =
JSON.parse((fetchSpy.mock.calls[0][1] as RequestInit).body as string);) so
failures show a clear assertion error instead of an unhelpful TypeError.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/providers/embedding/openai.ts`:
- Around line 19-30: The class currently hardcodes readonly dimensions = 1536
while allowing model to be configured in the constructor (via model,
getEnvVar("OPENAI_EMBEDDING_MODEL") or DEFAULT_MODEL), causing a mismatch if a
different model (e.g. text-embedding-3-large) is used; change the implementation
so dimensions is computed from the configured model (use a small lookup map
keyed by model name to set the correct dimension) and allow an override via an
environment variable (OPENAI_EMBEDDING_DIMENSIONS) as a fallback; update the
constructor to read OPENAI_EMBEDDING_DIMENSIONS first, then map
model→dimensions, and finally default to 1536 if neither is present, keeping
references to the existing symbols dimensions, constructor, model, getEnvVar and
DEFAULT_MODEL for locating the change.

---

Outside diff comments:
In `@src/providers/embedding/openai.ts`:
- Around line 27-39: Normalize OPENAI_BASE_URL when initializing baseUrl: strip
any trailing slash from the value returned by getEnvVar("OPENAI_BASE_URL")
(falling back to DEFAULT_BASE_URL) so that baseUrl never ends with "/",
preventing double slashes when building endpoints in methods like embedBatch
which constructs `${this.baseUrl}/v1/embeddings`. Update the constructor logic
that sets this.baseUrl to trim a trailing "/" (but preserve the host when value
is empty or undefined by using DEFAULT_BASE_URL).

---

Nitpick comments:
In `@test/embedding-provider.test.ts`:
- Around line 51-73: The suite's beforeEach currently deletes OPENAI_BASE_URL
and OPENAI_EMBEDDING_MODEL but not OPENAI_API_KEY, making the "throws when no
API key is provided" test brittle; update the beforeEach that runs for
OpenAIEmbeddingProvider to also delete process.env["OPENAI_API_KEY"] so the
OpenAIEmbeddingProvider constructor tests run in a clean environment (refer to
the beforeEach block and the OpenAIEmbeddingProvider constructor and the test
that deletes OPENAI_API_KEY).
- Line 101: Add a guard that verifies fetch was called with a second argument
before accessing fetchSpy.mock.calls[0][1] to avoid a TypeError; e.g., assert
fetchSpy.mock.calls.length > 0 and that fetchSpy.mock.calls[0].length > 1 (or
use expect(fetchSpy).toHaveBeenCalled()) prior to the line that builds body (the
const body = JSON.parse((fetchSpy.mock.calls[0][1] as RequestInit).body as
string);) so failures show a clear assertion error instead of an unhelpful
TypeError.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 73adf18c-158e-4403-9fe5-fba69c843256

📥 Commits

Reviewing files that changed from the base of the PR and between 480d8e8 and 6f87c29.

📒 Files selected for processing (2)
  • src/providers/embedding/openai.ts
  • test/embedding-provider.test.ts

Comment on lines 19 to +30
readonly dimensions = 1536;
private apiKey: string;
private baseUrl: string;
private model: string;

constructor(apiKey?: string) {
this.apiKey = apiKey || getEnvVar("OPENAI_API_KEY") || "";
if (!this.apiKey) throw new Error("OPENAI_API_KEY is required");
this.baseUrl =
getEnvVar("OPENAI_BASE_URL") || DEFAULT_BASE_URL;
this.model =
getEnvVar("OPENAI_EMBEDDING_MODEL") || DEFAULT_MODEL;
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

dimensions is hardcoded to 1536 but model is now configurable.

If a user sets OPENAI_EMBEDDING_MODEL=text-embedding-3-large (3072 dims) or text-embedding-ada-002 (1536) or any other compatible model, provider.dimensions will report a value inconsistent with the vectors actually returned by embed(). Downstream consumers (e.g. vector store schemas) relying on dimensions will silently get wrong sizing.

Consider either:

  • deriving dimensions from the configured model (lookup table with a sensible default), or
  • exposing OPENAI_EMBEDDING_DIMENSIONS env var, or
  • at minimum documenting the constraint that callers overriding the model are responsible for ensuring it returns 1536-dim vectors.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/providers/embedding/openai.ts` around lines 19 - 30, The class currently
hardcodes readonly dimensions = 1536 while allowing model to be configured in
the constructor (via model, getEnvVar("OPENAI_EMBEDDING_MODEL") or
DEFAULT_MODEL), causing a mismatch if a different model (e.g.
text-embedding-3-large) is used; change the implementation so dimensions is
computed from the configured model (use a small lookup map keyed by model name
to set the correct dimension) and allow an override via an environment variable
(OPENAI_EMBEDDING_DIMENSIONS) as a fallback; update the constructor to read
OPENAI_EMBEDDING_DIMENSIONS first, then map model→dimensions, and finally
default to 1536 if neither is present, keeping references to the existing
symbols dimensions, constructor, model, getEnvVar and DEFAULT_MODEL for locating
the change.

@rohitg00 rohitg00 merged commit 343b51c into rohitg00:main Apr 22, 2026
1 of 2 checks passed
@rohitg00
Copy link
Copy Markdown
Owner

Thanks @Edison-A-N for the contribution — merged!

Mirrors the MINIMAX_BASE_URL pattern cleanly and unlocks Azure OpenAI / vLLM / LM Studio for embeddings with zero breakage. Rebased on latest main before merging so the two-step CI install + lockfile gitignore from #184/#187/#188 are picked up.

Opened a small follow-up at #189 to also derive dimensions from the configured model (CodeRabbit flagged that text-embedding-3-large would still report 1536). New OPENAI_EMBEDDING_DIMENSIONS env var + known-models lookup table handle it, with a fallback to 1536 so existing deployments stay put.

rohitg00 added a commit that referenced this pull request Apr 22, 2026
Follow-up to #186. The PR made model configurable via OPENAI_EMBEDDING_MODEL
but left dimensions hardcoded at 1536. A user pointing at
text-embedding-3-large (3072 dims) would see provider.dimensions
return 1536, which vector-store schemas and hybrid-search weights
rely on for correct sizing.

- Add MODEL_DIMENSIONS table: text-embedding-3-small=1536,
  text-embedding-3-large=3072, text-embedding-ada-002=1536.
- Make dimensions a computed readonly field.
- New OPENAI_EMBEDDING_DIMENSIONS env var for custom / self-hosted
  OpenAI-compatible endpoints not in the table. Positive integers only;
  reject non-numeric / non-positive values with a clear error.
- Unknown model names fall back to 1536 with the explicit override
  available if the server returns a different size.
- Tests cover known models, dimension override, unknown-model fallback,
  and validation of the override env var.
@Edison-A-N Edison-A-N deleted the feat/openai-base-url branch April 22, 2026 14:44
rohitg00 added a commit that referenced this pull request Apr 22, 2026
- README provider table: surface the no-op default and call out the
  opt-in Claude-subscription fallback with AGENTMEMORY_ALLOW_AGENT_SDK
  (from #187) instead of listing 'Claude subscription' as the default.
- README env block: document OPENAI_BASE_URL / OPENAI_EMBEDDING_MODEL
  (#186) and OPENAI_EMBEDDING_DIMENSIONS (#189), plus MINIMAX_API_KEY.
- Hero stat-tests SVG: 654 -> 827 (both dark and light variants) to
  match current suite size after recursion guard + idempotent
  lesson/crystal tests + openai dimension tests landed.
- website/lib/generated-meta.json regenerated by
  website/scripts/gen-meta.mjs (v0.9.1, 51 tools, 12 hooks,
  119 endpoints, 848 tests).
@rohitg00 rohitg00 mentioned this pull request Apr 22, 2026
rohitg00 added a commit that referenced this pull request Apr 22, 2026
Rolls up #186 (OPENAI_BASE_URL / OPENAI_EMBEDDING_MODEL), #187 (Stop-hook
recursion 5-layer defense + NoopProvider + AGENTMEMORY_ALLOW_AGENT_SDK opt-in),
#188 (viewer empty-tabs + import-jsonl synthetic compression + auto-derived
lessons/crystals + richer session detail + audit/replay/frontier shape fixes),
#189 (OPENAI_EMBEDDING_DIMENSIONS + model-dimensions table), and #190
(README/website docs refresh).

Bumps: package.json, plugin/.claude-plugin/plugin.json, src/version.ts,
src/types.ts ExportData.version union, src/functions/export-import.ts
supportedVersions, test/export-import.test.ts assertion, and
packages/mcp/package.json shim (was stuck at 0.9.0).
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