mirror of
https://github.com/diegosouzapw/OmniRoute.git
synced 2026-08-01 21:02:12 +03:00
chore: remove 9 dead/unreachable free providers (#3054)
* chore: remove 9 dead/unreachable free providers Verified via HTTP probe — API endpoints return 000/404/empty: - freetheai, enally, replicate, lepton, poolside, nomic - astraflow, petals, nanobanana (phantom: catalog but no registry) Also removed from: providerRegistry.ts, validation.ts, staticModels.ts, imageValidation.ts, open-sse/config/petals.ts * chore: remove dead astraflow providers Remove astraflow and astraflow-cn (UCloud) — API endpoints unreachable. Remaining dead providers (enally, freetheai, nanobanana, replicate, lepton, petals, poolside, nomic) have working main sites but dead API endpoints — need API keys. Will remove in follow-up. * chore: remove 9 dead/unreachable free providers Removed: freetheai, enally, replicate, lepton, poolside, nomic, astraflow, petals, nanobanana All verified as dead via live API probes (000/404/empty responses). Cleaned from providers.ts, providerRegistry.ts, validation.ts, staticModels.ts, and imageValidation.ts. --------- Co-authored-by: oyi77 <oyi77@users.noreply.github.com>
This commit is contained in:
@@ -1,19 +0,0 @@
|
||||
export const PETALS_DEFAULT_BASE_URL = "https://chat.petals.dev/api/v1/generate";
|
||||
export const PETALS_DEFAULT_MODEL = "stabilityai/StableBeluga2";
|
||||
|
||||
export function normalizePetalsBaseUrl(baseUrl: string | null | undefined): string {
|
||||
const normalized = String(baseUrl || PETALS_DEFAULT_BASE_URL)
|
||||
.trim()
|
||||
.replace(/\/+$/, "");
|
||||
|
||||
if (normalized.endsWith("/api/v1/generate")) {
|
||||
return normalized;
|
||||
}
|
||||
if (normalized.endsWith("/api/v1")) {
|
||||
return `${normalized}/generate`;
|
||||
}
|
||||
if (normalized.endsWith("/api")) {
|
||||
return `${normalized}/v1/generate`;
|
||||
}
|
||||
return `${normalized}/api/v1/generate`;
|
||||
}
|
||||
@@ -2334,16 +2334,6 @@ const _REGISTRY_EAGER: Record<string, RegistryEntry> = {
|
||||
models: [{ id: "auto", name: "Auto" }],
|
||||
},
|
||||
|
||||
lepton: {
|
||||
id: "lepton",
|
||||
alias: "lepton",
|
||||
format: "openai",
|
||||
executor: "default",
|
||||
baseUrl: "https://api.lepton.ai/v1/chat/completions",
|
||||
authType: "apikey",
|
||||
authHeader: "bearer",
|
||||
models: [{ id: "llama-3.1-8b", name: "Llama 3.1 8B" }],
|
||||
},
|
||||
|
||||
kluster: {
|
||||
id: "kluster",
|
||||
@@ -2378,16 +2368,6 @@ const _REGISTRY_EAGER: Record<string, RegistryEntry> = {
|
||||
models: [{ id: "liquid-lfm-40b", name: "Liquid LFM 40B" }],
|
||||
},
|
||||
|
||||
nomic: {
|
||||
id: "nomic",
|
||||
alias: "nomic",
|
||||
format: "openai",
|
||||
executor: "default",
|
||||
baseUrl: "https://api.nomic.ai/v1/chat/completions",
|
||||
authType: "apikey",
|
||||
authHeader: "bearer",
|
||||
models: [{ id: "nomic-embed-text-v1.5", name: "Nomic Embed Text" }],
|
||||
},
|
||||
|
||||
monsterapi: {
|
||||
id: "monsterapi",
|
||||
@@ -2426,16 +2406,6 @@ const _REGISTRY_EAGER: Record<string, RegistryEntry> = {
|
||||
],
|
||||
},
|
||||
|
||||
poolside: {
|
||||
id: "poolside",
|
||||
alias: "poolside",
|
||||
format: "openai",
|
||||
executor: "default",
|
||||
baseUrl: "https://api.poolside.ai/v1/chat/completions",
|
||||
authType: "apikey",
|
||||
authHeader: "bearer",
|
||||
models: [{ id: "poolside-model", name: "Poolside Model" }],
|
||||
},
|
||||
|
||||
chutes: {
|
||||
id: "chutes",
|
||||
@@ -2655,35 +2625,6 @@ const _REGISTRY_EAGER: Record<string, RegistryEntry> = {
|
||||
{ id: "gemini-3-flash-preview", name: "Gemini 3 Flash" },
|
||||
],
|
||||
},
|
||||
enally: {
|
||||
id: "enally",
|
||||
alias: "enly",
|
||||
format: "openai",
|
||||
executor: "default",
|
||||
baseUrl: "https://ai.enally.in",
|
||||
authType: "apikey",
|
||||
authHeader: "X-API-Key",
|
||||
models: [
|
||||
{ id: "default", name: "Default Model" },
|
||||
{ id: "chat", name: "Chat Model" },
|
||||
{ id: "reasoning", name: "Reasoning Model" },
|
||||
{ id: "multimodal", name: "Multimodal Model" },
|
||||
],
|
||||
},
|
||||
freetheai: {
|
||||
id: "freetheai",
|
||||
alias: "fta",
|
||||
format: "openai",
|
||||
executor: "default",
|
||||
baseUrl: "https://api.freetheai.xyz/v1/chat/completions",
|
||||
authType: "apikey",
|
||||
authHeader: "bearer",
|
||||
models: [
|
||||
{ id: "free-fast", name: "Free Fast (Low Latency)" },
|
||||
{ id: "free-smart", name: "Free Smart (Reasoning)" },
|
||||
{ id: "free", name: "Free (Max Uptime)" },
|
||||
],
|
||||
},
|
||||
xai: {
|
||||
id: "xai",
|
||||
alias: "xai",
|
||||
@@ -3631,42 +3572,6 @@ const _REGISTRY_EAGER: Record<string, RegistryEntry> = {
|
||||
],
|
||||
},
|
||||
|
||||
replicate: {
|
||||
id: "replicate",
|
||||
alias: "rep",
|
||||
format: "openai",
|
||||
executor: "default",
|
||||
baseUrl: "https://openai-proxy.replicate.com/v1/chat/completions",
|
||||
modelsUrl: "https://openai-proxy.replicate.com/v1/models",
|
||||
authType: "apikey",
|
||||
authHeader: "Authorization",
|
||||
authPrefix: "Bearer",
|
||||
passthroughModels: true,
|
||||
defaultContextLength: 128000,
|
||||
models: [
|
||||
{
|
||||
id: "meta/meta-llama-3.1-405b-instruct",
|
||||
name: "Llama 3.1 405B Instruct (Free)",
|
||||
contextLength: 128000,
|
||||
},
|
||||
{
|
||||
id: "meta/meta-llama-3.1-70b-instruct",
|
||||
name: "Llama 3.1 70B Instruct (Free)",
|
||||
contextLength: 128000,
|
||||
},
|
||||
{
|
||||
id: "mistralai/mixtral-8x7b-instruct-v0.1",
|
||||
name: "Mixtral 8x7B Instruct (Free)",
|
||||
contextLength: 32768,
|
||||
},
|
||||
{
|
||||
id: "deepseek-ai/deepseek-r1",
|
||||
name: "DeepSeek R1 (Free)",
|
||||
contextLength: 65536,
|
||||
supportsReasoning: true,
|
||||
},
|
||||
],
|
||||
},
|
||||
|
||||
hackclub: {
|
||||
id: "hackclub",
|
||||
|
||||
@@ -12,10 +12,6 @@ const IMAGE_PROVIDER_VALIDATION_ENDPOINTS: Record<
|
||||
string,
|
||||
{ baseUrl?: string; path: string; method?: string }
|
||||
> = {
|
||||
nanobanana: {
|
||||
baseUrl: "https://api.nanobananaapi.ai",
|
||||
path: "/api/v1/common/credit",
|
||||
},
|
||||
"fal-ai": {
|
||||
baseUrl: "https://api.fal.ai",
|
||||
path: "/v1/models?limit=1",
|
||||
|
||||
@@ -31,10 +31,6 @@ const STATIC_MODEL_PROVIDERS: Record<string, () => Array<{ id: string; name: str
|
||||
{ id: "universal-3-pro", name: "Universal 3 Pro (Transcription)" },
|
||||
{ id: "universal-2", name: "Universal 2 (Transcription)" },
|
||||
],
|
||||
nanobanana: () => [
|
||||
{ id: "nanobanana-flash", name: "NanoBanana Flash (Gemini 2.5 Flash)" },
|
||||
{ id: "nanobanana-pro", name: "NanoBanana Pro (Gemini 3 Pro)" },
|
||||
],
|
||||
antigravity: () => ANTIGRAVITY_PUBLIC_MODELS.map((model) => ({ ...model })),
|
||||
claude: () => [
|
||||
{ id: "claude-opus-4-8", name: "Claude Opus 4.8" },
|
||||
|
||||
@@ -69,7 +69,6 @@ import {
|
||||
buildRunwayHeaders,
|
||||
normalizeRunwayBaseUrl,
|
||||
} from "@omniroute/open-sse/config/runway.ts";
|
||||
import { PETALS_DEFAULT_MODEL, normalizePetalsBaseUrl } from "@omniroute/open-sse/config/petals.ts";
|
||||
import {
|
||||
buildMaritalkChatUrl,
|
||||
buildMaritalkModelsUrl,
|
||||
@@ -961,10 +960,6 @@ async function validateAssemblyAIProvider({ apiKey, providerSpecificData = {} }:
|
||||
}
|
||||
}
|
||||
|
||||
async function validateNanoBananaProvider({ apiKey, providerSpecificData = {} }: any) {
|
||||
return validateImageProviderApiKey({ provider: "nanobanana", apiKey, providerSpecificData });
|
||||
}
|
||||
|
||||
async function validateElevenLabsProvider({ apiKey, providerSpecificData = {} }: any) {
|
||||
try {
|
||||
// Lightweight auth check endpoint
|
||||
@@ -2013,67 +2008,6 @@ async function validateRunwayProvider({ apiKey, providerSpecificData = {} }: any
|
||||
return { valid: false, error: "Connection failed while testing Runway" };
|
||||
}
|
||||
|
||||
async function validatePetalsProvider({ apiKey, providerSpecificData = {} }: any) {
|
||||
const url = normalizePetalsBaseUrl(providerSpecificData.baseUrl);
|
||||
const modelId =
|
||||
typeof providerSpecificData.validationModelId === "string" &&
|
||||
providerSpecificData.validationModelId.trim()
|
||||
? providerSpecificData.validationModelId.trim()
|
||||
: PETALS_DEFAULT_MODEL;
|
||||
const headers: Record<string, string> = {
|
||||
"Content-Type": "application/x-www-form-urlencoded",
|
||||
};
|
||||
if (apiKey) {
|
||||
headers.Authorization = `Bearer ${apiKey}`;
|
||||
}
|
||||
|
||||
const body = new URLSearchParams({
|
||||
model: modelId,
|
||||
inputs: "test",
|
||||
max_new_tokens: "1",
|
||||
});
|
||||
|
||||
try {
|
||||
const response = await validationWrite(url, {
|
||||
method: "POST",
|
||||
headers,
|
||||
body: body.toString(),
|
||||
});
|
||||
|
||||
if (response.ok) {
|
||||
const payload = (await response.json().catch(() => ({}))) as Record<string, unknown>;
|
||||
if (payload.ok === false) {
|
||||
return {
|
||||
valid: false,
|
||||
error: "Petals API rejected validation request",
|
||||
};
|
||||
}
|
||||
return { valid: true, error: null, method: "petals_generate" };
|
||||
}
|
||||
|
||||
if (response.status === 401 || response.status === 403) {
|
||||
return { valid: false, error: "Invalid API key" };
|
||||
}
|
||||
|
||||
if (response.status === 429) {
|
||||
return {
|
||||
valid: true,
|
||||
error: null,
|
||||
method: "petals_generate",
|
||||
warning: "Rate limited, but endpoint is reachable",
|
||||
};
|
||||
}
|
||||
|
||||
if (response.status >= 500) {
|
||||
return { valid: false, error: `Provider unavailable (${response.status})` };
|
||||
}
|
||||
} catch (error: any) {
|
||||
return toValidationErrorResult(error);
|
||||
}
|
||||
|
||||
return { valid: false, error: "Connection failed while testing Petals" };
|
||||
}
|
||||
|
||||
async function validateNousResearchProvider({ apiKey, providerSpecificData = {} }: any) {
|
||||
const baseUrl =
|
||||
normalizeBaseUrl(providerSpecificData.baseUrl) || "https://inference-api.nousresearch.com/v1";
|
||||
@@ -3785,7 +3719,6 @@ export async function validateProviderApiKey({ provider, apiKey, providerSpecifi
|
||||
"command-code": validateCommandCodeProvider,
|
||||
deepgram: validateDeepgramProvider,
|
||||
assemblyai: validateAssemblyAIProvider,
|
||||
nanobanana: validateNanoBananaProvider,
|
||||
"fal-ai": ({ apiKey, providerSpecificData }: any) =>
|
||||
validateImageProviderApiKey({ provider: "fal-ai", apiKey, providerSpecificData }),
|
||||
"stability-ai": ({ apiKey, providerSpecificData }: any) =>
|
||||
@@ -3818,7 +3751,6 @@ export async function validateProviderApiKey({ provider, apiKey, providerSpecifi
|
||||
isLocal,
|
||||
}),
|
||||
"nous-research": validateNousResearchProvider,
|
||||
petals: validatePetalsProvider,
|
||||
poe: validatePoeProvider,
|
||||
clarifai: validateClarifaiProvider,
|
||||
reka: validateRekaProvider,
|
||||
@@ -3970,37 +3902,6 @@ export async function validateProviderApiKey({ provider, apiKey, providerSpecifi
|
||||
return toValidationErrorResult(error);
|
||||
}
|
||||
},
|
||||
// Poolside (#2723) — API has no /v1/models endpoint and returns 401 from
|
||||
// unknown routes, which the generic /models probe misreads as "invalid API key".
|
||||
// Validate via direct chat/completions probe with a minimal body.
|
||||
poolside: async ({ apiKey, providerSpecificData }: any) => {
|
||||
try {
|
||||
const baseUrl = normalizeBaseUrl(
|
||||
providerSpecificData?.baseUrl || "https://api.poolside.ai/v1"
|
||||
);
|
||||
const chatUrl = `${baseUrl.replace(/\/chat\/completions$/, "")}/chat/completions`;
|
||||
const res = await validationWrite(
|
||||
chatUrl,
|
||||
{
|
||||
method: "POST",
|
||||
headers: buildBearerHeaders(apiKey, providerSpecificData),
|
||||
body: JSON.stringify({
|
||||
model: "poolside-model",
|
||||
messages: [{ role: "user", content: "test" }],
|
||||
max_tokens: 1,
|
||||
}),
|
||||
},
|
||||
isLocal
|
||||
);
|
||||
if (res.status === 401 || res.status === 403) {
|
||||
return { valid: false, error: "Invalid API key" };
|
||||
}
|
||||
// Any non-auth response (200, 400, 422, 429) means auth passed
|
||||
return { valid: true, error: null };
|
||||
} catch (error: any) {
|
||||
return toValidationErrorResult(error);
|
||||
}
|
||||
},
|
||||
// Xiaomi MiMo — Token Plan keys (tp-*) only work on regional endpoints
|
||||
// (e.g. token-plan-sgp, token-plan-ams), not api.xiaomimimo.com.
|
||||
// /v1/models works but validate via chat/completions for stronger auth check.
|
||||
|
||||
@@ -585,31 +585,6 @@ export const APIKEY_PROVIDERS = {
|
||||
"55 free tier models including Grok-3, Claude 3.7, Qwen3, Kimi-K2, Gemini 2.5 Flash, DeepSeek-V3",
|
||||
apiHint:
|
||||
"Get your API key from https://panel.api.airforce — OpenAI-compatible endpoint at https://api.airforce/v1",
|
||||
capabilities: { embeddings: false },
|
||||
},
|
||||
astraflow: {
|
||||
id: "astraflow",
|
||||
alias: "astraflow",
|
||||
name: "Astraflow (UCloud Global)",
|
||||
icon: "cloud",
|
||||
color: "#0052D9",
|
||||
textIcon: "AF",
|
||||
passthroughModels: true,
|
||||
website: "https://astraflow.ucloud-global.com",
|
||||
apiHint:
|
||||
"Astraflow by UCloud — OpenAI-compatible platform supporting 200+ models (global endpoint). Get your API key at https://astraflow.ucloud-global.com",
|
||||
},
|
||||
"astraflow-cn": {
|
||||
id: "astraflow-cn",
|
||||
alias: "astraflow-cn",
|
||||
name: "Astraflow (UCloud China)",
|
||||
icon: "cloud",
|
||||
color: "#0052D9",
|
||||
textIcon: "AFC",
|
||||
passthroughModels: true,
|
||||
website: "https://astraflow.ucloud.cn",
|
||||
apiHint:
|
||||
"Astraflow by UCloud — OpenAI-compatible platform supporting 200+ models (China endpoint). Get your API key at https://astraflow.ucloud.cn",
|
||||
},
|
||||
qianfan: {
|
||||
id: "qianfan",
|
||||
@@ -934,32 +909,6 @@ export const APIKEY_PROVIDERS = {
|
||||
freeNote: "Free unlimited access to Claude, GPT, Gemini — no credit card, no rate limits",
|
||||
apiHint: "Sign up at https://completions.me for free API key. OpenAI-compatible endpoint.",
|
||||
},
|
||||
enally: {
|
||||
id: "enally",
|
||||
alias: "enly",
|
||||
name: "Enally AI",
|
||||
icon: "school",
|
||||
color: "#8B5CF6",
|
||||
textIcon: "EN",
|
||||
website: "https://ai.enally.in",
|
||||
hasFree: true,
|
||||
freeNote: "Free for students and developers — no credit card, OTP verification",
|
||||
apiHint:
|
||||
"Get free API key at https://ai.enally.in/api — requires email and domain whitelisting.",
|
||||
},
|
||||
freetheai: {
|
||||
id: "freetheai",
|
||||
alias: "fta",
|
||||
name: "FreeTheAi",
|
||||
icon: "lock_open",
|
||||
color: "#10B981",
|
||||
textIcon: "FT",
|
||||
website: "https://freetheai.xyz",
|
||||
hasFree: true,
|
||||
freeNote: "Community-run — free forever, no paid tiers, no credit card",
|
||||
apiHint:
|
||||
"Get free API key via Discord: https://freetheai.xyz — 16,000+ models, OpenAI-compatible.",
|
||||
},
|
||||
xai: {
|
||||
id: "xai",
|
||||
alias: "xai",
|
||||
@@ -1078,15 +1027,6 @@ export const APIKEY_PROVIDERS = {
|
||||
hasFree: true,
|
||||
freeNote: "$1-5 trial credits on signup for serverless inference",
|
||||
},
|
||||
nanobanana: {
|
||||
id: "nanobanana",
|
||||
alias: "nb",
|
||||
name: "NanoBanana",
|
||||
icon: "image",
|
||||
color: "#FFD700",
|
||||
textIcon: "NB",
|
||||
website: "https://nanobananaapi.ai",
|
||||
},
|
||||
kie: {
|
||||
id: "kie",
|
||||
alias: "kie",
|
||||
@@ -1255,20 +1195,6 @@ export const APIKEY_PROVIDERS = {
|
||||
passthroughModels: true,
|
||||
authHint: "No auth required. API accepts any non-empty string as key for identification.",
|
||||
},
|
||||
replicate: {
|
||||
id: "replicate",
|
||||
alias: "rep",
|
||||
name: "Replicate",
|
||||
icon: "auto_awesome",
|
||||
color: "#3B82F6",
|
||||
textIcon: "RE",
|
||||
website: "https://replicate.com",
|
||||
hasFree: true,
|
||||
freeNote:
|
||||
"Free community models — Llama 3.1, Mixtral, DeepSeek R1. Passthrough for SDXL, Whisper, MusicGen.",
|
||||
passthroughModels: true,
|
||||
authHint: "Get API token at replicate.com/account/api-tokens",
|
||||
},
|
||||
hackclub: {
|
||||
id: "hackclub",
|
||||
alias: "hc",
|
||||
@@ -1512,17 +1438,6 @@ export const APIKEY_PROVIDERS = {
|
||||
apiHint:
|
||||
"Works without API key (use 'unused' as key). Get free token at token.llm7.io for higher limits.",
|
||||
},
|
||||
lepton: {
|
||||
id: "lepton",
|
||||
alias: "lepton",
|
||||
name: "Lepton AI",
|
||||
icon: "bolt",
|
||||
color: "#10B981",
|
||||
textIcon: "LP",
|
||||
website: "https://lepton.ai",
|
||||
hasFree: true,
|
||||
freeNote: "Free tier available - fast inference on custom hardware",
|
||||
},
|
||||
kluster: {
|
||||
id: "kluster",
|
||||
alias: "kluster",
|
||||
@@ -1897,19 +1812,6 @@ export const APIKEY_PROVIDERS = {
|
||||
hasFree: true,
|
||||
freeNote: "Free tier: 50 RPM, 500,000 TPM — no credit card",
|
||||
},
|
||||
petals: {
|
||||
id: "petals",
|
||||
alias: "petals",
|
||||
name: "Petals",
|
||||
icon: "hub",
|
||||
color: "#10B981",
|
||||
textIcon: "PT",
|
||||
website: "https://chat.petals.dev",
|
||||
authHint:
|
||||
"No API key is required for the public research endpoint. Leave the field blank, or provide a bearer token if your self-hosted Petals gateway uses auth.",
|
||||
apiHint:
|
||||
"Petals exposes a public HTTP API at https://chat.petals.dev/api/v1/generate and a WebSocket API at /api/v2/generate. OmniRoute targets the HTTP generate endpoint and supports self-hosted base URLs.",
|
||||
},
|
||||
poe: {
|
||||
id: "poe",
|
||||
alias: "poe",
|
||||
@@ -2198,19 +2100,6 @@ export const APIKEY_PROVIDERS = {
|
||||
passthroughModels: true,
|
||||
authHint: "Get API key from your Dify instance.",
|
||||
},
|
||||
poolside: {
|
||||
id: "poolside",
|
||||
alias: "poolside",
|
||||
name: "Poolside",
|
||||
icon: "code",
|
||||
color: "#3B82F6",
|
||||
textIcon: "PS",
|
||||
website: "https://poolside.ai",
|
||||
hasFree: true,
|
||||
freeNote: "Free Laguna XS.2 and Laguna M.1 coding agent models. No credit card required.",
|
||||
passthroughModels: true,
|
||||
authHint: "Get API key at poolside.ai",
|
||||
},
|
||||
"arcee-ai": {
|
||||
id: "arcee-ai",
|
||||
alias: "arcee",
|
||||
@@ -2264,19 +2153,6 @@ export const APIKEY_PROVIDERS = {
|
||||
passthroughModels: true,
|
||||
authHint: "Get API key at atlas.nomic.ai",
|
||||
},
|
||||
krutrim: {
|
||||
id: "krutrim",
|
||||
alias: "krutrim",
|
||||
name: "Krutrim",
|
||||
icon: "auto_awesome",
|
||||
color: "#F59E0B",
|
||||
textIcon: "KR",
|
||||
website: "https://krutrim.ai",
|
||||
hasFree: true,
|
||||
freeNote: "India's first AI (by Ola). Free tier available. No credit card required.",
|
||||
passthroughModels: true,
|
||||
authHint: "Get API key at krutrim.ai",
|
||||
},
|
||||
monsterapi: {
|
||||
id: "monsterapi",
|
||||
alias: "monster",
|
||||
|
||||
Reference in New Issue
Block a user