Compare commits

...

8 Commits

Author SHA1 Message Date
diegosouzapw
252098cbd9 Merge remote-tracking branch 'origin/release/v3.8.50' into HEAD 2026-08-09 00:39:17 -03:00
diegosouzapw
7f09814a2f Merge remote-tracking branch 'origin/release/v3.8.50' into feat/free-tier-providers-wave2-a
# Conflicts:
#	open-sse/config/providers/index.ts
#	open-sse/config/providers/registry/unorouter/index.ts
2026-08-08 11:31:02 -03:00
diegosouzapw
9db12119ea fix: align meganova provider slug 2026-08-02 01:37:52 -03:00
diegosouzapw
f2ba68022a feat: add wave2 free-tier provider registries 2026-08-02 01:31:36 -03:00
diegosouzapw
58eb441307 feat(providers): integrate audited free-tier gateways 2026-08-01 23:27:36 -03:00
diegosouzapw
ddc4c827a7 feat(providers): add LLMGateway and LLM Kiwi registries 2026-08-01 23:13:26 -03:00
diegosouzapw
c7d9e9c4c7 feat(providers): add FastRouter AnyAPI and ElectronHub registries 2026-08-01 23:13:26 -03:00
diegosouzapw
f3c2e30879 feat(providers): add Zylo UnoRouter and Poolside registries 2026-08-01 23:13:25 -03:00
16 changed files with 391 additions and 10 deletions

View File

@@ -225,6 +225,14 @@ import { digitaloceanProvider } from "./registry/digitalocean/index.ts";
import { hcnsecProvider } from "./registry/hcnsec/index.ts";
import { promptqlProvider } from "./registry/promptql/index.ts";
import { hyperagentProvider } from "./registry/hyperagent/index.ts";
import { zyloApiProvider } from "./registry/zylo-api/index.ts";
import { unorouterProvider } from "./registry/unorouter/index.ts";
import { poolsideProvider } from "./registry/poolside/index.ts";
import { fastrouterProvider } from "./registry/fastrouter/index.ts";
import { anyapiProvider } from "./registry/anyapi/index.ts";
import { electronhubProvider } from "./registry/electronhub/index.ts";
import { llmgatewayProvider } from "./registry/llmgateway/index.ts";
import { llmKiwiProvider } from "./registry/llm-kiwi/index.ts";
export const REGISTRY: Record<string, RegistryEntry> = {
aimlapi: aimlapiProvider,
@@ -451,5 +459,12 @@ export const REGISTRY: Record<string, RegistryEntry> = {
hcnsec: hcnsecProvider,
promptql: promptqlProvider,
hyperagent: hyperagentProvider,
"zylo-api": zyloApiProvider,
unorouter: unorouterProvider,
poolside: poolsideProvider,
fastrouter: fastrouterProvider,
anyapi: anyapiProvider,
electronhub: electronhubProvider,
llmgateway: llmgatewayProvider,
"llm-kiwi": llmKiwiProvider,
};

View File

@@ -0,0 +1,11 @@
import type { RegistryEntry } from "../../shared.ts";
import { buildOpenAiCompatibleRegistryEntry } from "../../shared.ts";
export const literouterProvider: RegistryEntry = buildOpenAiCompatibleRegistryEntry({
id: "literouter",
alias: "literouter",
baseUrl: "https://api.literouter.com/v1/chat/completions",
modelsUrl: "https://api.literouter.com/v1/models",
models: [],
passthroughModels: true,
});

View File

@@ -0,0 +1,11 @@
import type { RegistryEntry } from "../../shared.ts";
import { buildOpenAiCompatibleRegistryEntry } from "../../shared.ts";
export const meganovaAiProvider: RegistryEntry = buildOpenAiCompatibleRegistryEntry({
id: "meganova-ai",
alias: "meganova-ai",
baseUrl: "https://api.meganova.ai/v1/chat/completions",
modelsUrl: "https://api.meganova.ai/v1/models",
models: [],
passthroughModels: true,
});

View File

@@ -0,0 +1,11 @@
import type { RegistryEntry } from "../../shared.ts";
import { buildOpenAiCompatibleRegistryEntry } from "../../shared.ts";
export const mnnAiProvider: RegistryEntry = buildOpenAiCompatibleRegistryEntry({
id: "mnn-ai",
alias: "mnn-ai",
baseUrl: "https://api.mnnai.ru/v1/chat/completions",
modelsUrl: "https://api.mnnai.ru/v1/models",
models: [],
passthroughModels: true,
});

View File

@@ -0,0 +1,11 @@
import type { RegistryEntry } from "../../shared.ts";
import { buildOpenAiCompatibleRegistryEntry } from "../../shared.ts";
export const poolsideProvider: RegistryEntry = buildOpenAiCompatibleRegistryEntry({
id: "poolside",
alias: "poolside",
baseUrl: "https://inference.poolside.ai/v1/chat/completions",
modelsUrl: "https://inference.poolside.ai/v1/models",
models: [],
passthroughModels: true,
});

View File

@@ -1,14 +1,11 @@
import type { RegistryEntry } from "../../shared.ts";
import { buildOpenAiCompatibleRegistryEntry } from "../../shared.ts";
export const unorouterProvider: RegistryEntry = {
export const unorouterProvider: RegistryEntry = buildOpenAiCompatibleRegistryEntry({
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)" }],
baseUrl: "https://api.unorouter.com/v1/chat/completions",
modelsUrl: "https://api.unorouter.com/v1/models",
models: [],
passthroughModels: true,
};
});

