From d20011cd73b92108a4bbb68cbead5462aac8b521 Mon Sep 17 00:00:00 2001 From: Arthur Bodera Date: Tue, 30 Jun 2026 23:41:04 +1000 Subject: [PATCH] fix(chatgpt-web): restore dot-form Pro model ids (#5549) Integrated into release/v3.8.43 (drift-shed: cherry-picked the real change onto the release tip; stale-base drift dropped). --- .../providers/registry/chatgpt-web/index.ts | 18 ++--- open-sse/executors/chatgpt-web.ts | 14 ++-- tests/unit/chatgpt-web.test.ts | 67 +++++++++++-------- 3 files changed, 55 insertions(+), 44 deletions(-) diff --git a/open-sse/config/providers/registry/chatgpt-web/index.ts b/open-sse/config/providers/registry/chatgpt-web/index.ts index 1fcb470a9d..2106b94a0b 100644 --- a/open-sse/config/providers/registry/chatgpt-web/index.ts +++ b/open-sse/config/providers/registry/chatgpt-web/index.ts @@ -9,15 +9,15 @@ export const chatgpt_webProvider: RegistryEntry = { authType: "apikey", authHeader: "cookie", models: [ - { id: "gpt-5-5-pro", name: "GPT-5.5 Pro" }, // pro tier only, standard effort - { id: "gpt-5-5-pro-extended", name: "GPT-5.5 Pro Extended" }, // pro tier only, extended effort - { id: "gpt-5-5-thinking", name: "GPT-5.5 Thinking" }, // plus, pro tier - { id: "gpt-5-5", name: "GPT-5.5 Instant" }, // free, plus, pro tier - { id: "gpt-5-4-pro", name: "GPT-5.4 Pro" }, // pro tier only - { id: "gpt-5-4-thinking", name: "GPT-5.4 Thinking" }, // plus, pro tier - { id: "gpt-5-4-t-mini", name: "GPT-5.4 Thinking Mini" }, // free-login only - { id: "gpt-5-3", name: "GPT-5.3 Instant" }, // free, free-login, plus, pro tier - { id: "gpt-5-3-mini", name: "GPT-5.3 Mini" }, // limit fallback + { id: "gpt-5.5-pro", name: "GPT-5.5 Pro" }, // pro tier only, standard effort + { id: "gpt-5.5-pro-extended", name: "GPT-5.5 Pro Extended" }, // pro tier only, extended effort + { id: "gpt-5.5-thinking", name: "GPT-5.5 Thinking" }, // plus, pro tier + { id: "gpt-5.5", name: "GPT-5.5 Instant" }, // free, plus, pro tier + { id: "gpt-5.4-pro", name: "GPT-5.4 Pro" }, // pro tier only + { id: "gpt-5.4-thinking", name: "GPT-5.4 Thinking" }, // plus, pro tier + { id: "gpt-5.4-thinking-mini", name: "GPT-5.4 Thinking Mini" }, // free-login only + { id: "gpt-5.3", name: "GPT-5.3 Instant" }, // free, free-login, plus, pro tier + { id: "gpt-5.3-mini", name: "GPT-5.3 Mini" }, // limit fallback { id: "o3", name: "o3" }, // plus ~ tier ], }; diff --git a/open-sse/executors/chatgpt-web.ts b/open-sse/executors/chatgpt-web.ts index 135c9d450c..87a4bd3f29 100644 --- a/open-sse/executors/chatgpt-web.ts +++ b/open-sse/executors/chatgpt-web.ts @@ -79,13 +79,13 @@ function deviceIdFor(cookie: string): string { return id; } -// OmniRoute model ID → ChatGPT internal slug. OmniRoute uses dot-form IDs -// (e.g. "gpt-5.3-instant"), ChatGPT's web routes use dash-form -// (e.g. "gpt-5-3-instant"). The slug catalog comes from -// /backend-api/models on a logged-in account; "gpt-5-4-t-mini" is ChatGPT's -// abbreviated slug for "GPT-5.4 Thinking Mini". +// OmniRoute model ID → ChatGPT internal slug. The public ChatGPT Web catalog +// keeps OmniRoute's historical dot-form IDs (e.g. "gpt-5.5-pro"), while +// ChatGPT's backend routes use dash-form slugs (e.g. "gpt-5-5-pro"). The slug +// catalog comes from /backend-api/models on a logged-in account; +// "gpt-5-4-t-mini" is ChatGPT's abbreviated slug for "GPT-5.4 Thinking Mini". const MODEL_MAP: Record = { - // Current ChatGPT Web slugs (these are what the provider catalog exposes). + // ChatGPT backend slugs are also accepted directly for power users / tests. "gpt-5-5-pro": "gpt-5-5-pro", "gpt-5-5-pro-extended": "gpt-5-5-pro", "gpt-5-5-thinking": "gpt-5-5-thinking", @@ -96,7 +96,7 @@ const MODEL_MAP: Record = { "gpt-5-3": "gpt-5-3", "gpt-5-3-mini": "gpt-5-3-mini", - // Legacy OmniRoute dot-form aliases kept for direct provider/model callers. + // Public OmniRoute dot-form ids exposed by the provider catalog. "gpt-5.5-pro": "gpt-5-5-pro", "gpt-5.5-pro-extended": "gpt-5-5-pro", "gpt-5.5-thinking": "gpt-5-5-thinking", diff --git a/tests/unit/chatgpt-web.test.ts b/tests/unit/chatgpt-web.test.ts index 4191ff196e..7a206e7e5c 100644 --- a/tests/unit/chatgpt-web.test.ts +++ b/tests/unit/chatgpt-web.test.ts @@ -842,7 +842,7 @@ test("GPT-5.5 Pro non-streaming: stream_handoff polls conversation detail for fi try { const executor = new ChatGptWebExecutor(); const result = await executor.execute({ - model: "gpt-5-5-pro-extended", + model: "gpt-5.5-pro-extended", body: { messages: [{ role: "user", content: "hard problem" }] }, stream: false, credentials: { apiKey: "cookie-pro-poll" }, @@ -904,7 +904,7 @@ test("GPT-5.5 Pro streaming: preserves interim reasoning and appends final polle try { const executor = new ChatGptWebExecutor(); const result = await executor.execute({ - model: "gpt-5-5-pro-extended", + model: "gpt-5.5-pro-extended", body: { messages: [{ role: "user", content: "hard problem" }], stream: true }, stream: true, credentials: { apiKey: "cookie-pro-stream" }, @@ -1271,18 +1271,19 @@ test("Provider registry: chatgpt-web exposes the current ChatGPT Web model catal assert.equal(entry.authHeader, "cookie"); const ids = (entry.models || []).map((m) => m.id); - // Mirrors /backend-api/models for ChatGPT Web. Retired GPT-5/GPT-5.1 - // entries should stay out of this list. + // Public OmniRoute ids stay in historical dot form even though ChatGPT's + // backend routes use dash-form slugs. Retired GPT-5/GPT-5.1 entries should + // stay out of this list. assert.deepEqual(ids, [ - "gpt-5-5-pro", - "gpt-5-5-pro-extended", - "gpt-5-5-thinking", - "gpt-5-5", - "gpt-5-4-pro", - "gpt-5-4-thinking", - "gpt-5-4-t-mini", - "gpt-5-3", - "gpt-5-3-mini", + "gpt-5.5-pro", + "gpt-5.5-pro-extended", + "gpt-5.5-thinking", + "gpt-5.5", + "gpt-5.4-pro", + "gpt-5.4-thinking", + "gpt-5.4-thinking-mini", + "gpt-5.3", + "gpt-5.3-mini", "o3", ]); }); @@ -1292,19 +1293,21 @@ test("Executor MODEL_MAP: OmniRoute IDs translate to ChatGPT backend slugs", asy const m = installMockFetch(); try { const cases: Array<[string, string]> = [ - ["gpt-5-3", "gpt-5-3"], - ["gpt-5-5-thinking", "gpt-5-5-thinking"], - ["gpt-5-4-t-mini", "gpt-5-4-t-mini"], - ["gpt-5-5-pro", "gpt-5-5-pro"], - ["gpt-5-5-pro-extended", "gpt-5-5-pro"], - ["o3", "o3"], - // Legacy dot-form ids are still accepted for direct provider/model callers. + // Public catalog ids. ["gpt-5.3", "gpt-5-3"], ["gpt-5.5-thinking", "gpt-5-5-thinking"], ["gpt-5.4-thinking-mini", "gpt-5-4-t-mini"], ["gpt-5.5", "gpt-5-5"], ["gpt-5.5-pro", "gpt-5-5-pro"], + ["gpt-5.5-pro-extended", "gpt-5-5-pro"], ["gpt-5.4-pro", "gpt-5-4-pro"], + ["o3", "o3"], + // Backend dash-form slugs are still accepted for direct provider/model callers. + ["gpt-5-3", "gpt-5-3"], + ["gpt-5-5-thinking", "gpt-5-5-thinking"], + ["gpt-5-4-t-mini", "gpt-5-4-t-mini"], + ["gpt-5-5-pro", "gpt-5-5-pro"], + ["gpt-5-5-pro-extended", "gpt-5-5-pro"], ]; for (const [omniId, expectedSlug] of cases) { m.calls.urls.length = 0; @@ -1331,6 +1334,18 @@ test("MODEL_MAP drift guard: every advertised catalog id reaches ChatGPT as a ba reset(); const { getRegistryEntry } = await import("../../open-sse/config/providerRegistry.ts"); const ids = (getRegistryEntry("chatgpt-web")?.models || []).map((m) => m.id); + const expectedSlugById: Record = { + "gpt-5.5-pro": "gpt-5-5-pro", + "gpt-5.5-pro-extended": "gpt-5-5-pro", + "gpt-5.5-thinking": "gpt-5-5-thinking", + "gpt-5.5": "gpt-5-5", + "gpt-5.4-pro": "gpt-5-4-pro", + "gpt-5.4-thinking": "gpt-5-4-thinking", + "gpt-5.4-thinking-mini": "gpt-5-4-t-mini", + "gpt-5.3": "gpt-5-3", + "gpt-5.3-mini": "gpt-5-3-mini", + o3: "o3", + }; const m = installMockFetch(); try { for (const omniId of ids) { @@ -1351,11 +1366,7 @@ test("MODEL_MAP drift guard: every advertised catalog id reaches ChatGPT as a ba !body.model.includes("."), `${omniId} reached the backend as "${body.model}" (still dot-form)` ); - if (omniId.endsWith("-extended")) { - assert.equal(body.model, "gpt-5-5-pro", `${omniId} should select the base Pro slug`); - } else { - assert.equal(body.model, omniId, `${omniId} should pass through as its backend slug`); - } + assert.equal(body.model, expectedSlugById[omniId], `${omniId} should map to backend slug`); } } finally { m.restore(); @@ -1370,7 +1381,7 @@ test("GPT-5.5 Pro Extended sends base slug with extended effort and Temporary Ch try { const executor = new ChatGptWebExecutor(); const result = await executor.execute({ - model: "gpt-5-5-pro-extended", + model: "gpt-5.5-pro-extended", body: { messages: [{ role: "user", content: "hi" }] }, stream: false, credentials: { apiKey: "cookie-pro-extended" }, @@ -1399,7 +1410,7 @@ test("GPT-5.5 Pro standard sends standard effort", async () => { try { const executor = new ChatGptWebExecutor(); await executor.execute({ - model: "gpt-5-5-pro", + model: "gpt-5.5-pro", body: { messages: [{ role: "user", content: "hi" }] }, stream: false, credentials: { apiKey: "cookie-pro-standard" }, @@ -1422,7 +1433,7 @@ test("GPT-5.5 Pro store:false keeps Temporary Chat enabled for background utilit try { const executor = new ChatGptWebExecutor(); const result = await executor.execute({ - model: "gpt-5-5-pro-extended", + model: "gpt-5.5-pro-extended", body: { store: false, messages: [