From 24b53fcf4790a6fb4faf1a423ca6d38044883f0f Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Mon, 9 Mar 2026 00:20:18 +0000 Subject: [PATCH] refactor(agents): extract provider model normalization --- docs/refactor/cleanup.md | 2 +- .../model.provider-normalization.ts | 62 +++++++++++++++++++ src/agents/pi-embedded-runner/model.ts | 56 +---------------- 3 files changed, 65 insertions(+), 55 deletions(-) create mode 100644 src/agents/pi-embedded-runner/model.provider-normalization.ts diff --git a/docs/refactor/cleanup.md b/docs/refactor/cleanup.md index 04fc1f143fb..83b1fb47c5b 100644 --- a/docs/refactor/cleanup.md +++ b/docs/refactor/cleanup.md @@ -2,6 +2,6 @@ - [x] Extract `models list` row/supplement helpers. - [x] Split `models list` forward-compat tests by concern. -- [ ] Extract provider transport normalization from `pi-embedded-runner/model.ts`. +- [x] Extract provider transport normalization from `pi-embedded-runner/model.ts`. - [ ] Split `ensureOpenClawModelsJson()` into planning + IO layers. - [ ] Split provider discovery helpers out of `models-config.providers.ts`. diff --git a/src/agents/pi-embedded-runner/model.provider-normalization.ts b/src/agents/pi-embedded-runner/model.provider-normalization.ts new file mode 100644 index 00000000000..ecf1a25e7d3 --- /dev/null +++ b/src/agents/pi-embedded-runner/model.provider-normalization.ts @@ -0,0 +1,62 @@ +import type { Api, Model } from "@mariozechner/pi-ai"; +import { normalizeModelCompat } from "../model-compat.js"; +import { normalizeProviderId } from "../model-selection.js"; + +const OPENAI_CODEX_BASE_URL = "https://chatgpt.com/backend-api"; + +function isOpenAIApiBaseUrl(baseUrl?: string): boolean { + const trimmed = baseUrl?.trim(); + if (!trimmed) { + return false; + } + return /^https?:\/\/api\.openai\.com(?:\/v1)?\/?$/i.test(trimmed); +} + +function isOpenAICodexBaseUrl(baseUrl?: string): boolean { + const trimmed = baseUrl?.trim(); + if (!trimmed) { + return false; + } + return /^https?:\/\/chatgpt\.com\/backend-api\/?$/i.test(trimmed); +} + +function normalizeOpenAICodexTransport(params: { + provider: string; + model: Model; +}): Model { + if (normalizeProviderId(params.provider) !== "openai-codex") { + return params.model; + } + + const useCodexTransport = + !params.model.baseUrl || + isOpenAIApiBaseUrl(params.model.baseUrl) || + isOpenAICodexBaseUrl(params.model.baseUrl); + + const nextApi = + useCodexTransport && params.model.api === "openai-responses" + ? ("openai-codex-responses" as const) + : params.model.api; + const nextBaseUrl = + nextApi === "openai-codex-responses" && + (!params.model.baseUrl || isOpenAIApiBaseUrl(params.model.baseUrl)) + ? OPENAI_CODEX_BASE_URL + : params.model.baseUrl; + + if (nextApi === params.model.api && nextBaseUrl === params.model.baseUrl) { + return params.model; + } + + return { + ...params.model, + api: nextApi, + baseUrl: nextBaseUrl, + } as Model; +} + +export function normalizeResolvedProviderModel(params: { + provider: string; + model: Model; +}): Model { + return normalizeModelCompat(normalizeOpenAICodexTransport(params)); +} diff --git a/src/agents/pi-embedded-runner/model.ts b/src/agents/pi-embedded-runner/model.ts index 5995bb40099..638d66f787f 100644 --- a/src/agents/pi-embedded-runner/model.ts +++ b/src/agents/pi-embedded-runner/model.ts @@ -6,10 +6,10 @@ import { resolveOpenClawAgentDir } from "../agent-paths.js"; import { DEFAULT_CONTEXT_TOKENS } from "../defaults.js"; import { buildModelAliasLines } from "../model-alias-lines.js"; import { isSecretRefHeaderValueMarker } from "../model-auth-markers.js"; -import { normalizeModelCompat } from "../model-compat.js"; import { resolveForwardCompatModel } from "../model-forward-compat.js"; import { findNormalizedProviderValue, normalizeProviderId } from "../model-selection.js"; import { discoverAuthStorage, discoverModels } from "../pi-model-discovery.js"; +import { normalizeResolvedProviderModel } from "./model.provider-normalization.js"; type InlineModelEntry = ModelDefinitionConfig & { provider: string; @@ -23,8 +23,6 @@ type InlineProviderConfig = { headers?: unknown; }; -const OPENAI_CODEX_BASE_URL = "https://chatgpt.com/backend-api"; - function sanitizeModelHeaders( headers: unknown, opts?: { stripSecretRefMarkers?: boolean }, @@ -45,58 +43,8 @@ function sanitizeModelHeaders( return Object.keys(next).length > 0 ? next : undefined; } -function isOpenAIApiBaseUrl(baseUrl?: string): boolean { - const trimmed = baseUrl?.trim(); - if (!trimmed) { - return false; - } - return /^https?:\/\/api\.openai\.com(?:\/v1)?\/?$/i.test(trimmed); -} - -function isOpenAICodexBaseUrl(baseUrl?: string): boolean { - const trimmed = baseUrl?.trim(); - if (!trimmed) { - return false; - } - return /^https?:\/\/chatgpt\.com\/backend-api\/?$/i.test(trimmed); -} - -function normalizeOpenAICodexTransport(params: { - provider: string; - model: Model; -}): Model { - if (normalizeProviderId(params.provider) !== "openai-codex") { - return params.model; - } - - const useCodexTransport = - !params.model.baseUrl || - isOpenAIApiBaseUrl(params.model.baseUrl) || - isOpenAICodexBaseUrl(params.model.baseUrl); - - const nextApi = - useCodexTransport && params.model.api === "openai-responses" - ? ("openai-codex-responses" as const) - : params.model.api; - const nextBaseUrl = - nextApi === "openai-codex-responses" && - (!params.model.baseUrl || isOpenAIApiBaseUrl(params.model.baseUrl)) - ? OPENAI_CODEX_BASE_URL - : params.model.baseUrl; - - if (nextApi === params.model.api && nextBaseUrl === params.model.baseUrl) { - return params.model; - } - - return { - ...params.model, - api: nextApi, - baseUrl: nextBaseUrl, - } as Model; -} - function normalizeResolvedModel(params: { provider: string; model: Model }): Model { - return normalizeModelCompat(normalizeOpenAICodexTransport(params)); + return normalizeResolvedProviderModel(params); } export { buildModelAliasLines };