fiemwl / src /app /(dashboard) /dashboard /settings /components /CodexFastTierTab.tsx
automindy's picture
Upload 1980 files
6111b2b verified
Raw
History Blame Contribute Delete
7.69 kB
"use client";
import { useEffect, useMemo, useState } from "react";
import { Card, Toggle, Select } from "@/shared/components";
import { useTranslations } from "next-intl";
import {
CODEX_FAST_TIER_DEFAULT_SUPPORTED_MODELS,
resolveCodexGlobalFastServiceTier,
} from "@/lib/providers/codexFastTier";
type TierValue = "default" | "priority" | "flex";
// Fast-eligible Codex models per OpenAI ~/.codex/models_cache.json (service_tiers: priority).
// Other future Fast-eligible slugs can be added here without code changes once the user
// opts them in via the checkbox UI.
const CODEX_FAST_TIER_CATALOG: readonly string[] = ["gpt-5.5", "gpt-5.4"];
export default function CodexFastTierTab() {
const [enabled, setEnabled] = useState(false);
const [tier, setTier] = useState<TierValue>("priority");
const [supportedModels, setSupportedModels] = useState<string[]>([
...CODEX_FAST_TIER_DEFAULT_SUPPORTED_MODELS,
]);
const [loading, setLoading] = useState(true);
const [saving, setSaving] = useState(false);
const [status, setStatus] = useState<"" | "saved" | "error">("");
const [modelsOpen, setModelsOpen] = useState(false);
const t = useTranslations("settings");
useEffect(() => {
let cancelled = false;
fetch("/api/settings")
.then((res) => res.json())
.then((data) => {
if (cancelled) return;
const resolved = resolveCodexGlobalFastServiceTier(data);
setEnabled(resolved.enabled);
setTier(resolved.tier);
setSupportedModels([...resolved.supportedModels]);
setLoading(false);
})
.catch(() => {
if (!cancelled) setLoading(false);
});
return () => {
cancelled = true;
};
}, []);
const allCatalogModels = useMemo(() => {
// Union of the catalog and any custom models the user has stored, so we don't
// silently drop a slug the user added on a future Codex release.
const set = new Set<string>([...CODEX_FAST_TIER_CATALOG, ...supportedModels]);
return Array.from(set);
}, [supportedModels]);
const save = async (next: {
enabled?: boolean;
tier?: TierValue;
supportedModels?: string[];
}) => {
if (saving || loading) return;
setSaving(true);
setStatus("");
const previous = { enabled, tier, supportedModels };
const merged = {
enabled: next.enabled ?? enabled,
tier: next.tier ?? tier,
supportedModels: next.supportedModels ?? supportedModels,
};
setEnabled(merged.enabled);
setTier(merged.tier);
setSupportedModels(merged.supportedModels);
try {
const res = await fetch("/api/settings", {
method: "PATCH",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
codexServiceTier: {
enabled: merged.enabled,
tier: merged.tier,
supportedModels: merged.supportedModels,
},
}),
});
if (res.ok) {
setStatus("saved");
setTimeout(() => setStatus(""), 2000);
} else {
setEnabled(previous.enabled);
setTier(previous.tier);
setSupportedModels(previous.supportedModels);
setStatus("error");
}
} catch {
setEnabled(previous.enabled);
setTier(previous.tier);
setSupportedModels(previous.supportedModels);
setStatus("error");
} finally {
setSaving(false);
}
};
const toggleModel = (slug: string, checked: boolean) => {
const next = checked
? Array.from(new Set([...supportedModels, slug]))
: supportedModels.filter((m) => m !== slug);
save({ supportedModels: next });
};
return (
<Card>
<div className="flex items-center gap-3 mb-3">
<div className="p-2 rounded-lg bg-sky-500/10 text-sky-500">
<span className="material-symbols-outlined text-[20px]" aria-hidden="true">
bolt
</span>
</div>
<div className="flex-1">
<h3 className="text-lg font-semibold">{t("codexFastTierTitle")}</h3>
<p className="text-sm text-text-muted">{t("codexFastTierDesc")}</p>
</div>
<div className="flex items-center gap-3">
{status === "saved" && (
<span className="text-xs font-medium text-emerald-500 flex items-center gap-1">
<span className="material-symbols-outlined text-[14px]">check_circle</span>{" "}
{t("saved")}
</span>
)}
{status === "error" && (
<span className="text-xs font-medium text-rose-500 flex items-center gap-1">
<span className="material-symbols-outlined text-[14px]">error</span>{" "}
{t("codexFastTierSaveError")}
</span>
)}
<Toggle
checked={enabled}
onChange={(value) => save({ enabled: value })}
disabled={loading || saving}
ariaLabel={t("codexFastTierTitle")}
/>
</div>
</div>
{enabled && (
<div className="mt-4 flex flex-col gap-4 border-t border-border pt-4">
<Select
label={t("codexFastTierTierLabel")}
value={tier}
disabled={loading || saving}
onChange={(e) => save({ tier: e.target.value as TierValue })}
options={[
{ value: "priority", label: t("codexFastTierTierPriority") },
{ value: "flex", label: t("codexFastTierTierFlex") },
{ value: "default", label: t("codexFastTierTierDefault") },
]}
/>
<div>
<button
type="button"
className="flex items-center gap-1.5 text-sm font-medium text-text-main hover:text-text-muted"
onClick={() => setModelsOpen((open) => !open)}
aria-expanded={modelsOpen}
>
<span className="material-symbols-outlined text-[18px]" aria-hidden="true">
{modelsOpen ? "expand_less" : "expand_more"}
</span>
{t("codexFastTierModelsLabel")}
<span className="ml-1 text-xs text-text-muted">({supportedModels.length})</span>
</button>
{modelsOpen && (
<div className="mt-3 pl-6 flex flex-col gap-2">
<p className="text-xs text-text-muted/80">{t("codexFastTierModelsHint")}</p>
{allCatalogModels.map((slug) => {
const checked = supportedModels.includes(slug);
return (
<label key={slug} className="flex items-center gap-2 text-sm cursor-pointer">
<input
type="checkbox"
className="h-4 w-4"
checked={checked}
disabled={loading || saving}
onChange={(e) => toggleModel(slug, e.target.checked)}
aria-label={t("codexFastTierModelCheckbox", { model: slug })}
/>
<span className="font-mono text-xs">{slug}</span>
</label>
);
})}
</div>
)}
</div>
</div>
)}
<p className="mt-4 text-xs text-text-muted/80 flex items-start gap-1.5 leading-relaxed">
<span className="material-symbols-outlined text-[14px] mt-0.5">info</span>
<span>{t("codexFastTierHint")}</span>
</p>
</Card>
);
}