From 96d48fb0071066afbd4535d53d3bdb28bec58b2e Mon Sep 17 00:00:00 2001
From: Aayam Bansal
Date: Tue, 28 Jul 2026 00:19:18 +0800
Subject: [PATCH] Improve ChatGPT Fast mode controls
---
backend/cli/src/cli/cmd/models.ts | 4 +--
backend/cli/src/provider/provider.ts | 5 ++--
backend/cli/test/provider/provider.test.ts | 3 ++-
frontend/workspace/e2e/model-tier.spec.ts | 25 +++++++++----------
frontend/workspace/e2e/thinking-level.spec.ts | 10 +++++---
frontend/workspace/src/atlas/Composer.tsx | 2 +-
.../workspace/src/components/prompt-input.tsx | 16 +++++-------
.../src/components/settings/Credentials.tsx | 4 +--
8 files changed, 33 insertions(+), 36 deletions(-)
diff --git a/backend/cli/src/cli/cmd/models.ts b/backend/cli/src/cli/cmd/models.ts
index 7f53e525..daa355f6 100644
--- a/backend/cli/src/cli/cmd/models.ts
+++ b/backend/cli/src/cli/cmd/models.ts
@@ -9,7 +9,7 @@ import { EOL } from "os"
const PROVIDER_LABELS: Record = {
anthropic: "Anthropic",
openai: "OpenAI",
- "openai-codex": "OpenAI Codex",
+ "openai-codex": "OpenAI (ChatGPT subscription)",
google: "Google",
gemini: "Google Gemini",
openrouter: "OpenRouter",
@@ -25,7 +25,7 @@ const PROVIDER_LABELS: Record = {
* - anything else with a key → BYOK.
*/
function routingLabel(providerID: string, provider: Provider.Info): string {
- if (providerID === "openai-codex") return "Signed in with Codex"
+ if (providerID === "openai-codex") return "ChatGPT subscription"
// Read the EFFECTIVE credential, not just provider.key: a custom loader stores
// its key under options.apiKey (e.g. openrouter), and a multi-env provider
// (google's GEMINI_API_KEY + GOOGLE_GENERATIVE_AI_API_KEY) leaves provider.key
diff --git a/backend/cli/src/provider/provider.ts b/backend/cli/src/provider/provider.ts
index 76b289aa..80922437 100644
--- a/backend/cli/src/provider/provider.ts
+++ b/backend/cli/src/provider/provider.ts
@@ -66,8 +66,7 @@ export namespace Provider {
}
function codexOAuthModes(modelID: string) {
- if (/^gpt-5[.-]4-mini$/.test(modelID)) return undefined
- if (!/^gpt-5[.-](?:4|5|6)(?:-(?:sol|terra|luna))?$/.test(modelID)) return undefined
+ if (!/^gpt-5[.-](?:4(?:-mini)?|5|6(?:-(?:sol|terra|luna))?)$/.test(modelID)) return undefined
return {
fast: {
provider: {
@@ -1430,7 +1429,7 @@ export namespace Provider {
database["openai-codex"] = {
...baseOpenai,
id: "openai-codex",
- name: "OpenAI Codex (ChatGPT subscription)",
+ name: "OpenAI (ChatGPT subscription)",
env: [],
options: {},
models: codexModels,
diff --git a/backend/cli/test/provider/provider.test.ts b/backend/cli/test/provider/provider.test.ts
index 31b9f2d1..157823dd 100644
--- a/backend/cli/test/provider/provider.test.ts
+++ b/backend/cli/test/provider/provider.test.ts
@@ -132,7 +132,8 @@ test("synthesized Codex OAuth models use Codex variants and context instead of p
const codex54 = codex.models["gpt-5.4"]
expect(Object.keys(codex54.variants ?? {})).toEqual(["low", "medium", "high", "xhigh"])
expect(Object.keys(codex54.modes ?? {})).toEqual(["fast"])
- expect(codex.models["gpt-5.4-mini"].modes).toBeUndefined()
+ expect(Object.keys(codex.models["gpt-5.4-mini"].modes ?? {})).toEqual(["fast"])
+ expect(codex.name).toBe("OpenAI (ChatGPT subscription)")
const publicSol = providers.openai?.models["gpt-5.6-sol"]
expect(Object.keys(publicSol?.variants ?? {})).toEqual(["none", "low", "medium", "high", "xhigh", "max"])
diff --git a/frontend/workspace/e2e/model-tier.spec.ts b/frontend/workspace/e2e/model-tier.spec.ts
index 7874c363..19506d47 100644
--- a/frontend/workspace/e2e/model-tier.spec.ts
+++ b/frontend/workspace/e2e/model-tier.spec.ts
@@ -1,21 +1,19 @@
import { test, expect } from "./fixtures"
import { modelTierCycleSelector, promptSelector } from "./utils"
-test("model service mode cycles through supported modes and reaches the prompt request", async ({
- page,
- sdk,
- gotoSession,
-}) => {
+test("model Fast mode toggles and reaches the prompt request", async ({ page, sdk, gotoSession }) => {
await gotoSession()
await page.addStyleTag({
content: `${modelTierCycleSelector} { display: inline-block !important; }`,
})
- const button = page.locator(modelTierCycleSelector)
- await expect(button).toBeVisible()
- await expect(button).toHaveText("standard")
- await expect(button).toHaveAttribute("aria-label", /cycle tier: standard/i)
+ const toggle = page.locator(modelTierCycleSelector)
+ const input = toggle.locator('[data-slot="switch-input"]')
+ const control = toggle.locator('[data-slot="switch-control"]')
+ await expect(toggle).toBeVisible()
+ await expect(toggle).toHaveText("Fast")
+ await expect(input).toHaveAttribute("aria-checked", "false")
const send = async (tier?: string) => {
const request = page.waitForRequest((request) => {
@@ -53,12 +51,13 @@ test("model service mode cycles through supported modes and reaches the prompt r
)
.toContain(standard)
- await button.click()
- await expect(button).toHaveText("fast")
- await expect(button).toHaveAttribute("aria-label", /cycle tier: fast/i)
+ await control.click()
+ await expect(toggle).toHaveText("Fast")
+ await expect(input).toHaveAttribute("aria-checked", "true")
await page.reload()
- await expect(button).toHaveText("fast")
+ await expect(toggle).toHaveText("Fast")
+ await expect(input).toHaveAttribute("aria-checked", "true")
const fast = await send("fast")
await expect
diff --git a/frontend/workspace/e2e/thinking-level.spec.ts b/frontend/workspace/e2e/thinking-level.spec.ts
index 949b9540..fa1dcb54 100644
--- a/frontend/workspace/e2e/thinking-level.spec.ts
+++ b/frontend/workspace/e2e/thinking-level.spec.ts
@@ -14,7 +14,7 @@ test("thinking effort starts at standard and reaches the prompt request", async
if (!exists) return
await expect(button).toBeVisible()
- await expect(button).toHaveText("standard")
+ await expect(button).toHaveText("Thinking")
await expect(button).toHaveAttribute("aria-label", /thinking effort: standard/i)
const send = async (options: { variant?: string; tier?: string } = {}) => {
@@ -59,15 +59,17 @@ test("thinking effort starts at standard and reaches the prompt request", async
await expect(button).toHaveAttribute("aria-label", /thinking effort: high/i)
const tier = page.locator(modelTierCycleSelector)
- await tier.click()
- await expect(tier).toHaveText("fast")
+ await tier.locator('[data-slot="switch-control"]').click()
+ await expect(tier).toHaveText("Fast")
+ await expect(tier.locator('[data-slot="switch-input"]')).toHaveAttribute("aria-checked", "true")
const high = await send({ variant: "high", tier: "fast" })
await expect.poll(output, { timeout: 20_000 }).toContain(high)
await page.reload()
await expect(button).toHaveText("high")
- await expect(tier).toHaveText("fast")
+ await expect(tier).toHaveText("Fast")
+ await expect(tier.locator('[data-slot="switch-input"]')).toHaveAttribute("aria-checked", "true")
} finally {
await sdk.session.delete({ sessionID }).catch(() => undefined)
}
diff --git a/frontend/workspace/src/atlas/Composer.tsx b/frontend/workspace/src/atlas/Composer.tsx
index 43e2da9d..9ca18b3d 100644
--- a/frontend/workspace/src/atlas/Composer.tsx
+++ b/frontend/workspace/src/atlas/Composer.tsx
@@ -32,7 +32,7 @@ const BYOK_URL = URLS.dashboard
const PROVIDER_LABEL: Record = {
anthropic: "Anthropic",
openai: "OpenAI",
- "openai-codex": "OpenAI Codex",
+ "openai-codex": "OpenAI (ChatGPT subscription)",
google: "Google",
"google-vertex": "Google Vertex",
"github-copilot": "GitHub Copilot",
diff --git a/frontend/workspace/src/components/prompt-input.tsx b/frontend/workspace/src/components/prompt-input.tsx
index 9478b48c..eecb7a9b 100644
--- a/frontend/workspace/src/components/prompt-input.tsx
+++ b/frontend/workspace/src/components/prompt-input.tsx
@@ -33,6 +33,7 @@ import { useSync } from "@/context/sync"
import { useComments } from "@/context/comments"
import { FileIcon } from "@synsci/ui/file-icon"
import { Button } from "@synsci/ui/button"
+import { Switch as Toggle } from "@synsci/ui/switch"
import { Icon } from "@synsci/ui/icon"
import { ProviderIcon } from "@synsci/ui/provider-icon"
import type { IconName } from "@synsci/ui/icons/provider"
@@ -2050,19 +2051,14 @@ export const PromptInput: Component = (props) => {
title={language.t("command.tier.cycle")}
keybind={command.keybind("tier.cycle")}
>
- local.model.tier.set(local.model.tier.current() === "fast" ? "standard" : "fast")}
+ checked={local.model.tier.current() === "fast"}
+ class="text-text-base group-hover/prompt-input:flex items-center gap-1.5"
+ onChange={(checked) => local.model.tier.set(checked ? "fast" : "standard")}
>
Fast
-
+
diff --git a/frontend/workspace/src/components/settings/Credentials.tsx b/frontend/workspace/src/components/settings/Credentials.tsx
index df5c0171..d8bb8947 100644
--- a/frontend/workspace/src/components/settings/Credentials.tsx
+++ b/frontend/workspace/src/components/settings/Credentials.tsx
@@ -462,7 +462,7 @@ export const Credentials: Component = () => {
- {/* Sign in with ChatGPT (Codex OAuth) — an OpenAI plan's credits, no key */}
+ {/* Sign in with ChatGPT — an OpenAI plan's credits, no key */}
{
Sign in with ChatGPT
- Use your ChatGPT Plus / Pro / Business plan (Codex) — no API key needed.
+ Use your ChatGPT Plus / Pro / Business plan — no API key needed.