import { type ProviderDriverKind, type ProviderOptionDescriptor, type ProviderOptionSelection, type ScopedThreadRef, type ServerProviderModel, } from "@t3tools/contracts"; import { applyClaudePromptEffortPrefix, buildProviderOptionSelectionsFromDescriptors, getProviderOptionCurrentLabel, getProviderOptionCurrentValue, getProviderOptionDescriptors, isClaudeUltrathinkPrompt, } from "@t3tools/shared/model"; import { memo, useCallback, useState } from "react"; import type { VariantProps } from "class-variance-authority"; import { ChevronDownIcon } from "lucide-react"; import { Button, buttonVariants } from "../ui/button"; import { Menu, MenuGroup, MenuPopup, MenuRadioGroup, MenuRadioItem, MenuSeparator as MenuDivider, MenuTrigger, } from "../ui/menu"; import { useComposerDraftStore, DraftId } from "../../composerDraftStore"; import { getProviderModelCapabilities } from "../../providerModels"; import { cn } from "~/lib/utils"; type ProviderOptions = ReadonlyArray; type TraitsPersistence = | { threadRef?: ScopedThreadRef; draftId?: DraftId; onModelOptionsChange?: never; } | { threadRef?: undefined; onModelOptionsChange: (nextOptions: ProviderOptions | undefined) => void; }; const ULTRATHINK_PROMPT_PREFIX = "Ultrathink:\n"; function replaceDescriptorCurrentValue( descriptors: ReadonlyArray, descriptorId: string, currentValue: string | boolean | undefined, ): ReadonlyArray { return descriptors.map((descriptor) => descriptor.id !== descriptorId ? descriptor : descriptor.type === "boolean" ? { ...descriptor, ...(typeof currentValue === "boolean" ? { currentValue } : {}), } : { ...descriptor, ...(typeof currentValue === "string" ? { currentValue } : {}), }, ); } function getDescriptorStringValue( descriptor: Extract | null, ): string | null { if (!descriptor) { return null; } const value = getProviderOptionCurrentValue(descriptor); return typeof value === "string" ? value : null; } function getSelectedTraits( provider: ProviderDriverKind, models: ReadonlyArray, model: string | null | undefined, prompt: string, modelOptions: ProviderOptions | null | undefined, allowPromptInjectedEffort: boolean, ) { const caps = getProviderModelCapabilities(models, model, provider); const descriptors = getProviderOptionDescriptors({ caps, selections: modelOptions, }); const selectDescriptors = descriptors.filter( (descriptor): descriptor is Extract => descriptor.type === "select", ); const booleanDescriptors = descriptors.filter( (descriptor): descriptor is Extract => descriptor.type === "boolean", ); const primarySelectDescriptor = selectDescriptors[0] ?? null; const contextWindowDescriptor = selectDescriptors.find((descriptor) => descriptor.id === "contextWindow") ?? null; const agentDescriptor = selectDescriptors.find((descriptor) => descriptor.id === "agent") ?? null; const fastModeDescriptor = booleanDescriptors.find((descriptor) => descriptor.id === "fastMode") ?? null; const thinkingDescriptor = booleanDescriptors.find((descriptor) => descriptor.id === "thinking") ?? null; // Prompt-controlled effort (e.g. ultrathink in prompt text) const ultrathinkPromptControlled = allowPromptInjectedEffort && (primarySelectDescriptor?.promptInjectedValues?.length ?? 0) > 0 && isClaudeUltrathinkPrompt(prompt); // Check if "ultrathink" appears in the body text (not just our prefix) const ultrathinkInBodyText = ultrathinkPromptControlled && isClaudeUltrathinkPrompt(prompt.replace(/^Ultrathink:\s*/i, "")); const effort = (ultrathinkPromptControlled ? "ultrathink" : getDescriptorStringValue(primarySelectDescriptor)) ?? null; const thinkingEnabled = typeof thinkingDescriptor?.currentValue === "boolean" ? thinkingDescriptor.currentValue : null; const fastModeEnabled = typeof fastModeDescriptor?.currentValue === "boolean" ? fastModeDescriptor.currentValue : false; const contextWindow = getDescriptorStringValue(contextWindowDescriptor); const selectedAgent = getDescriptorStringValue(agentDescriptor); const selectedAgentLabel = agentDescriptor ? getProviderOptionCurrentLabel(agentDescriptor) : null; return { caps, descriptors, selectDescriptors, booleanDescriptors, primarySelectDescriptor, contextWindowDescriptor, agentDescriptor, fastModeDescriptor, thinkingDescriptor, effort, thinkingEnabled, fastModeEnabled, contextWindow, ultrathinkPromptControlled, ultrathinkInBodyText, selectedAgent, selectedAgentLabel, }; } function getTraitsSectionVisibility(input: { provider: ProviderDriverKind; models: ReadonlyArray; model: string | null | undefined; prompt: string; modelOptions: ProviderOptions | null | undefined; allowPromptInjectedEffort?: boolean; }) { const selected = getSelectedTraits( input.provider, input.models, input.model, input.prompt, input.modelOptions, input.allowPromptInjectedEffort ?? true, ); const showEffort = selected.primarySelectDescriptor !== null; const showThinking = selected.thinkingDescriptor !== null; const showFastMode = selected.fastModeDescriptor !== null; const showContextWindow = selected.contextWindowDescriptor !== null; const showAgent = selected.agentDescriptor !== null; return { ...selected, showEffort, showThinking, showFastMode, showContextWindow, showAgent, hasAnyControls: showEffort || showThinking || showFastMode || showContextWindow || showAgent, }; } export function shouldRenderTraitsControls(input: { provider: ProviderDriverKind; models: ReadonlyArray; model: string | null | undefined; prompt: string; modelOptions: ProviderOptions | null | undefined; allowPromptInjectedEffort?: boolean; }): boolean { return getTraitsSectionVisibility(input).hasAnyControls; } export interface TraitsMenuContentProps { provider: ProviderDriverKind; models: ReadonlyArray; model: string | null | undefined; prompt: string; onPromptChange: (prompt: string) => void; modelOptions?: ProviderOptions | null | undefined; allowPromptInjectedEffort?: boolean; triggerVariant?: VariantProps["variant"]; triggerClassName?: string; } export const TraitsMenuContent = memo(function TraitsMenuContentImpl({ provider, models, model, prompt, onPromptChange, modelOptions, allowPromptInjectedEffort = true, ...persistence }: TraitsMenuContentProps & TraitsPersistence) { const setProviderModelOptions = useComposerDraftStore((store) => store.setProviderModelOptions); const updateModelOptions = useCallback( (nextOptions: ProviderOptions | undefined) => { if ("onModelOptionsChange" in persistence) { persistence.onModelOptionsChange(nextOptions); return; } const threadTarget = persistence.threadRef ?? persistence.draftId; if (!threadTarget) { return; } setProviderModelOptions(threadTarget, provider, nextOptions, { model, persistSticky: true, }); }, [model, persistence, provider, setProviderModelOptions], ); const { descriptors, selectDescriptors, booleanDescriptors, primarySelectDescriptor, ultrathinkPromptControlled, ultrathinkInBodyText, hasAnyControls, } = getTraitsSectionVisibility({ provider, models, model, prompt, modelOptions, allowPromptInjectedEffort, }); const updateDescriptors = (nextDescriptors: ReadonlyArray) => { updateModelOptions(buildProviderOptionSelectionsFromDescriptors(nextDescriptors)); }; const handleSelectChange = ( descriptor: Extract, value: string, ) => { if (!value) return; if (descriptor.promptInjectedValues?.includes(value)) { const nextPrompt = prompt.trim().length === 0 ? ULTRATHINK_PROMPT_PREFIX : applyClaudePromptEffortPrefix(prompt, "ultrathink"); onPromptChange(nextPrompt); return; } if (ultrathinkInBodyText && descriptor.id === primarySelectDescriptor?.id) return; if (ultrathinkPromptControlled && descriptor.id === primarySelectDescriptor?.id) { const stripped = prompt.replace(/^Ultrathink:\s*/i, ""); onPromptChange(stripped); } updateDescriptors(replaceDescriptorCurrentValue(descriptors, descriptor.id, value)); }; if (!hasAnyControls) { return null; } return ( <> {selectDescriptors.map((descriptor, index) => (
{index > 0 ? : null}
{descriptor.label}
{ultrathinkInBodyText && descriptor.id === primarySelectDescriptor?.id ? (
Your prompt contains "ultrathink" in the text. Remove it to change this option.
) : null} handleSelectChange(descriptor, value)} > {descriptor.options.map((option) => ( {option.label} {option.isDefault ? " (default)" : ""} ))}
))} {booleanDescriptors.map((descriptor, index) => (
{index > 0 || selectDescriptors.length > 0 ? : null}
{descriptor.label}
{ updateDescriptors( replaceDescriptorCurrentValue(descriptors, descriptor.id, value === "on"), ); }} > On Off
))} ); }); export const TraitsPicker = memo(function TraitsPicker({ provider, models, model, prompt, onPromptChange, modelOptions, allowPromptInjectedEffort = true, triggerVariant, triggerClassName, ...persistence }: TraitsMenuContentProps & TraitsPersistence) { const [isMenuOpen, setIsMenuOpen] = useState(false); const { descriptors, primarySelectDescriptor, ultrathinkPromptControlled } = getTraitsSectionVisibility({ provider, models, model, prompt, modelOptions, allowPromptInjectedEffort, }); if ( !shouldRenderTraitsControls({ provider, models, model, prompt, modelOptions, allowPromptInjectedEffort, }) ) { return null; } const triggerLabel = descriptors .map((descriptor) => { if (ultrathinkPromptControlled && descriptor.id === primarySelectDescriptor?.id) { return "Ultrathink"; } if (descriptor.type === "boolean") { if (descriptor.id === "fastMode") { return descriptor.currentValue === true ? "Fast" : "Normal"; } return `${descriptor.label} ${descriptor.currentValue === true ? "On" : "Off"}`; } return getProviderOptionCurrentLabel(descriptor); }) .filter((label): label is string => typeof label === "string" && label.length > 0) .join(" ยท ") || ""; const isCodexStyle = provider === "codex"; return ( { setIsMenuOpen(open); }} > } > {isCodexStyle ? ( {triggerLabel} ) : ( <> {triggerLabel} ); });