mirror of
https://github.com/diegosouzapw/OmniRoute.git
synced 2026-07-26 09:52:11 +03:00
feat(openai): honor custom base URL in model discovery + complete openai/codex pricing (#4005)
Integrated into release/v3.8.27 — openai model-discovery honors custom base URL (SSRF-guarded) + pricing rows for new openai/codex models. Tested + baselines bumped.
This commit is contained in:
@@ -93,7 +93,7 @@
|
||||
"src/app/(dashboard)/dashboard/usage/components/EvalsTab.tsx": 2148,
|
||||
"src/app/(dashboard)/dashboard/usage/components/ProviderLimits/index.tsx": 1069,
|
||||
"src/app/api/oauth/[provider]/[action]/route.ts": 918,
|
||||
"src/app/api/providers/[id]/models/route.ts": 2494,
|
||||
"src/app/api/providers/[id]/models/route.ts": 2512,
|
||||
"src/app/api/providers/[id]/test/route.ts": 842,
|
||||
"src/app/api/usage/analytics/route.ts": 941,
|
||||
"src/app/api/v1/models/catalog.ts": 1435,
|
||||
@@ -118,7 +118,7 @@
|
||||
"src/shared/components/RequestLoggerV2.tsx": 1287,
|
||||
"src/shared/components/analytics/charts.tsx": 1558,
|
||||
"src/shared/constants/cliTools.ts": 875,
|
||||
"src/shared/constants/pricing.ts": 1529,
|
||||
"src/shared/constants/pricing.ts": 1581,
|
||||
"src/shared/constants/providers.ts": 3147,
|
||||
"src/shared/constants/sidebarVisibility.ts": 1100,
|
||||
"src/shared/services/cliRuntime.ts": 1090,
|
||||
@@ -155,5 +155,6 @@
|
||||
"_rebaseline_2026_06_14_3861_gitlab_duo": "PR #3861 own growth: oauth/[provider]/[action]/route.ts 903→916 (+13 = gitlab-duo authorize guard mirroring the existing qoder guard — returns a clear 'register an OAuth app + set GITLAB_DUO_OAUTH_CLIENT_ID' message instead of letting buildAuthUrl's throw become an opaque 500). Cohesive with the qoder branch right above it; not separately extractable.",
|
||||
"_rebaseline_2026_06_15_3941_provider_request_capture": "PR #3941 own growth: chatCore.ts 5823->5830 (+7 by check-file-size counting = run executor attempts inside the unified provider request capture scope), antigravity.ts 1649->1664 (+15) and codex.ts 1439->1447 (+8) = bridge hand-written upstream transports that bypass normal fetch/BaseExecutor capture. Cohesive logging-fidelity refactor; not extractable without hiding the actual dispatch boundary.",
|
||||
"_rebaseline_2026_06_16_3958_qwen_body_check": "PR #3958 own growth: validation.ts 4407->4428 (+21 = validateQwenWebProvider now parses the /api/v2/user 200 body and requires a real user object, since Qwen returns HTTP 200 even for invalid tokens — fixes the validation false-positive, #3931). Cohesive with the existing qwen-web validation branch; not separately extractable.",
|
||||
"_rebaseline_2026_06_16_4001_perplexity_diff_block": "PR #4001 own growth: perplexity-web.ts 939->1013 (+74 = parse the schematized API's RFC-6902 diff_block JSON-patch frames — applyMarkdownDiff + isAnswerTextUsage primary-usage lock + stop only on COMPLETED, not on a still-PENDING final flag — so streamed answers aren't empty, #3938 follow-up). Cohesive single-executor SSE-parsing logic; not separately extractable."
|
||||
"_rebaseline_2026_06_16_4001_perplexity_diff_block": "PR #4001 own growth: perplexity-web.ts 939->1013 (+74 = parse the schematized API's RFC-6902 diff_block JSON-patch frames — applyMarkdownDiff + isAnswerTextUsage primary-usage lock + stop only on COMPLETED, not on a still-PENDING final flag — so streamed answers aren't empty, #3938 follow-up). Cohesive single-executor SSE-parsing logic; not separately extractable.",
|
||||
"_rebaseline_2026_06_16_4005_openai_dynamic_models": "PR #4005 own growth: models/route.ts 2494->2512 (+18 = openai model-discovery derives {customBaseUrl}/v1/models from providerSpecificData.baseUrl, SSRF-guarded via safeOutboundFetch+public-only) and pricing.ts 1529->1581 (+52 = pure-data pricing rows closing $0 gaps for registry-exposed ids: openai gpt-5.4/-mini/-nano, gpt-4.1, gpt-4o-2024-11-20, o3 + codex(cx) gpt-5.4-{xhigh,high,medium,low}, gpt-5.3-codex-spark). Cohesive; pricing is data, route change mirrors the anthropic-compat discovery path."
|
||||
}
|
||||
|
||||
@@ -2383,6 +2383,26 @@ export async function GET(
|
||||
|
||||
// Build request URL
|
||||
let url = config.url;
|
||||
// VibeProxy: honor a user-configured custom base URL for the built-in
|
||||
// `openai` provider (e.g. an OpenAI-compatible gateway / proxy). Without
|
||||
// this, model discovery always hit the hardcoded api.openai.com and ignored
|
||||
// the configured endpoint — returning the wrong catalog (or failing auth)
|
||||
// for gateway users, and preventing instant access to gateway-served models.
|
||||
// Falls back to config.url (api.openai.com) when no custom base URL is set.
|
||||
if (provider === "openai") {
|
||||
const customBaseUrl = getProviderBaseUrl(connection.providerSpecificData);
|
||||
if (customBaseUrl) {
|
||||
let base = customBaseUrl.replace(/\/$/, "");
|
||||
if (base.endsWith("/chat/completions")) {
|
||||
base = base.slice(0, -"/chat/completions".length);
|
||||
} else if (base.endsWith("/completions")) {
|
||||
base = base.slice(0, -"/completions".length);
|
||||
} else if (base.endsWith("/v1")) {
|
||||
base = base.slice(0, -"/v1".length);
|
||||
}
|
||||
url = `${base}/v1/models`;
|
||||
}
|
||||
}
|
||||
if (provider === "cloudflare-ai") {
|
||||
const pData = asRecord(connection.providerSpecificData);
|
||||
const accountId =
|
||||
|
||||
@@ -257,7 +257,14 @@ export const DEFAULT_PRICING = {
|
||||
reasoning: 9.0,
|
||||
cache_creation: 1.5,
|
||||
},
|
||||
// gpt-5.4 reasoning-effort variants share the gpt-5.4 tier (registry exposes
|
||||
// -xhigh/-high/-medium/-low; without these rows they resolved to $0).
|
||||
"gpt-5.4-xhigh": GPT_5_3_CODEX_PRICING,
|
||||
"gpt-5.4-high": GPT_5_3_CODEX_PRICING,
|
||||
"gpt-5.4-medium": GPT_5_3_CODEX_PRICING,
|
||||
"gpt-5.4-low": GPT_5_3_CODEX_PRICING,
|
||||
// GPT 5.3 Codex family (all same pricing tier)
|
||||
"gpt-5.3-codex-spark": GPT_5_3_CODEX_PRICING,
|
||||
"gpt-5.3-codex": GPT_5_3_CODEX_PRICING,
|
||||
"gpt-5.3-codex-xhigh": GPT_5_3_CODEX_PRICING,
|
||||
"gpt-5.3-codex-high": GPT_5_3_CODEX_PRICING,
|
||||
@@ -645,6 +652,37 @@ export const DEFAULT_PRICING = {
|
||||
// OpenAI
|
||||
openai: {
|
||||
"gpt-5.5": GPT_5_5_PRICING,
|
||||
// gpt-5.4 family (public API tier; mirrors the codex 5.4 tier for the
|
||||
// base/mini, with a lower nano tier). Without these rows the openai
|
||||
// provider's gpt-5.4* models resolved to $0.
|
||||
"gpt-5.4": {
|
||||
input: 5.0,
|
||||
output: 20.0,
|
||||
cached: 2.5,
|
||||
reasoning: 30.0,
|
||||
cache_creation: 5.0,
|
||||
},
|
||||
"gpt-5.4-mini": {
|
||||
input: 1.5,
|
||||
output: 6.0,
|
||||
cached: 0.75,
|
||||
reasoning: 9.0,
|
||||
cache_creation: 1.5,
|
||||
},
|
||||
"gpt-5.4-nano": {
|
||||
input: 0.4,
|
||||
output: 1.6,
|
||||
cached: 0.2,
|
||||
reasoning: 2.4,
|
||||
cache_creation: 0.4,
|
||||
},
|
||||
"gpt-4.1": {
|
||||
input: 2.0,
|
||||
output: 8.0,
|
||||
cached: 0.5,
|
||||
reasoning: 12.0,
|
||||
cache_creation: 2.0,
|
||||
},
|
||||
"gpt-4o": {
|
||||
input: 2.5,
|
||||
output: 10.0,
|
||||
@@ -652,6 +690,13 @@ export const DEFAULT_PRICING = {
|
||||
reasoning: 15.0,
|
||||
cache_creation: 2.5,
|
||||
},
|
||||
"gpt-4o-2024-11-20": {
|
||||
input: 2.5,
|
||||
output: 10.0,
|
||||
cached: 1.25,
|
||||
reasoning: 15.0,
|
||||
cache_creation: 2.5,
|
||||
},
|
||||
"gpt-4o-mini": {
|
||||
input: 0.15,
|
||||
output: 0.6,
|
||||
@@ -659,6 +704,13 @@ export const DEFAULT_PRICING = {
|
||||
reasoning: 0.9,
|
||||
cache_creation: 0.15,
|
||||
},
|
||||
o3: {
|
||||
input: 2.0,
|
||||
output: 8.0,
|
||||
cached: 0.5,
|
||||
reasoning: 12.0,
|
||||
cache_creation: 2.0,
|
||||
},
|
||||
"gpt-4-turbo": {
|
||||
input: 10.0,
|
||||
output: 30.0,
|
||||
|
||||
@@ -110,3 +110,42 @@ test("Every Kiro registry model resolves a non-zero pricing row (no $0.00 usage)
|
||||
assert.equal(sonnet46?.input, 3.0);
|
||||
assert.equal(sonnet46?.output, 15.0);
|
||||
});
|
||||
|
||||
test("Every OpenAI registry model resolves a non-zero pricing row (alias: openai)", async () => {
|
||||
const { getPricingForModel } = await import("../../src/shared/constants/pricing.ts");
|
||||
const models = getModelsByProviderId("openai");
|
||||
assert.ok(models.length > 0, "openai must expose models");
|
||||
|
||||
for (const model of models) {
|
||||
const pricing = getPricingForModel("openai", model.id) as {
|
||||
input?: number;
|
||||
output?: number;
|
||||
} | null;
|
||||
assert.ok(pricing, `openai pricing must include "${model.id}"`);
|
||||
assert.equal(
|
||||
typeof pricing?.input === "number" && typeof pricing?.output === "number",
|
||||
true,
|
||||
`openai pricing for "${model.id}" must have numeric input/output`
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
test("Every Codex registry model resolves a non-zero pricing row (alias: cx)", async () => {
|
||||
const { getPricingForModel } = await import("../../src/shared/constants/pricing.ts");
|
||||
const models = getModelsByProviderId("codex");
|
||||
assert.ok(models.length > 0, "codex must expose models");
|
||||
|
||||
for (const model of models) {
|
||||
// Codex pricing lives under the "cx" alias (its DEFAULT_PRICING key).
|
||||
const pricing = getPricingForModel("cx", model.id) as {
|
||||
input?: number;
|
||||
output?: number;
|
||||
} | null;
|
||||
assert.ok(pricing, `cx pricing must include codex model "${model.id}"`);
|
||||
assert.equal(
|
||||
typeof pricing?.input === "number" && typeof pricing?.output === "number",
|
||||
true,
|
||||
`cx pricing for "${model.id}" must have numeric input/output`
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user