mirror of
https://github.com/diegosouzapw/OmniRoute.git
synced 2026-08-06 07:12:12 +03:00
Validated in local merge-train (devbox-vm-06-dev002) @ combined-tip (FAST gates green: static + changed tests + vitest — only pre-existing audit.test.ts flake). Evidence: /home/diegosouzapw/dev/proxys/OmniRoute/.claude/worktrees/merge-train-20260805-213228-suite.log
This commit is contained in:
1
changelog.d/features/8978-unorouter.md
Normal file
1
changelog.d/features/8978-unorouter.md
Normal file
@@ -0,0 +1 @@
|
||||
- **feat(providers): add UnoRouter provider** — UnoRouter is an OpenAI-compatible routing gateway supporting hundreds of models. It is now registered as an API-key provider. ([#8978](https://github.com/diegosouzapw/OmniRoute/issues/8978))
|
||||
@@ -1,4 +1,5 @@
|
||||
import type { RegistryEntry } from "./shared.ts";
|
||||
import { unorouterProvider } from "./registry/unorouter/index.ts";
|
||||
|
||||
import { aimlapiProvider } from "./registry/aimlapi/index.ts";
|
||||
import { byteplusProvider } from "./registry/byteplus/index.ts";
|
||||
@@ -444,4 +445,5 @@ export const REGISTRY: Record<string, RegistryEntry> = {
|
||||
hcnsec: hcnsecProvider,
|
||||
promptql: promptqlProvider,
|
||||
hyperagent: hyperagentProvider,
|
||||
unorouter: unorouterProvider,
|
||||
};
|
||||
|
||||
14
open-sse/config/providers/registry/unorouter/index.ts
Normal file
14
open-sse/config/providers/registry/unorouter/index.ts
Normal file
@@ -0,0 +1,14 @@
|
||||
import type { RegistryEntry } from "../../shared.ts";
|
||||
|
||||
export const unorouterProvider: RegistryEntry = {
|
||||
id: "unorouter",
|
||||
alias: "unorouter",
|
||||
format: "openai",
|
||||
executor: "default",
|
||||
baseUrl: "https://api.unorouter.ai/v1/chat/completions",
|
||||
authType: "apikey",
|
||||
authHeader: "bearer",
|
||||
defaultContextLength: 128000,
|
||||
models: [{ id: "auto", name: "Auto (Best Available)" }],
|
||||
passthroughModels: true,
|
||||
};
|
||||
1
public/providers/unorouter.svg
Normal file
1
public/providers/unorouter.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><title>UnoRouter</title><circle cx="12" cy="12" r="12" fill="#8B5CF6"/><text x="12" y="16" text-anchor="middle" fill="white" font-size="14" font-weight="bold" font-family="system-ui, sans-serif">U</text></svg>
|
||||
|
After Width: | Height: | Size: 270 B |
@@ -42,6 +42,7 @@ export const NAMED_OPENAI_STYLE_PROVIDERS = new Set([
|
||||
"ovhcloud",
|
||||
"sambanova",
|
||||
"orcarouter",
|
||||
"unorouter",
|
||||
"uncloseai",
|
||||
"opencode-go",
|
||||
"baseten",
|
||||
|
||||
@@ -5769,6 +5769,7 @@
|
||||
"onboardingProviderDescriptions": {
|
||||
"360ai": "Get API key at ai.360.cn",
|
||||
"agentrouter": "Get $200 free credits at https://agentrouter.org/register — no credit card required.",
|
||||
"unorouter": "Create an API key at https://unorouter.ai, then paste it here as a Bearer token.",
|
||||
"agnes": "Get API key at agnes-ai.com",
|
||||
"aimlapi": "Free tier paused (2026) — AI/ML API is now pay-as-you-go only (min $20 top-up); no recurring free credits.",
|
||||
"ai21": "$10 trial credits on signup (valid 3 months), no credit card required",
|
||||
|
||||
@@ -216,6 +216,7 @@ const KNOWN_SVGS = new Set([
|
||||
"trae",
|
||||
"udio",
|
||||
"uncloseai",
|
||||
"unorouter",
|
||||
"upstage",
|
||||
"v0",
|
||||
"veoaifree-web",
|
||||
|
||||
@@ -74,6 +74,7 @@ export const AGGREGATOR_PROVIDER_IDS = new Set([
|
||||
"getgoapi",
|
||||
"laozhang",
|
||||
"vercel-ai-gateway",
|
||||
"unorouter",
|
||||
"agentrouter",
|
||||
"thebai",
|
||||
"fenayai",
|
||||
|
||||
@@ -45,6 +45,17 @@ export const APIKEY_PROVIDERS_GATEWAYS = {
|
||||
freeNote: "$200 free credits on signup - multi-model routing gateway",
|
||||
apiHint: "Get $200 free credits at https://agentrouter.org/register — no credit card required.",
|
||||
},
|
||||
unorouter: {
|
||||
id: "unorouter",
|
||||
alias: "unorouter",
|
||||
name: "UnoRouter",
|
||||
icon: "unorouter",
|
||||
color: "#8B5CF6",
|
||||
textIcon: "UR",
|
||||
passthroughModels: true,
|
||||
website: "https://unorouter.ai",
|
||||
apiHint: "Create an API key at https://unorouter.ai, then paste it here as a Bearer token.",
|
||||
},
|
||||
"command-code": {
|
||||
id: "command-code",
|
||||
alias: "cmd",
|
||||
@@ -295,8 +306,7 @@ export const APIKEY_PROVIDERS_GATEWAYS = {
|
||||
textIcon: "G4F",
|
||||
website: "https://g4f.space",
|
||||
hasFree: true,
|
||||
freeNote:
|
||||
"Free no-key reverse proxy to Groq (gpt4free project) — rate-limited to 5 req/min.",
|
||||
freeNote: "Free no-key reverse proxy to Groq (gpt4free project) — rate-limited to 5 req/min.",
|
||||
passthroughModels: true,
|
||||
authHint:
|
||||
"No auth required. Free tier is limited to 5 requests/minute — sign up at g4f.dev/members.html for higher limits.",
|
||||
@@ -310,8 +320,7 @@ export const APIKEY_PROVIDERS_GATEWAYS = {
|
||||
textIcon: "G4F",
|
||||
website: "https://g4f.space",
|
||||
hasFree: true,
|
||||
freeNote:
|
||||
"Free no-key reverse proxy to Gemini (gpt4free project) — rate-limited to 5 req/min.",
|
||||
freeNote: "Free no-key reverse proxy to Gemini (gpt4free project) — rate-limited to 5 req/min.",
|
||||
passthroughModels: true,
|
||||
authHint:
|
||||
"No auth required. Free tier is limited to 5 requests/minute — sign up at g4f.dev/members.html for higher limits.",
|
||||
@@ -340,8 +349,7 @@ export const APIKEY_PROVIDERS_GATEWAYS = {
|
||||
textIcon: "G4F",
|
||||
website: "https://g4f.space",
|
||||
hasFree: true,
|
||||
freeNote:
|
||||
"Free no-key hosted Ollama gateway (gpt4free project) — rate-limited to 5 req/min.",
|
||||
freeNote: "Free no-key hosted Ollama gateway (gpt4free project) — rate-limited to 5 req/min.",
|
||||
passthroughModels: true,
|
||||
authHint:
|
||||
"No auth required. Free tier is limited to 5 requests/minute — sign up at g4f.dev/members.html for higher limits.",
|
||||
@@ -759,7 +767,8 @@ export const APIKEY_PROVIDERS_GATEWAYS = {
|
||||
website: "https://ainative.studio",
|
||||
hasFree: true,
|
||||
freeNote: "Free tier ~10M tokens/month (claimed) across Qwen3, Llama 4, DeepSeek R1 and more.",
|
||||
authHint: "Create a free API key at ainative.studio (no card), then paste it here as a Bearer token.",
|
||||
authHint:
|
||||
"Create a free API key at ainative.studio (no card), then paste it here as a Bearer token.",
|
||||
apiHint:
|
||||
"OpenAI-compatible endpoint at https://api.ainative.studio/api/v1 with a public /models catalog (84 models). OmniRoute lists models via passthrough.",
|
||||
},
|
||||
@@ -774,7 +783,8 @@ export const APIKEY_PROVIDERS_GATEWAYS = {
|
||||
website: "https://www.aionlabs.ai",
|
||||
hasFree: true,
|
||||
freeNote: "Free tier ~20k tokens/day across the Aion reasoning models.",
|
||||
authHint: "Create a free API key at aionlabs.ai (no card), then paste it here as a Bearer token.",
|
||||
authHint:
|
||||
"Create a free API key at aionlabs.ai (no card), then paste it here as a Bearer token.",
|
||||
apiHint:
|
||||
"OpenAI-compatible endpoint at https://api.aionlabs.ai/v1 with a public /models catalog carrying context and pricing.",
|
||||
},
|
||||
@@ -788,7 +798,8 @@ export const APIKEY_PROVIDERS_GATEWAYS = {
|
||||
passthroughModels: true,
|
||||
website: "https://routeway.ai",
|
||||
hasFree: true,
|
||||
freeNote: "Free models (:free suffix) at ~5 RPM / 200 RPD across Llama, Nemotron, Step and Laguna.",
|
||||
freeNote:
|
||||
"Free models (:free suffix) at ~5 RPM / 200 RPD across Llama, Nemotron, Step and Laguna.",
|
||||
authHint: "Create a free API key at routeway.ai, then paste it here as a Bearer token.",
|
||||
apiHint:
|
||||
"OpenAI-compatible endpoint at https://api.routeway.ai/v1 with a public /models catalog (236 models). Cloudflare fronts the API and requires a browser-style User-Agent.",
|
||||
@@ -804,7 +815,8 @@ export const APIKEY_PROVIDERS_GATEWAYS = {
|
||||
website: "https://bynara.id",
|
||||
hasFree: true,
|
||||
freeNote: "Free tier is a shared 5M tokens/day pool; some models are gated behind credit/plan.",
|
||||
authHint: "Get a free API key via NaraRouter's Telegram channel, then paste it here as a Bearer token.",
|
||||
authHint:
|
||||
"Get a free API key via NaraRouter's Telegram channel, then paste it here as a Bearer token.",
|
||||
apiHint:
|
||||
"OpenAI-compatible endpoint at https://router.bynara.id/v1. Free-tier models are pinned; others need credit.",
|
||||
},
|
||||
|
||||
42
tests/unit/unorouter-registry.test.ts
Normal file
42
tests/unit/unorouter-registry.test.ts
Normal file
@@ -0,0 +1,42 @@
|
||||
import test from "node:test";
|
||||
import assert from "node:assert/strict";
|
||||
|
||||
const { APIKEY_PROVIDERS } = await import("../../src/shared/constants/providers.ts");
|
||||
const { REGISTRY: providerRegistry } = await import("../../open-sse/config/providerRegistry.ts");
|
||||
const { isValidModel } = await import("../../src/shared/constants/models.ts");
|
||||
|
||||
const UNOROUTER_CHAT_URL = "https://api.unorouter.ai/v1/chat/completions";
|
||||
|
||||
test("unorouter is registered as an API-key gateway provider", () => {
|
||||
const entry = APIKEY_PROVIDERS.unorouter;
|
||||
assert.ok(entry, "APIKEY_PROVIDERS.unorouter must be defined");
|
||||
assert.equal(entry.id, "unorouter");
|
||||
assert.equal(entry.alias, "unorouter");
|
||||
assert.equal(entry.name, "UnoRouter");
|
||||
assert.equal(entry.website, "https://unorouter.ai");
|
||||
assert.equal(entry.passthroughModels, true);
|
||||
});
|
||||
|
||||
test("unorouter registry entry uses OpenAI format with bearer API-key auth", () => {
|
||||
const entry = providerRegistry.unorouter;
|
||||
assert.ok(entry, "providerRegistry.unorouter must be defined");
|
||||
assert.equal(entry.id, "unorouter");
|
||||
assert.equal(entry.alias, "unorouter");
|
||||
assert.equal(entry.format, "openai");
|
||||
assert.equal(entry.executor, "default");
|
||||
assert.equal(entry.authType, "apikey");
|
||||
assert.equal(entry.authHeader, "bearer");
|
||||
assert.equal(entry.baseUrl, UNOROUTER_CHAT_URL);
|
||||
assert.equal(entry.passthroughModels, true);
|
||||
});
|
||||
|
||||
test("unorouter ships with auto model", () => {
|
||||
assert.deepEqual(providerRegistry.unorouter.models, [
|
||||
{ id: "auto", name: "Auto (Best Available)" },
|
||||
]);
|
||||
});
|
||||
|
||||
test("unorouter accepts any model id via passthrough", () => {
|
||||
assert.equal(isValidModel("unorouter", "openai/gpt-4o"), true);
|
||||
assert.equal(isValidModel("unorouter", "anthropic/claude-3-5-sonnet"), true);
|
||||
});
|
||||
Reference in New Issue
Block a user