mirror of
https://github.com/diegosouzapw/OmniRoute.git
synced 2026-07-26 09:52:11 +03:00
This commit is contained in:
18
CHANGELOG.md
18
CHANGELOG.md
@@ -4,6 +4,24 @@
|
||||
|
||||
---
|
||||
|
||||
## [2.8.7] — 2026-03-20
|
||||
|
||||
> Sprint: Merge PR #495 (Bottleneck 429 drop), fix #496 (custom embedding providers), triage features.
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
- **Bottleneck 429 infinite wait** (PR #495 by @xandr0s): On 429, `limiter.stop({ dropWaitingJobs: true })` immediately fails all queued requests so upstream callers can trigger fallback. Limiter is deleted from Map so next request creates a fresh instance.
|
||||
- **Custom embedding models unresolvable** (#496): `POST /v1/embeddings` now resolves custom embedding models from ALL provider_nodes (not just localhost). Enables models like `google/gemini-embedding-001` added via dashboard.
|
||||
|
||||
### Issues Responded
|
||||
|
||||
- **#452** — Per-API-key request-count limits (acknowledged, on roadmap)
|
||||
- **#464** — Auto-issue API keys with provider/account limits (needs more detail)
|
||||
- **#488** — Auto-update model lists (acknowledged, on roadmap)
|
||||
- **#496** — Custom embedding provider resolution (fixed)
|
||||
|
||||
---
|
||||
|
||||
## [2.8.6] — 2026-03-20
|
||||
|
||||
> Sprint: Merge PR #494 (MiniMax role fix), fix KIRO MITM dashboard, triage 8 issues.
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
openapi: 3.1.0
|
||||
info:
|
||||
title: OmniRoute API
|
||||
version: 2.8.6
|
||||
version: 2.8.7
|
||||
description: |
|
||||
OmniRoute is a local-first AI API proxy router. It provides an OpenAI-compatible
|
||||
endpoint that routes requests to multiple AI providers with load balancing,
|
||||
|
||||
4
package-lock.json
generated
4
package-lock.json
generated
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "omniroute",
|
||||
"version": "2.8.6",
|
||||
"version": "2.8.7",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "omniroute",
|
||||
"version": "2.8.6",
|
||||
"version": "2.8.7",
|
||||
"hasInstallScript": true,
|
||||
"license": "MIT",
|
||||
"workspaces": [
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "omniroute",
|
||||
"version": "2.8.6",
|
||||
"version": "2.8.7",
|
||||
"description": "Smart AI Router with auto fallback — route to FREE & cheap models, zero downtime. Works with Cursor, Cline, Claude Desktop, Codex, and any OpenAI-compatible tool.",
|
||||
"type": "module",
|
||||
"bin": {
|
||||
|
||||
@@ -12,6 +12,7 @@ import {
|
||||
getEmbeddingProvider,
|
||||
buildDynamicEmbeddingProvider,
|
||||
type EmbeddingProviderNodeRow,
|
||||
type EmbeddingProvider,
|
||||
} from "@omniroute/open-sse/config/embeddingRegistry.ts";
|
||||
import { errorResponse } from "@omniroute/open-sse/utils/error.ts";
|
||||
import { HTTP_STATUS } from "@omniroute/open-sse/config/constants.ts";
|
||||
@@ -116,9 +117,9 @@ export async function POST(request) {
|
||||
// Load local provider_nodes for embedding routing (only localhost — prevents auth bypass/SSRF)
|
||||
let dynamicProviders: ReturnType<typeof buildDynamicEmbeddingProvider>[] = [];
|
||||
try {
|
||||
const nodes = await getProviderNodes();
|
||||
const nodes = (await getProviderNodes()) as unknown as EmbeddingProviderNodeRow[];
|
||||
dynamicProviders = (Array.isArray(nodes) ? nodes : [])
|
||||
.filter((n: EmbeddingProviderNodeRow) => {
|
||||
.filter((n) => {
|
||||
// provider_nodes apiType is "chat" or "responses" (not "embeddings") — local OpenAI-compatible
|
||||
// backends expose /embeddings under the same base URL as chat, so we build the URL as baseUrl + /embeddings.
|
||||
if (n.apiType !== "chat" && n.apiType !== "responses") return false;
|
||||
@@ -157,9 +158,38 @@ export async function POST(request) {
|
||||
}
|
||||
|
||||
// Resolve provider config — dynamic first (local override), then hardcoded
|
||||
const providerConfig =
|
||||
let providerConfig: EmbeddingProvider | null =
|
||||
dynamicProviders.find((dp) => dp.id === provider) || getEmbeddingProvider(provider) || null;
|
||||
|
||||
// #496: Fallback — resolve from ALL provider_nodes (not just localhost)
|
||||
// This enables custom embedding models (e.g. google/gemini-embedding-001) whose
|
||||
// providers have remote baseUrls. Safe because getProviderCredentials() authenticates.
|
||||
if (!providerConfig) {
|
||||
try {
|
||||
const allNodes = (await getProviderNodes()) as unknown as EmbeddingProviderNodeRow[];
|
||||
const matchingNode = (Array.isArray(allNodes) ? allNodes : []).find(
|
||||
(n) =>
|
||||
n.prefix === provider && (n.apiType === "chat" || n.apiType === "responses") && n.baseUrl
|
||||
);
|
||||
if (matchingNode) {
|
||||
const baseUrl = String(matchingNode.baseUrl).replace(/\/+$/, "");
|
||||
providerConfig = {
|
||||
id: matchingNode.prefix,
|
||||
baseUrl: `${baseUrl}/embeddings`,
|
||||
authType: "apikey",
|
||||
authHeader: "bearer",
|
||||
models: [],
|
||||
};
|
||||
log.info(
|
||||
"EMBED",
|
||||
`Resolved custom embedding provider: ${provider} → ${providerConfig.baseUrl}`
|
||||
);
|
||||
}
|
||||
} catch (err) {
|
||||
log.error("EMBED", `Failed to resolve custom embedding provider ${provider}: ${err}`);
|
||||
}
|
||||
}
|
||||
|
||||
if (!providerConfig) {
|
||||
return errorResponse(
|
||||
HTTP_STATUS.BAD_REQUEST,
|
||||
|
||||
Reference in New Issue
Block a user