From 8bf2e6929f490a53ec82177e107bd0e6703e3609 Mon Sep 17 00:00:00 2001 From: Diego Rodrigues de Sa e Souza <8016841+diegosouzapw@users.noreply.github.com> Date: Fri, 17 Jul 2026 22:09:37 -0300 Subject: [PATCH] feat(providers): add g4f.space no-key gateway (groq/gemini/pollinations/ollama/nvidia) (#6650) (#7647) Validated in local merge-train @ 8f27177d1 (full parity suite green: typecheck+file-size+complexity+cognitive+changelog+unit shards 1&2+vitest) --- AGENTS.md | 4 +- CLAUDE.md | 2 +- README.md | 10 +- .../features/6650-g4f-space-gateway.md | 1 + docs/reference/PROVIDER_REFERENCE.md | 9 +- open-sse/config/providers/index.ts | 10 ++ .../providers/registry/g4f-gemini/index.ts | 20 +++ .../providers/registry/g4f-groq/index.ts | 20 +++ .../providers/registry/g4f-nvidia/index.ts | 22 ++++ .../providers/registry/g4f-ollama/index.ts | 18 +++ .../registry/g4f-pollinations/index.ts | 20 +++ src/shared/constants/providers.ts | 32 +++-- .../constants/providers/apikey/gateways.ts | 75 ++++++++++++ tests/snapshots/provider/translate-path.json | 115 ++++++++++++++++++ tests/unit/g4f-space-gateway-6650.test.ts | 103 ++++++++++++++++ tests/unit/providers-constants-split.test.ts | 16 +-- 16 files changed, 453 insertions(+), 24 deletions(-) create mode 100644 changelog.d/features/6650-g4f-space-gateway.md create mode 100644 open-sse/config/providers/registry/g4f-gemini/index.ts create mode 100644 open-sse/config/providers/registry/g4f-groq/index.ts create mode 100644 open-sse/config/providers/registry/g4f-nvidia/index.ts create mode 100644 open-sse/config/providers/registry/g4f-ollama/index.ts create mode 100644 open-sse/config/providers/registry/g4f-pollinations/index.ts create mode 100644 tests/unit/g4f-space-gateway-6650.test.ts diff --git a/AGENTS.md b/AGENTS.md index 0c8d27008a..47fa7028eb 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -3,12 +3,12 @@ ## Project Unified AI proxy/router — route any LLM through one endpoint. Multi-provider support -with **259 provider entries** (OpenAI, Anthropic, Gemini, DeepSeek, Groq, xAI, Mistral, Fireworks, +with **264 provider entries** (OpenAI, Anthropic, Gemini, DeepSeek, Groq, xAI, Mistral, Fireworks, Cohere, NVIDIA, Cerebras, Pollinations, Puter, Cloudflare AI, HuggingFace, DeepInfra, SambaNova, Meta Llama API, Moonshot AI, AI21 Labs, Databricks, Snowflake, and many more) with **MCP Server** (94 tools), **A2A v0.3 Protocol**, and **Electron desktop app**. -> **Live counts (v3.8.49)**: providers 259 · MCP tools 94 · MCP scopes 30 · A2A skills 6 · +> **Live counts (v3.8.49)**: providers 264 · MCP tools 94 · MCP scopes 30 · A2A skills 6 · > open-sse services 134 · routing strategies 17 · auto-combo scoring factors 12 · > DB modules 95 · DB migrations 110 · base tables 17 · search providers 11 · > i18n locales 42. **Refresh with `npm run check:docs-all`.** diff --git a/CLAUDE.md b/CLAUDE.md index 82e30fb40d..51f08a8c8d 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -35,7 +35,7 @@ For full test matrix, see `CONTRIBUTING.md` → "Running Tests". For deep archit ## Project at a Glance -**OmniRoute** — unified AI proxy/router. One endpoint, 259 LLM providers, auto-fallback. +**OmniRoute** — unified AI proxy/router. One endpoint, 264 LLM providers, auto-fallback. | Layer | Location | Purpose | | ------------- | ----------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------ | diff --git a/README.md b/README.md index ab17737df5..c6c656b921 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ # 🚀 OmniRoute — The Free AI Gateway -### Never stop coding. Connect every AI tool to **259 providers** — **90+ free** — through one endpoint. +### Never stop coding. Connect every AI tool to **264 providers** — **90+ free** — through one endpoint. **Plug Claude Code, Codex, Cursor, Cline, Copilot & Antigravity into FREE Claude / GPT / Gemini. Auto-fallback.**
@@ -149,11 +149,11 @@ -> One endpoint. **259 providers.** Never stop building — and let OmniRoute pick the cheapest one that works. +> One endpoint. **264 providers.** Never stop building — and let OmniRoute pick the cheapest one that works. - + @@ -368,7 +368,7 @@ All **18** strategies — mix & match per combo step: -> The most complete catalog of any open-source router: **259 providers**, **90+ with a free tier**, **11 free forever**. +> The most complete catalog of any open-source router: **264 providers**, **90+ with a free tier**, **11 free forever**.
@@ -874,7 +874,7 @@ Compression: aggressive (~50%) → double your free quota · Cost: $0/mo **Will I be charged by OmniRoute?** No — it's free, open-source software on your machine. You only pay paid providers directly. OmniRoute has no billing system. **Are FREE providers really unlimited?** Mostly — Qoder, Pollinations, LongCat, and Cloudflare are free with no per-account credit cap. Kiro is free too but capped at ~50 credits/month per account. Stack multiple free providers in a combo and auto-fallback keeps you serving for $0. **Will compression hurt quality?** No — it only compresses the **input**; code, URLs, JSON are always protected. -**Does it work where AI is blocked?** Yes — 3-level proxy + 1proxy marketplace reach all 259 providers. +**Does it work where AI is blocked?** Yes — 3-level proxy + 1proxy marketplace reach all 264 providers. 📖 [User Guide](docs/guides/USER_GUIDE.md) · [API Reference](docs/reference/API_REFERENCE.md) · [Environment Config](docs/reference/ENVIRONMENT.md) diff --git a/changelog.d/features/6650-g4f-space-gateway.md b/changelog.d/features/6650-g4f-space-gateway.md new file mode 100644 index 0000000000..b3c8089f0d --- /dev/null +++ b/changelog.d/features/6650-g4f-space-gateway.md @@ -0,0 +1 @@ +- feat(sse): add 5 no-key g4f.space gateway providers — `g4f-groq`, `g4f-gemini`, `g4f-pollinations`, `g4f-ollama`, `g4f-nvidia` — a free, no-signup reverse proxy (gpt4free project) fronting Groq, Gemini, Pollinations, Ollama, and NVIDIA NIM, rate-limited to 5 req/min per IP (#6650 — thanks @chirag127). diff --git a/docs/reference/PROVIDER_REFERENCE.md b/docs/reference/PROVIDER_REFERENCE.md index 9eb0df2d0c..74298874cc 100644 --- a/docs/reference/PROVIDER_REFERENCE.md +++ b/docs/reference/PROVIDER_REFERENCE.md @@ -10,7 +10,7 @@ lastUpdated: 2026-07-17 > Regenerate with: `npm run gen:provider-reference` > **Last generated:** 2026-07-17 -Total providers: **259**. See category breakdown below. +Total providers: **264**. See category breakdown below. ## Categories @@ -90,7 +90,7 @@ Use the dashboard at `/dashboard/providers` to enable, configure, and test each | `zai-web` | `zw` | Z.ai Web (Free) | Web cookie | [link](https://chat.z.ai) | Paste the full Cookie header from chat.z.ai (must include the token= cookie) | | `zenmux-free` | `zmf` | ZenMux Free (Web) | Web cookie | [link](https://zenmux.ai) | Login at zenmux.ai, then export all cookies using EditThisCookie or Cookie-Editor and paste the full Cookie header string here. Refresh every ~30 days. | -## API Key Providers (paid / paid-with-free-credits) (172) +## API Key Providers (paid / paid-with-free-credits) (177) | ID | Alias | Name | Tags | Website | Notes | |----|-------|------|------|---------|-------| @@ -149,6 +149,11 @@ Use the dashboard at `/dashboard/providers` to enable, configure, and test each | `freepik` | `fpk` | Freepik (Mystic) | API key, image | [link](https://freepik.com) | Get API key at freepik.com/developers (Mystic image endpoint) | | `freetheai` | `fta` | FreeTheAi | API key, aggregator | [link](https://freetheai.xyz) | Join the FreeTheAi Discord to get your free API key. | | `friendliai` | `friendli` | FriendliAI | API key | [link](https://friendli.ai) | Free tier for serverless inference — no credit card required | +| `g4f-gemini` | `g4fgem` | g4f.space — Gemini | API key, aggregator | [link](https://g4f.space) | No auth required. Free tier is limited to 5 requests/minute — sign up at g4f.dev/members.html for higher limits. | +| `g4f-groq` | `g4fgroq` | g4f.space — Groq | API key, aggregator | [link](https://g4f.space) | No auth required. Free tier is limited to 5 requests/minute — sign up at g4f.dev/members.html for higher limits. | +| `g4f-nvidia` | `g4fnv` | g4f.space — NVIDIA | API key, aggregator | [link](https://g4f.space) | No auth required. Free tier is limited to 5 requests/minute — sign up at g4f.dev/members.html for higher limits. | +| `g4f-ollama` | `g4foll` | g4f.space — Ollama | API key, aggregator | [link](https://g4f.space) | No auth required. Free tier is limited to 5 requests/minute — sign up at g4f.dev/members.html for higher limits. | +| `g4f-pollinations` | `g4fpol` | g4f.space — Pollinations | API key, aggregator | [link](https://g4f.space) | No auth required. Free tier is limited to 5 requests/minute — sign up at g4f.dev/members.html for higher limits. | | `galadriel` | `galadriel` | Galadriel | API key | [link](https://galadriel.com) | ⚠️ **DEPRECATED.** api.galadriel.ai no longer resolves (sweep 2026-06-19); the inference API appears discontinued. | | `gemini` | `gemini` | Gemini (Google AI Studio) | API key | [link](https://aistudio.google.com) | Free forever: 1,500 req/day for Gemini 2.5 Flash — no credit card, get key at aistudio.google.com | | `getgoapi` | `ggo` | GoAPI | API key, aggregator | [link](https://api.getgoapi.com) | — | diff --git a/open-sse/config/providers/index.ts b/open-sse/config/providers/index.ts index c3943a95e6..ecdd7a82b5 100644 --- a/open-sse/config/providers/index.ts +++ b/open-sse/config/providers/index.ts @@ -66,6 +66,11 @@ import { cursorProvider } from "./registry/cursor/index.ts"; import { volcengineProvider } from "./registry/volcengine/index.ts"; import { hackclubProvider } from "./registry/hackclub/index.ts"; import { freetheaiProvider } from "./registry/freetheai/index.ts"; +import { g4f_groqProvider } from "./registry/g4f-groq/index.ts"; +import { g4f_geminiProvider } from "./registry/g4f-gemini/index.ts"; +import { g4f_pollinationsProvider } from "./registry/g4f-pollinations/index.ts"; +import { g4f_ollamaProvider } from "./registry/g4f-ollama/index.ts"; +import { g4f_nvidiaProvider } from "./registry/g4f-nvidia/index.ts"; import { tencentProvider } from "./registry/tencent/index.ts"; import { cozeProvider } from "./registry/coze/index.ts"; import { ai21Provider } from "./registry/ai21/index.ts"; @@ -256,6 +261,11 @@ export const REGISTRY: Record = { volcengine: volcengineProvider, hackclub: hackclubProvider, freetheai: freetheaiProvider, + "g4f-groq": g4f_groqProvider, + "g4f-gemini": g4f_geminiProvider, + "g4f-pollinations": g4f_pollinationsProvider, + "g4f-ollama": g4f_ollamaProvider, + "g4f-nvidia": g4f_nvidiaProvider, tencent: tencentProvider, coze: cozeProvider, ai21: ai21Provider, diff --git a/open-sse/config/providers/registry/g4f-gemini/index.ts b/open-sse/config/providers/registry/g4f-gemini/index.ts new file mode 100644 index 0000000000..1c977b90b7 --- /dev/null +++ b/open-sse/config/providers/registry/g4f-gemini/index.ts @@ -0,0 +1,20 @@ +import type { RegistryEntry } from "../../shared.ts"; + +// g4f.space/api/gemini — no-key reverse proxy to Gemini (gpt4free project, issue #6650). +// Distinct auth mechanism from the existing gemini-web (browser cookie): this is a +// plain no-key HTTP proxy. Same OpenAI-compatible shape as the other no-key gateways. +export const g4f_geminiProvider: RegistryEntry = { + id: "g4f-gemini", + alias: "g4fgem", + format: "openai", + executor: "default", + baseUrl: "https://g4f.space/api/gemini/v1/chat/completions", + modelsUrl: "https://g4f.space/api/gemini/v1/models", + authType: "optional", + authHeader: "bearer", + passthroughModels: true, + models: [ + { id: "models/gemini-2.5-flash", name: "Gemini 2.5 Flash (g4f)" }, + { id: "models/gemini-2.5-pro", name: "Gemini 2.5 Pro (g4f)" }, + ], +}; diff --git a/open-sse/config/providers/registry/g4f-groq/index.ts b/open-sse/config/providers/registry/g4f-groq/index.ts new file mode 100644 index 0000000000..665b79732c --- /dev/null +++ b/open-sse/config/providers/registry/g4f-groq/index.ts @@ -0,0 +1,20 @@ +import type { RegistryEntry } from "../../shared.ts"; + +// g4f.space/api/groq — no-key reverse proxy to Groq (gpt4free project, issue #6650). +// Same OpenAI-compatible shape as the other no-key gateways (hackclub, uncloseai): +// standard chat/completions + /v1/models discovery, no custom executor/translator. +export const g4f_groqProvider: RegistryEntry = { + id: "g4f-groq", + alias: "g4fgroq", + format: "openai", + executor: "default", + baseUrl: "https://g4f.space/api/groq/v1/chat/completions", + modelsUrl: "https://g4f.space/api/groq/v1/models", + authType: "optional", + authHeader: "bearer", + passthroughModels: true, + models: [ + { id: "llama-3.3-70b-versatile", name: "Llama 3.3 70B (g4f/Groq)" }, + { id: "llama-3.1-8b-instant", name: "Llama 3.1 8B Instant (g4f/Groq)" }, + ], +}; diff --git a/open-sse/config/providers/registry/g4f-nvidia/index.ts b/open-sse/config/providers/registry/g4f-nvidia/index.ts new file mode 100644 index 0000000000..a270130d53 --- /dev/null +++ b/open-sse/config/providers/registry/g4f-nvidia/index.ts @@ -0,0 +1,22 @@ +import type { RegistryEntry } from "../../shared.ts"; + +// g4f.space/api/nvidia — no-key reverse proxy to NVIDIA NIM (gpt4free project, +// issue #6650). The existing `nvidia` entry requires signup; this is the genuine +// no-key gap the reporter flagged. Free tier is rate-limited to 5 req/min +// (confirmed live via 429 upsell to g4f.dev/members.html). +export const g4f_nvidiaProvider: RegistryEntry = { + id: "g4f-nvidia", + alias: "g4fnv", + format: "openai", + executor: "default", + baseUrl: "https://g4f.space/api/nvidia/v1/chat/completions", + modelsUrl: "https://g4f.space/api/nvidia/v1/models", + authType: "optional", + authHeader: "bearer", + passthroughModels: true, + models: [ + { id: "nvidia/nemotron-3-nano-30b-a3b", name: "Nemotron 3 Nano 30B (g4f/NVIDIA)" }, + { id: "z-ai/glm-5.2", name: "GLM 5.2 (g4f/NVIDIA)" }, + { id: "minimaxai/minimax-m2.7", name: "MiniMax M2.7 (g4f/NVIDIA)" }, + ], +}; diff --git a/open-sse/config/providers/registry/g4f-ollama/index.ts b/open-sse/config/providers/registry/g4f-ollama/index.ts new file mode 100644 index 0000000000..588f1d96f7 --- /dev/null +++ b/open-sse/config/providers/registry/g4f-ollama/index.ts @@ -0,0 +1,18 @@ +import type { RegistryEntry } from "../../shared.ts"; + +// g4f.space/api/ollama — no-key hosted Ollama gateway (gpt4free project, issue #6650). +// Fills a niche none of the existing ollama-* entries cover (local/cloud/search) — +// this is a no-key *hosted* Ollama proxy. Same OpenAI-compatible shape as the other +// no-key gateways. +export const g4f_ollamaProvider: RegistryEntry = { + id: "g4f-ollama", + alias: "g4foll", + format: "openai", + executor: "default", + baseUrl: "https://g4f.space/api/ollama/v1/chat/completions", + modelsUrl: "https://g4f.space/api/ollama/v1/models", + authType: "optional", + authHeader: "bearer", + passthroughModels: true, + models: [{ id: "gemma3:4b", name: "Gemma 3 4B (g4f/Ollama)" }], +}; diff --git a/open-sse/config/providers/registry/g4f-pollinations/index.ts b/open-sse/config/providers/registry/g4f-pollinations/index.ts new file mode 100644 index 0000000000..9aaeb03d1b --- /dev/null +++ b/open-sse/config/providers/registry/g4f-pollinations/index.ts @@ -0,0 +1,20 @@ +import type { RegistryEntry } from "../../shared.ts"; + +// g4f.space/api/pollinations — no-key reverse proxy to Pollinations (gpt4free project, +// issue #6650). Separate route from the existing direct pollinations.ai entry; same +// OpenAI-compatible shape as the other no-key gateways. +export const g4f_pollinationsProvider: RegistryEntry = { + id: "g4f-pollinations", + alias: "g4fpol", + format: "openai", + executor: "default", + baseUrl: "https://g4f.space/api/pollinations/v1/chat/completions", + modelsUrl: "https://g4f.space/api/pollinations/v1/models", + authType: "optional", + authHeader: "bearer", + passthroughModels: true, + models: [ + { id: "openai", name: "OpenAI (g4f/Pollinations)" }, + { id: "openai-fast", name: "OpenAI Fast (g4f/Pollinations)" }, + ], +}; diff --git a/src/shared/constants/providers.ts b/src/shared/constants/providers.ts index 65aa041144..93c7cc4ef8 100644 --- a/src/shared/constants/providers.ts +++ b/src/shared/constants/providers.ts @@ -80,6 +80,11 @@ export const AGGREGATOR_PROVIDER_IDS = new Set([ "chutes", "hackclub", "freetheai", + "g4f-groq", + "g4f-gemini", + "g4f-pollinations", + "g4f-ollama", + "g4f-nvidia", ]); export const ENTERPRISE_CLOUD_PROVIDER_IDS = new Set([ @@ -165,17 +170,30 @@ export function isSelfHostedChatProvider(providerId: unknown): boolean { return typeof providerId === "string" && SELF_HOSTED_CHAT_PROVIDER_IDS.has(providerId); } +// Providers with heterogeneous/no-key auth that don't fit the NOAUTH_PROVIDERS +// registry (e.g. free-tier gateways where a key is accepted but not required). +// Kept as a Set (not an || chain) to keep providerAllowsOptionalApiKey's +// cyclomatic complexity flat as this list grows — see g4f.space (#6650). +const EXPLICIT_OPTIONAL_APIKEY_PROVIDER_IDS = new Set([ + "searxng-search", + "pollinations", + "copilot-web", + "hackclub", + "g4f-groq", + "g4f-gemini", + "g4f-pollinations", + "g4f-ollama", + "g4f-nvidia", + "huggingchat", + "gitlawb", + "gitlawb-gmi", +]); + export function providerAllowsOptionalApiKey(providerId: unknown): boolean { return ( // ponytail: any noAuth provider auto-qualifies — no per-provider maintenance (typeof providerId === "string" && providerId in NOAUTH_PROVIDERS) || - providerId === "searxng-search" || - providerId === "pollinations" || - providerId === "copilot-web" || - providerId === "hackclub" || - providerId === "huggingchat" || - providerId === "gitlawb" || - providerId === "gitlawb-gmi" || + (typeof providerId === "string" && EXPLICIT_OPTIONAL_APIKEY_PROVIDER_IDS.has(providerId)) || isLocalProvider(providerId) || isSelfHostedChatProvider(providerId) || isOpenAICompatibleProvider(providerId) || diff --git a/src/shared/constants/providers/apikey/gateways.ts b/src/shared/constants/providers/apikey/gateways.ts index 0e16497fbc..3e08899728 100644 --- a/src/shared/constants/providers/apikey/gateways.ts +++ b/src/shared/constants/providers/apikey/gateways.ts @@ -251,6 +251,81 @@ export const APIKEY_PROVIDERS_GATEWAYS = { passthroughModels: true, authHint: "Join the FreeTheAi Discord to get your free API key.", }, + "g4f-groq": { + id: "g4f-groq", + alias: "g4fgroq", + name: "g4f.space — Groq", + icon: "bolt", + color: "#F97316", + textIcon: "G4F", + website: "https://g4f.space", + hasFree: true, + 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.", + }, + "g4f-gemini": { + id: "g4f-gemini", + alias: "g4fgem", + name: "g4f.space — Gemini", + icon: "bolt", + color: "#F97316", + textIcon: "G4F", + website: "https://g4f.space", + hasFree: true, + 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.", + }, + "g4f-pollinations": { + id: "g4f-pollinations", + alias: "g4fpol", + name: "g4f.space — Pollinations", + icon: "bolt", + color: "#F97316", + textIcon: "G4F", + website: "https://g4f.space", + hasFree: true, + freeNote: + "Free no-key reverse proxy to Pollinations (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.", + }, + "g4f-ollama": { + id: "g4f-ollama", + alias: "g4foll", + name: "g4f.space — Ollama", + icon: "bolt", + color: "#F97316", + textIcon: "G4F", + website: "https://g4f.space", + hasFree: true, + 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.", + }, + "g4f-nvidia": { + id: "g4f-nvidia", + alias: "g4fnv", + name: "g4f.space — NVIDIA", + icon: "bolt", + color: "#F97316", + textIcon: "G4F", + website: "https://g4f.space", + hasFree: true, + freeNote: + "Free no-key reverse proxy to NVIDIA NIM (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.", + }, "vercel-ai-gateway": { id: "vercel-ai-gateway", alias: "vag", diff --git a/tests/snapshots/provider/translate-path.json b/tests/snapshots/provider/translate-path.json index 1242dc13d5..f386d3a883 100644 --- a/tests/snapshots/provider/translate-path.json +++ b/tests/snapshots/provider/translate-path.json @@ -1646,6 +1646,121 @@ "stream": "https://api.friendli.ai/serverless/v1/chat/completions" } }, + "g4f-gemini": { + "format": "openai", + "headers": { + "apiKey": { + "Accept": "text/event-stream", + "Authorization": "Bearer ", + "Content-Type": "application/json" + }, + "nonStream": { + "Authorization": "Bearer ", + "Content-Type": "application/json" + }, + "oauth": { + "Accept": "text/event-stream", + "Authorization": "Bearer ", + "Content-Type": "application/json" + } + }, + "url": { + "nonStream": "https://g4f.space/api/gemini/v1/chat/completions", + "stream": "https://g4f.space/api/gemini/v1/chat/completions" + } + }, + "g4f-groq": { + "format": "openai", + "headers": { + "apiKey": { + "Accept": "text/event-stream", + "Authorization": "Bearer ", + "Content-Type": "application/json" + }, + "nonStream": { + "Authorization": "Bearer ", + "Content-Type": "application/json" + }, + "oauth": { + "Accept": "text/event-stream", + "Authorization": "Bearer ", + "Content-Type": "application/json" + } + }, + "url": { + "nonStream": "https://g4f.space/api/groq/v1/chat/completions", + "stream": "https://g4f.space/api/groq/v1/chat/completions" + } + }, + "g4f-nvidia": { + "format": "openai", + "headers": { + "apiKey": { + "Accept": "text/event-stream", + "Authorization": "Bearer ", + "Content-Type": "application/json" + }, + "nonStream": { + "Authorization": "Bearer ", + "Content-Type": "application/json" + }, + "oauth": { + "Accept": "text/event-stream", + "Authorization": "Bearer ", + "Content-Type": "application/json" + } + }, + "url": { + "nonStream": "https://g4f.space/api/nvidia/v1/chat/completions", + "stream": "https://g4f.space/api/nvidia/v1/chat/completions" + } + }, + "g4f-ollama": { + "format": "openai", + "headers": { + "apiKey": { + "Accept": "text/event-stream", + "Authorization": "Bearer ", + "Content-Type": "application/json" + }, + "nonStream": { + "Authorization": "Bearer ", + "Content-Type": "application/json" + }, + "oauth": { + "Accept": "text/event-stream", + "Authorization": "Bearer ", + "Content-Type": "application/json" + } + }, + "url": { + "nonStream": "https://g4f.space/api/ollama/v1/chat/completions", + "stream": "https://g4f.space/api/ollama/v1/chat/completions" + } + }, + "g4f-pollinations": { + "format": "openai", + "headers": { + "apiKey": { + "Accept": "text/event-stream", + "Authorization": "Bearer ", + "Content-Type": "application/json" + }, + "nonStream": { + "Authorization": "Bearer ", + "Content-Type": "application/json" + }, + "oauth": { + "Accept": "text/event-stream", + "Authorization": "Bearer ", + "Content-Type": "application/json" + } + }, + "url": { + "nonStream": "https://g4f.space/api/pollinations/v1/chat/completions", + "stream": "https://g4f.space/api/pollinations/v1/chat/completions" + } + }, "galadriel": { "format": "openai", "headers": { diff --git a/tests/unit/g4f-space-gateway-6650.test.ts b/tests/unit/g4f-space-gateway-6650.test.ts new file mode 100644 index 0000000000..8f1aadc2bc --- /dev/null +++ b/tests/unit/g4f-space-gateway-6650.test.ts @@ -0,0 +1,103 @@ +/** + * Issue #6650 — Add g4f.space no-key gateway (Groq/Ollama/Pollinations/NVIDIA/Gemini + * via gpt4free). + * + * Live-verified reachability (triage, not re-verified in CI — see plan-file): + * GET https://g4f.space/api/nvidia/models → 200, real model list + * GET https://g4f.space/api/ollama/v1/models → 200, real model list + * GET https://g4f.space/api/pollinations/v1/models → 200, real model list + * GET https://g4f.space/api/gemini/v1/models → 200, real model list + * GET https://g4f.space/api/groq/... → live Groq backend + * + * Verifies each of the 5 sub-path providers is wired end-to-end the same way as + * the other no-key gateway providers (hackclub, uncloseai): + * - present in the executor REGISTRY with a no-key OpenAI-compatible shape + * - resolvable through getExecutor() (falls through to DefaultExecutor) + * - listed in AGGREGATOR_PROVIDER_IDS so it shows up in the aggregator + * category on the dashboard + * - allowed to skip API key validation (providerAllowsOptionalApiKey) + * - has provider metadata (name/website/free-tier note) in the apikey + * gateway catalog + */ +import test from "node:test"; +import assert from "node:assert/strict"; + +interface RegistryEntryShape { + format?: string; + executor?: string; + baseUrl?: string; + modelsUrl?: string; + authType?: string; + authHeader?: string; + passthroughModels?: boolean; + models?: unknown[]; +} + +interface ApikeyMetaShape { + id?: string; + name?: string; + website?: string; + hasFree?: boolean; + freeNote?: string; +} + +const { REGISTRY } = await import("../../open-sse/config/providerRegistry.ts"); +const { getExecutor, DefaultExecutor } = await import("../../open-sse/executors/index.ts"); +const { AGGREGATOR_PROVIDER_IDS, providerAllowsOptionalApiKey } = await import( + "../../src/shared/constants/providers.ts" +); +const { APIKEY_PROVIDERS } = await import("../../src/shared/constants/providers/apikey/index.ts"); + +const SUB_PATHS: Record = { + "g4f-groq": "groq", + "g4f-gemini": "gemini", + "g4f-pollinations": "pollinations", + "g4f-ollama": "ollama", + "g4f-nvidia": "nvidia", +}; + +for (const [id, subPath] of Object.entries(SUB_PATHS)) { + test(`#6650 ${id} is registered in the executor registry with a no-key OpenAI-compatible shape`, () => { + const entry = (REGISTRY as Record)[id]; + assert.ok(entry, `${id} should be present in the executor registry`); + assert.equal(entry.format, "openai"); + assert.equal(entry.executor, "default"); + assert.equal(entry.baseUrl, `https://g4f.space/api/${subPath}/v1/chat/completions`); + assert.equal(entry.modelsUrl, `https://g4f.space/api/${subPath}/v1/models`); + assert.equal(entry.authType, "optional"); + assert.equal(entry.passthroughModels, true); + assert.ok( + Array.isArray(entry.models) && entry.models.length > 0, + `${id} must seed a fallback model list` + ); + }); + + test(`#6650 ${id} resolves through getExecutor() as a DefaultExecutor instance`, () => { + const executor = getExecutor(id); + assert.ok( + executor instanceof DefaultExecutor, + `${id} has no custom executor — must fall through to DefaultExecutor` + ); + }); + + test(`#6650 ${id} is classified as an aggregator/gateway provider`, () => { + assert.ok( + AGGREGATOR_PROVIDER_IDS.has(id), + `${id} must be listed in AGGREGATOR_PROVIDER_IDS alongside hackclub/uncloseai` + ); + }); + + test(`#6650 ${id} allows optional (no-key) API key validation`, () => { + assert.equal(providerAllowsOptionalApiKey(id), true); + }); + + test(`#6650 ${id} has provider metadata with free-tier info`, () => { + const meta = (APIKEY_PROVIDERS as Record)[id]; + assert.ok(meta, `${id} should have an APIKEY_PROVIDERS metadata entry`); + assert.equal(meta.id, id); + assert.equal(meta.website, "https://g4f.space"); + assert.equal(meta.hasFree, true); + assert.equal(typeof meta.freeNote, "string"); + assert.ok((meta.freeNote as string).length > 0); + }); +} diff --git a/tests/unit/providers-constants-split.test.ts b/tests/unit/providers-constants-split.test.ts index 779834e0f1..546145468e 100644 --- a/tests/unit/providers-constants-split.test.ts +++ b/tests/unit/providers-constants-split.test.ts @@ -1,14 +1,16 @@ // Characterization of the providers.ts catalog split (god-file decomposition): the host became a // barrel that re-exports 10 data catalogs now living under constants/providers/*, and APIKEY is // merged from 6 semantic family files (apikey/.ts). Locks: the public surface (every catalog -// + helpers still exported), the spread-merge integrity (168 APIKEY entries, no loss/dup), and that +// + helpers still exported), the spread-merge integrity (177 APIKEY entries, no loss/dup), and that // load-time Zod validation still runs. Pure-data move → behavior must be identical. // Count was 171 before obsolete provider removals (PR #6675: glhf/kluster/cablyai/inclusionai etc., // 171->167) plus #6126 (ClinePass dual-auth): the API-key-only APIKEY_PROVIDERS_GATEWAYS entry was // removed as a duplicate now that clinepass is OAuth-primary (OAUTH_PROVIDERS.clinepass) with its // BYOK path admitted through the DUAL_AUTH_APIKEY_PROVIDER_IDS gate instead (167->166), then the // OpenVecta inference-gateway addition brought it back to 167, then #7246 (Chenzk API gateway) -// brought it to 168. +// brought it to 168, then more additions brought it to 172, then #6650 (g4f.space no-key gateway: +// 5 new sub-path entries — g4f-groq/g4f-gemini/g4f-pollinations/g4f-ollama/g4f-nvidia) brought it +// to 177. import { test } from "node:test"; import assert from "node:assert/strict"; @@ -37,12 +39,12 @@ test("barrel still exports every catalog + key helpers", () => { } }); -test("APIKEY_PROVIDERS merges the 6 family files into 172 entries (no loss / no dup)", async () => { +test("APIKEY_PROVIDERS merges the 6 family files into 177 entries (no loss / no dup)", async () => { const keys = Object.keys((P as Record).APIKEY_PROVIDERS); - assert.equal(keys.length, 172); - assert.equal(new Set(keys).size, 172, "duplicate keys after spread-merge"); + assert.equal(keys.length, 177); + assert.equal(new Set(keys).size, 177, "duplicate keys after spread-merge"); // the merged object's entry-count equals the sum of the 6 semantic family files; families are a - // strict partition (every provider in exactly one), so the sum must be exactly 172. + // strict partition (every provider in exactly one), so the sum must be exactly 177. const families: [string, string][] = [ ["gateways", "APIKEY_PROVIDERS_GATEWAYS"], ["frontier-labs", "APIKEY_PROVIDERS_FRONTIER"], @@ -62,7 +64,7 @@ test("APIKEY_PROVIDERS merges the 6 family files into 172 entries (no loss / no seen.add(k); } } - assert.equal(famTotal, 172, "families must partition all 172 providers"); + assert.equal(famTotal, 177, "families must partition all 177 providers"); }); test("AI_PROVIDERS Proxy aggregates all sections; lookups resolve", () => {
🚫 Never hit limits
Auto-fallback across 259 providers in milliseconds. Quota out? Next provider takes over — zero downtime.
🚫 Never hit limits
Auto-fallback across 264 providers in milliseconds. Quota out? Next provider takes over — zero downtime.
💸 Save up to 95% tokens
RTK + Caveman stacked compression cuts 15–95% of eligible tokens (~89% avg on tool-heavy sessions).
🆓 $0 to start
90+ providers with a free tier, 11 free forever (Kiro, Qoder, Pollinations, LongCat…). No card needed.