Files
openclaw/extensions/openai/shared.ts
Vincent Koc 5a5ca6d62c feat(codex): add gpt-5.4-pro forward compat (#66453)
* feat(openai-codex): add gpt-5.4-pro forward-compat #63404

* feat(openai-codex): add gpt-5.4-pro forward-compat #63404

* openai-codex: use patch.cost when forward-compat falls back to normalizeModelCompat

* feat(codex): add gpt-5.4-pro forward compat

* fix(codex): reuse gpt-5.4 fallback for gpt-5.4-pro

---------

Co-authored-by: jepson-liu <jepsonliu@gmail.com>
2026-04-14 11:05:24 +01:00

80 lines
2.3 KiB
TypeScript

import type { OpenClawConfig } from "openclaw/plugin-sdk/config-runtime";
import { findCatalogTemplate } from "openclaw/plugin-sdk/provider-catalog-shared";
import {
cloneFirstTemplateModel,
matchesExactOrPrefix,
} from "openclaw/plugin-sdk/provider-model-shared";
import { normalizeOptionalString } from "openclaw/plugin-sdk/text-runtime";
type SyntheticOpenAIModelCatalogCost = {
input: number;
output: number;
cacheRead: number;
cacheWrite: number;
};
type SyntheticOpenAIModelCatalogEntry = {
provider: string;
id: string;
name: string;
reasoning?: boolean;
input?: ("text" | "image")[];
contextWindow?: number;
contextTokens?: number;
cost?: SyntheticOpenAIModelCatalogCost;
};
export const OPENAI_API_BASE_URL = "https://api.openai.com/v1";
export function toOpenAIDataUrl(buffer: Buffer, mimeType: string): string {
return `data:${mimeType};base64,${buffer.toString("base64")}`;
}
export function resolveConfiguredOpenAIBaseUrl(cfg: OpenClawConfig | undefined): string {
return normalizeOptionalString(cfg?.models?.providers?.openai?.baseUrl) ?? OPENAI_API_BASE_URL;
}
export function isOpenAIApiBaseUrl(baseUrl?: string): boolean {
const trimmed = normalizeOptionalString(baseUrl);
if (!trimmed) {
return false;
}
return /^https?:\/\/api\.openai\.com(?:\/v1)?\/?$/i.test(trimmed);
}
export function isOpenAICodexBaseUrl(baseUrl?: string): boolean {
const trimmed = normalizeOptionalString(baseUrl);
if (!trimmed) {
return false;
}
return /^https?:\/\/chatgpt\.com\/backend-api\/?$/i.test(trimmed);
}
export function buildOpenAISyntheticCatalogEntry(
template: ReturnType<typeof findCatalogTemplate>,
entry: {
id: string;
reasoning: boolean;
input: readonly ("text" | "image")[];
contextWindow: number;
contextTokens?: number;
cost?: SyntheticOpenAIModelCatalogCost;
},
): SyntheticOpenAIModelCatalogEntry | undefined {
if (!template) {
return undefined;
}
return {
...template,
id: entry.id,
name: entry.id,
reasoning: entry.reasoning,
input: [...entry.input],
contextWindow: entry.contextWindow,
...(entry.contextTokens === undefined ? {} : { contextTokens: entry.contextTokens }),
...(entry.cost === undefined ? {} : { cost: entry.cost }),
};
}
export { cloneFirstTemplateModel, findCatalogTemplate, matchesExactOrPrefix };