From 33c3d956bdc5d3d2bd1044e6dc1290ce8d567892 Mon Sep 17 00:00:00 2001 From: Wibias <37517432+Wibias@users.noreply.github.com> Date: Mon, 3 Aug 2026 09:11:03 +0200 Subject: [PATCH 1/5] fix(gui): add Pool/Direct account mode switch to OpenAI provider settings The Providers workspace Settings tab for the canonical openai provider now renders a Pool/Direct selector backed by the existing dedicated PATCH (/api/providers?name=openai with codexAccountMode alone). The control applies immediately, shows per-mode descriptions and save state, and refreshes the config so the badge and banner stay in sync. --- .../provider-workspace/ProviderSettings.tsx | 45 ++++++++++++++++++- .../components/provider-workspace/types.ts | 2 + gui/src/i18n/de.ts | 2 + gui/src/i18n/en.ts | 2 + gui/src/i18n/ja.ts | 2 + gui/src/i18n/ko.ts | 2 + gui/src/i18n/ru.ts | 2 + gui/src/i18n/zh.ts | 2 + gui/src/provider-workspace/catalog.ts | 2 + .../styles/provider-workspace-settings.css | 3 ++ 10 files changed, 63 insertions(+), 1 deletion(-) diff --git a/gui/src/components/provider-workspace/ProviderSettings.tsx b/gui/src/components/provider-workspace/ProviderSettings.tsx index 5436651f54..e7ad13da07 100644 --- a/gui/src/components/provider-workspace/ProviderSettings.tsx +++ b/gui/src/components/provider-workspace/ProviderSettings.tsx @@ -13,6 +13,7 @@ import { readJsonIfOk } from "../../fetch-json"; import { useT } from "../../i18n/shared"; import { IconLock } from "../../icons"; import { isCatalogProviderId } from "../../provider-icons"; +import { openAiAccountProviderState } from "../../provider-payload"; import type { CatalogPreset } from "../provider-catalog/provider-presets"; import { authModeLabel } from "./ProviderRail"; import type { WorkspaceItem, ProviderUpdatePatch } from "./types"; @@ -46,6 +47,9 @@ export default function ProviderSettings({ const [liveModels, setLiveModels] = useState(item.liveModels !== false); const [saving, setSaving] = useState(false); const [msg, setMsg] = useState<{ ok: boolean; text: string } | null>(null); + const [accountMode, setAccountMode] = useState<"pool" | "direct">(item.codexAccountMode ?? "pool"); + const [modeSaving, setModeSaving] = useState(false); + const [modeMsg, setModeMsg] = useState<{ ok: boolean; text: string } | null>(null); const [baseUrlChoices, setBaseUrlChoices] = useState(); const [choicesStatus, setChoicesStatus] = useState(apiBase ? "loading" : "idle"); const [endpointChoice, setEndpointChoice] = useState(() => "custom"); @@ -60,9 +64,10 @@ export default function ProviderSettings({ setNote(item.note ?? ""); setAllowPrivateNetwork(item.allowPrivateNetwork ?? false); setLiveModels(item.liveModels !== false); + setAccountMode(item.codexAccountMode ?? "pool"); setMsg(null); queueMicrotask(() => setEndpointChoice(matchChoiceId(baseUrlChoices, item.baseUrl))); - }, [item.adapter, item.baseUrl, item.defaultModel, item.authMode, item.apiKeyTransport, item.keyOptional, item.note, item.allowPrivateNetwork, item.liveModels, baseUrlChoices]); + }, [item.adapter, item.baseUrl, item.defaultModel, item.authMode, item.apiKeyTransport, item.keyOptional, item.note, item.allowPrivateNetwork, item.liveModels, item.codexAccountMode, baseUrlChoices]); /* eslint-enable react-hooks/set-state-in-effect */ useEffect(() => { @@ -122,6 +127,8 @@ export default function ProviderSettings({ const isPreset = isCatalogProviderId(item.name); const hasEndpointPicker = choicesStatus === "ready" && !!(baseUrlChoices && baseUrlChoices.length > 0); const supportsApiKeyTransport = adapter.trim() === "anthropic" && authMode === "key"; + const openAiState = item.name === "openai" ? openAiAccountProviderState(item) : "invalid"; + const isCanonicalOpenAi = openAiState === "ready" || openAiState === "disabled"; // Lock plain baseUrl for presets while loading or when there is no picker. // On fetch error, keep it editable so allowBaseUrlOverride providers are not trapped. const plainBaseUrlLocked = isPreset && choicesStatus !== "error"; @@ -159,6 +166,19 @@ export default function ProviderSettings({ return () => onRegisterSave(null); }, [onRegisterSave]); + const applyAccountMode = async (next: "pool" | "direct") => { + if (modeSaving || next === accountMode) return; + if (!onUpdateProvider) { setModeMsg({ ok: false, text: t("pws.updatesUnavailable") }); return; } + setModeSaving(true); + setModeMsg(null); + try { + const res = await onUpdateProvider("openai", { codexAccountMode: next }); + setModeMsg(res.ok ? { ok: true, text: t("pws.accountModeSaved") } : { ok: false, text: res.error || t("pws.accountModeFailed") }); + } finally { + setModeSaving(false); + } + }; + const discard = () => { setAdapter(item.adapter); setBaseUrl(item.baseUrl); setDefaultModel(item.defaultModel ?? ""); setAuthMode(initialAuth); @@ -243,6 +263,29 @@ export default function ProviderSettings({ )} + {isCanonicalOpenAi && ( + + )} {supportsApiKeyTransport && (