| 1 | import { describe, expect, it } from "vite-plus/test"; |
| 2 | import { |
| 3 | ProviderInstanceId, |
| 4 | ProviderDriverKind, |
| 5 | type ProviderOptionDescriptor, |
| 6 | } from "@t3tools/contracts"; |
| 7 | import { buildTraitsTriggerDisplay, buildUnavailableModelOptionDescriptors } from "./TraitsPicker"; |
| 8 | |
| 9 | function selectDescriptor( |
| 10 | id: string, |
| 11 | options: ReadonlyArray<{ id: string; label: string; isDefault?: boolean }>, |
| 12 | currentValue: string, |
| 13 | ): Extract<ProviderOptionDescriptor, { type: "select" }> { |
| 14 | return { id, label: id, type: "select", options: [...options], currentValue }; |
| 15 | } |
| 16 | |
| 17 | function fastModeDescriptor( |
| 18 | currentValue: boolean, |
| 19 | ): Extract<ProviderOptionDescriptor, { type: "boolean" }> { |
| 20 | return { id: "fastMode", label: "Fast Mode", type: "boolean", currentValue }; |
| 21 | } |
| 22 | |
| 23 | function serviceTierDescriptor( |
| 24 | currentValue: "default" | "priority" | "ultrafast" | "flex", |
| 25 | ): Extract<ProviderOptionDescriptor, { type: "select" }> { |
| 26 | return { |
| 27 | id: "serviceTier", |
| 28 | label: "Service Tier", |
| 29 | type: "select", |
| 30 | options: [ |
| 31 | { id: "default", label: "Standard", isDefault: true }, |
| 32 | { id: "priority", label: "Fast" }, |
| 33 | { id: "ultrafast", label: "Ultrafast" }, |
| 34 | { id: "flex", label: "Flex" }, |
| 35 | ], |
| 36 | currentValue, |
| 37 | }; |
| 38 | } |
| 39 | |
| 40 | const EFFORT = selectDescriptor( |
| 41 | "reasoningEffort", |
| 42 | [ |
| 43 | { id: "high", label: "High" }, |
| 44 | { id: "max", label: "Max" }, |
| 45 | ], |
| 46 | "high", |
| 47 | ); |
| 48 | const CONTEXT_WINDOW = selectDescriptor( |
| 49 | "contextWindow", |
| 50 | [ |
| 51 | { id: "200k", label: "200k" }, |
| 52 | { id: "1m", label: "1M" }, |
| 53 | ], |
| 54 | "1m", |
| 55 | ); |
| 56 | |
| 57 | const CODEX = ProviderDriverKind.make("codex"); |
| 58 | |
| 59 | function display(descriptors: ReadonlyArray<ProviderOptionDescriptor>) { |
| 60 | return buildTraitsTriggerDisplay({ |
| 61 | provider: CODEX, |
| 62 | descriptors, |
| 63 | primarySelectDescriptorId: "reasoningEffort", |
| 64 | ultrathinkPromptControlled: false, |
| 65 | }); |
| 66 | } |
| 67 | |
| 68 | describe("buildTraitsTriggerDisplay", () => { |
| 69 | it("omits fast mode from the label entirely when it is off", () => { |
| 70 | expect(display([EFFORT, fastModeDescriptor(false), CONTEXT_WINDOW])).toEqual({ |
| 71 | label: "High · 1M", |
| 72 | }); |
| 73 | }); |
| 74 | |
| 75 | it("pairs fast mode with reasoning before the context window", () => { |
| 76 | expect(display([EFFORT, fastModeDescriptor(true), CONTEXT_WINDOW])).toEqual({ |
| 77 | label: "High Fast · 1M", |
| 78 | }); |
| 79 | expect(display([EFFORT, CONTEXT_WINDOW, fastModeDescriptor(true)])).toEqual({ |
| 80 | label: "High Fast · 1M", |
| 81 | }); |
| 82 | }); |
| 83 | |
| 84 | it.each(["reasoningEffort", "reasoning", "effort", "variant", "thinking"])( |
| 85 | "pairs Fast with %s even when context is the primary select", |
| 86 | (id) => { |
| 87 | for (const speed of [fastModeDescriptor(true), serviceTierDescriptor("priority")]) { |
| 88 | expect( |
| 89 | buildTraitsTriggerDisplay({ |
| 90 | provider: CODEX, |
| 91 | descriptors: [ |
| 92 | selectDescriptor("profile", [{ id: "balanced", label: "Balanced" }], "balanced"), |
| 93 | CONTEXT_WINDOW, |
| 94 | { ...EFFORT, id }, |
| 95 | speed, |
| 96 | ], |
| 97 | primarySelectDescriptorId: "profile", |
| 98 | ultrathinkPromptControlled: false, |
| 99 | }), |
| 100 | ).toEqual({ label: "Balanced · 1M · High Fast" }); |
| 101 | } |
| 102 | expect( |
| 103 | buildTraitsTriggerDisplay({ |
| 104 | provider: CODEX, |
| 105 | descriptors: [CONTEXT_WINDOW, { ...EFFORT, id }, serviceTierDescriptor("ultrafast")], |
| 106 | primarySelectDescriptorId: CONTEXT_WINDOW.id, |
| 107 | ultrathinkPromptControlled: false, |
| 108 | }), |
| 109 | ).toEqual({ label: "1M · High Ultrafast" }); |
| 110 | }, |
| 111 | ); |
| 112 | |
| 113 | it("pairs Cursor Fast with reasoning rather than the thinking toggle", () => { |
| 114 | expect( |
| 115 | buildTraitsTriggerDisplay({ |
| 116 | provider: ProviderDriverKind.make("cursor"), |
| 117 | descriptors: [ |
| 118 | { ...EFFORT, id: "reasoning" }, |
| 119 | fastModeDescriptor(true), |
| 120 | { id: "thinking", label: "Thinking", type: "boolean", currentValue: true }, |
| 121 | ], |
| 122 | primarySelectDescriptorId: "reasoning", |
| 123 | ultrathinkPromptControlled: false, |
| 124 | }), |
| 125 | ).toEqual({ label: "High Fast · Thinking On" }); |
| 126 | }); |
| 127 | |
| 128 | it.each([true, false])("keeps boolean thinking %s independent of Fast", (currentValue) => { |
| 129 | const thinking = { |
| 130 | id: "thinking", |
| 131 | label: "Thinking", |
| 132 | type: "boolean" as const, |
| 133 | currentValue, |
| 134 | }; |
| 135 | const label = `Thinking ${currentValue ? "On" : "Off"}`; |
| 136 | expect(display([thinking, fastModeDescriptor(true)])).toEqual({ label: `${label} · Fast` }); |
| 137 | expect(display([thinking, { ...EFFORT, id: "reasoning" }, fastModeDescriptor(true)])).toEqual({ |
| 138 | label: `${label} · High Fast`, |
| 139 | }); |
| 140 | expect(display([thinking, fastModeDescriptor(false)])).toEqual({ label }); |
| 141 | }); |
| 142 | |
| 143 | it("keeps speed separate when there is no reasoning descriptor", () => { |
| 144 | expect(display([CONTEXT_WINDOW, fastModeDescriptor(true)])).toEqual({ label: "1M · Fast" }); |
| 145 | }); |
| 146 | |
| 147 | it.each(["Low", "Medium", "High", "Extra High", "Max", "Ultra"])( |
| 148 | "preserves %s reasoning with Fast across harnesses", |
| 149 | (label) => { |
| 150 | for (const provider of ["codex", "claudeAgent", "cursor"]) { |
| 151 | expect( |
| 152 | buildTraitsTriggerDisplay({ |
| 153 | provider: ProviderDriverKind.make(provider), |
| 154 | descriptors: [ |
| 155 | { ...EFFORT, options: [{ id: "effort", label }], currentValue: "effort" }, |
| 156 | fastModeDescriptor(true), |
| 157 | ], |
| 158 | primarySelectDescriptorId: EFFORT.id, |
| 159 | ultrathinkPromptControlled: false, |
| 160 | }), |
| 161 | ).toEqual({ label: `${label} Fast` }); |
| 162 | } |
| 163 | }, |
| 164 | ); |
| 165 | |
| 166 | it("treats Codex standard and fast service tiers as fast mode states", () => { |
| 167 | expect(display([EFFORT, serviceTierDescriptor("default")])).toEqual({ label: "High" }); |
| 168 | expect(display([EFFORT, serviceTierDescriptor("priority")])).toEqual({ label: "High Fast" }); |
| 169 | }); |
| 170 | |
| 171 | it("keeps Codex Ultrafast distinct from Fast", () => { |
| 172 | expect(display([EFFORT, serviceTierDescriptor("ultrafast")])).toEqual({ |
| 173 | label: "High Ultrafast", |
| 174 | }); |
| 175 | }); |
| 176 | |
| 177 | it("uses Ultrafast without requiring a Fast tier", () => { |
| 178 | const descriptor = serviceTierDescriptor("ultrafast"); |
| 179 | expect( |
| 180 | display([ |
| 181 | EFFORT, |
| 182 | { ...descriptor, options: descriptor.options.filter(({ id }) => id !== "priority") }, |
| 183 | ]), |
| 184 | ).toEqual({ label: "High Ultrafast" }); |
| 185 | }); |
| 186 | |
| 187 | it("keeps other Codex service tiers in the label", () => { |
| 188 | expect(display([EFFORT, serviceTierDescriptor("flex")])).toEqual({ label: "High · Flex" }); |
| 189 | }); |
| 190 | |
| 191 | it("keeps Standard as text for models without speed tiers", () => { |
| 192 | const descriptor = serviceTierDescriptor("default"); |
| 193 | const nonSpeedDescriptor = { |
| 194 | ...descriptor, |
| 195 | options: descriptor.options.filter(({ id }) => id === "default" || id === "flex"), |
| 196 | }; |
| 197 | expect(display([EFFORT, nonSpeedDescriptor])).toEqual({ label: "High · Standard" }); |
| 198 | expect(display([nonSpeedDescriptor])).toEqual({ label: "Standard" }); |
| 199 | }); |
| 200 | |
| 201 | it("keeps the Codex service tier readable when it is the only trait", () => { |
| 202 | expect(display([serviceTierDescriptor("default")])).toEqual({ label: "Standard" }); |
| 203 | expect(display([serviceTierDescriptor("priority")])).toEqual({ label: "Fast" }); |
| 204 | }); |
| 205 | |
| 206 | it("keeps Ultrafast readable when it is the only trait", () => { |
| 207 | expect(display([serviceTierDescriptor("ultrafast")])).toEqual({ label: "Ultrafast" }); |
| 208 | }); |
| 209 | |
| 210 | it("keeps non-fastMode booleans as text labels", () => { |
| 211 | const thinking: Extract<ProviderOptionDescriptor, { type: "boolean" }> = { |
| 212 | id: "thinking", |
| 213 | label: "Thinking", |
| 214 | type: "boolean", |
| 215 | currentValue: true, |
| 216 | }; |
| 217 | expect(display([EFFORT, thinking])).toEqual({ label: "High · Thinking On" }); |
| 218 | }); |
| 219 | |
| 220 | it("falls back to a text label when fast mode is the only trait", () => { |
| 221 | expect(display([fastModeDescriptor(true)])).toEqual({ label: "Fast" }); |
| 222 | expect(display([fastModeDescriptor(false)])).toEqual({ label: "Normal" }); |
| 223 | }); |
| 224 | |
| 225 | it("does not add Fast to a model without a speed option", () => { |
| 226 | expect(display([EFFORT, CONTEXT_WINDOW])).toEqual({ label: "High · 1M" }); |
| 227 | }); |
| 228 | |
| 229 | it("stays blank when descriptors resolve to no label and there is no fast mode", () => { |
| 230 | // A select with neither a currentValue nor an isDefault option yields no |
| 231 | // label. Without a fastMode descriptor present that must stay blank rather |
| 232 | // than falling through to a bogus "Normal". |
| 233 | const unresolved: Extract<ProviderOptionDescriptor, { type: "select" }> = { |
| 234 | id: "effort", |
| 235 | label: "effort", |
| 236 | type: "select", |
| 237 | options: [ |
| 238 | { id: "low", label: "Low" }, |
| 239 | { id: "high", label: "High" }, |
| 240 | ], |
| 241 | }; |
| 242 | expect(display([unresolved])).toEqual({ label: "" }); |
| 243 | }); |
| 244 | |
| 245 | it("still renders prompt-controlled ultrathink with Fast", () => { |
| 246 | expect( |
| 247 | buildTraitsTriggerDisplay({ |
| 248 | provider: CODEX, |
| 249 | descriptors: [EFFORT, fastModeDescriptor(true)], |
| 250 | primarySelectDescriptorId: "reasoningEffort", |
| 251 | ultrathinkPromptControlled: true, |
| 252 | }), |
| 253 | ).toEqual({ label: "Ultrathink Fast" }); |
| 254 | }); |
| 255 | }); |
| 256 | |
| 257 | describe("buildUnavailableModelOptionDescriptors", () => { |
| 258 | it("shows only saved values without inventing alternatives", () => { |
| 259 | expect( |
| 260 | buildUnavailableModelOptionDescriptors([ |
| 261 | { id: "variant", value: "max" }, |
| 262 | { id: "agent", value: "build" }, |
| 263 | { id: "fastMode", value: true }, |
| 264 | ]), |
| 265 | ).toEqual([ |
| 266 | { |
| 267 | id: "variant", |
| 268 | label: "Reasoning", |
| 269 | type: "select", |
| 270 | options: [{ id: "max", label: "max" }], |
| 271 | currentValue: "max", |
| 272 | }, |
| 273 | { |
| 274 | id: "agent", |
| 275 | label: "Agent", |
| 276 | type: "select", |
| 277 | options: [{ id: "build", label: "build" }], |
| 278 | currentValue: "build", |
| 279 | }, |
| 280 | { |
| 281 | id: "fastMode", |
| 282 | label: "Fast Mode", |
| 283 | type: "boolean", |
| 284 | currentValue: true, |
| 285 | }, |
| 286 | ]); |
| 287 | }); |
| 288 | }); |
| 289 | |
| 290 | it("shows Unknown until a matching provider report provides Default", () => { |
| 291 | const selection = { |
| 292 | instanceId: ProviderInstanceId.make("opencode"), |
| 293 | model: "ling", |
| 294 | options: [], |
| 295 | }; |
| 296 | const input = { |
| 297 | provider: ProviderDriverKind.make("opencode"), |
| 298 | descriptors: [ |
| 299 | selectDescriptor( |
| 300 | "variant", |
| 301 | [ |
| 302 | { id: "none", label: "None" }, |
| 303 | { id: "thinking", label: "Thinking" }, |
| 304 | ], |
| 305 | "", |
| 306 | ), |
| 307 | ], |
| 308 | primarySelectDescriptorId: "variant", |
| 309 | ultrathinkPromptControlled: false, |
| 310 | modelSelection: selection, |
| 311 | }; |
| 312 | expect(buildTraitsTriggerDisplay(input).label).toBe("Unknown"); |
| 313 | expect( |
| 314 | buildTraitsTriggerDisplay({ |
| 315 | ...input, |
| 316 | reportedModelSelection: { ...selection, options: [{ id: "variant", value: "default" }] }, |
| 317 | }).label, |
| 318 | ).toBe("Default"); |
| 319 | }); |