diff --git a/docs/help/faq.md b/docs/help/faq.md index d8dbbb707b2..7113291a4aa 100644 --- a/docs/help/faq.md +++ b/docs/help/faq.md @@ -657,6 +657,31 @@ for usage/billing and raise limits as needed. OpenClaw supports **OpenAI Code (Codex)** via OAuth (ChatGPT sign-in). Onboarding can run the OAuth flow and will set the default model to `openai-codex/gpt-5.4` when appropriate. See [Model providers](/concepts/model-providers) and [Onboarding (CLI)](/start/wizard). + + OpenClaw treats the two routes separately: + + - `openai-codex/gpt-5.4` = ChatGPT/Codex OAuth + - `openai/gpt-5.4` = direct OpenAI Platform API + + In OpenClaw, ChatGPT/Codex sign-in is wired to the `openai-codex/*` route, + not the direct `openai/*` route. If you want the direct API path in + OpenClaw, set `OPENAI_API_KEY` (or the equivalent OpenAI provider config). + If you want ChatGPT/Codex sign-in in OpenClaw, use `openai-codex/*`. + + + + + `openai-codex/*` uses the Codex OAuth route, and its usable quota windows are + OpenAI-managed and plan-dependent. In practice, those limits can differ from + the ChatGPT website/app experience, even when both are tied to the same account. + + OpenClaw can show the currently visible provider usage/quota windows in + `openclaw models status`, but it does not invent or normalize ChatGPT-web + entitlements into direct API access. If you want the direct OpenAI Platform + billing/limit path, use `openai/*` with an API key. + + + Yes. OpenClaw fully supports **OpenAI Code (Codex) subscription OAuth**. OpenAI explicitly allows subscription OAuth usage in external tools/workflows diff --git a/docs/providers/openai.md b/docs/providers/openai.md index e9556bdb6e2..0dcd44b5d6a 100644 --- a/docs/providers/openai.md +++ b/docs/providers/openai.md @@ -82,6 +82,12 @@ openclaw config set plugins.entries.openai.config.personality off **Best for:** direct API access and usage-based billing. Get your API key from the OpenAI dashboard. +Route summary: + +- `openai/gpt-5.4` = direct OpenAI Platform API route +- Requires `OPENAI_API_KEY` (or equivalent OpenAI provider config) +- In OpenClaw, ChatGPT/Codex sign-in is routed through `openai-codex/*`, not `openai/*` + ### CLI setup ```bash @@ -172,6 +178,12 @@ parameters, provider selection, and failover behavior. **Best for:** using ChatGPT/Codex subscription access instead of an API key. Codex cloud requires ChatGPT sign-in, while the Codex CLI supports ChatGPT or API key sign-in. +Route summary: + +- `openai-codex/gpt-5.4` = ChatGPT/Codex OAuth route +- Uses ChatGPT/Codex sign-in, not a direct OpenAI Platform API key +- Provider-side limits for `openai-codex/*` can differ from the ChatGPT web/app experience + ### CLI setup (Codex OAuth) ```bash @@ -193,6 +205,10 @@ openclaw models auth login --provider openai-codex OpenAI's current Codex docs list `gpt-5.4` as the current Codex model. OpenClaw maps that to `openai-codex/gpt-5.4` for ChatGPT/Codex OAuth usage. +This route is intentionally separate from `openai/gpt-5.4`. If you want the +direct OpenAI Platform API path, use `openai/*` with an API key. If you want +ChatGPT/Codex sign-in, use `openai-codex/*`. + If onboarding reuses an existing Codex CLI login, those credentials stay managed by Codex CLI. On expiry, OpenClaw re-reads the external Codex source first and, when the provider can refresh it, writes the refreshed credential diff --git a/src/commands/model-picker.test.ts b/src/commands/model-picker.test.ts index 903171bb865..f4bbd52b9c7 100644 --- a/src/commands/model-picker.test.ts +++ b/src/commands/model-picker.test.ts @@ -88,6 +88,46 @@ beforeEach(() => { }); describe("promptDefaultModel", () => { + it("adds auth-route hints for OpenAI API and Codex OAuth models", async () => { + loadModelCatalog.mockResolvedValue([ + { + provider: "openai", + id: "gpt-5.4", + name: "GPT-5.4", + }, + { + provider: "openai-codex", + id: "gpt-5.4", + name: "GPT-5.4", + }, + ]); + + const select = vi.fn(async (params) => params.initialValue as never); + const prompter = makePrompter({ select }); + + await promptDefaultModel({ + config: { agents: { defaults: {} } } as OpenClawConfig, + prompter, + allowKeep: false, + includeManual: false, + ignoreAllowlist: true, + }); + + const options = select.mock.calls[0]?.[0]?.options ?? []; + expect(options).toEqual( + expect.arrayContaining([ + expect.objectContaining({ + value: "openai/gpt-5.4", + hint: expect.stringContaining("API key route"), + }), + expect.objectContaining({ + value: "openai-codex/gpt-5.4", + hint: expect.stringContaining("ChatGPT OAuth route"), + }), + ]), + ); + }); + it("treats byteplus plan models as preferred-provider matches", async () => { loadModelCatalog.mockResolvedValue([ { diff --git a/src/flows/model-picker.ts b/src/flows/model-picker.ts index 6d5b469307d..03c55d93249 100644 --- a/src/flows/model-picker.ts +++ b/src/flows/model-picker.ts @@ -115,6 +115,17 @@ function normalizeModelKeys(values: string[]): string[] { return next; } +function resolveModelRouteHint(provider: string): string | undefined { + const normalized = normalizeProviderId(provider); + if (normalized === "openai") { + return "API key route"; + } + if (normalized === "openai-codex") { + return "ChatGPT OAuth route"; + } + return undefined; +} + function addModelSelectOption(params: { entry: { provider: string; @@ -146,6 +157,10 @@ function addModelSelectOption(params: { if (aliases?.length) { hints.push(`alias: ${aliases.join(", ")}`); } + const routeHint = resolveModelRouteHint(params.entry.provider); + if (routeHint) { + hints.push(routeHint); + } if (!params.hasAuth(params.entry.provider)) { hints.push("auth missing"); }