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:
Paijo
2026-06-02 08:29:30 +07:00
committed by GitHub
parent 65195dcd7a
commit 76d697bb88
6 changed files with 0 additions and 345 deletions

View File

@@ -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`;
}

View File

@@ -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",

View File

@@ -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",

View File

@@ -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" },

View File

@@ -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.

View File

@@ -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",