Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions changelog.d/features/6674-gpt4free-batch-3-providers.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
- **feat(providers):** add Naga.ac and ChatAnywhere aggregator gateway providers (#6674 — thanks @chirag127)
4 changes: 4 additions & 0 deletions open-sse/config/providers/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -229,6 +229,8 @@ import { hcnsecProvider } from "./registry/hcnsec/index.ts";
import { promptqlProvider } from "./registry/promptql/index.ts";
import { hyperagentProvider } from "./registry/hyperagent/index.ts";
import { muse_codeProvider } from "./registry/muse-code/index.ts";
import { naga_acProvider } from "./registry/naga-ac/index.ts";
import { chatanywhereProvider } from "./registry/chatanywhere/index.ts";

export const REGISTRY: Record<string, RegistryEntry> = {
aimlapi: aimlapiProvider,
Expand Down Expand Up @@ -460,4 +462,6 @@ export const REGISTRY: Record<string, RegistryEntry> = {
hyperagent: hyperagentProvider,
"muse-code": muse_codeProvider,
unorouter: unorouterProvider,
"naga-ac": naga_acProvider,
chatanywhere: chatanywhereProvider,
};
17 changes: 17 additions & 0 deletions open-sse/config/providers/registry/chatanywhere/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import type { RegistryEntry } from "../../shared.ts";

// ChatAnywhere (api.chatanywhere.tech) — OpenAI-compatible gateway from the
// chatanywhere/GPT_API_free project (~38.7k GitHub stars). Requires GitHub-account-
// gated API key. Free tier is for personal non-commercial use only.
export const chatanywhereProvider: RegistryEntry = {
id: "chatanywhere",
alias: "chtany",
format: "openai",
executor: "default",
baseUrl: "https://api.chatanywhere.tech/v1/chat/completions",
modelsUrl: "https://api.chatanywhere.tech/v1/models",
authType: "apikey",
authHeader: "bearer",
passthroughModels: true,
models: [],
};
17 changes: 17 additions & 0 deletions open-sse/config/providers/registry/naga-ac/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import type { RegistryEntry } from "../../shared.ts";

// Naga.ac — OpenAI-compatible aggregator gateway with free models.
// See https://docs.naga.ac for API reference.
// Free models accept an optional API key; authenticated users get higher rate limits.
export const naga_acProvider: RegistryEntry = {
id: "naga-ac",
alias: "naga",
format: "openai",
executor: "default",
baseUrl: "https://api.naga.ac/v1/chat/completions",
modelsUrl: "https://api.naga.ac/v1/models",
authType: "optional",
authHeader: "bearer",
passthroughModels: true,
models: [],
};
3 changes: 3 additions & 0 deletions src/shared/constants/providers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,8 @@ export const AGGREGATOR_PROVIDER_IDS = new Set([
"g4f-pollinations",
"g4f-ollama",
"g4f-nvidia",
"naga-ac",
"chatanywhere",
]);

export const ENTERPRISE_CLOUD_PROVIDER_IDS = new Set([
Expand Down Expand Up @@ -196,6 +198,7 @@ const EXPLICIT_OPTIONAL_APIKEY_PROVIDER_IDS = new Set([
"huggingchat",
"gitlawb",
"gitlawb-gmi",
"naga-ac",
]);

export function providerAllowsOptionalApiKey(providerId: unknown): boolean {
Expand Down
30 changes: 30 additions & 0 deletions src/shared/constants/providers/apikey/gateways.ts
Original file line number Diff line number Diff line change
Expand Up @@ -832,5 +832,35 @@ export const APIKEY_PROVIDERS_GATEWAYS = {
authHint: "Get your Regolo API key from regolo.ai, then paste it here as a Bearer token.",
apiHint:
"OpenAI-compatible endpoint at https://api.regolo.ai/v1 with dynamic model discovery (19 models).",
"naga-ac": {
id: "naga-ac",
alias: "naga",
name: "Naga.ac",
icon: "bolt",
color: "#7C3AED",
textIcon: "NA",
website: "https://naga.ac",
docsUrl: "https://docs.naga.ac",
hasFree: true,
freeNote:
"Free models include Nemotron 3 Ultra (free) and Llama 3.3 70B Instruct (Free). Paid models require credits. Google/GitHub/Discord signup.",
passthroughModels: true,
authHint:
"Get API key at naga.ac — Google/GitHub/Discord signup available.",
},
chatanywhere: {
id: "chatanywhere",
alias: "chtany",
name: "ChatAnywhere",
icon: "chat",
color: "#10B981",
textIcon: "CA",
website: "https://api.chatanywhere.tech",
hasFree: true,
freeNote:
"Free tier: 5 req/day for GPT-5/4o/4.1, 30/day DeepSeek, 200/day gpt-4o-mini. Personal non-commercial use only — see chatanywhere/GPT_API_free. Requires GitHub-account-gated API key.",
passthroughModels: true,
authHint:
"Get free API key at api.chatanywhere.tech — requires GitHub account signup.",
},
};
102 changes: 102 additions & 0 deletions tests/unit/providers-g4f-batch3.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
/**
* Issue #6674 — Add Naga.ac and ChatAnywhere as gpt4free-ecosystem aggregator providers.
*
* Verifies both providers are wired end-to-end the same way as the other aggregator
* gateway providers (g4f-groq, freetheai, hackclub):
* - present in the executor REGISTRY with an OpenAI-compatible shape
* - resolvable through getExecutor() (falls through to DefaultExecutor)
* - listed in AGGREGATOR_PROVIDER_IDS so they show up in the aggregator category
* - has provider metadata (name/website/free-tier note) in the apikey gateway catalog
* - Naga.ac allows optional API key (free models); ChatAnywhere requires a key
*/
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;
passthroughModels?: boolean;
}

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 PROVIDERS: Record<string, { id: string; authType: string; baseUrl: string; website: string }> = {
"naga-ac": {
id: "naga-ac",
authType: "optional",
baseUrl: "https://api.naga.ac/v1/chat/completions",
website: "https://naga.ac",
},
chatanywhere: {
id: "chatanywhere",
authType: "apikey",
baseUrl: "https://api.chatanywhere.tech/v1/chat/completions",
website: "https://api.chatanywhere.tech",
},
};

for (const [id, info] of Object.entries(PROVIDERS)) {
test(`#6674 ${id} is registered in the executor registry with an OpenAI-compatible shape`, () => {
const entry = (REGISTRY as Record<string, RegistryEntryShape>)[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, info.baseUrl);
assert.equal(entry.authType, info.authType);
assert.equal(entry.authHeader, "bearer");
assert.equal(entry.passthroughModels, true);
});

test(`#6674 ${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(`#6674 ${id} is classified as an aggregator/gateway provider`, () => {
assert.ok(
AGGREGATOR_PROVIDER_IDS.has(id),
`${id} must be listed in AGGREGATOR_PROVIDER_IDS`
);
});

test(`#6674 ${id} has provider metadata with free-tier info`, () => {
const meta = (APIKEY_PROVIDERS as Record<string, ApikeyMetaShape>)[id];
assert.ok(meta, `${id} should have an APIKEY_PROVIDERS metadata entry`);
assert.equal(meta.id, id);
assert.equal(meta.website, info.website);
assert.equal(meta.hasFree, true);
assert.equal(typeof meta.freeNote, "string");
assert.ok((meta.freeNote as string).length > 0);
assert.equal(meta.passthroughModels, true);
});
}

test("#6674 naga-ac allows optional (no-key) API key validation", () => {
assert.equal(providerAllowsOptionalApiKey("naga-ac"), true);
});

test("#6674 chatanywhere requires an API key (not optional)", () => {
assert.equal(providerAllowsOptionalApiKey("chatanywhere"), false);
});
Loading