mirror of
https://github.com/diegosouzapw/OmniRoute.git
synced 2026-08-01 21:02:12 +03:00
* fix(providers): add missing poe registry baseUrl entry (#8082) The built-in poe provider (passthroughModels:true, NAMED_OPENAI_STYLE_PROVIDERS) had no open-sse/config/providers/ REGISTRY entry, so model discovery's getRegistryEntry("poe")?.baseUrl resolved to undefined and GET /api/providers/[id]/models always failed with {"error":"No base URL configured for provider"} even though credentials and inference worked fine (the validation/inference path already had a hardcoded https://api.poe.com/v1 fallback). Adds a real REGISTRY entry mirroring moonshot/byteplus, and points the audioMiscProviders.ts hardcoded fallback at the same POE_DEFAULT_BASE_URL constant so both paths agree going forward. * test: regenerate provider translate-path golden for poe (#8082)
This commit is contained in:
committed by
GitHub
parent
1503044055
commit
c252c9d885
1
changelog.d/fixes/8082-poe-baseurl.md
Normal file
1
changelog.d/fixes/8082-poe-baseurl.md
Normal file
@@ -0,0 +1 @@
|
||||
- **fix(providers):** add the missing `poe` REGISTRY entry so built-in Poe model discovery resolves a base URL instead of failing with "No base URL configured for provider" ([#8082](https://github.com/diegosouzapw/OmniRoute/issues/8082))
|
||||
@@ -101,6 +101,7 @@ import { t3_webProvider } from "./registry/t3-web/index.ts";
|
||||
import { iflytekProvider } from "./registry/iflytek/index.ts";
|
||||
import { crofProvider } from "./registry/crof/index.ts";
|
||||
import { moonshotProvider } from "./registry/moonshot/index.ts";
|
||||
import { poeProvider } from "./registry/poe/index.ts";
|
||||
import { bazaarlinkProvider } from "./registry/bazaarlink/index.ts";
|
||||
import { perplexityProvider } from "./registry/perplexity/index.ts";
|
||||
import { perplexity_webProvider } from "./registry/perplexity/web/index.ts";
|
||||
@@ -311,6 +312,7 @@ export const REGISTRY: Record<string, RegistryEntry> = {
|
||||
iflytek: iflytekProvider,
|
||||
crof: crofProvider,
|
||||
moonshot: moonshotProvider,
|
||||
poe: poeProvider,
|
||||
bazaarlink: bazaarlinkProvider,
|
||||
perplexity: perplexityProvider,
|
||||
"perplexity-web": perplexity_webProvider,
|
||||
|
||||
25
open-sse/config/providers/registry/poe/index.ts
Normal file
25
open-sse/config/providers/registry/poe/index.ts
Normal file
@@ -0,0 +1,25 @@
|
||||
import type { RegistryEntry } from "../../shared.ts";
|
||||
|
||||
// Poe (creator.poe.com) — OpenAI-compatible chat/responses gateway. #8082: the
|
||||
// built-in `poe` provider (NAMED_OPENAI_STYLE_PROVIDERS, passthroughModels:true)
|
||||
// had no REGISTRY entry, so model discovery's `getRegistryEntry("poe")?.baseUrl`
|
||||
// resolved to undefined and every request failed with "No base URL configured
|
||||
// for provider" even though credentials/inference worked fine. This base URL is
|
||||
// the single source of truth other Poe code paths should read from (see
|
||||
// src/lib/providers/validation/audioMiscProviders.ts::validatePoeProvider).
|
||||
export const POE_DEFAULT_BASE_URL = "https://api.poe.com/v1";
|
||||
|
||||
export const poeProvider: RegistryEntry = {
|
||||
id: "poe",
|
||||
alias: "poe",
|
||||
format: "openai",
|
||||
executor: "default",
|
||||
baseUrl: `${POE_DEFAULT_BASE_URL}/chat/completions`,
|
||||
authType: "apikey",
|
||||
authHeader: "bearer",
|
||||
models: [
|
||||
{ id: "gpt-5.2", name: "GPT-5.2" },
|
||||
{ id: "claude-opus-4.8", name: "Claude Opus 4.8" },
|
||||
{ id: "gemini-3.0-pro", name: "Gemini 3.0 Pro" },
|
||||
],
|
||||
};
|
||||
@@ -3,6 +3,7 @@
|
||||
// poe. Extracted from validation.ts (god-file decomposition) — top-level functions with no
|
||||
// dispatcher-state captures; behavior is byte-identical to the original inline defs.
|
||||
import { getRegistryEntry } from "@omniroute/open-sse/config/providerRegistry.ts";
|
||||
import { POE_DEFAULT_BASE_URL } from "@omniroute/open-sse/config/providers/registry/poe/index.ts";
|
||||
import { normalizeBaseUrl } from "./urlHelpers";
|
||||
import {
|
||||
applyCustomUserAgent,
|
||||
@@ -608,7 +609,7 @@ export async function validateNousResearchProvider({ apiKey, providerSpecificDat
|
||||
}
|
||||
|
||||
export async function validatePoeProvider({ apiKey, providerSpecificData = {} }: any) {
|
||||
const baseUrl = normalizeBaseUrl(providerSpecificData.baseUrl) || "https://api.poe.com/v1";
|
||||
const baseUrl = normalizeBaseUrl(providerSpecificData.baseUrl) || POE_DEFAULT_BASE_URL;
|
||||
const balanceUrl = new URL("/usage/current_balance", baseUrl).toString();
|
||||
|
||||
try {
|
||||
|
||||
@@ -3857,6 +3857,29 @@
|
||||
"stream": "https://api.pioneer.ai/v1/chat/completions"
|
||||
}
|
||||
},
|
||||
"poe": {
|
||||
"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://api.poe.com/v1/chat/completions",
|
||||
"stream": "https://api.poe.com/v1/chat/completions"
|
||||
}
|
||||
},
|
||||
"pollinations": {
|
||||
"format": "openai",
|
||||
"headers": {
|
||||
|
||||
73
tests/unit/poe-provider-models-baseurl.test.ts
Normal file
73
tests/unit/poe-provider-models-baseurl.test.ts
Normal file
@@ -0,0 +1,73 @@
|
||||
// #8082: the built-in `poe` provider (passthroughModels:true, no baseUrl field
|
||||
// exposed in the UI) is routed through the generic OpenAI-style model-discovery
|
||||
// branch, which resolves its base URL ONLY from
|
||||
// `connection.providerSpecificData.baseUrl` OR `getRegistryEntry("poe")?.baseUrl`.
|
||||
// There was no REGISTRY entry for "poe", so discovery always failed with
|
||||
// {"error":"No base URL configured for provider"} even though the actual
|
||||
// inference/validation path (src/lib/providers/validation/audioMiscProviders.ts)
|
||||
// has always had a hardcoded "https://api.poe.com/v1" fallback and worked fine.
|
||||
import test from "node:test";
|
||||
import assert from "node:assert/strict";
|
||||
import fs from "node:fs";
|
||||
import os from "node:os";
|
||||
import path from "node:path";
|
||||
|
||||
const TEST_DATA_DIR = fs.mkdtempSync(path.join(os.tmpdir(), "omniroute-poe-models-"));
|
||||
process.env.DATA_DIR = TEST_DATA_DIR;
|
||||
|
||||
const core = await import("../../src/lib/db/core.ts");
|
||||
const providersDb = await import("../../src/lib/db/providers.ts");
|
||||
const providerModelsRoute = await import("../../src/app/api/providers/[id]/models/route.ts");
|
||||
|
||||
const originalFetch = globalThis.fetch;
|
||||
|
||||
async function resetStorage() {
|
||||
globalThis.fetch = originalFetch;
|
||||
core.resetDbInstance();
|
||||
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true });
|
||||
fs.mkdirSync(TEST_DATA_DIR, { recursive: true });
|
||||
}
|
||||
|
||||
test.beforeEach(async () => {
|
||||
await resetStorage();
|
||||
});
|
||||
|
||||
test.after(async () => {
|
||||
globalThis.fetch = originalFetch;
|
||||
core.resetDbInstance();
|
||||
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true });
|
||||
});
|
||||
|
||||
test("provider models route resolves the built-in Poe registry base URL instead of failing with 'No base URL configured for provider' (#8082)", async () => {
|
||||
const connection = await providersDb.createProviderConnection({
|
||||
provider: "poe",
|
||||
authType: "apikey",
|
||||
name: "poe-8082",
|
||||
apiKey: "poe-test-key",
|
||||
isActive: true,
|
||||
testStatus: "active",
|
||||
// Mirrors the built-in provider's UI: no baseUrl field is exposed for Poe,
|
||||
// so providerSpecificData never carries one.
|
||||
providerSpecificData: {},
|
||||
});
|
||||
|
||||
const seenRequests: Array<{ url: string; authorization: string | null }> = [];
|
||||
globalThis.fetch = async (url, init) => {
|
||||
const headers = new Headers(init?.headers as HeadersInit | undefined);
|
||||
seenRequests.push({ url: String(url), authorization: headers.get("authorization") });
|
||||
return Response.json({ data: [{ id: "gpt-5.2", name: "GPT-5.2" }] });
|
||||
};
|
||||
|
||||
const response = await providerModelsRoute.GET(
|
||||
new Request(`http://localhost/api/providers/${connection.id}/models?refresh=true`),
|
||||
{ params: { id: connection.id } }
|
||||
);
|
||||
const body = (await response.json()) as { error?: string };
|
||||
|
||||
assert.notEqual(body.error, "No base URL configured for provider");
|
||||
assert.equal(response.status, 200);
|
||||
assert.ok(
|
||||
seenRequests.some((req) => req.url.startsWith("https://api.poe.com/v1")),
|
||||
`expected a request against the Poe registry base URL, got: ${JSON.stringify(seenRequests)}`
|
||||
);
|
||||
});
|
||||
Reference in New Issue
Block a user