fix: add implicit openai-codex provider snapshot

This commit is contained in:
Kros Dai
2026-03-08 09:09:09 -04:00
committed by Peter Steinberger
parent eebee84093
commit 3053324110
3 changed files with 124 additions and 0 deletions

View File

@@ -0,0 +1,107 @@
import fs from "node:fs/promises";
import path from "node:path";
import { describe, expect, it } from "vitest";
import { resolveOpenClawAgentDir } from "./agent-paths.js";
import {
installModelsConfigTestHooks,
MODELS_CONFIG_IMPLICIT_ENV_VARS,
unsetEnv,
withModelsTempHome,
withTempEnv,
} from "./models-config.e2e-harness.js";
import { ensureOpenClawModelsJson } from "./models-config.js";
import { resolveImplicitProviders } from "./models-config.providers.js";
import { readGeneratedModelsJson } from "./models-config.test-utils.js";
installModelsConfigTestHooks();
async function writeCodexOauthProfile(agentDir: string) {
await fs.mkdir(agentDir, { recursive: true });
await fs.writeFile(
path.join(agentDir, "auth-profiles.json"),
JSON.stringify(
{
version: 1,
profiles: {
"openai-codex:default": {
type: "oauth",
provider: "openai-codex",
access: "access-token",
refresh: "refresh-token",
expires: Date.now() + 60_000,
},
},
order: {
"openai-codex": ["openai-codex:default"],
},
},
null,
2,
),
"utf8",
);
}
describe("openai-codex implicit provider", () => {
it("injects an implicit provider when Codex OAuth exists", async () => {
await withModelsTempHome(async () => {
await withTempEnv(MODELS_CONFIG_IMPLICIT_ENV_VARS, async () => {
unsetEnv(MODELS_CONFIG_IMPLICIT_ENV_VARS);
const agentDir = resolveOpenClawAgentDir();
await writeCodexOauthProfile(agentDir);
const providers = await resolveImplicitProviders({ agentDir });
expect(providers?.["openai-codex"]).toMatchObject({
baseUrl: "https://chatgpt.com/backend-api",
api: "openai-codex-responses",
models: [],
});
});
});
});
it("replaces stale openai-codex baseUrl in generated models.json", async () => {
await withModelsTempHome(async () => {
await withTempEnv(MODELS_CONFIG_IMPLICIT_ENV_VARS, async () => {
unsetEnv(MODELS_CONFIG_IMPLICIT_ENV_VARS);
const agentDir = resolveOpenClawAgentDir();
await writeCodexOauthProfile(agentDir);
await fs.writeFile(
path.join(agentDir, "models.json"),
JSON.stringify(
{
providers: {
"openai-codex": {
baseUrl: "https://api.openai.com/v1",
api: "openai-responses",
models: [
{
id: "gpt-5.4",
name: "GPT-5.4",
api: "openai-responses",
contextWindow: 1_000_000,
maxTokens: 100_000,
},
],
},
},
},
null,
2,
),
"utf8",
);
await ensureOpenClawModelsJson({});
const parsed = await readGeneratedModelsJson<{
providers: Record<string, { baseUrl?: string; api?: string }>;
}>();
expect(parsed.providers["openai-codex"]).toMatchObject({
baseUrl: "https://chatgpt.com/backend-api",
api: "openai-codex-responses",
});
});
});
});
});

View File

@@ -207,6 +207,8 @@ const NVIDIA_DEFAULT_COST = {
cacheWrite: 0,
};
const OPENAI_CODEX_BASE_URL = "https://chatgpt.com/backend-api";
const log = createSubsystemLogger("agents/model-providers");
interface OllamaModel {
@@ -994,6 +996,14 @@ function buildOpenrouterProvider(): ProviderConfig {
};
}
function buildOpenAICodexProvider(): ProviderConfig {
return {
baseUrl: OPENAI_CODEX_BASE_URL,
api: "openai-codex-responses",
models: [],
};
}
async function buildVllmProvider(params?: {
baseUrl?: string;
apiKey?: string;
@@ -1302,6 +1312,11 @@ export async function resolveImplicitProviders(params: {
providers.openrouter = { ...buildOpenrouterProvider(), apiKey: openrouterKey };
}
const openaiCodexProfiles = listProfilesForProvider(authStore, "openai-codex");
if (openaiCodexProfiles.length > 0) {
providers["openai-codex"] = buildOpenAICodexProvider();
}
const nvidiaKey = resolveProviderApiKey("nvidia").apiKey;
if (nvidiaKey) {
providers.nvidia = { ...buildNvidiaProvider(), apiKey: nvidiaKey };

View File

@@ -22,6 +22,7 @@ type ModelsConfig = NonNullable<OpenClawConfig["models"]>;
const DEFAULT_MODE: NonNullable<ModelsConfig["mode"]> = "merge";
const MODELS_JSON_WRITE_LOCKS = new Map<string, Promise<void>>();
const AUTHORITATIVE_IMPLICIT_BASEURL_PROVIDERS = new Set(["openai-codex"]);
function isPositiveFiniteTokenLimit(value: unknown): value is number {
return typeof value === "number" && Number.isFinite(value) && value > 0;
@@ -198,6 +199,7 @@ function mergeWithExistingProviderSecrets(params: {
preserved.apiKey = existing.apiKey;
}
if (
!AUTHORITATIVE_IMPLICIT_BASEURL_PROVIDERS.has(key) &&
!explicitBaseUrlProviders.has(key) &&
typeof existing.baseUrl === "string" &&
existing.baseUrl