Skip to content
Source
Sign inSign up

modelTraits.ts

188 lines6.8 KB

1import type { ModelSelection, ProviderOptionDescriptor } from "@t3tools/contracts";
2import {
3 buildProviderOptionSelectionsFromDescriptors,
4 getProviderOptionCurrentLabel,
5 getProviderOptionCurrentValue,
6 isClaudeUltrathinkPrompt,
7} from "@t3tools/shared/model";
8import { REASONING_EFFORT_OPTION_IDS } from "./state/threadExecution.ts";
9​
10const REASONING_DESCRIPTOR_IDS: ReadonlySet<string> = new Set([
11 ...REASONING_EFFORT_OPTION_IDS,
12 "thinking",
13]);
14​
15/** The model's reasoning control: a known effort id, or a select its agent marked as reasoning. */
16export function isReasoningControl(descriptor: ProviderOptionDescriptor): boolean {
17 return (
18 (descriptor.type === "select" && descriptor.role === "reasoning") ||
19 REASONING_DESCRIPTOR_IDS.has(descriptor.id)
20 );
22​
23export function isReasoningSelect(
24 descriptor: ProviderOptionDescriptor,
25): descriptor is Extract<ProviderOptionDescriptor, { type: "select" }> {
26 return descriptor.type === "select" && isReasoningControl(descriptor);
28​
29export function replaceDescriptorCurrentValue(
30 descriptors: ReadonlyArray<ProviderOptionDescriptor>,
31 descriptorId: string,
32 currentValue: string | boolean | undefined,
33): ReadonlyArray<ProviderOptionDescriptor> {
34 return descriptors.map((descriptor) =>
35 descriptor.id !== descriptorId
36 ? descriptor
37 : descriptor.type === "boolean"
38 ? {
39 ...descriptor,
40 ...(typeof currentValue === "boolean" ? { currentValue } : {}),
41 }
42 : {
43 ...descriptor,
44 ...(typeof currentValue === "string" ? { currentValue } : {}),
45 },
46 );
48​
49export function getDescriptorStringValue(
50 descriptor: Extract<ProviderOptionDescriptor, { type: "select" }> | null,
51 selection?: ModelSelection | null,
52 reportedSelection?: ModelSelection | null,
53): string | null {
54 if (!descriptor) {
55 return null;
56 }
57 const value = getProviderOptionCurrentValue(descriptor, selection, reportedSelection);
58 return typeof value === "string" ? value : null;
60​
61// Prompt-injected levels such as Ultrathink still work when typed, but pickers do not offer them.
62export function selectableProviderOptionChoices(
63 descriptor: Extract<ProviderOptionDescriptor, { type: "select" }>,
64) {
65 return descriptor.options.filter(
66 (option) => !descriptor.promptInjectedValues?.includes(option.id),
67 );
69​
70export function getPromptControlledTraits(
71 descriptors: ReadonlyArray<ProviderOptionDescriptor>,
72 prompt: string,
73 allowPromptInjectedEffort = true,
74) {
75 const primarySelectDescriptor =
76 descriptors.find((descriptor) => descriptor.type === "select") ?? null;
77 const ultrathinkPromptControlled =
78 allowPromptInjectedEffort &&
79 (primarySelectDescriptor?.promptInjectedValues?.length ?? 0) > 0 &&
80 isClaudeUltrathinkPrompt(prompt);
81 const ultrathinkInBodyText =
82 ultrathinkPromptControlled && isClaudeUltrathinkPrompt(prompt.replace(/^Ultrathink:\s*/i, ""));
83 return { primarySelectDescriptor, ultrathinkPromptControlled, ultrathinkInBodyText };
85​
86export function getTraitSelectChange(
87 selected: ReturnType<typeof getPromptControlledTraits> & {
88 descriptors: ReadonlyArray<ProviderOptionDescriptor>;
89 },
90 descriptor: Extract<ProviderOptionDescriptor, { type: "select" }>,
91 prompt: string,
92 value: string,
93) {
94 if (!value || descriptor.promptInjectedValues?.includes(value)) return null;
95 const isPrimary = descriptor.id === selected.primarySelectDescriptor?.id;
96 if (isPrimary && selected.ultrathinkInBodyText) return null;
97 return {
98 prompt:
99 isPrimary && selected.ultrathinkPromptControlled
100 ? prompt.replace(/^Ultrathink:\s*/i, "")
101 : prompt,
102 modelOptions: buildProviderOptionSelectionsFromDescriptors(
103 replaceDescriptorCurrentValue(selected.descriptors, descriptor.id, value),
104 ),
105 };
107​
108export function buildTraitsTriggerDisplay(input: {
109 descriptors: ReadonlyArray<ProviderOptionDescriptor>;
110 primarySelectDescriptorId: string | null;
111 ultrathinkPromptControlled: boolean;
112 modelSelection?: ModelSelection | null;
113 reportedModelSelection?: ModelSelection | null | undefined;
114}): {
115 label: string;
116 /** "Fast" or "Ultrafast" when a speed option is on. */
117 speedLabel: string | null;
118 ultracode: boolean;
119} {
120 let fastModeFallbackLabel: string | null = null;
121 let speedLabel: string | null = null;
122 let reasoningLabelIndex = -1;
123 let ultracodeLabelIndex = -1;
124 const labels: Array<string> = [];
125 for (const descriptor of input.descriptors) {
126 if (descriptor.id === "fastMode" && descriptor.type === "boolean") {
127 speedLabel = descriptor.currentValue === true ? "Fast" : null;
128 fastModeFallbackLabel = speedLabel ?? "Normal";
129 continue;
130 }
131 if (descriptor.id === "ultracode" && descriptor.type === "boolean") {
132 if (descriptor.currentValue === true) {
133 ultracodeLabelIndex = labels.length;
134 labels.push(descriptor.label);
135 }
136 continue;
137 }
138 if (descriptor.id === "serviceTier" && descriptor.type === "select") {
139 const currentValue = getProviderOptionCurrentValue(descriptor);
140 const fastTier = descriptor.options.find(({ label }) => label === "Fast");
141 const ultrafastTier = descriptor.options.find(({ label }) => label === "Ultrafast");
142 if (
143 ((fastTier || ultrafastTier) && currentValue === "default") ||
144 (fastTier && currentValue === fastTier.id) ||
145 (ultrafastTier && currentValue === ultrafastTier.id)
146 ) {
147 speedLabel =
148 ultrafastTier && currentValue === ultrafastTier.id
149 ? "Ultrafast"
150 : fastTier && currentValue === fastTier.id
151 ? "Fast"
152 : null;
153 fastModeFallbackLabel =
154 descriptor.options.find(({ id }) => id === currentValue)?.label ?? "Normal";
155 continue;
156 }
157 }
158 const label =
159 input.ultrathinkPromptControlled && descriptor.id === input.primarySelectDescriptorId
160 ? "Ultrathink"
161 : descriptor.type === "boolean"
162 ? `${descriptor.label} ${descriptor.currentValue === true ? "On" : "Off"}`
163 : (getProviderOptionCurrentLabel(
164 descriptor,
165 input.modelSelection,
166 input.reportedModelSelection,
167 ) ?? (descriptor.id === "thinking" ? descriptor.label : null));
168 if (typeof label === "string" && label.length > 0) {
169 if (reasoningLabelIndex === -1 && isReasoningSelect(descriptor)) {
170 reasoningLabelIndex = labels.length;
171 }
172 labels.push(label);
173 }
174 }
175​
176 const ultracode = ultracodeLabelIndex >= 0;
177 if (labels.length === 0 && fastModeFallbackLabel !== null) {
178 return { label: fastModeFallbackLabel, speedLabel, ultracode };
179 }
180 if (speedLabel) {
181 if (reasoningLabelIndex >= 0) {
182 labels[reasoningLabelIndex] = `${labels[reasoningLabelIndex]} ${speedLabel}`;
183 } else {
184 labels.push(speedLabel);
185 }
186 }
187 return { label: labels.join(" · "), speedLabel, ultracode };