mirror of
https://github.com/diegosouzapw/OmniRoute.git
synced 2026-07-31 04:12:10 +03:00
feat(providers): add 5 free-tier providers (ainative, aion, sealion, routeway, nara) (#7887)
Five OpenAI-compatible free-tier aggregators OmniRoute did not cover yet, added
as a registry entry plus a canonical provider each.
ainative api.ainative.studio/api/v1 — 84-model public catalog, passthrough
aion api.aionlabs.ai/v1 — 5 models, public catalog w/ pricing
sealion api.sea-lion.ai/v1 — AI Singapore, pinned models (10 RPM)
routeway api.routeway.ai/v1 — 236-model catalog; browser UA pinned
because Cloudflare 1010s non-browser UAs
nara router.bynara.id/v1 — shared 5M/day pool, pinned free models
All five /models endpoints were probed live 2026-07-20 (200 for the public ones;
sealion/nara 401 without a key, as expected) and every pinned model id was
confirmed to exist upstream.
Free-catalog honesty: ainative ("~10M tok/mo claimed"), aion (20k tok/day),
sealion (10 RPM) and routeway (200 RPD) have no verifiable monthly TOKEN quota,
so they are recurring-uncapped — real access, never summed into the headline.
Only nara publishes a token figure (5M tokens/day shared = 150M/month), recorded
as one deduped pool. Net: 484 -> 514 models, 1.376B -> 1.526B tokens, the +150M
coming solely from nara.
This commit is contained in:
committed by
GitHub
parent
f909b1d45e
commit
11b5ce1f0e
@@ -499,4 +499,34 @@ export const FREE_MODEL_BUDGETS: FreeModelBudget[] = [
|
||||
{ provider: "aihorde", modelId: "aphrodite/TheDrummer/Cydonia-24B-v4.3", displayName: "Cydonia 24B (AI Horde)", monthlyTokens: 0, creditTokens: 0, freeType: "keyless", poolKey: "aihorde-anon", tos: "ok" },
|
||||
{ provider: "aihorde", modelId: "aphrodite/TheDrummer/Skyfall-31B-v4.2", displayName: "Skyfall 31B (AI Horde)", monthlyTokens: 0, creditTokens: 0, freeType: "keyless", poolKey: "aihorde-anon", tos: "ok" },
|
||||
{ provider: "aihorde", modelId: "google/gemma-4-31b", displayName: "Gemma 4 31B (AI Horde)", monthlyTokens: 0, creditTokens: 0, freeType: "keyless", poolKey: "aihorde-anon", tos: "ok" },
|
||||
{ provider: "ainative", modelId: "qwen3-235b-cerebras", displayName: "Qwen3 235B (Cerebras)", monthlyTokens: 0, creditTokens: 0, freeType: "recurring-uncapped", poolKey: "ainative-free", tos: "caution" },
|
||||
{ provider: "ainative", modelId: "qwen3-32b", displayName: "Qwen3 32B", monthlyTokens: 0, creditTokens: 0, freeType: "recurring-uncapped", poolKey: "ainative-free", tos: "caution" },
|
||||
{ provider: "ainative", modelId: "qwen3-14b", displayName: "Qwen3 14B", monthlyTokens: 0, creditTokens: 0, freeType: "recurring-uncapped", poolKey: "ainative-free", tos: "caution" },
|
||||
{ provider: "ainative", modelId: "qwen3-8b", displayName: "Qwen3 8B", monthlyTokens: 0, creditTokens: 0, freeType: "recurring-uncapped", poolKey: "ainative-free", tos: "caution" },
|
||||
{ provider: "ainative", modelId: "llama-4-maverick", displayName: "Llama 4 Maverick", monthlyTokens: 0, creditTokens: 0, freeType: "recurring-uncapped", poolKey: "ainative-free", tos: "caution" },
|
||||
{ provider: "ainative", modelId: "llama3.1-8b-cerebras", displayName: "Llama 3.1 8B (Cerebras)", monthlyTokens: 0, creditTokens: 0, freeType: "recurring-uncapped", poolKey: "ainative-free", tos: "caution" },
|
||||
{ provider: "ainative", modelId: "deepseek-r1", displayName: "DeepSeek R1", monthlyTokens: 0, creditTokens: 0, freeType: "recurring-uncapped", poolKey: "ainative-free", tos: "caution" },
|
||||
{ provider: "ainative", modelId: "nous-coder", displayName: "Nous Coder", monthlyTokens: 0, creditTokens: 0, freeType: "recurring-uncapped", poolKey: "ainative-free", tos: "caution" },
|
||||
{ provider: "ainative", modelId: "gemini-flash", displayName: "Gemini Flash", monthlyTokens: 0, creditTokens: 0, freeType: "recurring-uncapped", poolKey: "ainative-free", tos: "caution" },
|
||||
{ provider: "aion", modelId: "aion-labs/aion-3.0", displayName: "Aion 3.0", monthlyTokens: 0, creditTokens: 0, freeType: "recurring-uncapped", poolKey: "aion-free", tos: "ok" },
|
||||
{ provider: "aion", modelId: "aion-labs/aion-3.0-mini", displayName: "Aion 3.0 Mini", monthlyTokens: 0, creditTokens: 0, freeType: "recurring-uncapped", poolKey: "aion-free", tos: "ok" },
|
||||
{ provider: "aion", modelId: "aion-labs/aion-2.5", displayName: "Aion 2.5", monthlyTokens: 0, creditTokens: 0, freeType: "recurring-uncapped", poolKey: "aion-free", tos: "ok" },
|
||||
{ provider: "aion", modelId: "aion-labs/aion-2.0", displayName: "Aion 2.0", monthlyTokens: 0, creditTokens: 0, freeType: "recurring-uncapped", poolKey: "aion-free", tos: "ok" },
|
||||
{ provider: "aion", modelId: "aion-labs/aion-rp-llama-3.1-8b", displayName: "Aion RP Llama 3.1 8B", monthlyTokens: 0, creditTokens: 0, freeType: "recurring-uncapped", poolKey: "aion-free", tos: "ok" },
|
||||
{ provider: "sealion", modelId: "aisingapore/Llama-SEA-LION-v3.5-70B-R", displayName: "Llama SEA-LION v3.5 70B R", monthlyTokens: 0, creditTokens: 0, freeType: "recurring-uncapped", poolKey: "sealion-free", tos: "ok" },
|
||||
{ provider: "sealion", modelId: "aisingapore/Llama-SEA-LION-v3-70B-IT", displayName: "Llama SEA-LION v3 70B IT", monthlyTokens: 0, creditTokens: 0, freeType: "recurring-uncapped", poolKey: "sealion-free", tos: "ok" },
|
||||
{ provider: "sealion", modelId: "aisingapore/Gemma-SEA-LION-v4-27B-IT", displayName: "Gemma SEA-LION v4 27B IT", monthlyTokens: 0, creditTokens: 0, freeType: "recurring-uncapped", poolKey: "sealion-free", tos: "ok" },
|
||||
{ provider: "sealion", modelId: "aisingapore/Qwen-SEA-LION-v4.5-27B-IT", displayName: "Qwen SEA-LION v4.5 27B IT", monthlyTokens: 0, creditTokens: 0, freeType: "recurring-uncapped", poolKey: "sealion-free", tos: "ok" },
|
||||
{ provider: "sealion", modelId: "aisingapore/Qwen-SEA-LION-v4-32B-IT", displayName: "Qwen SEA-LION v4 32B IT", monthlyTokens: 0, creditTokens: 0, freeType: "recurring-uncapped", poolKey: "sealion-free", tos: "ok" },
|
||||
{ provider: "routeway", modelId: "llama-3.3-70b-instruct:free", displayName: "Llama 3.3 70B Instruct (free)", monthlyTokens: 0, creditTokens: 0, freeType: "recurring-uncapped", poolKey: "routeway-free", tos: "caution" },
|
||||
{ provider: "routeway", modelId: "nemotron-3-nano-30b-a3b:free", displayName: "Nemotron 3 Nano 30B (free)", monthlyTokens: 0, creditTokens: 0, freeType: "recurring-uncapped", poolKey: "routeway-free", tos: "caution" },
|
||||
{ provider: "routeway", modelId: "nemotron-nano-9b-v2:free", displayName: "Nemotron Nano 9B v2 (free)", monthlyTokens: 0, creditTokens: 0, freeType: "recurring-uncapped", poolKey: "routeway-free", tos: "caution" },
|
||||
{ provider: "routeway", modelId: "step-3.7-flash:free", displayName: "Step 3.7 Flash (free)", monthlyTokens: 0, creditTokens: 0, freeType: "recurring-uncapped", poolKey: "routeway-free", tos: "caution" },
|
||||
{ provider: "routeway", modelId: "step-3.5-flash:free", displayName: "Step 3.5 Flash (free)", monthlyTokens: 0, creditTokens: 0, freeType: "recurring-uncapped", poolKey: "routeway-free", tos: "caution" },
|
||||
{ provider: "routeway", modelId: "laguna-m.1:free", displayName: "Laguna M.1 (free)", monthlyTokens: 0, creditTokens: 0, freeType: "recurring-uncapped", poolKey: "routeway-free", tos: "caution" },
|
||||
{ provider: "routeway", modelId: "laguna-xs.2:free", displayName: "Laguna XS.2 (free)", monthlyTokens: 0, creditTokens: 0, freeType: "recurring-uncapped", poolKey: "routeway-free", tos: "caution" },
|
||||
{ provider: "routeway", modelId: "llama-3.2-3b-instruct:free", displayName: "Llama 3.2 3B Instruct (free)", monthlyTokens: 0, creditTokens: 0, freeType: "recurring-uncapped", poolKey: "routeway-free", tos: "caution" },
|
||||
{ provider: "nara", modelId: "tencent-hy3", displayName: "Tencent Hy3", monthlyTokens: 150000000, creditTokens: 0, freeType: "recurring-daily", poolKey: "nara-free", tos: "caution" },
|
||||
{ provider: "nara", modelId: "mistral-large", displayName: "Mistral Large", monthlyTokens: 150000000, creditTokens: 0, freeType: "recurring-daily", poolKey: "nara-free", tos: "caution" },
|
||||
{ provider: "nara", modelId: "mistral-medium-3-5", displayName: "Mistral Medium 3.5", monthlyTokens: 150000000, creditTokens: 0, freeType: "recurring-daily", poolKey: "nara-free", tos: "caution" },
|
||||
];
|
||||
|
||||
@@ -127,6 +127,8 @@ import { deepinfraProvider } from "./registry/deepinfra/index.ts";
|
||||
import { agyProvider } from "./registry/agy/index.ts";
|
||||
import { agnesProvider } from "./registry/agnes/index.ts";
|
||||
import { aihordeProvider } from "./registry/aihorde/index.ts";
|
||||
import { ainativeProvider } from "./registry/ainative/index.ts";
|
||||
import { aionProvider } from "./registry/aion/index.ts";
|
||||
import { udioProvider } from "./registry/udio/index.ts";
|
||||
import { longcatProvider } from "./registry/longcat/index.ts";
|
||||
import { vertex_partnerProvider } from "./registry/vertex/partner/index.ts";
|
||||
@@ -199,7 +201,10 @@ import { sumopodProvider } from "./registry/sumopod/index.ts";
|
||||
import { x5labProvider } from "./registry/x5lab/index.ts";
|
||||
import { kenariProvider } from "./registry/kenari/index.ts";
|
||||
import { navyProvider } from "./registry/navy/index.ts";
|
||||
import { naraProvider } from "./registry/nara/index.ts";
|
||||
import { requestyProvider } from "./registry/requesty/index.ts";
|
||||
import { sealionProvider } from "./registry/sealion/index.ts";
|
||||
import { routewayProvider } from "./registry/routeway/index.ts";
|
||||
import { digitaloceanProvider } from "./registry/digitalocean/index.ts";
|
||||
import { hcnsecProvider } from "./registry/hcnsec/index.ts";
|
||||
|
||||
@@ -329,6 +334,8 @@ export const REGISTRY: Record<string, RegistryEntry> = {
|
||||
agy: agyProvider,
|
||||
agnes: agnesProvider,
|
||||
aihorde: aihordeProvider,
|
||||
ainative: ainativeProvider,
|
||||
aion: aionProvider,
|
||||
udio: udioProvider,
|
||||
longcat: longcatProvider,
|
||||
"vertex-partner": vertex_partnerProvider,
|
||||
@@ -403,7 +410,10 @@ export const REGISTRY: Record<string, RegistryEntry> = {
|
||||
x5lab: x5labProvider,
|
||||
kenari: kenariProvider,
|
||||
navy: navyProvider,
|
||||
nara: naraProvider,
|
||||
requesty: requestyProvider,
|
||||
sealion: sealionProvider,
|
||||
routeway: routewayProvider,
|
||||
digitalocean: digitaloceanProvider,
|
||||
hcnsec: hcnsecProvider,
|
||||
};
|
||||
|
||||
27
open-sse/config/providers/registry/ainative/index.ts
Normal file
27
open-sse/config/providers/registry/ainative/index.ts
Normal file
@@ -0,0 +1,27 @@
|
||||
import type { RegistryEntry } from "../../shared.ts";
|
||||
import { buildOpenAiCompatibleRegistryEntry } from "../../shared.ts";
|
||||
|
||||
/**
|
||||
* AINative Studio — OpenAI-compatible aggregator (api.ainative.studio).
|
||||
*
|
||||
* The /models catalog is public (84 models,
|
||||
* verified 2026-07-20) so it is discovered via passthrough; the entries below
|
||||
* are the free-tier ones and act as a fallback when discovery fails.
|
||||
*/
|
||||
export const ainativeProvider: RegistryEntry = buildOpenAiCompatibleRegistryEntry({
|
||||
id: "ainative",
|
||||
baseUrl: "https://api.ainative.studio/api/v1/chat/completions",
|
||||
modelsUrl: "https://api.ainative.studio/api/v1/models",
|
||||
passthroughModels: true,
|
||||
models: [
|
||||
{ id: "qwen3-235b-cerebras", name: "Qwen3 235B (Cerebras)", contextLength: 131072, toolCalling: true },
|
||||
{ id: "qwen3-32b", name: "Qwen3 32B", contextLength: 131072, toolCalling: true },
|
||||
{ id: "qwen3-14b", name: "Qwen3 14B", contextLength: 131072, toolCalling: true },
|
||||
{ id: "qwen3-8b", name: "Qwen3 8B", contextLength: 131072, toolCalling: true },
|
||||
{ id: "llama-4-maverick", name: "Llama 4 Maverick", contextLength: 131072, toolCalling: true },
|
||||
{ id: "llama3.1-8b-cerebras", name: "Llama 3.1 8B (Cerebras)", contextLength: 131072, toolCalling: true },
|
||||
{ id: "deepseek-r1", name: "DeepSeek R1", contextLength: 65536, supportsReasoning: true },
|
||||
{ id: "nous-coder", name: "Nous Coder", contextLength: 131072, toolCalling: true },
|
||||
{ id: "gemini-flash", name: "Gemini Flash", contextLength: 131072, toolCalling: true },
|
||||
],
|
||||
});
|
||||
23
open-sse/config/providers/registry/aion/index.ts
Normal file
23
open-sse/config/providers/registry/aion/index.ts
Normal file
@@ -0,0 +1,23 @@
|
||||
import type { RegistryEntry } from "../../shared.ts";
|
||||
import { buildOpenAiCompatibleRegistryEntry } from "../../shared.ts";
|
||||
|
||||
/**
|
||||
* Aion Labs — OpenAI-compatible aggregator (api.aionlabs.ai).
|
||||
*
|
||||
* Free key from aionlabs.ai (no card). The
|
||||
* public /models catalog (5 models, verified 2026-07-20) carries context and
|
||||
* pricing; free tier is 20k tokens/day.
|
||||
*/
|
||||
export const aionProvider: RegistryEntry = buildOpenAiCompatibleRegistryEntry({
|
||||
id: "aion",
|
||||
baseUrl: "https://api.aionlabs.ai/v1/chat/completions",
|
||||
modelsUrl: "https://api.aionlabs.ai/v1/models",
|
||||
passthroughModels: true,
|
||||
models: [
|
||||
{ id: "aion-labs/aion-3.0", name: "Aion 3.0", contextLength: 131072 },
|
||||
{ id: "aion-labs/aion-3.0-mini", name: "Aion 3.0 Mini", contextLength: 131072 },
|
||||
{ id: "aion-labs/aion-2.5", name: "Aion 2.5", contextLength: 131072 },
|
||||
{ id: "aion-labs/aion-2.0", name: "Aion 2.0", contextLength: 131072 },
|
||||
{ id: "aion-labs/aion-rp-llama-3.1-8b", name: "Aion RP Llama 3.1 8B", contextLength: 32768 },
|
||||
],
|
||||
});
|
||||
19
open-sse/config/providers/registry/nara/index.ts
Normal file
19
open-sse/config/providers/registry/nara/index.ts
Normal file
@@ -0,0 +1,19 @@
|
||||
import type { RegistryEntry } from "../../shared.ts";
|
||||
import { buildOpenAiCompatibleRegistryEntry } from "../../shared.ts";
|
||||
|
||||
/**
|
||||
* NaraRouter — OpenAI-compatible aggregator (router.bynara.id).
|
||||
*
|
||||
* Free key issued via their Telegram channel. The free tier is a shared
|
||||
* 5M-tokens/day pool; many models are gated behind
|
||||
* credit/plan, so only the free-tier models are pinned.
|
||||
*/
|
||||
export const naraProvider: RegistryEntry = buildOpenAiCompatibleRegistryEntry({
|
||||
id: "nara",
|
||||
baseUrl: "https://router.bynara.id/v1/chat/completions",
|
||||
models: [
|
||||
{ id: "tencent-hy3", name: "Tencent Hy3", contextLength: 1000000 },
|
||||
{ id: "mistral-large", name: "Mistral Large", contextLength: 252000, toolCalling: true },
|
||||
{ id: "mistral-medium-3-5", name: "Mistral Medium 3.5", contextLength: 256000, toolCalling: true, supportsVision: true },
|
||||
],
|
||||
});
|
||||
28
open-sse/config/providers/registry/routeway/index.ts
Normal file
28
open-sse/config/providers/registry/routeway/index.ts
Normal file
@@ -0,0 +1,28 @@
|
||||
import type { RegistryEntry } from "../../shared.ts";
|
||||
import { buildOpenAiCompatibleRegistryEntry } from "../../shared.ts";
|
||||
|
||||
/**
|
||||
* Routeway — OpenAI-compatible aggregator (api.routeway.ai).
|
||||
*
|
||||
* Cloudflare fronts the API and rejects
|
||||
* non-browser User-Agents with error 1010, so a browser-style UA is pinned
|
||||
* (verified needed 2026-07-20). The public /models catalog has 236 models with
|
||||
* an `available`/pricing shape; free models carry the `:free` suffix.
|
||||
*/
|
||||
export const routewayProvider: RegistryEntry = buildOpenAiCompatibleRegistryEntry({
|
||||
id: "routeway",
|
||||
baseUrl: "https://api.routeway.ai/v1/chat/completions",
|
||||
modelsUrl: "https://api.routeway.ai/v1/models",
|
||||
passthroughModels: true,
|
||||
extraHeaders: { "User-Agent": "Mozilla/5.0 OmniRoute/1.0" },
|
||||
models: [
|
||||
{ id: "llama-3.3-70b-instruct:free", name: "Llama 3.3 70B Instruct (free)", contextLength: 131072, toolCalling: true },
|
||||
{ id: "nemotron-3-nano-30b-a3b:free", name: "Nemotron 3 Nano 30B (free)", contextLength: 256000, toolCalling: true },
|
||||
{ id: "nemotron-nano-9b-v2:free", name: "Nemotron Nano 9B v2 (free)", contextLength: 128000, toolCalling: true },
|
||||
{ id: "step-3.7-flash:free", name: "Step 3.7 Flash (free)", contextLength: 256000, toolCalling: true, supportsVision: true },
|
||||
{ id: "step-3.5-flash:free", name: "Step 3.5 Flash (free)", contextLength: 65536, toolCalling: true },
|
||||
{ id: "laguna-m.1:free", name: "Laguna M.1 (free)", contextLength: 131072, toolCalling: true },
|
||||
{ id: "laguna-xs.2:free", name: "Laguna XS.2 (free)", contextLength: 131072, toolCalling: true },
|
||||
{ id: "llama-3.2-3b-instruct:free", name: "Llama 3.2 3B Instruct (free)", contextLength: 16000, toolCalling: true },
|
||||
],
|
||||
});
|
||||
21
open-sse/config/providers/registry/sealion/index.ts
Normal file
21
open-sse/config/providers/registry/sealion/index.ts
Normal file
@@ -0,0 +1,21 @@
|
||||
import type { RegistryEntry } from "../../shared.ts";
|
||||
import { buildOpenAiCompatibleRegistryEntry } from "../../shared.ts";
|
||||
|
||||
/**
|
||||
* SEA-LION (AI Singapore) — OpenAI-compatible first-party API (api.sea-lion.ai).
|
||||
*
|
||||
* Free key from sea-lion.ai (Google sign-in, no
|
||||
* card); recurring free tier at 10 RPM. /models requires the key, so the free
|
||||
* models are pinned rather than discovered.
|
||||
*/
|
||||
export const sealionProvider: RegistryEntry = buildOpenAiCompatibleRegistryEntry({
|
||||
id: "sealion",
|
||||
baseUrl: "https://api.sea-lion.ai/v1/chat/completions",
|
||||
models: [
|
||||
{ id: "aisingapore/Llama-SEA-LION-v3.5-70B-R", name: "Llama SEA-LION v3.5 70B R", contextLength: 131072 },
|
||||
{ id: "aisingapore/Llama-SEA-LION-v3-70B-IT", name: "Llama SEA-LION v3 70B IT", contextLength: 131072 },
|
||||
{ id: "aisingapore/Gemma-SEA-LION-v4-27B-IT", name: "Gemma SEA-LION v4 27B IT", contextLength: 131072 },
|
||||
{ id: "aisingapore/Qwen-SEA-LION-v4.5-27B-IT", name: "Qwen SEA-LION v4.5 27B IT", contextLength: 32768 },
|
||||
{ id: "aisingapore/Qwen-SEA-LION-v4-32B-IT", name: "Qwen SEA-LION v4 32B IT", contextLength: 32768 },
|
||||
],
|
||||
});
|
||||
@@ -732,4 +732,64 @@ export const APIKEY_PROVIDERS_GATEWAYS = {
|
||||
"OpenAI-compatible endpoint at https://api.navy/v1 with a live /v1/models catalog that exposes " +
|
||||
"per-model token_multiplier and premium flags. Upstream requires an explicit User-Agent header.",
|
||||
},
|
||||
ainative: {
|
||||
id: "ainative",
|
||||
alias: "ainative",
|
||||
name: "AINative Studio",
|
||||
icon: "hub",
|
||||
color: "#7C3AED",
|
||||
textIcon: "AN",
|
||||
passthroughModels: true,
|
||||
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.",
|
||||
apiHint:
|
||||
"OpenAI-compatible endpoint at https://api.ainative.studio/api/v1 with a public /models catalog (84 models). OmniRoute lists models via passthrough.",
|
||||
},
|
||||
aion: {
|
||||
id: "aion",
|
||||
alias: "aion",
|
||||
name: "Aion Labs",
|
||||
icon: "hub",
|
||||
color: "#0EA5E9",
|
||||
textIcon: "AI",
|
||||
passthroughModels: true,
|
||||
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.",
|
||||
apiHint:
|
||||
"OpenAI-compatible endpoint at https://api.aionlabs.ai/v1 with a public /models catalog carrying context and pricing.",
|
||||
},
|
||||
routeway: {
|
||||
id: "routeway",
|
||||
alias: "routeway",
|
||||
name: "Routeway",
|
||||
icon: "hub",
|
||||
color: "#F59E0B",
|
||||
textIcon: "RW",
|
||||
passthroughModels: true,
|
||||
website: "https://routeway.ai",
|
||||
hasFree: true,
|
||||
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.",
|
||||
},
|
||||
nara: {
|
||||
id: "nara",
|
||||
alias: "nara",
|
||||
name: "NaraRouter",
|
||||
icon: "hub",
|
||||
color: "#EC4899",
|
||||
textIcon: "NA",
|
||||
passthroughModels: true,
|
||||
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.",
|
||||
apiHint:
|
||||
"OpenAI-compatible endpoint at https://router.bynara.id/v1. Free-tier models are pinned; others need credit.",
|
||||
},
|
||||
};
|
||||
|
||||
@@ -388,4 +388,16 @@ export const APIKEY_PROVIDERS_REGIONAL = {
|
||||
freeNote: "Permanently free API - no credit card required.",
|
||||
authHint: "Get API key at agnes-ai.com",
|
||||
},
|
||||
sealion: {
|
||||
id: "sealion",
|
||||
alias: "sealion",
|
||||
name: "SEA-LION",
|
||||
icon: "public",
|
||||
color: "#0D9488",
|
||||
textIcon: "SL",
|
||||
website: "https://sea-lion.ai",
|
||||
hasFree: true,
|
||||
freeNote: "Permanently free at 10 RPM — AI Singapore's Southeast-Asian models (Llama/Qwen/Gemma SEA-LION).",
|
||||
authHint: "Sign in at sea-lion.ai with Google (no card, no region wall), create an API key, then paste it here.",
|
||||
},
|
||||
};
|
||||
|
||||
64
tests/unit/free-providers-batch-2026-07.test.ts
Normal file
64
tests/unit/free-providers-batch-2026-07.test.ts
Normal file
@@ -0,0 +1,64 @@
|
||||
import assert from "node:assert/strict";
|
||||
import test from "node:test";
|
||||
|
||||
import {
|
||||
FREE_MODEL_BUDGETS,
|
||||
computeFreeModelTotals,
|
||||
} from "@omniroute/open-sse/config/freeModelCatalog.ts";
|
||||
import { REGISTRY } from "@omniroute/open-sse/config/providerRegistry.ts";
|
||||
import { AI_PROVIDERS } from "@/shared/constants/providers.ts";
|
||||
|
||||
/**
|
||||
* Five OpenAI-compatible free-tier aggregators (ainative, aion, sealion,
|
||||
* routeway, nara). The tests pin the decisions that are easy to break silently.
|
||||
*/
|
||||
|
||||
const NEW_PROVIDERS = ["ainative", "aion", "sealion", "routeway", "nara"] as const;
|
||||
const byProvider = (id: string) => FREE_MODEL_BUDGETS.filter((m) => m.provider === id);
|
||||
|
||||
test("each new provider is both routable and canonically registered", () => {
|
||||
for (const id of NEW_PROVIDERS) {
|
||||
assert.ok(REGISTRY[id], `${id} must exist in the execution REGISTRY`);
|
||||
assert.ok(AI_PROVIDERS[id], `${id} must exist as a canonical provider`);
|
||||
assert.equal(REGISTRY[id].format, "openai");
|
||||
assert.ok(byProvider(id).length > 0, `${id} must have free-catalog entries`);
|
||||
}
|
||||
});
|
||||
|
||||
test("routeway pins a browser User-Agent (Cloudflare rejects non-browser UAs with 1010)", () => {
|
||||
const ua = REGISTRY.routeway.extraHeaders?.["User-Agent"] ?? "";
|
||||
assert.match(ua, /Mozilla/, "routeway must send a browser-style User-Agent");
|
||||
});
|
||||
|
||||
test("providers with no published token quota never inflate the headline", () => {
|
||||
// ainative ("claimed"), aion (per-day request cap), sealion (RPM only),
|
||||
// routeway (RPD): real free access, no verifiable monthly token figure.
|
||||
for (const id of ["ainative", "aion", "sealion", "routeway"]) {
|
||||
const rows = byProvider(id);
|
||||
assert.ok(
|
||||
rows.every((m) => m.monthlyTokens === 0 && m.creditTokens === 0),
|
||||
`${id} has no published token quota — it must not carry invented numbers`
|
||||
);
|
||||
assert.ok(rows.every((m) => m.freeType === "recurring-uncapped"));
|
||||
}
|
||||
const totals = computeFreeModelTotals();
|
||||
for (const id of ["ainative", "aion", "sealion", "routeway"]) {
|
||||
assert.ok(totals.uncappedProviders.includes(id), `${id} must surface as uncapped`);
|
||||
}
|
||||
});
|
||||
|
||||
test("nara is a single shared 5M/day pool, counted once", () => {
|
||||
const rows = byProvider("nara");
|
||||
assert.ok(rows.length >= 1);
|
||||
// 5M tokens/day shared across all models => 150M/month, deduped by poolKey.
|
||||
assert.ok(rows.every((m) => m.poolKey === "nara-free"));
|
||||
assert.ok(rows.every((m) => m.monthlyTokens === 150_000_000));
|
||||
assert.ok(rows.every((m) => m.freeType === "recurring-daily"));
|
||||
});
|
||||
|
||||
test("every catalog provider id still resolves to a canonical provider", () => {
|
||||
const unknown = [...new Set(FREE_MODEL_BUDGETS.map((m) => m.provider))].filter(
|
||||
(id) => !AI_PROVIDERS[id]
|
||||
);
|
||||
assert.deepEqual(unknown, [], `dangling catalog providers: ${unknown.join(", ")}`);
|
||||
});
|
||||
Reference in New Issue
Block a user