Skip to content

feat: add /v1/embeddings endpoint (OpenAI-compatible) - #146

Merged
decolua merged 3 commits into
decolua:masterfrom
xuandung38:feat/embeddings-endpoint
Feb 18, 2026
Merged

decolua merged 3 commits into
decolua:masterfrom
xuandung38:feat/embeddings-endpoint

Conversation

@xuandung38

Copy link
Copy Markdown
Contributor

This adds a /v1/embeddings endpoint to 9router, making it work as a drop-in proxy for embedding requests the same way it already handles chat completions.

Why

9router is great for routing and load-balancing LLM calls, but until now it only handled /v1/chat/completions. A lot of practical workloads — RAG pipelines, memory layers, semantic search — also need embeddings, and there was no clean way to route those through 9router. This closes that gap.

Related: closes #117

What's in this PR

Core embeddings handler that follows the same request/response structure as OpenAI's /v1/embeddings. A cloud worker variant with auth handling and provider fallback. Route wiring so the endpoint is registered correctly. Unit tests — 59 passing, covering both the core handler and the cloud worker path.

Supported providers: OpenAI, OpenRouter, and any OpenAI-compatible endpoint.

Testing

Ran the full test suite locally, 59 tests pass. Tested manually against OpenAI and an OpenRouter-compatible backend. The response shape matches what you'd get hitting OpenAI directly, so existing clients should work without changes.

✓ 59 tests passing

Add OpenAI-compatible POST /v1/embeddings endpoint that routes through
the existing provider credential + fallback infrastructure.

Changes:
- open-sse/handlers/embeddingsCore.js: core handler (handleEmbeddingsCore)
  * Validates input (string or array), encoding_format
  * Builds provider-specific URL and headers for openai, openrouter,
    and openai-compatible providers
  * Handles 401/403 token refresh via executor.refreshCredentials
  * Returns normalized OpenAI-format response { object: 'list', data, model, usage }
- cloud/src/handlers/embeddings.js: cloud Worker handler (handleEmbeddings)
  * Auth + machineId resolution identical to handleChat
  * Provider credential fallback loop with rate-limit tracking
- cloud/src/index.js: wire new routes
  * POST /v1/embeddings  (new format — machineId from API key)
  * POST /{machineId}/v1/embeddings  (old format — machineId from URL)
- Setup vitest as test framework (tests/ directory)
- embeddingsCore.test.js (36 tests):
  - buildEmbeddingsBody: single string, array, encoding_format, default float
  - buildEmbeddingsUrl: openai, openrouter, openai-compatible-*, unsupported
  - buildEmbeddingsHeaders: per-provider headers, accessToken fallback
  - handleEmbeddingsCore: input validation, success path, provider errors,
    network errors, invalid JSON, token refresh 401 handling
- embeddings.cloud.test.js (23 tests):
  - CORS OPTIONS preflight
  - Auth: missing/invalid/old-format/wrong key → 401/400
  - Body validation: bad JSON, missing model, missing input, bad model → 400
  - Happy path: single string, array, delegation, CORS header, machineId override
  - Rate limiting: all-rate-limited → 429 + Retry-After, no credentials → 400
  - Error propagation: non-fallback errors, 429 exhausts accounts

Total: 59/59 tests passing
Framework: vitest v4.0.18, Node v22.22.0
Wire the embeddings handler into Next.js App Router.

- src/app/api/v1/embeddings/route.js: Next.js API route (POST + OPTIONS)
- src/sse/handlers/embeddings.js: SSE-layer handler mirroring chat.js pattern

