Spaces:
Paused
Paused
icebear0828 Claude Opus 4.6 commited on
Commit Β·
c52388f
1
Parent(s): 794d451
fix: collapsible model dropdown, default to gpt-5.4, remove legacy gpt-5
Browse files- Model selector now collapses after selection (click-outside close)
- Default model changed from gpt-5.2-codex to gpt-5.4
- Removed deprecated gpt-5, gpt-5-codex, gpt-5-codex-mini
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- config/default.yaml +1 -1
- config/models.yaml +2 -36
- shared/hooks/use-status.ts +4 -4
- web/src/components/ApiConfig.tsx +50 -20
config/default.yaml
CHANGED
|
@@ -9,7 +9,7 @@ client:
|
|
| 9 |
arch: arm64
|
| 10 |
chromium_version: "144"
|
| 11 |
model:
|
| 12 |
-
default: gpt-5.
|
| 13 |
default_reasoning_effort: medium
|
| 14 |
suppress_desktop_directives: true
|
| 15 |
auth:
|
|
|
|
| 9 |
arch: arm64
|
| 10 |
chromium_version: "144"
|
| 11 |
model:
|
| 12 |
+
default: gpt-5.4
|
| 13 |
default_reasoning_effort: medium
|
| 14 |
suppress_desktop_directives: true
|
| 15 |
auth:
|
config/models.yaml
CHANGED
|
@@ -11,7 +11,7 @@ models:
|
|
| 11 |
- id: gpt-5.4
|
| 12 |
displayName: GPT-5.4
|
| 13 |
description: Latest Codex flagship model
|
| 14 |
-
isDefault:
|
| 15 |
supportedReasoningEfforts:
|
| 16 |
- { reasoningEffort: minimal, description: "Minimal reasoning" }
|
| 17 |
- { reasoningEffort: low, description: "Fastest responses" }
|
|
@@ -110,7 +110,7 @@ models:
|
|
| 110 |
- id: gpt-5.2-codex
|
| 111 |
displayName: GPT-5.2 Codex
|
| 112 |
description: GPT-5.2 Codex flagship
|
| 113 |
-
isDefault:
|
| 114 |
supportedReasoningEfforts:
|
| 115 |
- { reasoningEffort: low, description: "Fastest responses" }
|
| 116 |
- { reasoningEffort: medium, description: "Balanced speed and quality" }
|
|
@@ -250,29 +250,6 @@ models:
|
|
| 250 |
supportsPersonality: false
|
| 251 |
upgrade: null
|
| 252 |
|
| 253 |
-
# ββ GPT-5 Codex (legacy) ββββββββββββββββββββββββββββββββββββββββββ
|
| 254 |
-
- id: gpt-5-codex
|
| 255 |
-
displayName: GPT-5 Codex
|
| 256 |
-
description: Original GPT-5 Codex
|
| 257 |
-
isDefault: false
|
| 258 |
-
supportedReasoningEfforts:
|
| 259 |
-
- { reasoningEffort: medium, description: "Default" }
|
| 260 |
-
defaultReasoningEffort: medium
|
| 261 |
-
inputModalities: [text]
|
| 262 |
-
supportsPersonality: false
|
| 263 |
-
upgrade: null
|
| 264 |
-
|
| 265 |
-
- id: gpt-5-codex-mini
|
| 266 |
-
displayName: GPT-5 Codex Mini
|
| 267 |
-
description: Original lightweight Codex
|
| 268 |
-
isDefault: false
|
| 269 |
-
supportedReasoningEfforts:
|
| 270 |
-
- { reasoningEffort: medium, description: "Default" }
|
| 271 |
-
defaultReasoningEffort: medium
|
| 272 |
-
inputModalities: [text]
|
| 273 |
-
supportsPersonality: false
|
| 274 |
-
upgrade: null
|
| 275 |
-
|
| 276 |
# ββ Base GPT models (also usable via Codex endpoint) βββββββββββββββ
|
| 277 |
- id: gpt-5.3
|
| 278 |
displayName: GPT-5.3
|
|
@@ -313,16 +290,5 @@ models:
|
|
| 313 |
supportsPersonality: true
|
| 314 |
upgrade: null
|
| 315 |
|
| 316 |
-
- id: gpt-5
|
| 317 |
-
displayName: GPT-5
|
| 318 |
-
description: General-purpose GPT-5
|
| 319 |
-
isDefault: false
|
| 320 |
-
supportedReasoningEfforts:
|
| 321 |
-
- { reasoningEffort: medium, description: "Default" }
|
| 322 |
-
defaultReasoningEffort: medium
|
| 323 |
-
inputModalities: [text, image]
|
| 324 |
-
supportsPersonality: true
|
| 325 |
-
upgrade: null
|
| 326 |
-
|
| 327 |
aliases:
|
| 328 |
codex: "gpt-5.4"
|
|
|
|
| 11 |
- id: gpt-5.4
|
| 12 |
displayName: GPT-5.4
|
| 13 |
description: Latest Codex flagship model
|
| 14 |
+
isDefault: true
|
| 15 |
supportedReasoningEfforts:
|
| 16 |
- { reasoningEffort: minimal, description: "Minimal reasoning" }
|
| 17 |
- { reasoningEffort: low, description: "Fastest responses" }
|
|
|
|
| 110 |
- id: gpt-5.2-codex
|
| 111 |
displayName: GPT-5.2 Codex
|
| 112 |
description: GPT-5.2 Codex flagship
|
| 113 |
+
isDefault: false
|
| 114 |
supportedReasoningEfforts:
|
| 115 |
- { reasoningEffort: low, description: "Fastest responses" }
|
| 116 |
- { reasoningEffort: medium, description: "Balanced speed and quality" }
|
|
|
|
| 250 |
supportsPersonality: false
|
| 251 |
upgrade: null
|
| 252 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 253 |
# ββ Base GPT models (also usable via Codex endpoint) βββββββββββββββ
|
| 254 |
- id: gpt-5.3
|
| 255 |
displayName: GPT-5.3
|
|
|
|
| 290 |
supportsPersonality: true
|
| 291 |
upgrade: null
|
| 292 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 293 |
aliases:
|
| 294 |
codex: "gpt-5.4"
|
shared/hooks/use-status.ts
CHANGED
|
@@ -44,8 +44,8 @@ function isTierVariant(id: string): boolean {
|
|
| 44 |
export function useStatus(accountCount: number) {
|
| 45 |
const [baseUrl, setBaseUrl] = useState("Loading...");
|
| 46 |
const [apiKey, setApiKey] = useState("Loading...");
|
| 47 |
-
const [models, setModels] = useState<string[]>(["
|
| 48 |
-
const [selectedModel, setSelectedModel] = useState("
|
| 49 |
const [modelCatalog, setModelCatalog] = useState<CatalogModel[]>([]);
|
| 50 |
const [selectedEffort, setSelectedEffort] = useState("medium");
|
| 51 |
|
|
@@ -62,11 +62,11 @@ export function useStatus(accountCount: number) {
|
|
| 62 |
const ids: string[] = data.data.map((m: { id: string }) => m.id);
|
| 63 |
if (ids.length > 0) {
|
| 64 |
setModels(ids);
|
| 65 |
-
const preferred = ids.find((n) => n === "
|
| 66 |
if (preferred) setSelectedModel(preferred);
|
| 67 |
}
|
| 68 |
} catch {
|
| 69 |
-
setModels(["
|
| 70 |
}
|
| 71 |
}, []);
|
| 72 |
|
|
|
|
| 44 |
export function useStatus(accountCount: number) {
|
| 45 |
const [baseUrl, setBaseUrl] = useState("Loading...");
|
| 46 |
const [apiKey, setApiKey] = useState("Loading...");
|
| 47 |
+
const [models, setModels] = useState<string[]>(["gpt-5.4"]);
|
| 48 |
+
const [selectedModel, setSelectedModel] = useState("gpt-5.4");
|
| 49 |
const [modelCatalog, setModelCatalog] = useState<CatalogModel[]>([]);
|
| 50 |
const [selectedEffort, setSelectedEffort] = useState("medium");
|
| 51 |
|
|
|
|
| 62 |
const ids: string[] = data.data.map((m: { id: string }) => m.id);
|
| 63 |
if (ids.length > 0) {
|
| 64 |
setModels(ids);
|
| 65 |
+
const preferred = ids.find((n) => n === "gpt-5.4");
|
| 66 |
if (preferred) setSelectedModel(preferred);
|
| 67 |
}
|
| 68 |
} catch {
|
| 69 |
+
setModels(["gpt-5.4"]);
|
| 70 |
}
|
| 71 |
}, []);
|
| 72 |
|
web/src/components/ApiConfig.tsx
CHANGED
|
@@ -1,6 +1,6 @@
|
|
| 1 |
import { useT } from "../../../shared/i18n/context";
|
| 2 |
import { CopyButton } from "./CopyButton";
|
| 3 |
-
import { useCallback } from "preact/hooks";
|
| 4 |
import type { ModelFamily } from "../../../shared/hooks/use-status";
|
| 5 |
|
| 6 |
interface ApiConfigProps {
|
|
@@ -38,10 +38,26 @@ export function ApiConfig({
|
|
| 38 |
const getBaseUrl = useCallback(() => baseUrl, [baseUrl]);
|
| 39 |
const getApiKey = useCallback(() => apiKey, [apiKey]);
|
| 40 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 41 |
// When a family is selected, update model + snap effort to default if current effort is unsupported
|
| 42 |
const handleFamilySelect = useCallback(
|
| 43 |
(family: ModelFamily) => {
|
| 44 |
onModelChange(family.id);
|
|
|
|
| 45 |
const supportedEfforts = family.efforts.map((e) => e.reasoningEffort);
|
| 46 |
if (!supportedEfforts.includes(selectedEffort)) {
|
| 47 |
onEffortChange(family.defaultEffort);
|
|
@@ -85,26 +101,40 @@ export function ApiConfig({
|
|
| 85 |
<div class="space-y-1.5">
|
| 86 |
<label class="text-xs font-semibold text-slate-700 dark:text-text-main">{t("defaultModel")}</label>
|
| 87 |
{showMatrix ? (
|
| 88 |
-
<div class="
|
| 89 |
-
{/*
|
| 90 |
-
<
|
| 91 |
-
{
|
| 92 |
-
|
| 93 |
-
|
| 94 |
-
|
| 95 |
-
|
| 96 |
-
|
| 97 |
-
|
| 98 |
-
|
| 99 |
-
|
| 100 |
-
|
| 101 |
-
|
| 102 |
-
<
|
| 103 |
-
|
| 104 |
-
|
| 105 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 106 |
{currentEfforts.length > 1 && (
|
| 107 |
-
<div class="flex gap-1.5
|
| 108 |
{currentEfforts.map((e) => (
|
| 109 |
<button
|
| 110 |
key={e.reasoningEffort}
|
|
|
|
| 1 |
import { useT } from "../../../shared/i18n/context";
|
| 2 |
import { CopyButton } from "./CopyButton";
|
| 3 |
+
import { useCallback, useState, useEffect, useRef } from "preact/hooks";
|
| 4 |
import type { ModelFamily } from "../../../shared/hooks/use-status";
|
| 5 |
|
| 6 |
interface ApiConfigProps {
|
|
|
|
| 38 |
const getBaseUrl = useCallback(() => baseUrl, [baseUrl]);
|
| 39 |
const getApiKey = useCallback(() => apiKey, [apiKey]);
|
| 40 |
|
| 41 |
+
const [open, setOpen] = useState(false);
|
| 42 |
+
const dropdownRef = useRef<HTMLDivElement>(null);
|
| 43 |
+
|
| 44 |
+
// Close dropdown on click outside
|
| 45 |
+
useEffect(() => {
|
| 46 |
+
if (!open) return;
|
| 47 |
+
const handler = (e: MouseEvent) => {
|
| 48 |
+
if (dropdownRef.current && !dropdownRef.current.contains(e.target as Node)) {
|
| 49 |
+
setOpen(false);
|
| 50 |
+
}
|
| 51 |
+
};
|
| 52 |
+
document.addEventListener("mousedown", handler);
|
| 53 |
+
return () => document.removeEventListener("mousedown", handler);
|
| 54 |
+
}, [open]);
|
| 55 |
+
|
| 56 |
// When a family is selected, update model + snap effort to default if current effort is unsupported
|
| 57 |
const handleFamilySelect = useCallback(
|
| 58 |
(family: ModelFamily) => {
|
| 59 |
onModelChange(family.id);
|
| 60 |
+
setOpen(false);
|
| 61 |
const supportedEfforts = family.efforts.map((e) => e.reasoningEffort);
|
| 62 |
if (!supportedEfforts.includes(selectedEffort)) {
|
| 63 |
onEffortChange(family.defaultEffort);
|
|
|
|
| 101 |
<div class="space-y-1.5">
|
| 102 |
<label class="text-xs font-semibold text-slate-700 dark:text-text-main">{t("defaultModel")}</label>
|
| 103 |
{showMatrix ? (
|
| 104 |
+
<div ref={dropdownRef} class="relative">
|
| 105 |
+
{/* Trigger button */}
|
| 106 |
+
<button
|
| 107 |
+
onClick={() => setOpen(!open)}
|
| 108 |
+
class="w-full flex items-center justify-between px-3 py-2.5 bg-white dark:bg-bg-dark border border-gray-200 dark:border-border-dark rounded-lg text-[0.78rem] text-slate-700 dark:text-text-main font-medium focus:ring-1 focus:ring-primary focus:border-primary outline-none cursor-pointer transition-colors"
|
| 109 |
+
>
|
| 110 |
+
<span>{currentFamily?.displayName ?? selectedModel}</span>
|
| 111 |
+
<svg class={`size-[18px] text-slate-500 dark:text-text-dim transition-transform ${open ? "rotate-180" : ""}`} viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
| 112 |
+
<path stroke-linecap="round" stroke-linejoin="round" d="M19.5 8.25l-7.5 7.5-7.5-7.5" />
|
| 113 |
+
</svg>
|
| 114 |
+
</button>
|
| 115 |
+
{/* Dropdown list */}
|
| 116 |
+
{open && (
|
| 117 |
+
<div class="absolute z-10 mt-1 w-full border border-gray-200 dark:border-border-dark rounded-lg overflow-hidden bg-white dark:bg-card-dark shadow-lg">
|
| 118 |
+
<div class="max-h-[200px] overflow-y-auto">
|
| 119 |
+
{modelFamilies.map((f) => (
|
| 120 |
+
<button
|
| 121 |
+
key={f.id}
|
| 122 |
+
onClick={() => handleFamilySelect(f)}
|
| 123 |
+
class={`w-full text-left px-3 py-2 text-[0.78rem] font-medium border-b border-gray-100 dark:border-border-dark last:border-b-0 transition-colors ${
|
| 124 |
+
selectedModel === f.id
|
| 125 |
+
? "bg-primary/10 text-primary dark:bg-primary/20"
|
| 126 |
+
: "text-slate-700 dark:text-text-main hover:bg-slate-50 dark:hover:bg-[#21262d]"
|
| 127 |
+
}`}
|
| 128 |
+
>
|
| 129 |
+
{f.displayName}
|
| 130 |
+
</button>
|
| 131 |
+
))}
|
| 132 |
+
</div>
|
| 133 |
+
</div>
|
| 134 |
+
)}
|
| 135 |
+
{/* Reasoning effort buttons β always visible */}
|
| 136 |
{currentEfforts.length > 1 && (
|
| 137 |
+
<div class="flex gap-1.5 mt-2 flex-wrap">
|
| 138 |
{currentEfforts.map((e) => (
|
| 139 |
<button
|
| 140 |
key={e.reasoningEffort}
|