View File

@@ -0,0 +1,11 @@
import type { RegistryEntry } from "../../shared.ts";
import { buildOpenAiCompatibleRegistryEntry } from "../../shared.ts";
export const zyloApiProvider: RegistryEntry = buildOpenAiCompatibleRegistryEntry({
id: "zylo-api",
alias: "zylo",
baseUrl: "https://api.zyloai.net/v1/chat/completions",
modelsUrl: "https://api.zyloai.net/v1/models",
models: [],
passthroughModels: true,
});

View File

@@ -7,6 +7,14 @@ export const PROVIDER_ENDPOINTS = {
dgrid: "https://api.dgrid.ai/v1/chat/completions",
bai: "https://api.b.ai/v1/chat/completions",
qiniu: "https://api.qnaigc.com/v1/chat/completions",
"zylo-api": "https://api.zyloai.net/v1/chat/completions",
unorouter: "https://api.unorouter.com/v1/chat/completions",
poolside: "https://inference.poolside.ai/v1/chat/completions",
fastrouter: "https://api.fastrouter.ai/api/v1/chat/completions",
anyapi: "https://api.anyapi.ai/v1/chat/completions",
electronhub: "https://api.electronhub.ai/v1/chat/completions",
llmgateway: "https://api.llmgateway.io/v1/chat/completions",
"llm-kiwi": "https://api.llm.kiwi/v1/chat/completions",
glm: "https://api.z.ai/api/anthropic/v1/messages",
glmt: "https://api.z.ai/api/anthropic/v1/messages",
"bailian-coding-plan": "https://coding-intl.dashscope.aliyuncs.com/apps/anthropic/v1/messages",

View File

@@ -21,7 +21,10 @@ import { PROVIDER_MODELS as MODELS } from "@omniroute/open-sse/config/providerMo
const PASSTHROUGH_PROVIDERS = new Set(
Object.entries(AI_PROVIDERS)
.filter(([, p]) => (p as any).passthroughModels)
.map(([key]) => key)
.flatMap(([key, provider]) => {
const alias = (provider as { alias?: unknown }).alias;
return typeof alias === "string" && alias.length > 0 ? [key, alias] : [key];
})
);
// Wrap isValidModel with passthrough providers

View File

@@ -92,6 +92,13 @@ export const AGGREGATOR_PROVIDER_IDS = new Set([
"g4f-pollinations",
"g4f-ollama",
"g4f-nvidia",
"zylo-api",
"unorouter",
"fastrouter",
"anyapi",
"electronhub",
"llmgateway",
"llm-kiwi",
]);
export const ENTERPRISE_CLOUD_PROVIDER_IDS = new Set([

View File

@@ -95,6 +95,111 @@ export const APIKEY_PROVIDERS_GATEWAYS = {
"Create an API key at https://app.requesty.ai, then paste it here as a Bearer token. " +
"OpenAI-compatible endpoint at https://router.requesty.ai/v1, with a live /v1/models catalog.",
},
"zylo-api": {
id: "zylo-api",
alias: "zylo",
name: "Zylo API",
icon: "hub",
color: "#2563EB",
textIcon: "ZY",
passthroughModels: true,
website: "https://zyloai.net",
hasFree: true,
freeNote:
"Basic plan: 10 RPM, 7,200 requests/day and 200,000 tokens/day; limited to Basic text models.",
apiHint:
"Create a free Zylo API key at https://zyloai.net, then use https://api.zyloai.net/v1 as the OpenAI-compatible base URL.",
},
unorouter: {
id: "unorouter",
alias: "unorouter",
name: "UnoRouter",
icon: "router",
color: "#7C3AED",
textIcon: "UR",
passthroughModels: true,
website: "https://unorouter.com",
hasFree: true,
freeNote:
"Models with the :free suffix do not debit balance; limit is 1 request/minute per free model per user.",
apiHint:
"Create an UnoRouter token, then use https://api.unorouter.com/v1 as the OpenAI-compatible base URL.",
},
fastrouter: {
id: "fastrouter",
alias: "fastrouter",
name: "FastRouter",
icon: "speed",
color: "#F97316",
textIcon: "FR",
passthroughModels: true,
website: "https://fastrouter.ai",
hasFree: true,
freeNote:
"Models with the :free suffix allow 10 requests/day per organization and model; availability may change.",
apiHint:
"Create a FastRouter API key, then use https://api.fastrouter.ai/api/v1 as the OpenAI-compatible base URL.",
},
anyapi: {
id: "anyapi",
alias: "anyapi",
name: "AnyAPI AI",
icon: "hub",
color: "#0EA5E9",
textIcon: "AA",
passthroughModels: true,
website: "https://anyapi.ai",
hasFree: true,
freeNote:
"Free plan: 100,000 ANY Tokens/day and 100 RPM for eligible Free/Basic models; no credit card required.",
apiHint:
"Create and verify an AnyAPI account, then use https://api.anyapi.ai/v1 as the OpenAI-compatible base URL.",
},
electronhub: {
id: "electronhub",
alias: "electronhub",
name: "Electron Hub",
icon: "hub",
color: "#22C55E",
textIcon: "EH",
passthroughModels: true,
website: "https://www.electronhub.ai",
hasFree: true,
freeNote:
"Free plan: 5 RPM, $0.25 weekly credits and 10 Neutrinos/day for :free models; family budgets also apply.",
apiHint:
"Create a free API key at https://app.electronhub.ai, then use https://api.electronhub.ai/v1 as the OpenAI-compatible base URL.",
},
llmgateway: {
id: "llmgateway",
alias: "llmgateway",
name: "LLM Gateway",
icon: "router",
color: "#6366F1",
textIcon: "LG",
passthroughModels: true,
website: "https://llmgateway.io",
hasFree: true,
freeNote:
"Hosted Free plan: free-priced models are limited to 5 requests per 10 minutes when the account has no credits.",
apiHint:
"Create an LLM Gateway API key, then use https://api.llmgateway.io/v1 as the OpenAI-compatible base URL.",
},
"llm-kiwi": {
id: "llm-kiwi",
alias: "llmkiwi",
name: "LLM.Kiwi",
icon: "hub",
color: "#84CC16",
textIcon: "LK",
passthroughModels: true,
website: "https://llm.kiwi",
hasFree: true,
freeNote:
"Free plan exposes auto and hrLLM; the published 40 requests/hour limit applies to hrLLM.",
apiHint:
"Create a free LLM.Kiwi key, then use https://api.llm.kiwi/v1 as the OpenAI-compatible base URL.",
},
dgrid: {
id: "dgrid",
alias: "dgrid",

View File

@@ -32,6 +32,21 @@ export const APIKEY_PROVIDERS_INFERENCE = {
freeNote:
"Free credits on signup for OpenAI-compatible inference across LLMs, embeddings, and reasoning models",
},
poolside: {
id: "poolside",
alias: "poolside",
name: "Poolside",
icon: "memory",
color: "#111827",
textIcon: "PS",
passthroughModels: true,
website: "https://poolside.ai",
hasFree: true,
freeNote:
"Laguna S 2.1 and XS 2.1 are free during Preview; no public numeric quota is published.",
apiHint:
"Create a free developer API key, then use https://inference.poolside.ai/v1 as the OpenAI-compatible base URL.",
},
fireworks: {
id: "fireworks",
alias: "fireworks",

View File

@@ -0,0 +1,55 @@
import assert from "node:assert/strict";
import test from "node:test";
const { REGISTRY } = await import("../../open-sse/config/providerRegistry.ts");
const { DefaultExecutor, getExecutor } = await import("../../open-sse/executors/index.ts");
const { PROVIDER_ENDPOINTS } = await import("../../src/shared/constants/config.ts");
const { isValidModel } = await import("../../src/shared/constants/models.ts");
const { AGGREGATOR_PROVIDER_IDS } = await import("../../src/shared/constants/providers.ts");
const { APIKEY_PROVIDERS } = await import("../../src/shared/constants/providers/apikey/index.ts");
const providers = [
["zylo-api", "zylo", "https://api.zyloai.net/v1/chat/completions"],
["unorouter", "unorouter", "https://api.unorouter.com/v1/chat/completions"],
["poolside", "poolside", "https://inference.poolside.ai/v1/chat/completions"],
["fastrouter", "fastrouter", "https://api.fastrouter.ai/api/v1/chat/completions"],
["anyapi", "anyapi", "https://api.anyapi.ai/v1/chat/completions"],
["electronhub", "electronhub", "https://api.electronhub.ai/v1/chat/completions"],
["llmgateway", "llmgateway", "https://api.llmgateway.io/v1/chat/completions"],
["llm-kiwi", "llmkiwi", "https://api.llm.kiwi/v1/chat/completions"],
] as const;
for (const [id, alias, endpoint] of providers) {
test(`${id} is wired through registry, metadata, endpoint and default executor`, () => {
const registry = REGISTRY[id];
const metadata = APIKEY_PROVIDERS[id];
assert.ok(registry);
assert.ok(metadata);
assert.equal(registry.id, id);
assert.equal(registry.alias, alias);
assert.equal(registry.baseUrl, endpoint);
assert.equal(PROVIDER_ENDPOINTS[id], endpoint);
assert.equal(metadata.id, id);
assert.equal(metadata.alias, alias);
assert.equal(metadata.hasFree, true);
assert.equal(metadata.passthroughModels, true);
assert.ok(typeof metadata.freeNote === "string" && metadata.freeNote.length > 0);
assert.ok(getExecutor(id) instanceof DefaultExecutor);
assert.equal(isValidModel(id, "future/live-catalog-model"), true);
assert.equal(isValidModel(alias, "future/live-catalog-model"), true);
});
}
test("gateway providers are classified as aggregators while direct Poolside is not", () => {
const gatewayIds = providers.map(([id]) => id).filter((id) => id !== "poolside");
for (const id of gatewayIds) assert.equal(AGGREGATOR_PROVIDER_IDS.has(id), true);
assert.equal(AGGREGATOR_PROVIDER_IDS.has("poolside"), false);
});
test("LLM.Kiwi statically exposes only the confirmed Free plan models", () => {
assert.deepEqual(REGISTRY["llm-kiwi"].models, [
{ id: "auto", name: "Auto" },
{ id: "hrLLM", name: "hrLLM" },
]);
});

View File

@@ -0,0 +1,55 @@
import assert from "node:assert/strict";
import test from "node:test";
import { poolsideProvider } from "../../open-sse/config/providers/registry/poolside/index.ts";
import { unorouterProvider } from "../../open-sse/config/providers/registry/unorouter/index.ts";
import { zyloApiProvider } from "../../open-sse/config/providers/registry/zylo-api/index.ts";
import type { RegistryEntry } from "../../open-sse/config/providers/shared.ts";
const providers: Array<{
entry: RegistryEntry;
id: string;
alias: string;
chatUrl: string;
modelsUrl: string;
}> = [
{
entry: zyloApiProvider,
id: "zylo-api",
alias: "zylo",
chatUrl: "https://api.zyloai.net/v1/chat/completions",
modelsUrl: "https://api.zyloai.net/v1/models",
},
{
entry: unorouterProvider,
id: "unorouter",
alias: "unorouter",
chatUrl: "https://api.unorouter.com/v1/chat/completions",
modelsUrl: "https://api.unorouter.com/v1/models",
},
{
entry: poolsideProvider,
id: "poolside",
alias: "poolside",
chatUrl: "https://inference.poolside.ai/v1/chat/completions",
modelsUrl: "https://inference.poolside.ai/v1/models",
},
];
for (const { entry, id, alias, chatUrl, modelsUrl } of providers) {
test(`${id} uses the standard OpenAI-compatible API-key registry shape`, () => {
assert.equal(entry.id, id);
assert.equal(entry.alias, alias);
assert.equal(entry.format, "openai");
assert.equal(entry.executor, "default");
assert.equal(entry.authType, "apikey");
assert.equal(entry.authHeader, "bearer");
assert.equal(entry.baseUrl, chatUrl);
assert.equal(entry.modelsUrl, modelsUrl);
assert.equal(entry.passthroughModels, true);
});
test(`${id} relies on its live catalog without invented static model ids`, () => {
assert.deepEqual(entry.models, []);
});
}

View File

@@ -0,0 +1,51 @@
import assert from "node:assert/strict";
import test from "node:test";
import { literouterProvider } from "../../open-sse/config/providers/registry/literouter/index.ts";
import { meganovaAiProvider } from "../../open-sse/config/providers/registry/meganova-ai/index.ts";
import { mnnAiProvider } from "../../open-sse/config/providers/registry/mnn-ai/index.ts";
import type { RegistryEntry } from "../../open-sse/config/providers/shared.ts";
const providers: Array<{
entry: RegistryEntry;
id: string;
chatUrl: string;
modelsUrl: string;
}> = [
{
entry: literouterProvider,
id: "literouter",
chatUrl: "https://api.literouter.com/v1/chat/completions",
modelsUrl: "https://api.literouter.com/v1/models",
},
{
entry: mnnAiProvider,
id: "mnn-ai",
chatUrl: "https://api.mnnai.ru/v1/chat/completions",
modelsUrl: "https://api.mnnai.ru/v1/models",
},
{
entry: meganovaAiProvider,
id: "meganova-ai",
chatUrl: "https://api.meganova.ai/v1/chat/completions",
modelsUrl: "https://api.meganova.ai/v1/models",
},
];
for (const { entry, id, chatUrl, modelsUrl } of providers) {
test(`${id} uses an OpenAI-compatible Bearer registry entry`, () => {
assert.equal(entry.id, id);
assert.equal(entry.alias, id);
assert.equal(entry.format, "openai");
assert.equal(entry.executor, "default");
assert.equal(entry.authType, "apikey");
assert.equal(entry.authHeader, "bearer");
assert.equal(entry.baseUrl, chatUrl);
assert.equal(entry.modelsUrl, modelsUrl);
assert.equal(entry.passthroughModels, true);
});
test(`${id} leaves model discovery to the live upstream catalog`, () => {
assert.deepEqual(entry.models, []);
});
}

View File

@@ -0,0 +1,15 @@
import assert from "node:assert/strict";
import test from "node:test";
const { isValidModel } = await import("../../src/shared/constants/models.ts");
test("passthrough providers accept live model ids through both provider id and alias", () => {
for (const [id, alias] of [
["zylo-api", "zylo"],
["llm-kiwi", "llmkiwi"],
["freetheai", "fta"],
] as const) {
assert.equal(isValidModel(id, "future/live-catalog-model"), true);
assert.equal(isValidModel(alias, "future/live-catalog-model"), true);
}
});