import type { CodexModelOptions, CodexReasoningEffort, ProviderKind, ThreadId, } from "@t3tools/contracts"; import { getDefaultReasoningEffort, getReasoningEffortOptions, normalizeCodexModelOptions, resolveReasoningEffortForProvider, } from "@t3tools/shared/model"; import { memo, useState } from "react"; import { ChevronDownIcon } from "lucide-react"; import { useComposerDraftStore, useComposerThreadDraft } from "../../composerDraftStore"; import { Button } from "../ui/button"; import { Menu, MenuGroup, MenuPopup, MenuRadioGroup, MenuRadioItem, MenuSeparator as MenuDivider, MenuTrigger, } from "../ui/menu"; const PROVIDER = "codex" as const satisfies ProviderKind; const CODEX_REASONING_LABELS: Record = { low: "Low", medium: "Medium", high: "High", xhigh: "Extra High", }; function getSelectedCodexTraits(modelOptions: CodexModelOptions | null | undefined): { effort: CodexReasoningEffort; fastModeEnabled: boolean; } { const defaultReasoningEffort = getDefaultReasoningEffort(PROVIDER); return { effort: resolveReasoningEffortForProvider(PROVIDER, modelOptions?.reasoningEffort) ?? defaultReasoningEffort, fastModeEnabled: modelOptions?.fastMode === true, }; } function CodexTraitsMenuContentImpl(props: { threadId: ThreadId }) { const draft = useComposerThreadDraft(props.threadId); const modelOptions = draft.modelOptions?.[PROVIDER]; const setProviderModelOptions = useComposerDraftStore((store) => store.setProviderModelOptions); const options = getReasoningEffortOptions(PROVIDER); const defaultReasoningEffort = getDefaultReasoningEffort(PROVIDER); const { effort, fastModeEnabled } = getSelectedCodexTraits(modelOptions); return ( <>
Reasoning
{ if (!value) return; const nextEffort = options.find((option) => option === value); if (!nextEffort) return; setProviderModelOptions( props.threadId, PROVIDER, normalizeCodexModelOptions({ ...modelOptions, reasoningEffort: nextEffort, }), { persistSticky: true }, ); }} > {options.map((option) => ( {CODEX_REASONING_LABELS[option]} {option === defaultReasoningEffort ? " (default)" : ""} ))}
Fast Mode
{ setProviderModelOptions( props.threadId, PROVIDER, normalizeCodexModelOptions({ ...modelOptions, fastMode: value === "on", }), { persistSticky: true }, ); }} > off on
); } export const CodexTraitsMenuContent = memo(CodexTraitsMenuContentImpl); export const CodexTraitsPicker = memo(function CodexTraitsPicker(props: { threadId: ThreadId }) { const [isMenuOpen, setIsMenuOpen] = useState(false); const modelOptions = useComposerThreadDraft(props.threadId).modelOptions?.codex; const { effort, fastModeEnabled } = getSelectedCodexTraits(modelOptions); const triggerLabel = [CODEX_REASONING_LABELS[effort], ...(fastModeEnabled ? ["Fast"] : [])] .filter(Boolean) .join(" ยท "); return ( { setIsMenuOpen(open); }} > } > {triggerLabel} ); });