@stll/ai-catalog 0.4.0 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,2138 @@
1
+ import * as v from "valibot";
2
+ //#region src/capabilities.gen.ts
3
+ /**
4
+ * Offered models whose provider API accepts PDF/file content. Generated from
5
+ * live input-modality metadata plus reviewed source corrections.
6
+ */
7
+ const MODEL_DOCUMENT_INPUT_OPTIONS = {
8
+ google: [
9
+ "gemini-3.8-flash",
10
+ "gemini-3.7-flash",
11
+ "gemini-3.6-flash",
12
+ "gemini-3.5-flash-lite",
13
+ "gemini-3.1-pro-preview",
14
+ "gemini-3.5-flash",
15
+ "gemini-3.1-flash-lite"
16
+ ],
17
+ openrouter: [
18
+ "openai/gpt-6.1-sol",
19
+ "openai/gpt-6-astra",
20
+ "openai/gpt-6-sol",
21
+ "openai/gpt-6-luna",
22
+ "openai/gpt-5.6-sol",
23
+ "openai/gpt-5.6-terra",
24
+ "openai/gpt-5.6-luna",
25
+ "google/gemini-3.8-flash",
26
+ "google/gemini-3.7-flash",
27
+ "google/gemini-3.6-flash",
28
+ "google/gemini-3.5-flash-lite",
29
+ "google/gemini-3.1-pro-preview",
30
+ "google/gemini-3.5-flash",
31
+ "google/gemini-3.1-flash-lite",
32
+ "anthropic/claude-sonnet-5.5",
33
+ "anthropic/claude-sonnet-5",
34
+ "anthropic/claude-opus-5",
35
+ "anthropic/claude-opus-4.8",
36
+ "anthropic/claude-sonnet-4.6",
37
+ "openai/gpt-5.5",
38
+ "openai/gpt-5.4-mini"
39
+ ],
40
+ openai: [
41
+ "gpt-6.1-sol",
42
+ "gpt-6-astra",
43
+ "gpt-6-sol",
44
+ "gpt-6-luna",
45
+ "gpt-5.6",
46
+ "gpt-5.6-terra",
47
+ "gpt-5.6-luna",
48
+ "gpt-5.5",
49
+ "gpt-5.4",
50
+ "gpt-5.4-mini",
51
+ "gpt-5.4-nano",
52
+ "gpt-5.2"
53
+ ],
54
+ anthropic: [
55
+ "claude-sonnet-5-5",
56
+ "claude-sonnet-5",
57
+ "claude-fable-5-1",
58
+ "claude-fable-5",
59
+ "claude-opus-5-5",
60
+ "claude-opus-5",
61
+ "claude-opus-4-8",
62
+ "claude-opus-4-7",
63
+ "claude-sonnet-4-6",
64
+ "claude-opus-4-6",
65
+ "claude-haiku-4-5-20251001"
66
+ ],
67
+ bedrock: [
68
+ "us.anthropic.claude-sonnet-4-5-20250929-v1:0",
69
+ "us.anthropic.claude-haiku-4-5-20251001-v1:0",
70
+ "us.amazon.nova-pro-v1:0",
71
+ "us.amazon.nova-lite-v1:0"
72
+ ],
73
+ mistral: []
74
+ };
75
+ /** Every offered provider/model pair has explicit image-input evidence. */
76
+ const MODEL_IMAGE_INPUT_CAPABILITIES = {
77
+ google: {
78
+ "gemini-3.8-flash": "supported",
79
+ "gemini-3.7-flash": "supported",
80
+ "gemini-3.6-flash": "supported",
81
+ "gemini-3.5-flash-lite": "supported",
82
+ "gemini-3.1-pro-preview": "supported",
83
+ "gemini-3.5-flash": "supported",
84
+ "gemini-3.1-flash-lite": "supported"
85
+ },
86
+ openrouter: {
87
+ "openai/gpt-6.1-sol": "supported",
88
+ "openai/gpt-6-astra": "supported",
89
+ "openai/gpt-6-sol": "supported",
90
+ "openai/gpt-6-luna": "supported",
91
+ "openai/gpt-5.6-sol": "supported",
92
+ "openai/gpt-5.6-terra": "supported",
93
+ "openai/gpt-5.6-luna": "supported",
94
+ "google/gemini-3.8-flash": "supported",
95
+ "google/gemini-3.7-flash": "supported",
96
+ "google/gemini-3.6-flash": "supported",
97
+ "google/gemini-3.5-flash-lite": "supported",
98
+ "google/gemini-3.1-pro-preview": "supported",
99
+ "google/gemini-3.5-flash": "supported",
100
+ "google/gemini-3.1-flash-lite": "supported",
101
+ "anthropic/claude-sonnet-5.5": "supported",
102
+ "anthropic/claude-sonnet-5": "supported",
103
+ "anthropic/claude-opus-5": "supported",
104
+ "anthropic/claude-opus-4.8": "supported",
105
+ "anthropic/claude-sonnet-4.6": "supported",
106
+ "openai/gpt-5.5": "supported",
107
+ "openai/gpt-5.4-mini": "supported"
108
+ },
109
+ openai: {
110
+ "gpt-6.1-sol": "supported",
111
+ "gpt-6-astra": "supported",
112
+ "gpt-6-sol": "supported",
113
+ "gpt-6-luna": "supported",
114
+ "gpt-5.6": "supported",
115
+ "gpt-5.6-terra": "supported",
116
+ "gpt-5.6-luna": "supported",
117
+ "gpt-5.5": "supported",
118
+ "gpt-5.4": "supported",
119
+ "gpt-5.4-mini": "supported",
120
+ "gpt-5.4-nano": "supported",
121
+ "gpt-5.2": "supported"
122
+ },
123
+ anthropic: {
124
+ "claude-sonnet-5-5": "supported",
125
+ "claude-sonnet-5": "supported",
126
+ "claude-fable-5-1": "supported",
127
+ "claude-fable-5": "supported",
128
+ "claude-opus-5-5": "supported",
129
+ "claude-opus-5": "supported",
130
+ "claude-opus-4-8": "supported",
131
+ "claude-opus-4-7": "supported",
132
+ "claude-sonnet-4-6": "supported",
133
+ "claude-opus-4-6": "supported",
134
+ "claude-haiku-4-5-20251001": "supported"
135
+ },
136
+ bedrock: {
137
+ "us.anthropic.claude-sonnet-4-5-20250929-v1:0": "supported",
138
+ "us.anthropic.claude-haiku-4-5-20251001-v1:0": "supported",
139
+ "us.amazon.nova-pro-v1:0": "supported",
140
+ "us.amazon.nova-lite-v1:0": "supported",
141
+ "us.amazon.nova-micro-v1:0": "unsupported",
142
+ "openai.gpt-oss-120b-1:0": "unsupported",
143
+ "openai.gpt-oss-20b-1:0": "unsupported"
144
+ },
145
+ mistral: {
146
+ "mistral-large-latest": "supported",
147
+ "mistral-medium-latest": "supported",
148
+ "mistral-small-latest": "supported"
149
+ }
150
+ };
151
+ /**
152
+ * Reasoning-effort values each offered model accepts, `null` when the
153
+ * model exposes no effort-style reasoning control. A missing `"none"`
154
+ * means the model's reasoning cannot be disabled; consumers must go
155
+ * through `resolveReasoningEffort`.
156
+ */
157
+ const MODEL_REASONING_EFFORTS = {
158
+ "gemini-3.8-flash": [
159
+ "low",
160
+ "medium",
161
+ "high"
162
+ ],
163
+ "gemini-3.7-flash": [
164
+ "low",
165
+ "medium",
166
+ "high"
167
+ ],
168
+ "gemini-3.6-flash": [
169
+ "minimal",
170
+ "low",
171
+ "medium",
172
+ "high"
173
+ ],
174
+ "gemini-3.5-flash-lite": [
175
+ "minimal",
176
+ "low",
177
+ "medium",
178
+ "high"
179
+ ],
180
+ "gemini-3.1-pro-preview": [
181
+ "low",
182
+ "medium",
183
+ "high"
184
+ ],
185
+ "gemini-3.5-flash": [
186
+ "minimal",
187
+ "low",
188
+ "medium",
189
+ "high"
190
+ ],
191
+ "gemini-3.1-flash-lite": [
192
+ "minimal",
193
+ "low",
194
+ "medium",
195
+ "high"
196
+ ],
197
+ "openai/gpt-6.1-sol": [
198
+ "low",
199
+ "medium",
200
+ "high",
201
+ "xhigh",
202
+ "max"
203
+ ],
204
+ "openai/gpt-6-astra": [
205
+ "low",
206
+ "medium",
207
+ "high",
208
+ "xhigh",
209
+ "max"
210
+ ],
211
+ "openai/gpt-6-sol": [
212
+ "none",
213
+ "low",
214
+ "medium",
215
+ "high",
216
+ "xhigh",
217
+ "max"
218
+ ],
219
+ "openai/gpt-6-luna": [
220
+ "none",
221
+ "low",
222
+ "medium",
223
+ "high",
224
+ "xhigh",
225
+ "max"
226
+ ],
227
+ "openai/gpt-5.6-sol": [
228
+ "none",
229
+ "low",
230
+ "medium",
231
+ "high",
232
+ "xhigh",
233
+ "max"
234
+ ],
235
+ "openai/gpt-5.6-terra": [
236
+ "none",
237
+ "low",
238
+ "medium",
239
+ "high",
240
+ "xhigh",
241
+ "max"
242
+ ],
243
+ "openai/gpt-5.6-luna": [
244
+ "none",
245
+ "low",
246
+ "medium",
247
+ "high",
248
+ "xhigh",
249
+ "max"
250
+ ],
251
+ "google/gemini-3.8-flash": [
252
+ "low",
253
+ "medium",
254
+ "high"
255
+ ],
256
+ "google/gemini-3.7-flash": [
257
+ "low",
258
+ "medium",
259
+ "high"
260
+ ],
261
+ "google/gemini-3.6-flash": [
262
+ "minimal",
263
+ "low",
264
+ "medium",
265
+ "high"
266
+ ],
267
+ "google/gemini-3.5-flash-lite": [
268
+ "minimal",
269
+ "low",
270
+ "medium",
271
+ "high"
272
+ ],
273
+ "google/gemini-3.1-pro-preview": [
274
+ "low",
275
+ "medium",
276
+ "high"
277
+ ],
278
+ "google/gemini-3.5-flash": [
279
+ "minimal",
280
+ "low",
281
+ "medium",
282
+ "high"
283
+ ],
284
+ "google/gemini-3.1-flash-lite": [
285
+ "none",
286
+ "minimal",
287
+ "low",
288
+ "medium",
289
+ "high"
290
+ ],
291
+ "anthropic/claude-sonnet-5.5": [
292
+ "low",
293
+ "medium",
294
+ "high",
295
+ "xhigh",
296
+ "max"
297
+ ],
298
+ "anthropic/claude-sonnet-5": [
299
+ "none",
300
+ "low",
301
+ "medium",
302
+ "high",
303
+ "xhigh",
304
+ "max"
305
+ ],
306
+ "anthropic/claude-opus-5": [
307
+ "none",
308
+ "low",
309
+ "medium",
310
+ "high",
311
+ "xhigh",
312
+ "max"
313
+ ],
314
+ "anthropic/claude-opus-4.8": [
315
+ "none",
316
+ "low",
317
+ "medium",
318
+ "high",
319
+ "xhigh",
320
+ "max"
321
+ ],
322
+ "anthropic/claude-sonnet-4.6": [
323
+ "none",
324
+ "low",
325
+ "medium",
326
+ "high",
327
+ "max"
328
+ ],
329
+ "openai/gpt-5.5": [
330
+ "none",
331
+ "low",
332
+ "medium",
333
+ "high",
334
+ "xhigh"
335
+ ],
336
+ "openai/gpt-5.4-mini": [
337
+ "none",
338
+ "low",
339
+ "medium",
340
+ "high",
341
+ "xhigh"
342
+ ],
343
+ "gpt-6.1-sol": [
344
+ "low",
345
+ "medium",
346
+ "high",
347
+ "xhigh",
348
+ "max"
349
+ ],
350
+ "gpt-6-astra": [
351
+ "low",
352
+ "medium",
353
+ "high",
354
+ "xhigh",
355
+ "max"
356
+ ],
357
+ "gpt-6-sol": [
358
+ "none",
359
+ "low",
360
+ "medium",
361
+ "high",
362
+ "xhigh",
363
+ "max"
364
+ ],
365
+ "gpt-6-luna": [
366
+ "none",
367
+ "low",
368
+ "medium",
369
+ "high",
370
+ "xhigh",
371
+ "max"
372
+ ],
373
+ "gpt-5.6": [
374
+ "none",
375
+ "low",
376
+ "medium",
377
+ "high",
378
+ "xhigh",
379
+ "max"
380
+ ],
381
+ "gpt-5.6-terra": [
382
+ "none",
383
+ "low",
384
+ "medium",
385
+ "high",
386
+ "xhigh",
387
+ "max"
388
+ ],
389
+ "gpt-5.6-luna": [
390
+ "none",
391
+ "low",
392
+ "medium",
393
+ "high",
394
+ "xhigh",
395
+ "max"
396
+ ],
397
+ "gpt-5.5": [
398
+ "none",
399
+ "low",
400
+ "medium",
401
+ "high",
402
+ "xhigh"
403
+ ],
404
+ "gpt-5.4": [
405
+ "none",
406
+ "low",
407
+ "medium",
408
+ "high",
409
+ "xhigh"
410
+ ],
411
+ "gpt-5.4-mini": [
412
+ "none",
413
+ "low",
414
+ "medium",
415
+ "high",
416
+ "xhigh"
417
+ ],
418
+ "gpt-5.4-nano": [
419
+ "none",
420
+ "low",
421
+ "medium",
422
+ "high",
423
+ "xhigh"
424
+ ],
425
+ "gpt-5.2": [
426
+ "none",
427
+ "low",
428
+ "medium",
429
+ "high",
430
+ "xhigh"
431
+ ],
432
+ "claude-sonnet-5-5": [
433
+ "low",
434
+ "medium",
435
+ "high",
436
+ "xhigh",
437
+ "max"
438
+ ],
439
+ "claude-sonnet-5": [
440
+ "none",
441
+ "low",
442
+ "medium",
443
+ "high",
444
+ "xhigh",
445
+ "max"
446
+ ],
447
+ "claude-fable-5-1": [
448
+ "low",
449
+ "medium",
450
+ "high",
451
+ "xhigh",
452
+ "max"
453
+ ],
454
+ "claude-fable-5": [
455
+ "low",
456
+ "medium",
457
+ "high",
458
+ "xhigh",
459
+ "max"
460
+ ],
461
+ "claude-opus-5-5": [
462
+ "low",
463
+ "medium",
464
+ "high",
465
+ "xhigh",
466
+ "max"
467
+ ],
468
+ "claude-opus-5": [
469
+ "low",
470
+ "medium",
471
+ "high",
472
+ "xhigh",
473
+ "max"
474
+ ],
475
+ "claude-opus-4-8": [
476
+ "low",
477
+ "medium",
478
+ "high",
479
+ "xhigh",
480
+ "max"
481
+ ],
482
+ "claude-opus-4-7": [
483
+ "low",
484
+ "medium",
485
+ "high",
486
+ "xhigh",
487
+ "max"
488
+ ],
489
+ "claude-sonnet-4-6": [
490
+ "low",
491
+ "medium",
492
+ "high",
493
+ "max"
494
+ ],
495
+ "claude-opus-4-6": [
496
+ "low",
497
+ "medium",
498
+ "high",
499
+ "max"
500
+ ],
501
+ "claude-haiku-4-5-20251001": null,
502
+ "us.anthropic.claude-sonnet-4-5-20250929-v1:0": null,
503
+ "us.anthropic.claude-haiku-4-5-20251001-v1:0": null,
504
+ "us.amazon.nova-pro-v1:0": null,
505
+ "us.amazon.nova-lite-v1:0": null,
506
+ "us.amazon.nova-micro-v1:0": null,
507
+ "openai.gpt-oss-120b-1:0": [
508
+ "low",
509
+ "medium",
510
+ "high"
511
+ ],
512
+ "openai.gpt-oss-20b-1:0": [
513
+ "low",
514
+ "medium",
515
+ "high"
516
+ ],
517
+ "mistral-large-latest": null,
518
+ "mistral-medium-latest": ["none", "high"],
519
+ "mistral-small-latest": ["none", "high"]
520
+ };
521
+ /**
522
+ * Concrete provider-default effort where the upstream catalogue publishes
523
+ * one. OpenRouter is the source today; null means the provider does not expose
524
+ * a truthful named default, so clients must retain a separate Default choice.
525
+ */
526
+ const MODEL_DEFAULT_REASONING_EFFORTS = {
527
+ "gemini-3.8-flash": null,
528
+ "gemini-3.7-flash": null,
529
+ "gemini-3.6-flash": null,
530
+ "gemini-3.5-flash-lite": null,
531
+ "gemini-3.1-pro-preview": null,
532
+ "gemini-3.5-flash": null,
533
+ "gemini-3.1-flash-lite": null,
534
+ "openai/gpt-6.1-sol": "medium",
535
+ "openai/gpt-6-astra": "medium",
536
+ "openai/gpt-6-sol": "medium",
537
+ "openai/gpt-6-luna": "medium",
538
+ "openai/gpt-5.6-sol": "medium",
539
+ "openai/gpt-5.6-terra": "medium",
540
+ "openai/gpt-5.6-luna": "medium",
541
+ "google/gemini-3.8-flash": "medium",
542
+ "google/gemini-3.7-flash": "medium",
543
+ "google/gemini-3.6-flash": "medium",
544
+ "google/gemini-3.5-flash-lite": "minimal",
545
+ "google/gemini-3.1-pro-preview": "medium",
546
+ "google/gemini-3.5-flash": "medium",
547
+ "google/gemini-3.1-flash-lite": "minimal",
548
+ "anthropic/claude-sonnet-5.5": "high",
549
+ "anthropic/claude-sonnet-5": "high",
550
+ "anthropic/claude-opus-5": "high",
551
+ "anthropic/claude-opus-4.8": "high",
552
+ "anthropic/claude-sonnet-4.6": "medium",
553
+ "openai/gpt-5.5": "medium",
554
+ "openai/gpt-5.4-mini": "medium",
555
+ "gpt-6.1-sol": null,
556
+ "gpt-6-astra": null,
557
+ "gpt-6-sol": null,
558
+ "gpt-6-luna": null,
559
+ "gpt-5.6": null,
560
+ "gpt-5.6-terra": null,
561
+ "gpt-5.6-luna": null,
562
+ "gpt-5.5": null,
563
+ "gpt-5.4": null,
564
+ "gpt-5.4-mini": null,
565
+ "gpt-5.4-nano": null,
566
+ "gpt-5.2": null,
567
+ "claude-sonnet-5-5": null,
568
+ "claude-sonnet-5": null,
569
+ "claude-fable-5-1": null,
570
+ "claude-fable-5": null,
571
+ "claude-opus-5-5": null,
572
+ "claude-opus-5": null,
573
+ "claude-opus-4-8": null,
574
+ "claude-opus-4-7": null,
575
+ "claude-sonnet-4-6": null,
576
+ "claude-opus-4-6": null,
577
+ "claude-haiku-4-5-20251001": null,
578
+ "us.anthropic.claude-sonnet-4-5-20250929-v1:0": null,
579
+ "us.anthropic.claude-haiku-4-5-20251001-v1:0": null,
580
+ "us.amazon.nova-pro-v1:0": null,
581
+ "us.amazon.nova-lite-v1:0": null,
582
+ "us.amazon.nova-micro-v1:0": null,
583
+ "openai.gpt-oss-120b-1:0": null,
584
+ "openai.gpt-oss-20b-1:0": null,
585
+ "mistral-large-latest": null,
586
+ "mistral-medium-latest": null,
587
+ "mistral-small-latest": null
588
+ };
589
+ /**
590
+ * Whether stella should emit a `temperature` sampling override.
591
+ * `"omit"` covers rejected, deprecated, and accepted-but-ignored
592
+ * parameters. Consumers must go through `shouldEmitTemperature`.
593
+ */
594
+ const MODEL_TEMPERATURE_POLICIES = {
595
+ "gemini-3.8-flash": "omit",
596
+ "gemini-3.7-flash": "omit",
597
+ "gemini-3.6-flash": "omit",
598
+ "gemini-3.5-flash-lite": "omit",
599
+ "gemini-3.1-pro-preview": "emit",
600
+ "gemini-3.5-flash": "emit",
601
+ "gemini-3.1-flash-lite": "emit",
602
+ "openai/gpt-6.1-sol": "omit",
603
+ "openai/gpt-6-astra": "omit",
604
+ "openai/gpt-6-sol": "omit",
605
+ "openai/gpt-6-luna": "omit",
606
+ "openai/gpt-5.6-sol": "omit",
607
+ "openai/gpt-5.6-terra": "omit",
608
+ "openai/gpt-5.6-luna": "omit",
609
+ "google/gemini-3.8-flash": "omit",
610
+ "google/gemini-3.7-flash": "omit",
611
+ "google/gemini-3.6-flash": "omit",
612
+ "google/gemini-3.5-flash-lite": "omit",
613
+ "google/gemini-3.1-pro-preview": "emit",
614
+ "google/gemini-3.5-flash": "emit",
615
+ "google/gemini-3.1-flash-lite": "emit",
616
+ "anthropic/claude-sonnet-5.5": "emit",
617
+ "anthropic/claude-sonnet-5": "omit",
618
+ "anthropic/claude-opus-5": "emit",
619
+ "anthropic/claude-opus-4.8": "emit",
620
+ "anthropic/claude-sonnet-4.6": "emit",
621
+ "openai/gpt-5.5": "omit",
622
+ "openai/gpt-5.4-mini": "omit",
623
+ "gpt-6.1-sol": "omit",
624
+ "gpt-6-astra": "omit",
625
+ "gpt-6-sol": "omit",
626
+ "gpt-6-luna": "omit",
627
+ "gpt-5.6": "omit",
628
+ "gpt-5.6-terra": "omit",
629
+ "gpt-5.6-luna": "omit",
630
+ "gpt-5.5": "omit",
631
+ "gpt-5.4": "emit",
632
+ "gpt-5.4-mini": "emit",
633
+ "gpt-5.4-nano": "emit",
634
+ "gpt-5.2": "emit",
635
+ "claude-sonnet-5-5": "omit",
636
+ "claude-sonnet-5": "omit",
637
+ "claude-fable-5-1": "omit",
638
+ "claude-fable-5": "omit",
639
+ "claude-opus-5-5": "omit",
640
+ "claude-opus-5": "omit",
641
+ "claude-opus-4-8": "omit",
642
+ "claude-opus-4-7": "omit",
643
+ "claude-sonnet-4-6": "emit",
644
+ "claude-opus-4-6": "emit",
645
+ "claude-haiku-4-5-20251001": "emit",
646
+ "us.anthropic.claude-sonnet-4-5-20250929-v1:0": "emit",
647
+ "us.anthropic.claude-haiku-4-5-20251001-v1:0": "emit",
648
+ "us.amazon.nova-pro-v1:0": "emit",
649
+ "us.amazon.nova-lite-v1:0": "emit",
650
+ "us.amazon.nova-micro-v1:0": "emit",
651
+ "openai.gpt-oss-120b-1:0": "emit",
652
+ "openai.gpt-oss-20b-1:0": "emit",
653
+ "mistral-large-latest": "emit",
654
+ "mistral-medium-latest": "emit",
655
+ "mistral-small-latest": "emit"
656
+ };
657
+ /**
658
+ * The most output tokens one response of each offered model may carry
659
+ * (models.dev `limit.output`). Consumers must go through
660
+ * `getOutputTokenLimit`.
661
+ */
662
+ const MODEL_OUTPUT_TOKEN_LIMITS = {
663
+ "gemini-3.8-flash": 65536,
664
+ "gemini-3.7-flash": 65536,
665
+ "gemini-3.6-flash": 65536,
666
+ "gemini-3.5-flash-lite": 65536,
667
+ "gemini-3.1-pro-preview": 65536,
668
+ "gemini-3.5-flash": 65536,
669
+ "gemini-3.1-flash-lite": 65536,
670
+ "openai/gpt-6.1-sol": 128e3,
671
+ "openai/gpt-6-astra": 128e3,
672
+ "openai/gpt-6-sol": 128e3,
673
+ "openai/gpt-6-luna": 128e3,
674
+ "openai/gpt-5.6-sol": 128e3,
675
+ "openai/gpt-5.6-terra": 128e3,
676
+ "openai/gpt-5.6-luna": 128e3,
677
+ "google/gemini-3.8-flash": 65536,
678
+ "google/gemini-3.7-flash": 65536,
679
+ "google/gemini-3.6-flash": 65536,
680
+ "google/gemini-3.5-flash-lite": 65536,
681
+ "google/gemini-3.1-pro-preview": 65536,
682
+ "google/gemini-3.5-flash": 65536,
683
+ "google/gemini-3.1-flash-lite": 65536,
684
+ "anthropic/claude-sonnet-5.5": 128e3,
685
+ "anthropic/claude-sonnet-5": 128e3,
686
+ "anthropic/claude-opus-5": 128e3,
687
+ "anthropic/claude-opus-4.8": 128e3,
688
+ "anthropic/claude-sonnet-4.6": 128e3,
689
+ "openai/gpt-5.5": 128e3,
690
+ "openai/gpt-5.4-mini": 128e3,
691
+ "gpt-6.1-sol": 128e3,
692
+ "gpt-6-astra": 128e3,
693
+ "gpt-6-sol": 128e3,
694
+ "gpt-6-luna": 128e3,
695
+ "gpt-5.6": 128e3,
696
+ "gpt-5.6-terra": 128e3,
697
+ "gpt-5.6-luna": 128e3,
698
+ "gpt-5.5": 128e3,
699
+ "gpt-5.4": 128e3,
700
+ "gpt-5.4-mini": 128e3,
701
+ "gpt-5.4-nano": 128e3,
702
+ "gpt-5.2": 128e3,
703
+ "claude-sonnet-5-5": 128e3,
704
+ "claude-sonnet-5": 128e3,
705
+ "claude-fable-5-1": 128e3,
706
+ "claude-fable-5": 128e3,
707
+ "claude-opus-5-5": 128e3,
708
+ "claude-opus-5": 128e3,
709
+ "claude-opus-4-8": 128e3,
710
+ "claude-opus-4-7": 128e3,
711
+ "claude-sonnet-4-6": 128e3,
712
+ "claude-opus-4-6": 128e3,
713
+ "claude-haiku-4-5-20251001": 64e3,
714
+ "us.anthropic.claude-sonnet-4-5-20250929-v1:0": 64e3,
715
+ "us.anthropic.claude-haiku-4-5-20251001-v1:0": 64e3,
716
+ "us.amazon.nova-pro-v1:0": 1e4,
717
+ "us.amazon.nova-lite-v1:0": 1e4,
718
+ "us.amazon.nova-micro-v1:0": 1e4,
719
+ "openai.gpt-oss-120b-1:0": 128e3,
720
+ "openai.gpt-oss-20b-1:0": 128e3,
721
+ "mistral-large-latest": 262144,
722
+ "mistral-medium-latest": 262144,
723
+ "mistral-small-latest": 256e3
724
+ };
725
+ //#endregion
726
+ //#region src/model-rates.gen.ts
727
+ const MODEL_RATES = {
728
+ "gemini-3.8-flash": {
729
+ kind: "flat",
730
+ inputPerMTok: 75e3,
731
+ outputPerMTok: 375e3,
732
+ cachedInputPerMTok: 7500
733
+ },
734
+ "gemini-3.7-flash": {
735
+ kind: "flat",
736
+ inputPerMTok: 75e3,
737
+ outputPerMTok: 375e3,
738
+ cachedInputPerMTok: 7500
739
+ },
740
+ "gemini-3.6-flash": {
741
+ kind: "flat",
742
+ inputPerMTok: 75e3,
743
+ outputPerMTok: 375e3,
744
+ cachedInputPerMTok: 7500
745
+ },
746
+ "gemini-3.5-flash-lite": {
747
+ kind: "flat",
748
+ inputPerMTok: 3e4,
749
+ outputPerMTok: 25e4,
750
+ cachedInputPerMTok: 3e3
751
+ },
752
+ "gemini-3.1-pro-preview": {
753
+ kind: "input-token-tiered",
754
+ inputTokenThreshold: 2e5,
755
+ standard: {
756
+ inputPerMTok: 2e5,
757
+ outputPerMTok: 12e5,
758
+ cachedInputPerMTok: 2e4
759
+ },
760
+ aboveThreshold: {
761
+ inputPerMTok: 4e5,
762
+ outputPerMTok: 18e5,
763
+ cachedInputPerMTok: 4e4
764
+ }
765
+ },
766
+ "gemini-3.5-flash": {
767
+ kind: "flat",
768
+ inputPerMTok: 15e4,
769
+ outputPerMTok: 9e5,
770
+ cachedInputPerMTok: 15e3
771
+ },
772
+ "gemini-3.1-flash-lite": {
773
+ kind: "flat",
774
+ inputPerMTok: 25e3,
775
+ outputPerMTok: 15e4,
776
+ cachedInputPerMTok: 2500
777
+ },
778
+ "gpt-6.1-sol": {
779
+ kind: "input-token-tiered",
780
+ inputTokenThreshold: 272e3,
781
+ standard: {
782
+ inputPerMTok: 2e5,
783
+ outputPerMTok: 1e6,
784
+ cachedInputPerMTok: 1e4,
785
+ cachedWriteInputPerMTok: 25e4
786
+ },
787
+ aboveThreshold: {
788
+ inputPerMTok: 4e5,
789
+ outputPerMTok: 15e5,
790
+ cachedInputPerMTok: 2e4,
791
+ cachedWriteInputPerMTok: 5e5
792
+ }
793
+ },
794
+ "gpt-6-astra": {
795
+ kind: "input-token-tiered",
796
+ inputTokenThreshold: 272e3,
797
+ standard: {
798
+ inputPerMTok: 1e6,
799
+ outputPerMTok: 5e6,
800
+ cachedInputPerMTok: 1e5,
801
+ cachedWriteInputPerMTok: 125e4
802
+ },
803
+ aboveThreshold: {
804
+ inputPerMTok: 2e6,
805
+ outputPerMTok: 75e5,
806
+ cachedInputPerMTok: 2e5,
807
+ cachedWriteInputPerMTok: 25e5
808
+ }
809
+ },
810
+ "gpt-6-sol": {
811
+ kind: "input-token-tiered",
812
+ inputTokenThreshold: 272e3,
813
+ standard: {
814
+ inputPerMTok: 2e5,
815
+ outputPerMTok: 1e6,
816
+ cachedInputPerMTok: 2e4,
817
+ cachedWriteInputPerMTok: 25e4
818
+ },
819
+ aboveThreshold: {
820
+ inputPerMTok: 4e5,
821
+ outputPerMTok: 15e5,
822
+ cachedInputPerMTok: 4e4,
823
+ cachedWriteInputPerMTok: 5e5
824
+ }
825
+ },
826
+ "gpt-6-luna": {
827
+ kind: "input-token-tiered",
828
+ inputTokenThreshold: 272e3,
829
+ standard: {
830
+ inputPerMTok: 1e4,
831
+ outputPerMTok: 5e4,
832
+ cachedInputPerMTok: 1e3,
833
+ cachedWriteInputPerMTok: 12500
834
+ },
835
+ aboveThreshold: {
836
+ inputPerMTok: 2e4,
837
+ outputPerMTok: 75e3,
838
+ cachedInputPerMTok: 2e3,
839
+ cachedWriteInputPerMTok: 25e3
840
+ }
841
+ },
842
+ "gpt-5.6": {
843
+ kind: "input-token-tiered",
844
+ inputTokenThreshold: 272e3,
845
+ standard: {
846
+ inputPerMTok: 4e5,
847
+ outputPerMTok: 2e6,
848
+ cachedInputPerMTok: 4e4,
849
+ cachedWriteInputPerMTok: 5e5
850
+ },
851
+ aboveThreshold: {
852
+ inputPerMTok: 8e5,
853
+ outputPerMTok: 3e6,
854
+ cachedInputPerMTok: 8e4,
855
+ cachedWriteInputPerMTok: 1e6
856
+ }
857
+ },
858
+ "gpt-5.6-terra": {
859
+ kind: "input-token-tiered",
860
+ inputTokenThreshold: 272e3,
861
+ standard: {
862
+ inputPerMTok: 2e5,
863
+ outputPerMTok: 12e5,
864
+ cachedInputPerMTok: 2e4,
865
+ cachedWriteInputPerMTok: 25e4
866
+ },
867
+ aboveThreshold: {
868
+ inputPerMTok: 4e5,
869
+ outputPerMTok: 18e5,
870
+ cachedInputPerMTok: 4e4,
871
+ cachedWriteInputPerMTok: 5e5
872
+ }
873
+ },
874
+ "gpt-5.6-luna": {
875
+ kind: "input-token-tiered",
876
+ inputTokenThreshold: 272e3,
877
+ standard: {
878
+ inputPerMTok: 2e4,
879
+ outputPerMTok: 12e4,
880
+ cachedInputPerMTok: 2e3,
881
+ cachedWriteInputPerMTok: 25e3
882
+ },
883
+ aboveThreshold: {
884
+ inputPerMTok: 4e4,
885
+ outputPerMTok: 18e4,
886
+ cachedInputPerMTok: 4e3,
887
+ cachedWriteInputPerMTok: 5e4
888
+ }
889
+ },
890
+ "gpt-5.5": {
891
+ kind: "input-token-tiered",
892
+ inputTokenThreshold: 272e3,
893
+ standard: {
894
+ inputPerMTok: 5e5,
895
+ outputPerMTok: 3e6,
896
+ cachedInputPerMTok: 5e4
897
+ },
898
+ aboveThreshold: {
899
+ inputPerMTok: 1e6,
900
+ outputPerMTok: 45e5,
901
+ cachedInputPerMTok: 1e5
902
+ }
903
+ },
904
+ "gpt-5.4": {
905
+ kind: "input-token-tiered",
906
+ inputTokenThreshold: 272e3,
907
+ standard: {
908
+ inputPerMTok: 25e4,
909
+ outputPerMTok: 15e5,
910
+ cachedInputPerMTok: 25e3
911
+ },
912
+ aboveThreshold: {
913
+ inputPerMTok: 5e5,
914
+ outputPerMTok: 225e4,
915
+ cachedInputPerMTok: 5e4
916
+ }
917
+ },
918
+ "gpt-5.4-mini": {
919
+ kind: "flat",
920
+ inputPerMTok: 75e3,
921
+ outputPerMTok: 45e4,
922
+ cachedInputPerMTok: 7500
923
+ },
924
+ "gpt-5.4-nano": {
925
+ kind: "flat",
926
+ inputPerMTok: 2e4,
927
+ outputPerMTok: 125e3,
928
+ cachedInputPerMTok: 2e3
929
+ },
930
+ "gpt-5.2": {
931
+ kind: "flat",
932
+ inputPerMTok: 175e3,
933
+ outputPerMTok: 14e5,
934
+ cachedInputPerMTok: 17500
935
+ },
936
+ "claude-sonnet-5-5": {
937
+ kind: "flat",
938
+ inputPerMTok: 2e5,
939
+ outputPerMTok: 1e6,
940
+ cachedInputPerMTok: 2e4,
941
+ cachedWriteInputPerMTok: 25e4
942
+ },
943
+ "claude-sonnet-5": {
944
+ kind: "flat",
945
+ inputPerMTok: 2e5,
946
+ outputPerMTok: 1e6,
947
+ cachedInputPerMTok: 2e4,
948
+ cachedWriteInputPerMTok: 25e4
949
+ },
950
+ "claude-fable-5-1": {
951
+ kind: "flat",
952
+ inputPerMTok: 1e6,
953
+ outputPerMTok: 5e6,
954
+ cachedInputPerMTok: 25e3,
955
+ cachedWriteInputPerMTok: 125e4
956
+ },
957
+ "claude-fable-5": {
958
+ kind: "flat",
959
+ inputPerMTok: 1e6,
960
+ outputPerMTok: 5e6,
961
+ cachedInputPerMTok: 1e5,
962
+ cachedWriteInputPerMTok: 125e4
963
+ },
964
+ "claude-opus-5-5": {
965
+ kind: "flat",
966
+ inputPerMTok: 4e5,
967
+ outputPerMTok: 2e6,
968
+ cachedInputPerMTok: 2e4,
969
+ cachedWriteInputPerMTok: 5e5
970
+ },
971
+ "claude-opus-5": {
972
+ kind: "flat",
973
+ inputPerMTok: 5e5,
974
+ outputPerMTok: 25e5,
975
+ cachedInputPerMTok: 5e4,
976
+ cachedWriteInputPerMTok: 625e3
977
+ },
978
+ "claude-opus-4-8": {
979
+ kind: "flat",
980
+ inputPerMTok: 5e5,
981
+ outputPerMTok: 25e5,
982
+ cachedInputPerMTok: 5e4,
983
+ cachedWriteInputPerMTok: 625e3
984
+ },
985
+ "claude-opus-4-7": {
986
+ kind: "flat",
987
+ inputPerMTok: 5e5,
988
+ outputPerMTok: 25e5,
989
+ cachedInputPerMTok: 5e4,
990
+ cachedWriteInputPerMTok: 625e3
991
+ },
992
+ "claude-sonnet-4-6": {
993
+ kind: "flat",
994
+ inputPerMTok: 3e5,
995
+ outputPerMTok: 15e5,
996
+ cachedInputPerMTok: 3e4,
997
+ cachedWriteInputPerMTok: 375e3
998
+ },
999
+ "claude-opus-4-6": {
1000
+ kind: "flat",
1001
+ inputPerMTok: 5e5,
1002
+ outputPerMTok: 25e5,
1003
+ cachedInputPerMTok: 5e4,
1004
+ cachedWriteInputPerMTok: 625e3
1005
+ },
1006
+ "claude-haiku-4-5-20251001": {
1007
+ kind: "flat",
1008
+ inputPerMTok: 1e5,
1009
+ outputPerMTok: 5e5,
1010
+ cachedInputPerMTok: 1e4,
1011
+ cachedWriteInputPerMTok: 125e3
1012
+ },
1013
+ "us.anthropic.claude-sonnet-4-5-20250929-v1:0": {
1014
+ kind: "flat",
1015
+ inputPerMTok: 33e4,
1016
+ outputPerMTok: 165e4,
1017
+ cachedInputPerMTok: 33e3,
1018
+ cachedWriteInputPerMTok: 412500
1019
+ },
1020
+ "us.anthropic.claude-haiku-4-5-20251001-v1:0": {
1021
+ kind: "flat",
1022
+ inputPerMTok: 11e4,
1023
+ outputPerMTok: 55e4,
1024
+ cachedInputPerMTok: 11e3,
1025
+ cachedWriteInputPerMTok: 137500
1026
+ },
1027
+ "us.amazon.nova-pro-v1:0": {
1028
+ kind: "flat",
1029
+ inputPerMTok: 8e4,
1030
+ outputPerMTok: 32e4,
1031
+ cachedInputPerMTok: 2e4,
1032
+ cachedWriteInputPerMTok: 8e4
1033
+ },
1034
+ "us.amazon.nova-lite-v1:0": {
1035
+ kind: "flat",
1036
+ inputPerMTok: 6e3,
1037
+ outputPerMTok: 24e3,
1038
+ cachedInputPerMTok: 1500,
1039
+ cachedWriteInputPerMTok: 6e3
1040
+ },
1041
+ "us.amazon.nova-micro-v1:0": {
1042
+ kind: "flat",
1043
+ inputPerMTok: 3500,
1044
+ outputPerMTok: 14e3,
1045
+ cachedInputPerMTok: 875,
1046
+ cachedWriteInputPerMTok: 3500
1047
+ },
1048
+ "openai.gpt-oss-120b-1:0": {
1049
+ kind: "flat",
1050
+ inputPerMTok: 15e3,
1051
+ outputPerMTok: 6e4
1052
+ },
1053
+ "openai.gpt-oss-20b-1:0": {
1054
+ kind: "flat",
1055
+ inputPerMTok: 7e3,
1056
+ outputPerMTok: 3e4
1057
+ },
1058
+ "mistral-large-latest": {
1059
+ kind: "flat",
1060
+ inputPerMTok: 5e4,
1061
+ outputPerMTok: 15e4,
1062
+ cachedInputPerMTok: 5e3
1063
+ },
1064
+ "mistral-medium-latest": {
1065
+ kind: "flat",
1066
+ inputPerMTok: 15e4,
1067
+ outputPerMTok: 75e4,
1068
+ cachedInputPerMTok: 15e3
1069
+ },
1070
+ "mistral-small-latest": {
1071
+ kind: "flat",
1072
+ inputPerMTok: 15e3,
1073
+ outputPerMTok: 6e4,
1074
+ cachedInputPerMTok: 1500
1075
+ },
1076
+ "gemini-2.5-flash": {
1077
+ kind: "flat",
1078
+ inputPerMTok: 3e4,
1079
+ outputPerMTok: 25e4,
1080
+ cachedInputPerMTok: 3e3
1081
+ },
1082
+ "gemini-2.5-pro": {
1083
+ kind: "input-token-tiered",
1084
+ inputTokenThreshold: 2e5,
1085
+ standard: {
1086
+ inputPerMTok: 125e3,
1087
+ outputPerMTok: 1e6,
1088
+ cachedInputPerMTok: 12500
1089
+ },
1090
+ aboveThreshold: {
1091
+ inputPerMTok: 25e4,
1092
+ outputPerMTok: 15e5,
1093
+ cachedInputPerMTok: 25e3
1094
+ }
1095
+ },
1096
+ "gpt-4o-mini": {
1097
+ kind: "flat",
1098
+ inputPerMTok: 15e3,
1099
+ outputPerMTok: 6e4,
1100
+ cachedInputPerMTok: 7500
1101
+ },
1102
+ "gpt-4o": {
1103
+ kind: "flat",
1104
+ inputPerMTok: 25e4,
1105
+ outputPerMTok: 1e6,
1106
+ cachedInputPerMTok: 125e3
1107
+ },
1108
+ "us.deepseek.r1-v1:0": {
1109
+ kind: "flat",
1110
+ inputPerMTok: 135e3,
1111
+ outputPerMTok: 54e4
1112
+ }
1113
+ };
1114
+ //#endregion
1115
+ //#region src/model-rate.ts
1116
+ /**
1117
+ * models.dev publishes USD prices per one million tokens. Stella stores the
1118
+ * same rates as integer ledger units so usage arithmetic never depends on
1119
+ * floating point values.
1120
+ */
1121
+ const MODEL_RATE_UNITS_PER_USD = 1e5;
1122
+ const getStandardModelRate = (rate) => rate.kind === "flat" ? rate : rate.standard;
1123
+ const resolveModelRate = (rate, inputTokens) => rate.kind === "input-token-tiered" && inputTokens > rate.inputTokenThreshold ? rate.aboveThreshold : getStandardModelRate(rate);
1124
+ //#endregion
1125
+ //#region src/model-rate-policy.ts
1126
+ const MODELS_DEV_RATE_PROVIDER_BY_CATALOG_PROVIDER = {
1127
+ google: "google",
1128
+ openai: "openai",
1129
+ anthropic: "anthropic",
1130
+ bedrock: "amazon-bedrock",
1131
+ mistral: "mistral"
1132
+ };
1133
+ const defineRateSourceAliases = (aliases) => aliases;
1134
+ /**
1135
+ * Explicit source coordinates for non-offered model IDs that remain valid in
1136
+ * deployment overrides and usage attribution. Their numeric rates still come
1137
+ * exclusively from models.dev.
1138
+ */
1139
+ const RETAINED_MODELS_DEV_RATE_ENTRIES = {
1140
+ "gemini-2.5-flash": {
1141
+ modelId: "gemini-2.5-flash",
1142
+ provider: "google"
1143
+ },
1144
+ "gemini-2.5-pro": {
1145
+ modelId: "gemini-2.5-pro",
1146
+ provider: "google"
1147
+ },
1148
+ "gpt-4o-mini": {
1149
+ modelId: "gpt-4o-mini",
1150
+ provider: "openai"
1151
+ },
1152
+ "gpt-4o": {
1153
+ modelId: "gpt-4o",
1154
+ provider: "openai"
1155
+ },
1156
+ "us.deepseek.r1-v1:0": {
1157
+ modelId: "us.deepseek.r1-v1:0",
1158
+ provider: "amazon-bedrock"
1159
+ }
1160
+ };
1161
+ /**
1162
+ * Exact mappings where Stella uses a provider routing ID while models.dev
1163
+ * publishes pricing under the corresponding base model ID. Generation rejects
1164
+ * a mapping once models.dev begins publishing the Stella ID directly, so the
1165
+ * map is empty whenever upstream covers every offered ID.
1166
+ */
1167
+ const MODELS_DEV_RATE_SOURCE_ALIASES = defineRateSourceAliases({});
1168
+ //#endregion
1169
+ //#region src/capabilities-overrides.ts
1170
+ const CAPABILITY_OVERRIDES = {};
1171
+ /** Only official provider documentation may override input-modality evidence. */
1172
+ const IMAGE_INPUT_OVERRIDES = {};
1173
+ //#endregion
1174
+ //#region src/document-input-overrides.ts
1175
+ /**
1176
+ * Reviewed document-input corrections for models.dev records that exist but
1177
+ * under-report the provider API's file-input support. The generator rejects an
1178
+ * override once upstream agrees, so sourced data takes ownership automatically.
1179
+ */
1180
+ const DOCUMENT_INPUT_OVERRIDES = {
1181
+ "gpt-5.4-mini": {
1182
+ supported: true,
1183
+ reason: "2026-08-20: OpenAI Responses accepts input_file PDF content for this vision-capable model; models.dev currently omits the pdf modality"
1184
+ },
1185
+ "gpt-5.4-nano": {
1186
+ supported: true,
1187
+ reason: "2026-08-20: OpenAI Responses accepts input_file PDF content for this vision-capable model; models.dev currently omits the pdf modality"
1188
+ },
1189
+ "gpt-5.2": {
1190
+ supported: true,
1191
+ reason: "2026-08-20: OpenAI Responses accepts input_file PDF content for this vision-capable model; models.dev currently omits the pdf modality"
1192
+ }
1193
+ };
1194
+ //#endregion
1195
+ //#region src/index.ts
1196
+ /**
1197
+ * Canonical AI provider and model catalog.
1198
+ *
1199
+ * Single source of truth for every provider/model identifier stella
1200
+ * offers. Imported by the API runtime
1201
+ * (`apps/api/src/lib/tanstack-ai-models.ts`)
1202
+ * and the BYOK settings UI (`apps/web`), so the picker can never offer
1203
+ * a model the backend rejects, and vice versa.
1204
+ *
1205
+ * Pure data: no provider SDKs, no env access, no side effects. This is
1206
+ * deliberate so scripts (the nightly upstream-validation check) and
1207
+ * both apps can import it cheaply. Keep it that way. The only runtime
1208
+ * dependency is valibot, used solely to construct branded types the
1209
+ * same way the rest of the repo does.
1210
+ *
1211
+ * Model IDs go stale when providers rename or retire models. The
1212
+ * nightly `model-catalog-upstream` check
1213
+ * (`packages/scripts/src/model-catalog-upstream.ts`) validates every
1214
+ * ID here against live provider/aggregator listings so a retired model
1215
+ * fails CI instead of 400-ing a user at runtime.
1216
+ */
1217
+ /**
1218
+ * Logical model roles. Call sites declare *what* they need, not
1219
+ * *which* model to use.
1220
+ *
1221
+ * - fast: classification, extraction, short generation
1222
+ * - chat: conversational with tool use and streaming
1223
+ * - reasoning: complex multi-step legal analysis
1224
+ * - pdf: native PDF/image understanding
1225
+ */
1226
+ const MODEL_ROLES = [
1227
+ "fast",
1228
+ "chat",
1229
+ "reasoning",
1230
+ "pdf"
1231
+ ];
1232
+ const TEMPERATURE_POLICIES = ["emit", "omit"];
1233
+ const IMAGE_INPUT_CAPABILITIES = [
1234
+ "supported",
1235
+ "unsupported",
1236
+ "unknown"
1237
+ ];
1238
+ const STREAMING_TOOL_USE_SUPPORTS = ["supported", "unsupported"];
1239
+ const AI_PROVIDERS = [
1240
+ "google",
1241
+ "openrouter",
1242
+ "openai",
1243
+ "azure_foundry",
1244
+ "anthropic",
1245
+ "bedrock",
1246
+ "mistral",
1247
+ "openai_compatible",
1248
+ "huggingface"
1249
+ ];
1250
+ const TANSTACK_AI_PROVIDERS = [
1251
+ "google",
1252
+ "openrouter",
1253
+ "openai",
1254
+ "anthropic",
1255
+ "bedrock",
1256
+ "mistral"
1257
+ ];
1258
+ const MODEL_CATALOG_PROVIDER_KIND = {
1259
+ google: "first-party",
1260
+ openrouter: "aggregator",
1261
+ openai: "first-party",
1262
+ anthropic: "first-party",
1263
+ bedrock: "platform",
1264
+ mistral: "first-party"
1265
+ };
1266
+ /**
1267
+ * Runtime form of the first-party provider union. The conditional annotation
1268
+ * makes adding a first-party policy without adding its discovery implementation
1269
+ * a compile error.
1270
+ */
1271
+ const FIRST_PARTY_MODEL_PROVIDERS = [
1272
+ "google",
1273
+ "openai",
1274
+ "anthropic",
1275
+ "mistral"
1276
+ ];
1277
+ /**
1278
+ * Provider IDs for the GPT-6.1 family, kept in one row per member like GPT-6
1279
+ * below.
1280
+ */
1281
+ const GPT_61_MODEL_IDS = { sol: {
1282
+ openai: "gpt-6.1-sol",
1283
+ openrouter: "openai/gpt-6.1-sol"
1284
+ } };
1285
+ const GPT_61_OPENAI_MODEL_IDS = Object.values(GPT_61_MODEL_IDS).map(({ openai }) => openai);
1286
+ const GPT_61_OPENROUTER_MODEL_IDS = Object.values(GPT_61_MODEL_IDS).map(({ openrouter }) => openrouter);
1287
+ /**
1288
+ * Provider IDs for the GPT-6 family. Like GPT-5.6 below, one row feeds both
1289
+ * the OpenAI and the OpenRouter picker.
1290
+ */
1291
+ const GPT_6_MODEL_IDS = {
1292
+ astra: {
1293
+ openai: "gpt-6-astra",
1294
+ openrouter: "openai/gpt-6-astra"
1295
+ },
1296
+ sol: {
1297
+ openai: "gpt-6-sol",
1298
+ openrouter: "openai/gpt-6-sol"
1299
+ },
1300
+ luna: {
1301
+ openai: "gpt-6-luna",
1302
+ openrouter: "openai/gpt-6-luna"
1303
+ }
1304
+ };
1305
+ const GPT_6_OPENAI_MODEL_IDS = Object.values(GPT_6_MODEL_IDS).map(({ openai }) => openai);
1306
+ const GPT_6_OPENROUTER_MODEL_IDS = Object.values(GPT_6_MODEL_IDS).map(({ openrouter }) => openrouter);
1307
+ /**
1308
+ * Provider IDs for the GPT-5.6 family. OpenAI exposes Sol through the
1309
+ * `gpt-5.6` alias while OpenRouter uses the explicit `gpt-5.6-sol` ID. Keep
1310
+ * both routes in one row so adding a family member cannot update one picker
1311
+ * and silently omit the other.
1312
+ */
1313
+ const GPT_56_MODEL_IDS = {
1314
+ sol: {
1315
+ openai: "gpt-5.6",
1316
+ openrouter: "openai/gpt-5.6-sol"
1317
+ },
1318
+ terra: {
1319
+ openai: "gpt-5.6-terra",
1320
+ openrouter: "openai/gpt-5.6-terra"
1321
+ },
1322
+ luna: {
1323
+ openai: "gpt-5.6-luna",
1324
+ openrouter: "openai/gpt-5.6-luna"
1325
+ }
1326
+ };
1327
+ const GPT_56_OPENAI_MODEL_IDS = Object.values(GPT_56_MODEL_IDS).map(({ openai }) => openai);
1328
+ const GPT_56_OPENROUTER_MODEL_IDS = Object.values(GPT_56_MODEL_IDS).map(({ openrouter }) => openrouter);
1329
+ /**
1330
+ * Per-role default model IDs for the BYOK-capable cloud providers.
1331
+ * Shared between the instance default table (`DEFAULT_MODELS`) and the
1332
+ * settings-UI default selection, so a default is defined exactly once.
1333
+ */
1334
+ const BYOK_DEFAULT_MODELS = {
1335
+ google: {
1336
+ fast: "gemini-3.8-flash",
1337
+ chat: "gemini-3.8-flash",
1338
+ reasoning: "gemini-3.8-flash",
1339
+ pdf: "gemini-3.8-flash"
1340
+ },
1341
+ openrouter: {
1342
+ fast: GPT_6_MODEL_IDS.luna.openrouter,
1343
+ chat: GPT_6_MODEL_IDS.sol.openrouter,
1344
+ reasoning: GPT_6_MODEL_IDS.sol.openrouter,
1345
+ pdf: GPT_6_MODEL_IDS.sol.openrouter
1346
+ },
1347
+ openai: {
1348
+ fast: GPT_6_MODEL_IDS.luna.openai,
1349
+ chat: GPT_6_MODEL_IDS.sol.openai,
1350
+ reasoning: GPT_6_MODEL_IDS.sol.openai,
1351
+ pdf: GPT_6_MODEL_IDS.sol.openai
1352
+ },
1353
+ anthropic: {
1354
+ fast: "claude-opus-5-5",
1355
+ chat: "claude-opus-5-5",
1356
+ reasoning: "claude-opus-5-5",
1357
+ pdf: "claude-opus-5-5"
1358
+ },
1359
+ bedrock: {
1360
+ fast: "us.anthropic.claude-sonnet-4-5-20250929-v1:0",
1361
+ chat: "us.anthropic.claude-sonnet-4-5-20250929-v1:0",
1362
+ reasoning: "us.anthropic.claude-sonnet-4-5-20250929-v1:0",
1363
+ pdf: "us.anthropic.claude-sonnet-4-5-20250929-v1:0"
1364
+ },
1365
+ mistral: {
1366
+ fast: "mistral-small-latest",
1367
+ chat: "mistral-medium-latest",
1368
+ reasoning: "mistral-medium-latest",
1369
+ pdf: "mistral-large-latest"
1370
+ }
1371
+ };
1372
+ /**
1373
+ * Instance-level default model IDs per provider. Extends the BYOK
1374
+ * defaults with the providers that only the instance path uses
1375
+ * (custom deployments and OpenAI-compatible endpoints).
1376
+ */
1377
+ const DEFAULT_MODELS = {
1378
+ ...BYOK_DEFAULT_MODELS,
1379
+ azure_foundry: {
1380
+ fast: GPT_56_MODEL_IDS.luna.openai,
1381
+ chat: GPT_56_MODEL_IDS.terra.openai,
1382
+ reasoning: GPT_56_MODEL_IDS.terra.openai,
1383
+ pdf: GPT_56_MODEL_IDS.terra.openai
1384
+ },
1385
+ openai_compatible: {
1386
+ fast: "default",
1387
+ chat: "default",
1388
+ reasoning: "default",
1389
+ pdf: "default"
1390
+ },
1391
+ huggingface: {
1392
+ fast: "speakleash/Bielik-1.5B-v3.0-Instruct",
1393
+ chat: "speakleash/Bielik-11B-v2.3-Instruct",
1394
+ reasoning: "speakleash/Bielik-11B-v2.3-Instruct",
1395
+ pdf: "speakleash/Bielik-11B-v2.3-Instruct"
1396
+ }
1397
+ };
1398
+ /**
1399
+ * BYOK-offered model IDs per provider — the curated catalog users pick
1400
+ * from in org settings, and the server-side allowlist the API enforces.
1401
+ * The frontend list is not a security boundary; this is what the API
1402
+ * accepts.
1403
+ *
1404
+ * Limited to providers the TanStack AI integration supports for BYOK.
1405
+ * Providers without a first-class TanStack adapter path are intentionally
1406
+ * not accepted at this boundary.
1407
+ */
1408
+ const BYOK_MODEL_OPTIONS = {
1409
+ google: [
1410
+ "gemini-3.8-flash",
1411
+ "gemini-3.7-flash",
1412
+ "gemini-3.6-flash",
1413
+ "gemini-3.5-flash-lite",
1414
+ "gemini-3.1-pro-preview",
1415
+ "gemini-3.5-flash",
1416
+ "gemini-3.1-flash-lite"
1417
+ ],
1418
+ anthropic: [
1419
+ "claude-sonnet-5-5",
1420
+ "claude-sonnet-5",
1421
+ "claude-fable-5-1",
1422
+ "claude-fable-5",
1423
+ "claude-opus-5-5",
1424
+ "claude-opus-5",
1425
+ "claude-opus-4-8",
1426
+ "claude-opus-4-7",
1427
+ "claude-sonnet-4-6",
1428
+ "claude-opus-4-6",
1429
+ "claude-haiku-4-5-20251001"
1430
+ ],
1431
+ openai: [
1432
+ ...GPT_61_OPENAI_MODEL_IDS,
1433
+ ...GPT_6_OPENAI_MODEL_IDS,
1434
+ ...GPT_56_OPENAI_MODEL_IDS,
1435
+ "gpt-5.5",
1436
+ "gpt-5.4",
1437
+ "gpt-5.4-mini",
1438
+ "gpt-5.4-nano",
1439
+ "gpt-5.2"
1440
+ ],
1441
+ openrouter: [
1442
+ ...GPT_61_OPENROUTER_MODEL_IDS,
1443
+ ...GPT_6_OPENROUTER_MODEL_IDS,
1444
+ ...GPT_56_OPENROUTER_MODEL_IDS,
1445
+ "google/gemini-3.8-flash",
1446
+ "google/gemini-3.7-flash",
1447
+ "google/gemini-3.6-flash",
1448
+ "google/gemini-3.5-flash-lite",
1449
+ "google/gemini-3.1-pro-preview",
1450
+ "google/gemini-3.5-flash",
1451
+ "google/gemini-3.1-flash-lite",
1452
+ "anthropic/claude-sonnet-5.5",
1453
+ "anthropic/claude-sonnet-5",
1454
+ "anthropic/claude-opus-5",
1455
+ "anthropic/claude-opus-4.8",
1456
+ "anthropic/claude-sonnet-4.6",
1457
+ "openai/gpt-5.5",
1458
+ "openai/gpt-5.4-mini"
1459
+ ],
1460
+ bedrock: [
1461
+ "us.anthropic.claude-sonnet-4-5-20250929-v1:0",
1462
+ "us.anthropic.claude-haiku-4-5-20251001-v1:0",
1463
+ "us.amazon.nova-pro-v1:0",
1464
+ "us.amazon.nova-lite-v1:0",
1465
+ "us.amazon.nova-micro-v1:0",
1466
+ "openai.gpt-oss-120b-1:0",
1467
+ "openai.gpt-oss-20b-1:0"
1468
+ ],
1469
+ mistral: [
1470
+ "mistral-large-latest",
1471
+ "mistral-medium-latest",
1472
+ "mistral-small-latest"
1473
+ ]
1474
+ };
1475
+ /**
1476
+ * Presentation metadata for every offered model. Keeping this exhaustive beside
1477
+ * the allowlist prevents a newly offered model from leaking a raw provider ID
1478
+ * into user-facing pickers. OpenRouter entries deliberately carry their model
1479
+ * creator's icon while their routing provider remains available separately.
1480
+ */
1481
+ const MODEL_DISPLAY_METADATA = {
1482
+ "gemini-3.8-flash": {
1483
+ displayName: "Gemini 3.8 Flash",
1484
+ iconProvider: "google"
1485
+ },
1486
+ "gemini-3.7-flash": {
1487
+ displayName: "Gemini 3.7 Flash",
1488
+ iconProvider: "google"
1489
+ },
1490
+ "gemini-3.6-flash": {
1491
+ displayName: "Gemini 3.6 Flash",
1492
+ iconProvider: "google"
1493
+ },
1494
+ "gemini-3.5-flash-lite": {
1495
+ displayName: "Gemini 3.5 Flash Lite",
1496
+ iconProvider: "google"
1497
+ },
1498
+ "gemini-3.1-pro-preview": {
1499
+ displayName: "Gemini 3.1 Pro Preview",
1500
+ iconProvider: "google"
1501
+ },
1502
+ "gemini-3.5-flash": {
1503
+ displayName: "Gemini 3.5 Flash",
1504
+ iconProvider: "google"
1505
+ },
1506
+ "gemini-3.1-flash-lite": {
1507
+ displayName: "Gemini 3.1 Flash Lite",
1508
+ iconProvider: "google"
1509
+ },
1510
+ "claude-sonnet-5-5": {
1511
+ displayName: "Claude Sonnet 5.5",
1512
+ iconProvider: "anthropic"
1513
+ },
1514
+ "claude-sonnet-5": {
1515
+ displayName: "Claude Sonnet 5",
1516
+ iconProvider: "anthropic"
1517
+ },
1518
+ "claude-fable-5-1": {
1519
+ displayName: "Claude Fable 5.1",
1520
+ iconProvider: "anthropic"
1521
+ },
1522
+ "claude-fable-5": {
1523
+ displayName: "Claude Fable 5",
1524
+ iconProvider: "anthropic"
1525
+ },
1526
+ "claude-opus-5-5": {
1527
+ displayName: "Claude Opus 5.5",
1528
+ iconProvider: "anthropic"
1529
+ },
1530
+ "claude-opus-5": {
1531
+ displayName: "Claude Opus 5",
1532
+ iconProvider: "anthropic"
1533
+ },
1534
+ "claude-opus-4-8": {
1535
+ displayName: "Claude Opus 4.8",
1536
+ iconProvider: "anthropic"
1537
+ },
1538
+ "claude-opus-4-7": {
1539
+ displayName: "Claude Opus 4.7",
1540
+ iconProvider: "anthropic"
1541
+ },
1542
+ "claude-sonnet-4-6": {
1543
+ displayName: "Claude Sonnet 4.6",
1544
+ iconProvider: "anthropic"
1545
+ },
1546
+ "claude-opus-4-6": {
1547
+ displayName: "Claude Opus 4.6",
1548
+ iconProvider: "anthropic"
1549
+ },
1550
+ "claude-haiku-4-5-20251001": {
1551
+ displayName: "Claude Haiku 4.5",
1552
+ iconProvider: "anthropic"
1553
+ },
1554
+ [GPT_61_MODEL_IDS.sol.openai]: {
1555
+ displayName: "GPT-6.1 Sol",
1556
+ iconProvider: "openai"
1557
+ },
1558
+ [GPT_6_MODEL_IDS.astra.openai]: {
1559
+ displayName: "GPT-6 Astra",
1560
+ iconProvider: "openai"
1561
+ },
1562
+ [GPT_6_MODEL_IDS.sol.openai]: {
1563
+ displayName: "GPT-6 Sol",
1564
+ iconProvider: "openai"
1565
+ },
1566
+ [GPT_6_MODEL_IDS.luna.openai]: {
1567
+ displayName: "GPT-6 Luna",
1568
+ iconProvider: "openai"
1569
+ },
1570
+ [GPT_56_MODEL_IDS.sol.openai]: {
1571
+ displayName: "GPT-5.6 Sol",
1572
+ iconProvider: "openai"
1573
+ },
1574
+ [GPT_56_MODEL_IDS.terra.openai]: {
1575
+ displayName: "GPT-5.6 Terra",
1576
+ iconProvider: "openai"
1577
+ },
1578
+ [GPT_56_MODEL_IDS.luna.openai]: {
1579
+ displayName: "GPT-5.6 Luna",
1580
+ iconProvider: "openai"
1581
+ },
1582
+ "gpt-5.5": {
1583
+ displayName: "GPT-5.5",
1584
+ iconProvider: "openai"
1585
+ },
1586
+ "gpt-5.4": {
1587
+ displayName: "GPT-5.4",
1588
+ iconProvider: "openai"
1589
+ },
1590
+ "gpt-5.4-mini": {
1591
+ displayName: "GPT-5.4 Mini",
1592
+ iconProvider: "openai"
1593
+ },
1594
+ "gpt-5.4-nano": {
1595
+ displayName: "GPT-5.4 Nano",
1596
+ iconProvider: "openai"
1597
+ },
1598
+ "gpt-5.2": {
1599
+ displayName: "GPT-5.2",
1600
+ iconProvider: "openai"
1601
+ },
1602
+ [GPT_61_MODEL_IDS.sol.openrouter]: {
1603
+ displayName: "GPT-6.1 Sol",
1604
+ iconProvider: "openai"
1605
+ },
1606
+ [GPT_6_MODEL_IDS.astra.openrouter]: {
1607
+ displayName: "GPT-6 Astra",
1608
+ iconProvider: "openai"
1609
+ },
1610
+ [GPT_6_MODEL_IDS.sol.openrouter]: {
1611
+ displayName: "GPT-6 Sol",
1612
+ iconProvider: "openai"
1613
+ },
1614
+ [GPT_6_MODEL_IDS.luna.openrouter]: {
1615
+ displayName: "GPT-6 Luna",
1616
+ iconProvider: "openai"
1617
+ },
1618
+ [GPT_56_MODEL_IDS.sol.openrouter]: {
1619
+ displayName: "GPT-5.6 Sol",
1620
+ iconProvider: "openai"
1621
+ },
1622
+ [GPT_56_MODEL_IDS.luna.openrouter]: {
1623
+ displayName: "GPT-5.6 Luna",
1624
+ iconProvider: "openai"
1625
+ },
1626
+ [GPT_56_MODEL_IDS.terra.openrouter]: {
1627
+ displayName: "GPT-5.6 Terra",
1628
+ iconProvider: "openai"
1629
+ },
1630
+ "google/gemini-3.8-flash": {
1631
+ displayName: "Gemini 3.8 Flash",
1632
+ iconProvider: "google"
1633
+ },
1634
+ "google/gemini-3.7-flash": {
1635
+ displayName: "Gemini 3.7 Flash",
1636
+ iconProvider: "google"
1637
+ },
1638
+ "google/gemini-3.6-flash": {
1639
+ displayName: "Gemini 3.6 Flash",
1640
+ iconProvider: "google"
1641
+ },
1642
+ "google/gemini-3.5-flash-lite": {
1643
+ displayName: "Gemini 3.5 Flash Lite",
1644
+ iconProvider: "google"
1645
+ },
1646
+ "google/gemini-3.1-pro-preview": {
1647
+ displayName: "Gemini 3.1 Pro Preview",
1648
+ iconProvider: "google"
1649
+ },
1650
+ "google/gemini-3.5-flash": {
1651
+ displayName: "Gemini 3.5 Flash",
1652
+ iconProvider: "google"
1653
+ },
1654
+ "google/gemini-3.1-flash-lite": {
1655
+ displayName: "Gemini 3.1 Flash Lite",
1656
+ iconProvider: "google"
1657
+ },
1658
+ "anthropic/claude-sonnet-5.5": {
1659
+ displayName: "Claude Sonnet 5.5",
1660
+ iconProvider: "anthropic"
1661
+ },
1662
+ "anthropic/claude-sonnet-5": {
1663
+ displayName: "Claude Sonnet 5",
1664
+ iconProvider: "anthropic"
1665
+ },
1666
+ "anthropic/claude-opus-5": {
1667
+ displayName: "Claude Opus 5",
1668
+ iconProvider: "anthropic"
1669
+ },
1670
+ "anthropic/claude-opus-4.8": {
1671
+ displayName: "Claude Opus 4.8",
1672
+ iconProvider: "anthropic"
1673
+ },
1674
+ "anthropic/claude-sonnet-4.6": {
1675
+ displayName: "Claude Sonnet 4.6",
1676
+ iconProvider: "anthropic"
1677
+ },
1678
+ "openai/gpt-5.5": {
1679
+ displayName: "GPT-5.5",
1680
+ iconProvider: "openai"
1681
+ },
1682
+ "openai/gpt-5.4-mini": {
1683
+ displayName: "GPT-5.4 Mini",
1684
+ iconProvider: "openai"
1685
+ },
1686
+ "us.anthropic.claude-sonnet-4-5-20250929-v1:0": {
1687
+ displayName: "Claude Sonnet 4.5",
1688
+ iconProvider: "anthropic"
1689
+ },
1690
+ "us.anthropic.claude-haiku-4-5-20251001-v1:0": {
1691
+ displayName: "Claude Haiku 4.5",
1692
+ iconProvider: "anthropic"
1693
+ },
1694
+ "us.amazon.nova-pro-v1:0": {
1695
+ displayName: "Amazon Nova Pro",
1696
+ iconProvider: "bedrock"
1697
+ },
1698
+ "us.amazon.nova-lite-v1:0": {
1699
+ displayName: "Amazon Nova Lite",
1700
+ iconProvider: "bedrock"
1701
+ },
1702
+ "us.amazon.nova-micro-v1:0": {
1703
+ displayName: "Amazon Nova Micro",
1704
+ iconProvider: "bedrock"
1705
+ },
1706
+ "openai.gpt-oss-120b-1:0": {
1707
+ displayName: "GPT OSS 120B",
1708
+ iconProvider: "openai"
1709
+ },
1710
+ "openai.gpt-oss-20b-1:0": {
1711
+ displayName: "GPT OSS 20B",
1712
+ iconProvider: "openai"
1713
+ },
1714
+ "mistral-large-latest": {
1715
+ displayName: "Mistral Large",
1716
+ iconProvider: "mistral"
1717
+ },
1718
+ "mistral-medium-latest": {
1719
+ displayName: "Mistral Medium",
1720
+ iconProvider: "mistral"
1721
+ },
1722
+ "mistral-small-latest": {
1723
+ displayName: "Mistral Small",
1724
+ iconProvider: "mistral"
1725
+ }
1726
+ };
1727
+ const MODEL_DISPLAY_METADATA_BY_ID = MODEL_DISPLAY_METADATA;
1728
+ const getModelDisplayMetadata = (modelId) => MODEL_DISPLAY_METADATA_BY_ID[normalizeModelCatalogId(modelId)] ?? null;
1729
+ /**
1730
+ * Offered models whose provider API accepts PDF/file content. Generated from
1731
+ * live models.dev input modalities plus dated, reviewed source corrections;
1732
+ * the nightly upstream check rejects drift.
1733
+ */
1734
+ const BYOK_DOCUMENT_INPUT_MODEL_OPTIONS = MODEL_DOCUMENT_INPUT_OPTIONS;
1735
+ const IMAGE_INPUT_BY_PROVIDER = MODEL_IMAGE_INPUT_CAPABILITIES;
1736
+ /** Unoffered IDs have no entry; offered IDs explicitly include unknown. */
1737
+ const getModelImageInputCapability = ({ provider, modelId }) => {
1738
+ const capabilities = IMAGE_INPUT_BY_PROVIDER[provider];
1739
+ const normalized = normalizeModelCatalogId(modelId);
1740
+ return Object.hasOwn(capabilities, normalized) ? capabilities[normalized] : void 0;
1741
+ };
1742
+ const isBYOKProviderRoleSupported = ({ provider, role }) => role !== "pdf" || BYOK_DOCUMENT_INPUT_MODEL_OPTIONS[provider].length > 0;
1743
+ const isBYOKModelRoleSupported = ({ provider, modelId, role }) => {
1744
+ if (role !== "pdf") return true;
1745
+ return BYOK_DOCUMENT_INPUT_MODEL_OPTIONS[provider].includes(modelId);
1746
+ };
1747
+ /**
1748
+ * Models whose provider adapter can receive a PDF `document` content part in a
1749
+ * chat turn without throwing. This is a narrower, runtime question than the
1750
+ * pdf ROLE (`BYOK_DOCUMENT_INPUT_MODEL_OPTIONS`, which curates model selection
1751
+ * for PDF-processing flows): it only asks "will the chat stream survive this
1752
+ * attachment." The Mistral text adapter maps PDF `document` parts to
1753
+ * `document_url`, so its
1754
+ * vision models accept PDF attachments even though Mistral is deliberately not
1755
+ * offered as a pdf-role provider. Mistral's `document_url` takes PDF but NOT
1756
+ * textual documents (docx/txt/csv/md), so those still gate on
1757
+ * `BYOK_DOCUMENT_INPUT_MODEL_OPTIONS` (which excludes Mistral). Superset
1758
+ * invariant: every pdf-role document model also accepts a PDF chat attachment.
1759
+ */
1760
+ const CHAT_PDF_ATTACHMENT_MODEL_OPTIONS = {
1761
+ ...BYOK_DOCUMENT_INPUT_MODEL_OPTIONS,
1762
+ mistral: ["mistral-medium-latest", "mistral-small-latest"]
1763
+ };
1764
+ const isChatPdfAttachmentModelSupported = ({ provider, modelId }) => {
1765
+ return CHAT_PDF_ATTACHMENT_MODEL_OPTIONS[provider].includes(modelId);
1766
+ };
1767
+ /**
1768
+ * Whether a model id is currently offered for this provider+role: it
1769
+ * must be in the curated catalog for the provider AND satisfy the
1770
+ * role's input-modality constraint (PDF needs a document-capable
1771
+ * model). This is the runtime allowlist, so a model dropped by a
1772
+ * catalog bump stops being valid here even though it is still a
1773
+ * non-empty string in a stored org config.
1774
+ */
1775
+ const isOfferedBYOKModelForRole = ({ provider, modelId, role }) => {
1776
+ return BYOK_MODEL_OPTIONS[provider].includes(modelId) && isBYOKModelRoleSupported({
1777
+ provider,
1778
+ modelId,
1779
+ role
1780
+ });
1781
+ };
1782
+ /**
1783
+ * Resolve a model id that will actually work for this provider+role,
1784
+ * keeping the SAME provider. Returns the caller's model unchanged when
1785
+ * it is still offered; otherwise falls back to the provider's per-role
1786
+ * default (`BYOK_DEFAULT_MODELS`). Returns `null` only when the
1787
+ * provider has no valid model for the role at all — the sole case
1788
+ * today is `mistral` + `pdf`, because the TanStack Mistral adapter
1789
+ * exposes no `document` input modality, so no Mistral model (not even
1790
+ * the default) can serve the PDF role.
1791
+ *
1792
+ * Used to auto-heal org AI configs whose pinned model was removed by a
1793
+ * catalog bump, so generation resolves to a supported model instead of
1794
+ * 400-ing (or forwarding a retired id to the provider).
1795
+ */
1796
+ const resolveWorkingBYOKModelForRole = ({ provider, modelId, role }) => {
1797
+ if (isOfferedBYOKModelForRole({
1798
+ provider,
1799
+ modelId,
1800
+ role
1801
+ })) return modelId;
1802
+ const fallback = BYOK_DEFAULT_MODELS[provider][role];
1803
+ return isOfferedBYOKModelForRole({
1804
+ provider,
1805
+ modelId: fallback,
1806
+ role
1807
+ }) ? fallback : null;
1808
+ };
1809
+ /**
1810
+ * Anthropic models that use the adaptive-thinking request shape
1811
+ * (`thinking: { type: "adaptive" }`). Newer Claude models reject the
1812
+ * legacy budget-based form, so every Opus 4.6+/Sonnet 4.6/Fable entry
1813
+ * offered above must appear here or it will 400 on the reasoning role.
1814
+ */
1815
+ const ANTHROPIC_ADAPTIVE_THINKING_MODELS = [
1816
+ "claude-sonnet-5-5",
1817
+ "claude-sonnet-5",
1818
+ "claude-opus-5",
1819
+ "claude-sonnet-4-6",
1820
+ "claude-opus-4-6",
1821
+ "claude-opus-4-7",
1822
+ "claude-opus-4-8",
1823
+ "claude-fable-5",
1824
+ "claude-fable-5-1",
1825
+ "claude-opus-5-5"
1826
+ ];
1827
+ /**
1828
+ * Canonical reasoning-effort ladder, ordered weakest to strongest.
1829
+ * Union of every effort keyword the offered providers accept; no
1830
+ * single model accepts all of them, which is exactly why the
1831
+ * generated per-model `MODEL_REASONING_EFFORTS` exists.
1832
+ */
1833
+ const REASONING_EFFORTS = [
1834
+ "none",
1835
+ "minimal",
1836
+ "low",
1837
+ "medium",
1838
+ "high",
1839
+ "xhigh",
1840
+ "max"
1841
+ ];
1842
+ const resolvedReasoningEffortSchema = v.pipe(v.picklist(REASONING_EFFORTS), v.brand("ResolvedReasoningEffort"));
1843
+ /**
1844
+ * Per-provider model served when a user's included budget is exhausted
1845
+ * and no explicit selection overrides it. Falls back to each
1846
+ * provider's fast-role default; providers without a first-class
1847
+ * adapter path cannot serve the lane. Total over providers so a new
1848
+ * provider lands with an explicit decision.
1849
+ */
1850
+ const FALLBACK_CHAT_MODEL_BY_PROVIDER = {
1851
+ google: DEFAULT_MODELS.google.fast,
1852
+ openrouter: DEFAULT_MODELS.openrouter.fast,
1853
+ openai: DEFAULT_MODELS.openai.fast,
1854
+ anthropic: DEFAULT_MODELS.anthropic.fast,
1855
+ bedrock: DEFAULT_MODELS.bedrock.fast,
1856
+ mistral: DEFAULT_MODELS.mistral.fast,
1857
+ azure_foundry: null,
1858
+ openai_compatible: null,
1859
+ huggingface: null
1860
+ };
1861
+ /**
1862
+ * Provider-native IDs that are exact aliases of an offered catalog ID.
1863
+ * Every metadata lookup normalizes here, so instance/dev overrides cannot
1864
+ * bypass rates or capabilities and duplicated alias rows cannot drift apart.
1865
+ */
1866
+ const MODEL_CATALOG_ID_ALIASES = {
1867
+ "gpt-5.6-sol": "gpt-5.6",
1868
+ "claude-opus-4.8": "claude-opus-4-8",
1869
+ "claude-sonnet-5.5": "claude-sonnet-5-5",
1870
+ "claude-sonnet-4.6": "claude-sonnet-4-6"
1871
+ };
1872
+ const MODEL_CATALOG_ID_ALIAS_TARGET_BY_ID = MODEL_CATALOG_ID_ALIASES;
1873
+ const normalizeModelCatalogId = (modelId) => Object.hasOwn(MODEL_CATALOG_ID_ALIAS_TARGET_BY_ID, modelId) ? MODEL_CATALOG_ID_ALIAS_TARGET_BY_ID[modelId] ?? modelId : modelId;
1874
+ const MODEL_TEMPERATURE_POLICY_BY_ID = MODEL_TEMPERATURE_POLICIES;
1875
+ /**
1876
+ * Whether stella should emit a `temperature` override for this model.
1877
+ * This is deliberately stricter than provider acceptance: deprecated
1878
+ * parameters that are accepted but ignored resolve to `false`. Unknown ids
1879
+ * also resolve to `false` (send nothing; provider default). Callers must
1880
+ * never index `MODEL_TEMPERATURE_POLICIES` directly with
1881
+ * a runtime string.
1882
+ */
1883
+ const shouldEmitTemperature = (modelId) => MODEL_TEMPERATURE_POLICY_BY_ID[normalizeModelCatalogId(modelId)] === "emit";
1884
+ const MODEL_OUTPUT_TOKEN_LIMIT_BY_ID = MODEL_OUTPUT_TOKEN_LIMITS;
1885
+ /**
1886
+ * The most output tokens one response of `modelId` may carry, from the
1887
+ * catalog. `undefined` for an id the catalog does not list (a deployment
1888
+ * override, a dev model): the catalog knows nothing about it, so callers leave
1889
+ * the allowance to the provider rather than guess one. Callers must never
1890
+ * index `MODEL_OUTPUT_TOKEN_LIMITS` directly with a runtime string.
1891
+ */
1892
+ const getOutputTokenLimit = (modelId) => MODEL_OUTPUT_TOKEN_LIMIT_BY_ID[normalizeModelCatalogId(modelId)];
1893
+ /**
1894
+ * Whether a model accepts tool use on a streaming request.
1895
+ *
1896
+ * Every tool-carrying request stella sends is a stream, and structured
1897
+ * output is a forced tool call, so `"unsupported"` withdraws both: the
1898
+ * provider answers such a request with a fatal stream error rather than a
1899
+ * degraded result. models.dev does not publish this, so the map is
1900
+ * hand-declared here rather than in the generated `capabilities.gen.ts`,
1901
+ * and it is total over the offered catalog: a new model forces a decision.
1902
+ *
1903
+ * Consumers must go through `supportsStreamingToolUse`.
1904
+ */
1905
+ const MODEL_STREAMING_TOOL_USE = {
1906
+ "gemini-3.8-flash": "supported",
1907
+ "gemini-3.7-flash": "supported",
1908
+ "gemini-3.6-flash": "supported",
1909
+ "gemini-3.5-flash-lite": "supported",
1910
+ "gemini-3.1-pro-preview": "supported",
1911
+ "gemini-3.5-flash": "supported",
1912
+ "gemini-3.1-flash-lite": "supported",
1913
+ "openai/gpt-6.1-sol": "supported",
1914
+ "openai/gpt-6-astra": "supported",
1915
+ "openai/gpt-6-sol": "supported",
1916
+ "openai/gpt-6-luna": "supported",
1917
+ "openai/gpt-5.6-sol": "supported",
1918
+ "openai/gpt-5.6-terra": "supported",
1919
+ "openai/gpt-5.6-luna": "supported",
1920
+ "google/gemini-3.8-flash": "supported",
1921
+ "google/gemini-3.7-flash": "supported",
1922
+ "google/gemini-3.6-flash": "supported",
1923
+ "google/gemini-3.5-flash-lite": "supported",
1924
+ "google/gemini-3.1-pro-preview": "supported",
1925
+ "google/gemini-3.5-flash": "supported",
1926
+ "google/gemini-3.1-flash-lite": "supported",
1927
+ "anthropic/claude-sonnet-5.5": "supported",
1928
+ "anthropic/claude-sonnet-5": "supported",
1929
+ "anthropic/claude-opus-5": "supported",
1930
+ "anthropic/claude-opus-4.8": "supported",
1931
+ "anthropic/claude-sonnet-4.6": "supported",
1932
+ "openai/gpt-5.5": "supported",
1933
+ "openai/gpt-5.4-mini": "supported",
1934
+ "gpt-6.1-sol": "supported",
1935
+ "gpt-6-astra": "supported",
1936
+ "gpt-6-sol": "supported",
1937
+ "gpt-6-luna": "supported",
1938
+ "gpt-5.6": "supported",
1939
+ "gpt-5.6-terra": "supported",
1940
+ "gpt-5.6-luna": "supported",
1941
+ "gpt-5.5": "supported",
1942
+ "gpt-5.4": "supported",
1943
+ "gpt-5.4-mini": "supported",
1944
+ "gpt-5.4-nano": "supported",
1945
+ "gpt-5.2": "supported",
1946
+ "claude-sonnet-5-5": "supported",
1947
+ "claude-sonnet-5": "supported",
1948
+ "claude-fable-5-1": "supported",
1949
+ "claude-fable-5": "supported",
1950
+ "claude-opus-5-5": "supported",
1951
+ "claude-opus-5": "supported",
1952
+ "claude-opus-4-8": "supported",
1953
+ "claude-opus-4-7": "supported",
1954
+ "claude-sonnet-4-6": "supported",
1955
+ "claude-opus-4-6": "supported",
1956
+ "claude-haiku-4-5-20251001": "supported",
1957
+ "us.anthropic.claude-sonnet-4-5-20250929-v1:0": "supported",
1958
+ "us.anthropic.claude-haiku-4-5-20251001-v1:0": "supported",
1959
+ "us.amazon.nova-pro-v1:0": "supported",
1960
+ "us.amazon.nova-lite-v1:0": "supported",
1961
+ "us.amazon.nova-micro-v1:0": "supported",
1962
+ "openai.gpt-oss-120b-1:0": "supported",
1963
+ "openai.gpt-oss-20b-1:0": "supported",
1964
+ "mistral-large-latest": "supported",
1965
+ "mistral-medium-latest": "supported",
1966
+ "mistral-small-latest": "supported"
1967
+ };
1968
+ const MODEL_STREAMING_TOOL_USE_BY_ID = {
1969
+ "gemini-2.5-flash": "supported",
1970
+ "gemini-2.5-pro": "supported",
1971
+ "gpt-4o-mini": "supported",
1972
+ "gpt-4o": "supported",
1973
+ "us.deepseek.r1-v1:0": "unsupported",
1974
+ ...MODEL_STREAMING_TOOL_USE
1975
+ };
1976
+ /**
1977
+ * Whether stella may send tools (or a structured-output schema) to this
1978
+ * model on a streaming request. Unlisted ids resolve to `true`: custom
1979
+ * deployments and env overrides are overwhelmingly tool-capable, and
1980
+ * withdrawing tools from an uncatalogued model would silently strip the
1981
+ * agent loop rather than fail where it can be seen.
1982
+ */
1983
+ const supportsStreamingToolUse = (modelId) => MODEL_STREAMING_TOOL_USE_BY_ID[normalizeModelCatalogId(modelId)] !== "unsupported";
1984
+ const MODEL_REASONING_EFFORTS_BY_ID = MODEL_REASONING_EFFORTS;
1985
+ /**
1986
+ * Declared reasoning-effort values for a model ID, or `null` when the
1987
+ * model has no effort control or is not catalogued (custom deployments,
1988
+ * env overrides). Callers must never index `MODEL_REASONING_EFFORTS`
1989
+ * directly with a runtime string.
1990
+ */
1991
+ const getModelReasoningEfforts = (modelId) => MODEL_REASONING_EFFORTS_BY_ID[normalizeModelCatalogId(modelId)] ?? null;
1992
+ const MODEL_DEFAULT_REASONING_EFFORTS_BY_ID = MODEL_DEFAULT_REASONING_EFFORTS;
1993
+ /**
1994
+ * Concrete provider-default effort published for a model, or null when the
1995
+ * provider exposes no named default. Unknown ids also resolve to null.
1996
+ */
1997
+ const getModelDefaultReasoningEffort = (modelId) => MODEL_DEFAULT_REASONING_EFFORTS_BY_ID[normalizeModelCatalogId(modelId)] ?? null;
1998
+ const asResolvedReasoningEffort = (effort) => v.parse(resolvedReasoningEffortSchema, effort);
1999
+ /**
2000
+ * Clamp a requested reasoning effort into the target model's declared
2001
+ * capability.
2002
+ *
2003
+ * - Unknown model or no effort control → `null` (send no effort at
2004
+ * all; the provider default is the only universally safe choice).
2005
+ * - Requested value supported → returned unchanged.
2006
+ * - Otherwise → the supported value nearest on `REASONING_EFFORTS`,
2007
+ * preferring the weaker side on ties, so a "none" request against a
2008
+ * reasoning-mandatory model degrades to its weakest tier instead of
2009
+ * erroring, and requests above the model's ceiling clamp down to it.
2010
+ */
2011
+ const resolveReasoningEffort = ({ modelId, requested }) => {
2012
+ const efforts = getModelReasoningEfforts(modelId);
2013
+ const weakest = efforts?.at(0);
2014
+ if (efforts === null || weakest === void 0) return null;
2015
+ if (efforts.includes(requested)) return asResolvedReasoningEffort(requested);
2016
+ const requestedRank = REASONING_EFFORTS.indexOf(requested);
2017
+ let nearest = weakest;
2018
+ let nearestDistance = Number.POSITIVE_INFINITY;
2019
+ for (const effort of efforts) {
2020
+ const distance = Math.abs(REASONING_EFFORTS.indexOf(effort) - requestedRank);
2021
+ if (distance < nearestDistance || distance === nearestDistance && REASONING_EFFORTS.indexOf(effort) < REASONING_EFFORTS.indexOf(nearest)) {
2022
+ nearest = effort;
2023
+ nearestDistance = distance;
2024
+ }
2025
+ }
2026
+ return asResolvedReasoningEffort(nearest);
2027
+ };
2028
+ const MODEL_RATES_BY_ID = MODEL_RATES;
2029
+ const OPENROUTER_UNDERLYING_MODEL_ID_BY_ID = Object.fromEntries(BYOK_MODEL_OPTIONS.openrouter.map((modelId) => [modelId, modelId.slice(modelId.indexOf("/") + 1)]));
2030
+ const getModelRate = (modelId) => {
2031
+ const normalizedModelId = normalizeModelCatalogId(modelId);
2032
+ const exactRate = MODEL_RATES_BY_ID[normalizedModelId];
2033
+ if (exactRate !== void 0) return exactRate;
2034
+ const underlyingModelId = OPENROUTER_UNDERLYING_MODEL_ID_BY_ID[normalizedModelId];
2035
+ return underlyingModelId === void 0 ? void 0 : MODEL_RATES_BY_ID[normalizeModelCatalogId(underlyingModelId)];
2036
+ };
2037
+ /**
2038
+ * Documented input context-window sizes (in tokens) per model ID.
2039
+ *
2040
+ * Keys are the canonical model IDs stella resolves for the provider
2041
+ * adapters (the same forms used in `MODEL_RATES`, `BYOK_MODEL_OPTIONS`,
2042
+ * and `DEFAULT_MODELS`, including OpenRouter provider-prefixed slugs and
2043
+ * AWS Bedrock IDs). Values are the providers' publicly documented input
2044
+ * windows; where a provider offers a larger beta window we intentionally
2045
+ * take the conservative default (e.g. Claude's standard 200K rather than
2046
+ * the 1M beta) so context budgeting never overpromises.
2047
+ *
2048
+ * Consumers must go through `getContextWindowTokens`, which falls back to
2049
+ * `DEFAULT_CONTEXT_WINDOW_TOKENS` for any unlisted ID. Unlike
2050
+ * `MODEL_RATES`, the nightly `model-catalog-upstream` check does not
2051
+ * validate this map, so an unknown model degrades to the conservative
2052
+ * default rather than failing CI.
2053
+ */
2054
+ const CONTEXT_WINDOW_TOKENS = {
2055
+ "gemini-2.5-flash": 1048576,
2056
+ "gemini-2.5-pro": 1048576,
2057
+ "gemini-3.1-flash-lite": 1048576,
2058
+ "gemini-3.5-flash": 1048576,
2059
+ "gemini-3.5-flash-lite": 1048576,
2060
+ "gemini-3.6-flash": 1048576,
2061
+ "gemini-3.7-flash": 1048576,
2062
+ "gemini-3.8-flash": 1048576,
2063
+ "gemini-3.1-pro-preview": 1048576,
2064
+ "gpt-4o-mini": 128e3,
2065
+ "gpt-4o": 128e3,
2066
+ "gpt-5.2": 4e5,
2067
+ "gpt-5.4-nano": 4e5,
2068
+ "gpt-5.4-mini": 4e5,
2069
+ "gpt-5.4": 4e5,
2070
+ "gpt-5.5": 4e5,
2071
+ "gpt-6.1-sol": 922e3,
2072
+ "gpt-6-astra": 922e3,
2073
+ "gpt-6-sol": 922e3,
2074
+ "gpt-6-luna": 922e3,
2075
+ "gpt-5.6": 922e3,
2076
+ "gpt-5.6-luna": 922e3,
2077
+ "gpt-5.6-terra": 922e3,
2078
+ "claude-haiku-4-5-20251001": 2e5,
2079
+ "claude-sonnet-4-6": 2e5,
2080
+ "claude-sonnet-5": 1e6,
2081
+ "claude-sonnet-5-5": 1e6,
2082
+ "claude-opus-4-6": 2e5,
2083
+ "claude-opus-4-7": 2e5,
2084
+ "claude-opus-4-8": 2e5,
2085
+ "claude-opus-5": 1e6,
2086
+ "claude-opus-5-5": 1e6,
2087
+ "claude-fable-5": 2e5,
2088
+ "claude-fable-5-1": 1e6,
2089
+ "mistral-small-latest": 128e3,
2090
+ "mistral-large-latest": 128e3,
2091
+ "mistral-medium-latest": 128e3,
2092
+ "mistral-medium-3-5": 128e3,
2093
+ "magistral-small-latest": 128e3,
2094
+ "google/gemini-3.8-flash": 1048576,
2095
+ "google/gemini-3.7-flash": 1048576,
2096
+ "google/gemini-3.6-flash": 1048576,
2097
+ "google/gemini-3.5-flash-lite": 1048576,
2098
+ "google/gemini-3.1-pro-preview": 1048576,
2099
+ "google/gemini-3.5-flash": 1048576,
2100
+ "google/gemini-3.1-flash-lite": 1048576,
2101
+ "anthropic/claude-sonnet-5.5": 1e6,
2102
+ "anthropic/claude-sonnet-5": 1e6,
2103
+ "anthropic/claude-opus-5": 1e6,
2104
+ "anthropic/claude-opus-4.8": 2e5,
2105
+ "anthropic/claude-sonnet-4.6": 2e5,
2106
+ "openai/gpt-6.1-sol": 922e3,
2107
+ "openai/gpt-6-astra": 922e3,
2108
+ "openai/gpt-6-sol": 922e3,
2109
+ "openai/gpt-6-luna": 922e3,
2110
+ "openai/gpt-5.6-sol": 922e3,
2111
+ "openai/gpt-5.6-luna": 922e3,
2112
+ "openai/gpt-5.6-terra": 922e3,
2113
+ "openai/gpt-5.5": 4e5,
2114
+ "openai/gpt-5.4-mini": 4e5,
2115
+ "us.anthropic.claude-sonnet-4-5-20250929-v1:0": 2e5,
2116
+ "us.anthropic.claude-haiku-4-5-20251001-v1:0": 2e5,
2117
+ "us.amazon.nova-pro-v1:0": 3e5,
2118
+ "us.amazon.nova-lite-v1:0": 3e5,
2119
+ "us.amazon.nova-micro-v1:0": 128e3,
2120
+ "openai.gpt-oss-120b-1:0": 128e3,
2121
+ "openai.gpt-oss-20b-1:0": 128e3,
2122
+ "us.deepseek.r1-v1:0": 128e3
2123
+ };
2124
+ const CONTEXT_WINDOW_TOKENS_BY_ID = CONTEXT_WINDOW_TOKENS;
2125
+ /**
2126
+ * Conservative window assumed for any model ID absent from
2127
+ * `CONTEXT_WINDOW_TOKENS` (custom deployments, OpenAI-compatible
2128
+ * endpoints, brand-new IDs not yet catalogued).
2129
+ */
2130
+ const DEFAULT_CONTEXT_WINDOW_TOKENS = 128e3;
2131
+ /**
2132
+ * Documented input context window for a model ID, or the conservative
2133
+ * default for unlisted IDs. Callers must never index
2134
+ * `CONTEXT_WINDOW_TOKENS` directly.
2135
+ */
2136
+ const getContextWindowTokens = (modelId) => CONTEXT_WINDOW_TOKENS_BY_ID[normalizeModelCatalogId(modelId)] ?? 128e3;
2137
+ //#endregion
2138
+ export { isBYOKProviderRoleSupported as A, MODELS_DEV_RATE_PROVIDER_BY_CATALOG_PROVIDER as B, getModelDefaultReasoningEffort as C, getModelReasoningEfforts as D, getModelRate as E, shouldEmitTemperature as F, resolveModelRate as G, RETAINED_MODELS_DEV_RATE_ENTRIES as H, supportsStreamingToolUse as I, MODEL_IMAGE_INPUT_CAPABILITIES as J, MODEL_RATES as K, DOCUMENT_INPUT_OVERRIDES as L, normalizeModelCatalogId as M, resolveReasoningEffort as N, getOutputTokenLimit as O, resolveWorkingBYOKModelForRole as P, CAPABILITY_OVERRIDES as R, getContextWindowTokens as S, getModelImageInputCapability as T, MODEL_RATE_UNITS_PER_USD as U, MODELS_DEV_RATE_SOURCE_ALIASES as V, getStandardModelRate as W, MODEL_REASONING_EFFORTS as X, MODEL_OUTPUT_TOKEN_LIMITS as Y, MODEL_TEMPERATURE_POLICIES as Z, MODEL_STREAMING_TOOL_USE as _, BYOK_MODEL_OPTIONS as a, TANSTACK_AI_PROVIDERS as b, DEFAULT_CONTEXT_WINDOW_TOKENS as c, FIRST_PARTY_MODEL_PROVIDERS as d, IMAGE_INPUT_CAPABILITIES as f, MODEL_ROLES as g, MODEL_DISPLAY_METADATA as h, BYOK_DOCUMENT_INPUT_MODEL_OPTIONS as i, isChatPdfAttachmentModelSupported as j, isBYOKModelRoleSupported as k, DEFAULT_MODELS as l, MODEL_CATALOG_PROVIDER_KIND as m, ANTHROPIC_ADAPTIVE_THINKING_MODELS as n, CHAT_PDF_ATTACHMENT_MODEL_OPTIONS as o, MODEL_CATALOG_ID_ALIASES as p, MODEL_DEFAULT_REASONING_EFFORTS as q, BYOK_DEFAULT_MODELS as r, CONTEXT_WINDOW_TOKENS as s, AI_PROVIDERS as t, FALLBACK_CHAT_MODEL_BY_PROVIDER as u, REASONING_EFFORTS as v, getModelDisplayMetadata as w, TEMPERATURE_POLICIES as x, STREAMING_TOOL_USE_SUPPORTS as y, IMAGE_INPUT_OVERRIDES as z };