mirror of
https://github.com/diegosouzapw/OmniRoute.git
synced 2026-08-05 06:42:12 +03:00
feat(provider): add Chenzk API OpenAI-compatible gateway (#7246)
* feat(provider): add Chenzk API OpenAI-compatible gateway
Registers Chenzk (chenzk.top) as a new API-key gateway provider — an
OpenAI-compatible aggregator exposing GPT/Claude/DeepSeek/GLM model groups
behind one endpoint. Adapted to OmniRoute's directory-per-provider registry
(open-sse/config/providers/registry/) and metadata catalog
(src/shared/constants/providers/apikey/gateways.ts), following the same
passthrough-models pattern already used for kenari/x5lab/sumopod (live
/v1/models catalog resolves the model list instead of a hardcoded array).
Co-authored-by: Ahmad Putra Cahyo <CahyokPutraDev99@users.noreply.github.com>
Inspired-by: https://github.com/decolua/9router/pull/2437
* chore(changelog): fragment for #7246
* test(provider): regen golden snapshot + bump family-count for Chenzk gateway
The Chenzk provider added in a616b88c9 registered a new APIKEY_PROVIDERS
entry (gateways.ts) but did not update the two characterization tests
that assert exact provider counts: the translate-path golden snapshot
(missing the chenzk entry) and the 167-entry family-merge count in
providers-constants-split.test.ts (now 168, verified as a strict
partition sum across the 6 family files, no loss/dup).
Co-authored-by: Ahmad Putra Cahyo <CahyokPutraDev99@users.noreply.github.com>
---------
Co-authored-by: Ahmad Putra Cahyo <CahyokPutraDev99@users.noreply.github.com>
This commit is contained in:
committed by
GitHub
parent
62bea04b25
commit
ea32dcf863
1
changelog.d/features/7246-chenzk-provider.md
Normal file
1
changelog.d/features/7246-chenzk-provider.md
Normal file
@@ -0,0 +1 @@
|
||||
- **feat(provider):** add Chenzk API OpenAI-compatible gateway. (thanks @CahyokPutraDev99)
|
||||
@@ -151,6 +151,7 @@ import { gigachatProvider } from "./registry/gigachat/index.ts";
|
||||
import { devin_cliProvider } from "./registry/devin-cli/index.ts";
|
||||
import { auggieProvider } from "./registry/auggie/index.ts";
|
||||
import { chutesProvider } from "./registry/chutes/index.ts";
|
||||
import { chenzkProvider } from "./registry/chenzk/index.ts";
|
||||
import { factoryProvider } from "./registry/factory/index.ts";
|
||||
import { databricksProvider } from "./registry/databricks/index.ts";
|
||||
import { rekaProvider } from "./registry/reka/index.ts";
|
||||
@@ -336,6 +337,7 @@ export const REGISTRY: Record<string, RegistryEntry> = {
|
||||
"devin-cli": devin_cliProvider,
|
||||
auggie: auggieProvider,
|
||||
chutes: chutesProvider,
|
||||
chenzk: chenzkProvider,
|
||||
factory: factoryProvider,
|
||||
databricks: databricksProvider,
|
||||
reka: rekaProvider,
|
||||
|
||||
15
open-sse/config/providers/registry/chenzk/index.ts
Normal file
15
open-sse/config/providers/registry/chenzk/index.ts
Normal file
@@ -0,0 +1,15 @@
|
||||
import type { RegistryEntry } from "../../shared.ts";
|
||||
|
||||
export const chenzkProvider: RegistryEntry = {
|
||||
id: "chenzk",
|
||||
alias: "chenzk",
|
||||
format: "openai",
|
||||
executor: "default",
|
||||
baseUrl: "https://chenzk.top/v1/chat/completions",
|
||||
authType: "apikey",
|
||||
authHeader: "bearer",
|
||||
modelsUrl: "https://chenzk.top/v1/models",
|
||||
defaultContextLength: 128000,
|
||||
models: [],
|
||||
passthroughModels: true,
|
||||
};
|
||||
@@ -23,6 +23,7 @@ export const PROVIDER_ENDPOINTS = {
|
||||
sumopod: "https://ai.sumopod.com/v1/chat/completions",
|
||||
x5lab: "https://api.x5lab.dev/v1/chat/completions",
|
||||
kenari: "https://kenari.id/v1/chat/completions",
|
||||
chenzk: "https://chenzk.top/v1/chat/completions",
|
||||
openai: "https://api.openai.com/v1/chat/completions",
|
||||
anthropic: "https://api.anthropic.com/v1/messages",
|
||||
gemini: "https://generativelanguage.googleapis.com/v1beta/models",
|
||||
|
||||
@@ -579,6 +579,19 @@ export const APIKEY_PROVIDERS_GATEWAYS = {
|
||||
apiHint:
|
||||
"X5Lab exposes an OpenAI-compatible chat completions endpoint at https://api.x5lab.dev/v1/chat/completions, plus a live /v1/models catalog. OmniRoute uses the OpenAI protocol and lists models via passthrough.",
|
||||
},
|
||||
chenzk: {
|
||||
id: "chenzk",
|
||||
alias: "chenzk",
|
||||
name: "Chenzk API",
|
||||
icon: "hub",
|
||||
color: "#10B981",
|
||||
textIcon: "CZ",
|
||||
passthroughModels: true,
|
||||
website: "https://chenzk.top",
|
||||
apiHint:
|
||||
"Create an API key at https://chenzk.top/token, then paste it here as a Bearer token. " +
|
||||
"OpenAI-compatible endpoint at https://chenzk.top/v1, with a live /v1/models catalog.",
|
||||
},
|
||||
kenari: {
|
||||
id: "kenari",
|
||||
alias: "kenari",
|
||||
|
||||
@@ -658,6 +658,29 @@
|
||||
"stream": "https://chatgpt.com/backend-api/conversation"
|
||||
}
|
||||
},
|
||||
"chenzk": {
|
||||
"format": "openai",
|
||||
"headers": {
|
||||
"apiKey": {
|
||||
"Accept": "text/event-stream",
|
||||
"Authorization": "Bearer <TOK>",
|
||||
"Content-Type": "application/json"
|
||||
},
|
||||
"nonStream": {
|
||||
"Authorization": "Bearer <TOK>",
|
||||
"Content-Type": "application/json"
|
||||
},
|
||||
"oauth": {
|
||||
"Accept": "text/event-stream",
|
||||
"Authorization": "Bearer <TOK>",
|
||||
"Content-Type": "application/json"
|
||||
}
|
||||
},
|
||||
"url": {
|
||||
"nonStream": "https://chenzk.top/v1/chat/completions",
|
||||
"stream": "https://chenzk.top/v1/chat/completions"
|
||||
}
|
||||
},
|
||||
"chipotle": {
|
||||
"format": "openai",
|
||||
"headers": {
|
||||
|
||||
49
tests/unit/chenzk-provider-2437.test.ts
Normal file
49
tests/unit/chenzk-provider-2437.test.ts
Normal file
@@ -0,0 +1,49 @@
|
||||
import test from "node:test";
|
||||
import assert from "node:assert/strict";
|
||||
|
||||
const { APIKEY_PROVIDERS } = await import("../../src/shared/constants/providers.ts");
|
||||
const { PROVIDER_ENDPOINTS } = await import("../../src/shared/constants/config.ts");
|
||||
const { REGISTRY: providerRegistry } = await import("../../open-sse/config/providerRegistry.ts");
|
||||
|
||||
const CHENZK_CHAT_URL = "https://chenzk.top/v1/chat/completions";
|
||||
const CHENZK_MODELS_URL = "https://chenzk.top/v1/models";
|
||||
|
||||
// Port of decolua/9router#2437 ("feat: add Chenzk API provider"), adapted to
|
||||
// OmniRoute's directory-per-provider registry (`open-sse/config/providers/registry/`)
|
||||
// and the `src/shared/constants/providers/apikey/*` metadata catalog, instead of
|
||||
// upstream's flat `open-sse/providers/registry/*.js` + hardcoded model array. Chenzk
|
||||
// exposes a "New API"-style OpenAI-compatible gateway with a live /v1/models catalog,
|
||||
// so — matching the sibling kenari/x5lab/sumopod gateways already in this catalog —
|
||||
// models are resolved via passthrough rather than a speculative hardcoded list.
|
||||
test("Chenzk is registered as an OpenAI-compatible API-key gateway", () => {
|
||||
const entry = APIKEY_PROVIDERS.chenzk;
|
||||
assert.ok(entry, "APIKEY_PROVIDERS.chenzk must be defined");
|
||||
assert.equal(entry.id, "chenzk");
|
||||
assert.equal(entry.alias, "chenzk");
|
||||
assert.equal(entry.name, "Chenzk API");
|
||||
assert.equal(entry.website, "https://chenzk.top");
|
||||
assert.equal(entry.passthroughModels, true);
|
||||
});
|
||||
|
||||
test("Chenzk exposes the OpenAI-compatible chat completions endpoint", () => {
|
||||
assert.equal(PROVIDER_ENDPOINTS.chenzk, CHENZK_CHAT_URL);
|
||||
});
|
||||
|
||||
test("Chenzk registry entry uses OpenAI format with bearer API-key auth and passthrough models", () => {
|
||||
const entry = providerRegistry.chenzk;
|
||||
assert.ok(entry, "providerRegistry.chenzk must be defined");
|
||||
assert.equal(entry.id, "chenzk");
|
||||
assert.equal(entry.alias, "chenzk");
|
||||
assert.equal(entry.format, "openai");
|
||||
assert.equal(entry.executor, "default");
|
||||
assert.equal(entry.authType, "apikey");
|
||||
assert.equal(entry.authHeader, "bearer");
|
||||
assert.equal(entry.baseUrl, CHENZK_CHAT_URL);
|
||||
assert.equal(entry.modelsUrl, CHENZK_MODELS_URL);
|
||||
assert.equal(entry.passthroughModels, true);
|
||||
assert.deepEqual(
|
||||
entry.models,
|
||||
[],
|
||||
"Chenzk ships no speculative seeded models — live catalog via passthrough only"
|
||||
);
|
||||
});
|
||||
@@ -1,13 +1,14 @@
|
||||
// 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/<family>.ts). Locks: the public surface (every catalog
|
||||
// + helpers still exported), the spread-merge integrity (167 APIKEY entries, no loss/dup), and that
|
||||
// + helpers still exported), the spread-merge integrity (168 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.
|
||||
// OpenVecta inference-gateway addition brought it back to 167, then #7246 (Chenzk API gateway)
|
||||
// brought it to 168.
|
||||
import { test } from "node:test";
|
||||
import assert from "node:assert/strict";
|
||||
|
||||
@@ -36,10 +37,10 @@ test("barrel still exports every catalog + key helpers", () => {
|
||||
}
|
||||
});
|
||||
|
||||
test("APIKEY_PROVIDERS merges the 6 family files into 167 entries (no loss / no dup)", async () => {
|
||||
test("APIKEY_PROVIDERS merges the 6 family files into 168 entries (no loss / no dup)", async () => {
|
||||
const keys = Object.keys((P as Record<string, object>).APIKEY_PROVIDERS);
|
||||
assert.equal(keys.length, 167);
|
||||
assert.equal(new Set(keys).size, 167, "duplicate keys after spread-merge");
|
||||
assert.equal(keys.length, 168);
|
||||
assert.equal(new Set(keys).size, 168, "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 167.
|
||||
const families: [string, string][] = [
|
||||
@@ -61,7 +62,7 @@ test("APIKEY_PROVIDERS merges the 6 family files into 167 entries (no loss / no
|
||||
seen.add(k);
|
||||
}
|
||||
}
|
||||
assert.equal(famTotal, 167, "families must partition all 167 providers");
|
||||
assert.equal(famTotal, 168, "families must partition all 168 providers");
|
||||
});
|
||||
|
||||
test("AI_PROVIDERS Proxy aggregates all sections; lookups resolve", () => {
|
||||
|
||||
Reference in New Issue
Block a user