Uses handleEmbeddingsCore from open-sse/handlers/embeddingsCore.js with
the same auth, credential fallback, and token refresh logic as the chat
handler. Supports REQUIRE_API_KEY env var, provider fallback loop, and
consistent logging.
@decolua
decolua merged commit e1b8361 into decolua:master Feb 18, 2026
kwanLeeFrmVi pushed a commit to kwanLeeFrmVi/9router that referenced this pull request Feb 26, 2026
* feat: implement /v1/embeddings endpoint (decolua#117)

Add OpenAI-compatible POST /v1/embeddings endpoint that routes through
the existing provider credential + fallback infrastructure.

Changes:
- open-sse/handlers/embeddingsCore.js: core handler (handleEmbeddingsCore)
  * Validates input (string or array), encoding_format
  * Builds provider-specific URL and headers for openai, openrouter,
    and openai-compatible providers
  * Handles 401/403 token refresh via executor.refreshCredentials
  * Returns normalized OpenAI-format response { object: 'list', data, model, usage }
- cloud/src/handlers/embeddings.js: cloud Worker handler (handleEmbeddings)
  * Auth + machineId resolution identical to handleChat
  * Provider credential fallback loop with rate-limit tracking
- cloud/src/index.js: wire new routes
  * POST /v1/embeddings  (new format — machineId from API key)
  * POST /{machineId}/v1/embeddings  (old format — machineId from URL)

* test: add unit tests for /v1/embeddings endpoint

- Setup vitest as test framework (tests/ directory)
- embeddingsCore.test.js (36 tests):
  - buildEmbeddingsBody: single string, array, encoding_format, default float
  - buildEmbeddingsUrl: openai, openrouter, openai-compatible-*, unsupported
  - buildEmbeddingsHeaders: per-provider headers, accessToken fallback
  - handleEmbeddingsCore: input validation, success path, provider errors,
    network errors, invalid JSON, token refresh 401 handling
- embeddings.cloud.test.js (23 tests):
  - CORS OPTIONS preflight
  - Auth: missing/invalid/old-format/wrong key → 401/400
  - Body validation: bad JSON, missing model, missing input, bad model → 400
  - Happy path: single string, array, delegation, CORS header, machineId override
  - Rate limiting: all-rate-limited → 429 + Retry-After, no credentials → 400
  - Error propagation: non-fallback errors, 429 exhausts accounts

Total: 59/59 tests passing
Framework: vitest v4.0.18, Node v22.22.0

* feat: add Next.js API route for /v1/embeddings endpoint

Wire the embeddings handler into Next.js App Router.

- src/app/api/v1/embeddings/route.js: Next.js API route (POST + OPTIONS)
- src/sse/handlers/embeddings.js: SSE-layer handler mirroring chat.js pattern

Uses handleEmbeddingsCore from open-sse/handlers/embeddingsCore.js with
the same auth, credential fallback, and token refresh logic as the chat
handler. Supports REQUIRE_API_KEY env var, provider fallback loop, and
consistent logging.
involvex added a commit to involvex/involvex-claude-router that referenced this pull request Feb 28, 2026
### Bug Fixes

* remove duplicated opencode prefix from model IDs and handle it in executor ([180f884](180f884))
* update opencode endpoint and model IDs ([6be8a03](6be8a03))

### Features

* add opencode provider ([03c846e](03c846e))

## [0.2.96](5645d0a...v0.2.96) (2026-02-24)

### Bug Fixes

*  GitHub Copilot model ([95fd950](95fd950))
* **auth:** allow HTTP for local network ([0a394d0](0a394d0))
* **auth:** prevent auto-login after logout ([49df3dc](49df3dc))
* **codex:** use user-agent detection for Droid CLI compatibility ([8c6e3b8](8c6e3b8))
* Correct indentation for clarity in chatCore and claude-to-openai response handlers ([fa06226](fa06226))
* correct token extraction for Claude non-streaming responses ([decolua#131](https://github.com/involvex/involvex-claude-router/issues/131)) ([9fbd6e6](9fbd6e6))
* **dashboard:** resolve 'Provider not found' for free providers ([45a4d3b](45a4d3b))
* **db:** improve error handling and null checks ([e6ef852](e6ef852))
* **gemini:** improve base64 image data parsing ([5645d0a](5645d0a))
* **github:** Implement dynamic fallback for Codex models requiring /responses endpoint ([decolua#127](https://github.com/involvex/involvex-claude-router/issues/127)) ([6913129](6913129))
* improve code formatting and reduce auto-refresh interval ([7f71916](7f71916))
* improve cursor auto-import reliability on macOS ([decolua#161](https://github.com/involvex/involvex-claude-router/issues/161)) ([d7e06c3](d7e06c3))
* **login:** avoid infinite loading on settings fetch failure ([01c9410](01c9410))
* **open-sse:** emit [DONE] in passthrough SSE mode ([decolua#142](https://github.com/involvex/involvex-claude-router/issues/142)) ([b9a6979](b9a6979))
* prevent race conditions in sticky round-robin ([3ad2f8d](3ad2f8d))
* resolve SonarQube findings and Next.js Image warnings ([7058b06](7058b06))
* update Codex executor for gpt-5.3-codex support ([d7d5dc9](d7d5dc9))

### Features

* add /v1/embeddings endpoint (OpenAI-compatible) ([decolua#146](https://github.com/involvex/involvex-claude-router/issues/146)) ([e1b8361](e1b8361)), closes [decolua#117](https://github.com/involvex/involvex-claude-router/issues/117)
* Add Anthropic Compatible provider support ([da5bdef](da5bdef))
* add API endpoint dimension to usage statistics dashboard ([decolua#152](https://github.com/involvex/involvex-claude-router/issues/152)) ([806bd4a](806bd4a))
* add Claude Opus 4.6 to GitHub Copilot provider ([decolua#97](https://github.com/involvex/involvex-claude-router/issues/97)) ([3d60597](3d60597))
* Add Claude Sonnet 4.6 to GitHub Copilot ([decolua#149](https://github.com/involvex/involvex-claude-router/issues/149)) ([4e2a3f8](4e2a3f8))
* add CLI entry points for claude-router commands ([62c632d](62c632d))
* add enable/disable toggle for provider connections ([ed796d2](ed796d2))
* add Gemini 3.1 Pro models to provider ([f2025cc](f2025cc))
* add Gemini embeddings support + Letta compatibility fixes ([a57a8ce](a57a8ce)), closes [decolua#148](decolua#148)
* add GLM 5 and MiniMax M2.5 models to providerModels.js; add Claude Sonnet 4.6 to CLI tools ([e1e5a81](e1e5a81))
* add GLM Coding (China) provider and Usage by API Keys statistics ([1ae4e31](1ae4e31))
* add GPT 4o to GitHub Copilot provider ([decolua#98](https://github.com/involvex/involvex-claude-router/issues/98)) ([c090bb0](c090bb0))
* add GPT 5.3 Codex Spark model to pricing and provider models ([decolua#133](https://github.com/involvex/involvex-claude-router/issues/133)) ([c7d4410](c7d4410))
* Add GPT 5.3 Codex to GitHub Copilot ([decolua#150](https://github.com/involvex/involvex-claude-router/issues/150)) ([c4aa424](c4aa424))
* add GPT-3.5 Turbo to GitHub Copilot provider ([e3dbd44](e3dbd44))
* add GPT-4 to GitHub Copilot provider ([6ade8ef](6ade8ef))
* add GPT-4o mini to GitHub Copilot provider ([053e490](053e490))
* add models management and router process control commands ([c65fd42](c65fd42))
* Add OpenAI-compatible provider nodes ([0a28f9f](0a28f9f))
* add password change functionality and dependencies ([23cfb19](23cfb19))
* add pause/resume functionality for API keys ([decolua#158](https://github.com/involvex/involvex-claude-router/issues/158)) ([73388a0](73388a0))
* add Qwen3.5 Coder Model configuration ([decolua#156](https://github.com/involvex/involvex-claude-router/issues/156)) ([f933dd9](f933dd9))
* add request logging functionality and usage metrics display ([e476907](e476907))
* add round-robin routing strategy ([9ebd7d3](9ebd7d3))
* add sticky round-robin routing strategy ([4f292aa](4f292aa))
* add support for GLM 5 (if) ([decolua#123](https://github.com/involvex/involvex-claude-router/issues/123)) ([03ab554](03ab554))
* add URL-based tab state persistence in usage page ([decolua#129](https://github.com/involvex/involvex-claude-router/issues/129)) ([6caef7f](6caef7f))
* allow custom user data directory via DATA_DIR environment variable ([d83bd86](d83bd86))
* **antigravity:** initial steps for Antigravity anti-ban alignment ([a229d79](a229d79)), closes [decolua#141](decolua#141)
* **antigravity:** integrate Antigravity tool with MITM support and update CLI tools ([2e854bd](2e854bd))
* **auth:** add model-level rate limit locking for multi-bucket providers ([decolua#120](https://github.com/involvex/involvex-claude-router/issues/120)) ([202fee7](202fee7)), closes [decolua#110](https://github.com/involvex/involvex-claude-router/issues/110)
* **auth:** Enhance authentication flow and settings management ([249fc28](249fc28))
* **cli-tools:** update CLI tools and add new models ([a2122e3](a2122e3))
* **cli-tools:** update default local endpoint port to 20128 ([6c41573](6c41573))
* **cli:** add new CLI package with basic scaffolding ([9cf4628](9cf4628))
* **cloud:** harden sync/auth flow, SSE fallback, and update changelog ([3d43983](3d43983))
* **codex:** add GPT 5.3, fix API translation, add thinking levels ([127475d](127475d))
* **codex:** Cursor compatibility + Next.js 16 proxy migration ([1c6dd6d](1c6dd6d))
* **codex:** Cursor compatibility + Next.js 16 proxy migration ([7b864a9](7b864a9))
* **codex:** Cursor compatibility + Next.js 16 proxy migration ([e9b0a73](e9b0a73))
* **config:** add Cloudflare MCP server and account ID configuration ([67297ea](67297ea))
* **cursor:** Add cursor Provider ([0a026c7](0a026c7))
* **cursor:** Integrate Cursor IDE support with OAuth import token flow ([137f315](137f315))
* **docker:** add Docker setup, environment examples, and architecture docs ([5e4a15b](5e4a15b))
* enhance disconnect handling and request tracking in chatCore.js ([decolua#126](https://github.com/involvex/involvex-claude-router/issues/126)) ([3d29b86](3d29b86))
* enhance request handling and error management in chatCore and streamToJsonConverter ([e2db638](e2db638))
* enhance usage stats with sortable columns and improved data handling ([bf6e09b](bf6e09b))
* Enhance usage tracking across response handlers ([a33924b](a33924b))
* **executors:**  Improved UI components for displaying provider limits and usage statistics in the dashboard. ([32aefe5](32aefe5))
* **iflow:** add IFlowExecutor with HMAC-SHA256 signature and enable models ([bd23ab4](bd23ab4))
* **iflow:** add kimi-k2.5 model support ([9e357a7](9e357a7))
* implement API key requirement toggle ([4cf25dc](4cf25dc))
* Implement buffer addition to usage tracking for improved context handling ([7881db8](7881db8))
* implement lazy loading for UsagePage with suspense fallback ([decolua#136](https://github.com/involvex/involvex-claude-router/issues/136)) ([05b09e6](05b09e6))
* implement provider connection reordering on create, update, and delete ([f2abcc6](f2abcc6))
* implement real project ID fetching for Antigravity ([decolua#170](https://github.com/involvex/involvex-claude-router/issues/170)) ([ea67742](ea67742))
* implement request tracking and enhance usage stats display ([e4f92cd](e4f92cd))
* implement usage tracking for AI requests ([9c3d6f4](9c3d6f4))
* Improve Antigravity quota monitoring and fix Droid CLI compatibility ([3c65e0c](3c65e0c))
* **open-sse:** add Claude Sonnet 4.6 ([b057c43](b057c43))
* OpenAI compatibility improvements & build fixes ([d9b8e48](d9b8e48)), closes [decolua#18](https://github.com/involvex/involvex-claude-router/issues/18)
* **provider:** add free providers and enhance error handling ([bdbe816](bdbe816))
* **providers:** add Minimax Coding (China) provider ([7c609d7](7c609d7))
* **providers:** add provider icons to dashboard ([60bd686](60bd686))
* **providers:** auto-validate API keys on save ([b275dfd](b275dfd))
* rename app to involvex-claude-router and add lenient JSON parsing ([bec206d](bec206d))
* **responses:** respect client streaming preference + string input support ([decolua#121](https://github.com/involvex/involvex-claude-router/issues/121)) ([ac7cedd](ac7cedd))
* **translator:** add thinking parameter support in OpenAI → Claude ([54e01d6](54e01d6))
* **ui:** add cost tracking to usage dashboard and pricing settings ([f302c88](f302c88))
* **ui:** add model support for custom providers and improve UX ([a7a52be](a7a52be))
* Update response handling and logging for improved usage tracking ([df0e1d6](df0e1d6))
* **usage:** implement cost tracking backend and pricing configuration ([a36afaa](a36afaa))
idbara pushed a commit to idbara/9router that referenced this pull request Apr 23, 2026
* feat: implement /v1/embeddings endpoint (decolua#117)

Add OpenAI-compatible POST /v1/embeddings endpoint that routes through
the existing provider credential + fallback infrastructure.

Changes:
- open-sse/handlers/embeddingsCore.js: core handler (handleEmbeddingsCore)
  * Validates input (string or array), encoding_format
  * Builds provider-specific URL and headers for openai, openrouter,
    and openai-compatible providers
  * Handles 401/403 token refresh via executor.refreshCredentials
  * Returns normalized OpenAI-format response { object: 'list', data, model, usage }
- cloud/src/handlers/embeddings.js: cloud Worker handler (handleEmbeddings)
  * Auth + machineId resolution identical to handleChat
  * Provider credential fallback loop with rate-limit tracking
- cloud/src/index.js: wire new routes
  * POST /v1/embeddings  (new format — machineId from API key)
  * POST /{machineId}/v1/embeddings  (old format — machineId from URL)

* test: add unit tests for /v1/embeddings endpoint

- Setup vitest as test framework (tests/ directory)
- embeddingsCore.test.js (36 tests):
  - buildEmbeddingsBody: single string, array, encoding_format, default float
  - buildEmbeddingsUrl: openai, openrouter, openai-compatible-*, unsupported
  - buildEmbeddingsHeaders: per-provider headers, accessToken fallback
  - handleEmbeddingsCore: input validation, success path, provider errors,
    network errors, invalid JSON, token refresh 401 handling
- embeddings.cloud.test.js (23 tests):
  - CORS OPTIONS preflight
  - Auth: missing/invalid/old-format/wrong key → 401/400
  - Body validation: bad JSON, missing model, missing input, bad model → 400
  - Happy path: single string, array, delegation, CORS header, machineId override
  - Rate limiting: all-rate-limited → 429 + Retry-After, no credentials → 400
  - Error propagation: non-fallback errors, 429 exhausts accounts

Total: 59/59 tests passing
Framework: vitest v4.0.18, Node v22.22.0

* feat: add Next.js API route for /v1/embeddings endpoint

Wire the embeddings handler into Next.js App Router.

- src/app/api/v1/embeddings/route.js: Next.js API route (POST + OPTIONS)
- src/sse/handlers/embeddings.js: SSE-layer handler mirroring chat.js pattern

Uses handleEmbeddingsCore from open-sse/handlers/embeddingsCore.js with
the same auth, credential fallback, and token refresh logic as the chat
handler. Supports REQUIRE_API_KEY env var, provider fallback loop, and
consistent logging.
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.

Feature Request: Add embeddings endpoint support

2 participants