refactor(agents): extract provider model normalization

This commit is contained in:
Peter Steinberger
2026-03-09 00:20:18 +00:00
parent dfc18b7a2b
commit 24b53fcf47
3 changed files with 65 additions and 55 deletions

View File

@@ -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`.

View File

@@ -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<Api>;
}): Model<Api> {
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<Api>;
}
export function normalizeResolvedProviderModel(params: {
provider: string;
model: Model<Api>;
}): Model<Api> {
return normalizeModelCompat(normalizeOpenAICodexTransport(params));
}

View File

@@ -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<Api>;
}): Model<Api> {
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<Api>;
}
function normalizeResolvedModel(params: { provider: string; model: Model<Api> }): Model<Api> {
return normalizeModelCompat(normalizeOpenAICodexTransport(params));
return normalizeResolvedProviderModel(params);
}
export { buildModelAliasLines };