From 4dc959c1f2b4d2c63032e361ff65bba004172e7e Mon Sep 17 00:00:00 2001 From: backryun Date: Tue, 5 May 2026 04:45:24 +0900 Subject: [PATCH] chore(provider): Add reka models list (#1956) Integrated into release/v3.7.9 --- open-sse/config/providerRegistry.ts | 14 ++++++ src/app/api/providers/[id]/models/route.ts | 5 ++ src/app/api/v1/models/catalog.ts | 2 + src/lib/providers/validation.ts | 2 +- src/shared/constants/providers.ts | 1 - .../unit/chat-openai-compat-providers.test.ts | 1 + tests/unit/provider-models-config.test.ts | 10 ++++ tests/unit/provider-models-route.test.ts | 50 +++++++++++-------- 8 files changed, 63 insertions(+), 22 deletions(-) diff --git a/open-sse/config/providerRegistry.ts b/open-sse/config/providerRegistry.ts index e1876559c6..b02226a8fd 100644 --- a/open-sse/config/providerRegistry.ts +++ b/open-sse/config/providerRegistry.ts @@ -2077,6 +2077,20 @@ export const REGISTRY: Record = { { id: "Hermes-4-70B", name: "Hermes 4 70B (Nous Research)" }, ], }, + + reka: { + id: "reka", + alias: "reka", + format: "openai", + executor: "default", + baseUrl: "https://api.reka.ai/v1/chat/completions", + authType: "apikey", + authHeader: "bearer", + models: [ + { id: "reka-flash-3", name: "Reka Flash 3" }, + { id: "reka-edge-2603", name: "Reka Edge 2603" }, + ], + }, }; // ── Generator Functions ─────────────────────────────────────────────────── diff --git a/src/app/api/providers/[id]/models/route.ts b/src/app/api/providers/[id]/models/route.ts index 1e6e7e5962..7b004dc278 100755 --- a/src/app/api/providers/[id]/models/route.ts +++ b/src/app/api/providers/[id]/models/route.ts @@ -949,6 +949,11 @@ export async function GET( }); }; + if (provider === "reka") { + const localCatalog = buildLocalCatalogResponse(); + if (localCatalog) return localCatalog; + } + if ( isOpenAICompatibleProvider(provider) || isLocalOpenAIStyleProvider(provider) || diff --git a/src/app/api/v1/models/catalog.ts b/src/app/api/v1/models/catalog.ts index 4445b0d72c..7783bcfc6f 100644 --- a/src/app/api/v1/models/catalog.ts +++ b/src/app/api/v1/models/catalog.ts @@ -390,6 +390,7 @@ export async function getUnifiedModelsResponse( for (const [providerId, syncedModels] of Object.entries(syncedModelsByProvider)) { if (!Array.isArray(syncedModels) || syncedModels.length === 0) continue; if (blockedProviders.has(providerId)) continue; + if (providerId === "reka") continue; const prefix = providerIdToPrefix[providerId]; const alias = prefix || providerIdToAlias[providerId] || providerId; @@ -637,6 +638,7 @@ export async function getUnifiedModelsResponse( for (const [providerId, rawProviderCustomModels] of Object.entries(customModelsMap)) { // Skip Gemini — handled by syncedAvailableModels above if (providerId === "gemini") continue; + if (providerId === "reka") continue; const providerCustomModels = Array.isArray(rawProviderCustomModels) ? rawProviderCustomModels.filter( (model): model is Record => diff --git a/src/lib/providers/validation.ts b/src/lib/providers/validation.ts index 7f6b5d0a77..8ea188b5f7 100644 --- a/src/lib/providers/validation.ts +++ b/src/lib/providers/validation.ts @@ -1531,7 +1531,7 @@ async function validateRekaProvider({ apiKey, providerSpecificData = {} }: any) method: "POST", headers, body: JSON.stringify({ - model: providerSpecificData.validationModelId || "reka-flash", + model: providerSpecificData.validationModelId || "reka-flash-3", messages: [{ role: "user", content: "test" }], max_tokens: 1, }), diff --git a/src/shared/constants/providers.ts b/src/shared/constants/providers.ts index 61640335ba..69b1f43091 100644 --- a/src/shared/constants/providers.ts +++ b/src/shared/constants/providers.ts @@ -408,7 +408,6 @@ export const APIKEY_PROVIDERS = { "Reka Chat is OpenAI-compatible on /v1. OmniRoute probes /v1/models and routes chat traffic to /v1/chat/completions.", hasFree: true, freeNote: "$10/month recurring free API credits", - passthroughModels: true, }, nlpcloud: { id: "nlpcloud", diff --git a/tests/unit/chat-openai-compat-providers.test.ts b/tests/unit/chat-openai-compat-providers.test.ts index 0ded73cc32..72ca9e31d7 100644 --- a/tests/unit/chat-openai-compat-providers.test.ts +++ b/tests/unit/chat-openai-compat-providers.test.ts @@ -39,6 +39,7 @@ const CHAT_OPENAI_COMPAT_PROVIDER_IDS = [ "nanogpt", "predibase", "bytez", + "reka", ]; test("chat-openai-compat providers are registered across provider metadata, registry and local catalog", () => { diff --git a/tests/unit/provider-models-config.test.ts b/tests/unit/provider-models-config.test.ts index 7908d199a3..5149eaa5d4 100644 --- a/tests/unit/provider-models-config.test.ts +++ b/tests/unit/provider-models-config.test.ts @@ -50,6 +50,16 @@ test("provider models helpers resolve provider IDs through aliases", () => { assert.deepEqual(getModelsByProviderId("provider-that-does-not-exist"), []); }); +test("Reka registry exposes preset models", () => { + const rekaModels = getModelsByProviderId("reka"); + const ids = rekaModels.map((model) => model.id); + + assert.equal(PROVIDER_ID_TO_ALIAS.reka, "reka"); + assert.equal(getDefaultModel("reka"), "reka-flash-3"); + assert.deepEqual(ids, ["reka-flash-3", "reka-edge-2603"]); + assert.equal(isValidModel("reka", "reka-edge-2603"), true); +}); + test("GitHub Copilot registry reflects the current supported model lineup", () => { const githubModels = getProviderModels("gh"); const ids = new Set(githubModels.map((model) => model.id)); diff --git a/tests/unit/provider-models-route.test.ts b/tests/unit/provider-models-route.test.ts index a67d850f8d..c781105d0f 100644 --- a/tests/unit/provider-models-route.test.ts +++ b/tests/unit/provider-models-route.test.ts @@ -1258,7 +1258,7 @@ test("provider models route discovers Modal models from the configured OpenAI-co ]); }); -test("provider models route discovers Reka models from the named OpenAI-compatible /v1 endpoint", async () => { +test("provider models route always returns the Reka preset catalog", async () => { const connection = await seedConnection("reka", { apiKey: "reka-key", providerSpecificData: { @@ -1266,13 +1266,8 @@ test("provider models route discovers Reka models from the named OpenAI-compatib }, }); - globalThis.fetch = async (url, init = {}) => { - assert.equal(String(url), "https://api.reka.ai/v1/models"); - assert.equal(init.method, "GET"); - assert.equal(init.headers.Authorization, "Bearer reka-key"); - assert.equal(init.headers["X-Api-Key"], "reka-key"); - - return Response.json([{ id: "reka-core", name: "Reka Core" }, { id: "reka-flash" }]); + globalThis.fetch = async () => { + throw new Error("Reka models endpoint should not be probed"); }; const response = await callRoute(connection.id); @@ -1280,19 +1275,34 @@ test("provider models route discovers Reka models from the named OpenAI-compatib assert.equal(response.status, 200); assert.equal(body.provider, "reka"); - assert.equal(body.source, "api"); - assert.deepEqual(body.models, [ - { - id: "reka-core", - name: "Reka Core", - owned_by: "reka", + assert.equal(body.source, "local_catalog"); + assert.deepEqual( + body.models.map((model) => model.id), + ["reka-flash-3", "reka-edge-2603"] + ); +}); + +test("provider models route returns Reka local catalog without an API key", async () => { + const connection = await seedConnection("reka", { + providerSpecificData: { + baseUrl: "https://api.reka.ai/v1", }, - { - id: "reka-flash", - name: "reka-flash", - owned_by: "reka", - }, - ]); + }); + + globalThis.fetch = async () => { + throw new Error("Reka models endpoint should not be probed without a token"); + }; + + const response = await callRoute(connection.id); + const body = (await response.json()) as any; + + assert.equal(response.status, 200); + assert.equal(body.provider, "reka"); + assert.equal(body.source, "local_catalog"); + assert.deepEqual( + body.models.map((model) => model.id), + ["reka-flash-3", "reka-edge-2603"] + ); }); test("provider models route discovers SAP models from AI_API_URL derived from deploymentUrl", async () => {