@shop-os/modelkit 0.1.5 → 0.1.7

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.
package/dist/index.js CHANGED
@@ -83,6 +83,7 @@ var Catalog = class _Catalog {
83
83
 
84
84
  // src/catalog/image.ts
85
85
  var IMAGE_USAGE = { unit: "images", modality: "IMAGE" };
86
+ var IMAGE_GENERATE_EDIT = { imageGenerate: true, imageEdit: true };
86
87
  var GEMINI_RATIOS = [
87
88
  "1:1",
88
89
  "2:3",
@@ -96,7 +97,7 @@ var GEMINI_RATIOS = [
96
97
  "5:4",
97
98
  "adaptive"
98
99
  ];
99
- function geminiImage(id, label, aliases, sizes) {
100
+ function geminiImage(id, label, aliases, sizes, display) {
100
101
  const model = {
101
102
  id,
102
103
  aliases,
@@ -105,25 +106,47 @@ function geminiImage(id, label, aliases, sizes) {
105
106
  adapter: "google-image",
106
107
  providerModelId: id,
107
108
  lifecycle: "active",
108
- constraints: { aspectRatios: [...GEMINI_RATIOS], sizes, supports: ["safetySettings"] },
109
+ constraints: {
110
+ aspectRatios: [...GEMINI_RATIOS],
111
+ sizes,
112
+ supports: ["safetySettings", "temperature"]
113
+ },
109
114
  defaults: { size: "1K", aspectRatio: "1:1" },
110
115
  usage: { ...IMAGE_USAGE },
111
- pricingKind: "image_size"
116
+ pricingKind: "image_size",
117
+ display,
118
+ capabilities: { ...IMAGE_GENERATE_EDIT }
112
119
  };
113
120
  return model;
114
121
  }
115
122
  var IMAGE_MODELS = [
116
- geminiImage("gemini-3-pro-image-preview", "Nano Banana Pro", ["nb-pro"], ["1K", "2K", "4K"]),
123
+ geminiImage("gemini-3-pro-image-preview", "Nano Banana Pro", ["nb-pro"], ["1K", "2K", "4K"], {
124
+ provider: "google",
125
+ description: "Create stunning, production-quality visuals with exceptional realism, detail, and typography. Best for campaigns, e-commerce, product photography, and hero creatives.",
126
+ logo: "https://d31vvfpk2ppdyq.cloudfront.net/assets/image/192485d2-a76a-4657-9c06-930b2c70badd_compressed.png"
127
+ }),
117
128
  geminiImage(
118
129
  "gemini-3.1-flash-image-preview",
119
130
  "Nano Banana Flash",
120
131
  ["nb-2"],
121
132
  // Flash 3.1 is the only Gemini image model with the 512 tier.
122
- ["512", "1K", "2K", "4K"]
133
+ ["512", "1K", "2K", "4K"],
134
+ {
135
+ provider: "google",
136
+ description: "Generate high-quality images at speed. Perfect for brainstorming, moodboards, concept testing, creative exploration, and large batch workflows.",
137
+ logo: "https://d31vvfpk2ppdyq.cloudfront.net/assets/image/192485d2-a76a-4657-9c06-930b2c70badd_compressed.png"
138
+ }
123
139
  ),
124
- geminiImage("gemini-3.1-flash-lite-image", "Nano Banana Lite", ["nb-lite"], ["1K"]),
140
+ geminiImage("gemini-3.1-flash-lite-image", "Nano Banana Lite", ["nb-lite"], ["1K"], {
141
+ provider: "google",
142
+ description: "The fastest and most affordable image model. Ideal for rapid drafts, bulk exploration, and iterative concept testing at 1K resolution.",
143
+ logo: "https://d31vvfpk2ppdyq.cloudfront.net/assets/image/192485d2-a76a-4657-9c06-930b2c70badd_compressed.png"
144
+ }),
125
145
  // trigger-workflows validates 1K only; pricing seeds 2K/4K rows so allow all three.
126
- geminiImage("gemini-2.5-flash-image", "Gemini 2.5 Flash Image", [], ["1K", "2K", "4K"]),
146
+ geminiImage("gemini-2.5-flash-image", "Gemini 2.5 Flash Image", [], ["1K", "2K", "4K"], {
147
+ provider: "google",
148
+ description: "Nano Banana (legacy Gemini 2.5 Flash Image) native image generation and editing model for high-volume, low-latency creative workflows."
149
+ }),
127
150
  {
128
151
  // Cowork "auto" lets OpenAI pick the size; the canonical equivalent is adaptive.
129
152
  id: "gpt-image-2",
@@ -141,7 +164,13 @@ var IMAGE_MODELS = [
141
164
  },
142
165
  defaults: { size: "1K", aspectRatio: "adaptive", quality: "auto" },
143
166
  usage: { ...IMAGE_USAGE },
144
- pricingKind: "image_size"
167
+ pricingKind: "image_size",
168
+ display: {
169
+ provider: "openai",
170
+ description: "The most capable model for image editing and complex creative instructions. Excels at text rendering, visual consistency, design generation, and iterative refinement.",
171
+ logo: "https://d31vvfpk2ppdyq.cloudfront.net/assets/image/1204b494-8962-4f7f-af5e-699a8bdc98e3_compressed.png"
172
+ },
173
+ capabilities: { ...IMAGE_GENERATE_EDIT }
145
174
  },
146
175
  {
147
176
  // Same Images API surface as gpt-image-2 plus the "xhigh" and "max" quality tiers.
@@ -160,7 +189,12 @@ var IMAGE_MODELS = [
160
189
  },
161
190
  defaults: { size: "1K", aspectRatio: "adaptive", quality: "auto" },
162
191
  usage: { ...IMAGE_USAGE },
163
- pricingKind: "image_size"
192
+ pricingKind: "image_size",
193
+ display: {
194
+ provider: "openai",
195
+ description: "OpenAI's most capable GPT Image 2.5 tier, built for editing precision; takes text and image input and outputs images in low, medium, high, xhigh, or max quality."
196
+ },
197
+ capabilities: { ...IMAGE_GENERATE_EDIT }
164
198
  },
165
199
  {
166
200
  // Same Images API surface as gpt-image-2 plus the "xhigh" and "max" quality tiers.
@@ -179,7 +213,12 @@ var IMAGE_MODELS = [
179
213
  },
180
214
  defaults: { size: "1K", aspectRatio: "adaptive", quality: "auto" },
181
215
  usage: { ...IMAGE_USAGE },
182
- pricingKind: "image_size"
216
+ pricingKind: "image_size",
217
+ display: {
218
+ provider: "openai",
219
+ description: "OpenAI's fastest GPT Image 2.5 tier for high-quality everyday image generation and editing; takes text and image input and outputs images."
220
+ },
221
+ capabilities: { ...IMAGE_GENERATE_EDIT }
183
222
  },
184
223
  {
185
224
  // Cowork allows 7 ratios; trigger-workflows allows 9 (adds 3:2, 9:21, drops 4:5/5:4).
@@ -211,7 +250,13 @@ var IMAGE_MODELS = [
211
250
  },
212
251
  defaults: { size: "1K", aspectRatio: "1:1", outputFormat: "png", count: 1 },
213
252
  usage: { ...IMAGE_USAGE },
214
- pricingKind: "image_size"
253
+ pricingKind: "image_size",
254
+ display: {
255
+ provider: "meta",
256
+ description: "Meta's image model with built-in world knowledge and strong prompt adherence. Great for concept-rich scenes, realistic compositions, and reference-guided edits.",
257
+ logo: "https://d31vvfpk2ppdyq.cloudfront.net/assets/characters/1787909065794-k9a7zz.png"
258
+ },
259
+ capabilities: { ...IMAGE_GENERATE_EDIT }
215
260
  },
216
261
  {
217
262
  id: "seedream-5-0-pro",
@@ -231,7 +276,12 @@ var IMAGE_MODELS = [
231
276
  },
232
277
  defaults: { size: "1K", aspectRatio: "1:1" },
233
278
  usage: { ...IMAGE_USAGE },
234
- pricingKind: "image_size"
279
+ pricingKind: "image_size",
280
+ display: {
281
+ provider: "bytedance",
282
+ description: "ByteDance Seedream 5.0 Pro text-to-image and multi-reference image-to-image (up to 10 inputs) with region editing; outputs 1K, 1.5K, or 2K."
283
+ },
284
+ capabilities: { ...IMAGE_GENERATE_EDIT }
235
285
  },
236
286
  {
237
287
  id: "seedream-4.5",
@@ -244,14 +294,31 @@ var IMAGE_MODELS = [
244
294
  constraints: {
245
295
  // 97:30 has no Ark size spelling; the A+ flow pads inputs to 97:30 and
246
296
  // omits the ratio instead. Advertise only sizes the adapter can send.
247
- aspectRatios: ["1:1", "2:3", "3:2", "4:3", "3:4", "4:5", "5:4", "9:16", "16:9", "21:9"],
248
- sizes: ["1K", "2K"],
297
+ aspectRatios: [
298
+ "1:1",
299
+ "2:3",
300
+ "3:2",
301
+ "4:3",
302
+ "3:4",
303
+ "4:5",
304
+ "5:4",
305
+ "9:16",
306
+ "16:9",
307
+ "21:9",
308
+ "9:21"
309
+ ],
310
+ sizes: ["1K", "2K", "4K"],
249
311
  references: { images: 10 },
250
312
  supports: ["seed"]
251
313
  },
252
314
  defaults: { size: "1K", aspectRatio: "1:1" },
253
315
  usage: { ...IMAGE_USAGE },
254
- pricingKind: "image_size"
316
+ pricingKind: "image_size",
317
+ display: {
318
+ provider: "bytedance",
319
+ description: "ByteDance Seedream 4.5 text-to-image and image-to-image with up to 14 reference images and batch image-set generation; outputs 2K or 4K."
320
+ },
321
+ capabilities: { ...IMAGE_GENERATE_EDIT }
255
322
  },
256
323
  {
257
324
  id: "flux-2-pro",
@@ -269,7 +336,12 @@ var IMAGE_MODELS = [
269
336
  },
270
337
  defaults: { size: "1K", aspectRatio: "1:1" },
271
338
  usage: { ...IMAGE_USAGE },
272
- pricingKind: "image_size"
339
+ pricingKind: "image_size",
340
+ display: {
341
+ provider: "black-forest-labs",
342
+ description: "Black Forest Labs FLUX.2 [pro] text-to-image and multi-reference editing model served via Prodia; takes a prompt and up to 8 input images and outputs up to 4096x4096."
343
+ },
344
+ capabilities: { ...IMAGE_GENERATE_EDIT }
273
345
  },
274
346
  {
275
347
  // Prodia's gemini-3-pro jobs take aspect_ratio + resolution (1K/2K/4K), not
@@ -290,7 +362,12 @@ var IMAGE_MODELS = [
290
362
  },
291
363
  defaults: { size: "1K", aspectRatio: "1:1" },
292
364
  usage: { ...IMAGE_USAGE },
293
- pricingKind: "image_size"
365
+ pricingKind: "image_size",
366
+ display: {
367
+ provider: "google",
368
+ description: "Google Gemini 3 Pro image generation and editing served via Prodia; takes a prompt plus up to 3 reference images and outputs 1K, 2K, or 4K."
369
+ },
370
+ capabilities: { ...IMAGE_GENERATE_EDIT }
294
371
  },
295
372
  {
296
373
  // Nano Banana (img2img.v2) is flat-rate with aspect_ratio only; 3:2, 4:5,
@@ -310,7 +387,12 @@ var IMAGE_MODELS = [
310
387
  },
311
388
  defaults: { size: "1K", aspectRatio: "1:1" },
312
389
  usage: { ...IMAGE_USAGE },
313
- pricingKind: "image_size"
390
+ pricingKind: "image_size",
391
+ display: {
392
+ provider: "google",
393
+ description: "Google Nano Banana image generation and editing served via Prodia; takes a prompt plus 1-3 input images and matches the first input's aspect ratio in roughly 8 seconds."
394
+ },
395
+ capabilities: { ...IMAGE_GENERATE_EDIT }
314
396
  },
315
397
  {
316
398
  // WAN 2.7 takes `images` (up to 9) and a `size` enum whose exact values
@@ -331,7 +413,12 @@ var IMAGE_MODELS = [
331
413
  },
332
414
  defaults: { size: "1K", aspectRatio: "1:1" },
333
415
  usage: { unit: "compute_seconds", modality: "IMAGE" },
334
- pricingKind: "image_size"
416
+ pricingKind: "image_size",
417
+ display: {
418
+ provider: "alibaba",
419
+ description: "Alibaba Wan 2.7 Image Pro text-to-image and image-editing model; takes a prompt plus up to 9 reference images and generates up to 4K (4096x4096)."
420
+ },
421
+ capabilities: { ...IMAGE_GENERATE_EDIT }
335
422
  },
336
423
  {
337
424
  // Qwen Image 2 Pro has aspect_ratio but no size input; resolution follows
@@ -353,7 +440,12 @@ var IMAGE_MODELS = [
353
440
  },
354
441
  defaults: { aspectRatio: "1:1" },
355
442
  usage: { unit: "compute_seconds", modality: "IMAGE" },
356
- pricingKind: "image_size"
443
+ pricingKind: "image_size",
444
+ display: {
445
+ provider: "alibaba",
446
+ description: "Alibaba Qwen Image 2 Pro unified text-to-image and editing model; takes a prompt plus an optional single reference image and generates native 2K output."
447
+ },
448
+ capabilities: { ...IMAGE_GENERATE_EDIT }
357
449
  },
358
450
  {
359
451
  // Qwen Image Edit Plus takes aspect_ratio (or match_input_image) and
@@ -375,7 +467,12 @@ var IMAGE_MODELS = [
375
467
  },
376
468
  defaults: { outputFormat: "png" },
377
469
  usage: { unit: "compute_seconds", modality: "IMAGE" },
378
- pricingKind: "image_size"
470
+ pricingKind: "image_size",
471
+ display: {
472
+ provider: "alibaba",
473
+ description: "Alibaba Qwen Image Edit Plus with Lightning and a skin-retouching LoRA; takes one or more reference images plus an edit prompt and outputs webp, jpg, or png."
474
+ },
475
+ capabilities: { ...IMAGE_GENERATE_EDIT }
379
476
  }
380
477
  ];
381
478
 
@@ -396,7 +493,11 @@ var OTHER_MODELS = [
396
493
  },
397
494
  defaults: {},
398
495
  usage: { unit: "images", modality: "AUDIO" },
399
- pricingKind: "flat"
496
+ pricingKind: "flat",
497
+ display: {
498
+ provider: "google",
499
+ description: "Google Lyria 3 Clip short music clips, loops, and previews; takes text and up to 10 image prompts and returns a fixed 30-second MP3 plus lyrics."
500
+ }
400
501
  },
401
502
  {
402
503
  id: "lyria-3-pro-preview",
@@ -412,7 +513,11 @@ var OTHER_MODELS = [
412
513
  },
413
514
  defaults: {},
414
515
  usage: { unit: "images", modality: "AUDIO" },
415
- pricingKind: "flat"
516
+ pricingKind: "flat",
517
+ display: {
518
+ provider: "google",
519
+ description: "Google Lyria 3 Pro flagship music model for full-length songs with verses, choruses, and bridges; takes text and up to 10 image prompts and returns MP3 plus lyrics."
520
+ }
416
521
  },
417
522
  {
418
523
  id: "google/lyria-2",
@@ -428,7 +533,11 @@ var OTHER_MODELS = [
428
533
  },
429
534
  defaults: {},
430
535
  usage: { unit: "compute_seconds", modality: "AUDIO" },
431
- pricingKind: "flat"
536
+ pricingKind: "flat",
537
+ display: {
538
+ provider: "google",
539
+ description: "Lyria 2 music generation on Replicate; produces 30 seconds of 48 kHz stereo audio from a text prompt."
540
+ }
432
541
  },
433
542
  {
434
543
  id: "mmaudio",
@@ -445,7 +554,11 @@ var OTHER_MODELS = [
445
554
  },
446
555
  defaults: { durationSeconds: 8 },
447
556
  usage: { unit: "compute_seconds", modality: "AUDIO" },
448
- pricingKind: "flat"
557
+ pricingKind: "flat",
558
+ display: {
559
+ provider: "mmaudio",
560
+ description: "MMAudio V2 video-to-audio model on Replicate; takes a source video and prompt and synthesizes synchronized environmental, action, and speech-like audio."
561
+ }
449
562
  },
450
563
  {
451
564
  id: "elevenlabs-music",
@@ -462,7 +575,11 @@ var OTHER_MODELS = [
462
575
  defaults: { durationSeconds: 15, instrumental: true },
463
576
  // Replicate's run() does not report predict_time; billed by generated length.
464
577
  usage: { unit: "seconds", modality: "AUDIO" },
465
- pricingKind: "flat"
578
+ pricingKind: "flat",
579
+ display: {
580
+ provider: "elevenlabs",
581
+ description: "ElevenLabs Music text-to-music on Replicate; generates full songs, instrumental or with vocals, up to 5 minutes long at up to 44.1 kHz."
582
+ }
466
583
  },
467
584
  {
468
585
  id: "topaz",
@@ -475,11 +592,16 @@ var OTHER_MODELS = [
475
592
  constraints: {
476
593
  references: { images: 1 },
477
594
  requires: ["references"],
478
- supports: ["scaleFactor", "outputFormat"]
595
+ supports: ["scaleFactor", "outputFormat", "upscale"]
479
596
  },
480
597
  defaults: { scaleFactor: 2, outputFormat: "png" },
481
598
  usage: { unit: "compute_seconds", modality: "IMAGE" },
482
- pricingKind: "flat"
599
+ pricingKind: "flat",
600
+ display: {
601
+ provider: "topaz",
602
+ description: "Topaz Labs AI image upscaling on Replicate; takes one input image and upscales up to 6x with selectable enhancement models and optional face enhancement."
603
+ },
604
+ capabilities: { imageEdit: true }
483
605
  },
484
606
  {
485
607
  id: "bg-remove",
@@ -496,7 +618,12 @@ var OTHER_MODELS = [
496
618
  },
497
619
  defaults: {},
498
620
  usage: { unit: "images", modality: "IMAGE" },
499
- pricingKind: "flat"
621
+ pricingKind: "flat",
622
+ display: {
623
+ provider: "prodia",
624
+ description: "Prodia background removal; takes one input image and returns a PNG cut-out of the subject plus an optional black-and-white mask."
625
+ },
626
+ capabilities: { imageEdit: true }
500
627
  }
501
628
  ];
502
629
 
@@ -519,22 +646,112 @@ function text(id, label, providerModelId, extra) {
519
646
  };
520
647
  }
521
648
  var TEXT_MODELS = [
522
- text("gpt-4o", "GPT-4o", "gpt-4o"),
523
- text("gpt-4o-mini", "GPT-4o mini", "gpt-4o-mini"),
524
- text("gpt-5.1", "GPT-5.1", "gpt-5.1"),
525
- text("gpt-5.2", "GPT-5.2", "gpt-5.2"),
526
- text("gpt-5.4", "GPT-5.4", "gpt-5.4"),
649
+ text("gpt-4o", "GPT-4o", "gpt-4o", {
650
+ display: {
651
+ provider: "openai",
652
+ description: "OpenAI GPT-4o flagship multimodal model; text and image input, 128K-token context."
653
+ },
654
+ capabilities: { vision: true, tools: true, structuredOutput: true, contextWindow: 128e3 }
655
+ }),
656
+ text("gpt-4o-mini", "GPT-4o mini", "gpt-4o-mini", {
657
+ display: {
658
+ provider: "openai",
659
+ description: "OpenAI GPT-4o mini fast, low-cost multimodal model; text and image input, 128K-token context."
660
+ },
661
+ capabilities: { vision: true, tools: true, structuredOutput: true, contextWindow: 128e3 }
662
+ }),
663
+ text("gpt-5.1", "GPT-5.1", "gpt-5.1", {
664
+ display: {
665
+ provider: "openai",
666
+ description: "OpenAI GPT-5.1 reasoning model for coding and agentic tasks; text and image input, 400K-token context."
667
+ },
668
+ capabilities: { vision: true, tools: true, structuredOutput: true, contextWindow: 4e5 }
669
+ }),
670
+ text("gpt-5.2", "GPT-5.2", "gpt-5.2", {
671
+ display: {
672
+ provider: "openai",
673
+ description: "OpenAI GPT-5.2 reasoning model for complex professional work; text and image input, 400K-token context."
674
+ },
675
+ capabilities: { vision: true, tools: true, structuredOutput: true, contextWindow: 4e5 }
676
+ }),
677
+ // 1.05M-token context: https://platform.openai.com/docs/models/gpt-5.4
678
+ text("gpt-5.4", "GPT-5.4", "gpt-5.4", {
679
+ display: {
680
+ provider: "openai",
681
+ description: "OpenAI GPT-5.4 flagship reasoning model; text and image input, 1.05M-token context."
682
+ },
683
+ capabilities: {
684
+ vision: true,
685
+ tools: true,
686
+ structuredOutput: true,
687
+ contextWindow: 105e4
688
+ }
689
+ }),
527
690
  // OpenAI never shipped gpt-5.1-mini or gpt-5.2-mini; callers that still send them get 5.4 mini.
528
691
  text("gpt-5.4-mini", "GPT-5.4 mini", "gpt-5.4-mini", {
529
- aliases: ["gpt-5.1-mini", "gpt-5.2-mini"]
692
+ aliases: ["gpt-5.1-mini", "gpt-5.2-mini"],
693
+ display: {
694
+ provider: "openai",
695
+ description: "OpenAI GPT-5.4 mini efficient reasoning model for high-volume workloads; text and image input, 400K-token context."
696
+ },
697
+ capabilities: { vision: true, tools: true, structuredOutput: true, contextWindow: 4e5 }
698
+ }),
699
+ text("gpt-5.5", "GPT-5.5", "gpt-5.5", {
700
+ display: {
701
+ provider: "openai",
702
+ description: "OpenAI GPT-5.5 flagship reasoning model for the most complex professional and coding work; text and image input, 1.05M-token context."
703
+ },
704
+ capabilities: {
705
+ vision: true,
706
+ tools: true,
707
+ structuredOutput: true,
708
+ contextWindow: 105e4
709
+ }
710
+ }),
711
+ text("gpt-5.6-luna", "GPT-5.6 Luna", "gpt-5.6-luna", {
712
+ display: {
713
+ provider: "openai",
714
+ description: "OpenAI GPT-5.6 Luna cost-optimized tier of the GPT-5.6 reasoning family; text and image input, 1.05M-token context."
715
+ },
716
+ capabilities: {
717
+ vision: true,
718
+ tools: true,
719
+ structuredOutput: true,
720
+ contextWindow: 105e4
721
+ }
722
+ }),
723
+ text("gpt-5.6-terra", "GPT-5.6 Terra", "gpt-5.6-terra", {
724
+ display: {
725
+ provider: "openai",
726
+ description: "OpenAI GPT-5.6 Terra balanced tier of the GPT-5.6 reasoning family; text and image input, 1.05M-token context."
727
+ },
728
+ capabilities: {
729
+ vision: true,
730
+ tools: true,
731
+ structuredOutput: true,
732
+ contextWindow: 105e4
733
+ }
734
+ }),
735
+ text("gpt-5.6-sol", "GPT-5.6 Sol", "gpt-5.6-sol", {
736
+ display: {
737
+ provider: "openai",
738
+ description: "OpenAI GPT-5.6 Sol flagship tier of the GPT-5.6 reasoning family; text and image input, 1.05M-token context."
739
+ },
740
+ capabilities: {
741
+ vision: true,
742
+ tools: true,
743
+ structuredOutput: true,
744
+ contextWindow: 105e4
745
+ }
530
746
  }),
531
- text("gpt-5.5", "GPT-5.5", "gpt-5.5"),
532
- text("gpt-5.6-luna", "GPT-5.6 Luna", "gpt-5.6-luna"),
533
- text("gpt-5.6-terra", "GPT-5.6 Terra", "gpt-5.6-terra"),
534
- text("gpt-5.6-sol", "GPT-5.6 Sol", "gpt-5.6-sol"),
535
747
  // Library key "claude-sonnet-4.5" maps to the SDK id "claude-sonnet-4-5".
536
748
  text("claude-sonnet-4-5", "Claude Sonnet 4.5", "claude-sonnet-4-5", {
537
- aliases: ["claude-sonnet-4.5", "claude-sonnet-4-5-20250929"]
749
+ aliases: ["claude-sonnet-4.5", "claude-sonnet-4-5-20250929"],
750
+ display: {
751
+ provider: "anthropic",
752
+ description: "Anthropic Claude Sonnet 4.5 (legacy) balanced model; text and image input, 200K-token context."
753
+ },
754
+ capabilities: { vision: true, tools: true, structuredOutput: true, contextWindow: 2e5 }
538
755
  }),
539
756
  // Anthropic retired the dated 4.0 ids; callers that still send them get 4.6.
540
757
  text("claude-sonnet-4-6", "Claude Sonnet 4.6", "claude-sonnet-4-6", {
@@ -543,42 +760,280 @@ var TEXT_MODELS = [
543
760
  "claude-sonnet-4",
544
761
  "claude-sonnet-4.0",
545
762
  "claude-sonnet-4-20250514"
546
- ]
763
+ ],
764
+ display: {
765
+ provider: "anthropic",
766
+ description: "Anthropic Claude Sonnet 4.6 (legacy) balanced model; text and image input, 1M-token context."
767
+ },
768
+ capabilities: {
769
+ vision: true,
770
+ tools: true,
771
+ structuredOutput: true,
772
+ contextWindow: 1e6
773
+ }
774
+ }),
775
+ text("claude-sonnet-5", "Claude Sonnet 5", "claude-sonnet-5", {
776
+ display: {
777
+ provider: "anthropic",
778
+ description: "Anthropic Claude Sonnet 5 current balanced model; text and image input, 1M-token context."
779
+ },
780
+ capabilities: {
781
+ vision: true,
782
+ tools: true,
783
+ structuredOutput: true,
784
+ contextWindow: 1e6
785
+ }
547
786
  }),
548
- text("claude-sonnet-5", "Claude Sonnet 5", "claude-sonnet-5"),
549
787
  text("claude-opus-4-8", "Claude Opus 4.8", "claude-opus-4-8", {
550
- aliases: ["claude-opus-4.8", "claude-opus-4", "claude-opus-4.0", "claude-opus-4-20250514"]
788
+ aliases: ["claude-opus-4.8", "claude-opus-4", "claude-opus-4.0", "claude-opus-4-20250514"],
789
+ display: {
790
+ provider: "anthropic",
791
+ description: "Anthropic Claude Opus 4.8 (legacy) frontier model; text and image input, 1M-token context."
792
+ },
793
+ capabilities: {
794
+ vision: true,
795
+ tools: true,
796
+ structuredOutput: true,
797
+ contextWindow: 1e6
798
+ }
551
799
  }),
552
800
  text("claude-haiku-4-5", "Claude Haiku 4.5", "claude-haiku-4-5", {
553
- aliases: ["claude-haiku-4.5", "claude-haiku-4-5-20251001", "claude-haiku-4"]
801
+ aliases: ["claude-haiku-4.5", "claude-haiku-4-5-20251001", "claude-haiku-4"],
802
+ display: {
803
+ provider: "anthropic",
804
+ description: "Anthropic Claude Haiku 4.5 fast model with near-frontier intelligence; text and image input, 200K-token context."
805
+ },
806
+ capabilities: { vision: true, tools: true, structuredOutput: true, contextWindow: 2e5 }
554
807
  }),
555
808
  text("claude-opus-5", "Claude Opus 5", "claude-opus-5", {
556
- aliases: ["claude-opus-5.0"]
809
+ aliases: ["claude-opus-5.0"],
810
+ display: {
811
+ provider: "anthropic",
812
+ description: "Anthropic Claude Opus 5 current frontier model for agentic coding and enterprise work; text and image input, 1M-token context."
813
+ },
814
+ capabilities: {
815
+ vision: true,
816
+ tools: true,
817
+ structuredOutput: true,
818
+ contextWindow: 1e6
819
+ }
820
+ }),
821
+ text("claude-fable-5", "Claude Fable 5", "claude-fable-5", {
822
+ display: {
823
+ provider: "anthropic",
824
+ description: "Anthropic Claude Fable 5 (legacy) model for demanding reasoning and long-horizon agentic work; text and image input, 1M-token context."
825
+ },
826
+ capabilities: {
827
+ vision: true,
828
+ tools: true,
829
+ structuredOutput: true,
830
+ contextWindow: 1e6
831
+ }
832
+ }),
833
+ text("gemini-2.5-flash", "Gemini 2.5 Flash", "gemini-2.5-flash", {
834
+ display: {
835
+ provider: "google",
836
+ description: "Google Gemini 2.5 Flash low-latency multimodal model for high-volume reasoning over text, images, video, and audio; 1M-token context."
837
+ },
838
+ capabilities: {
839
+ vision: true,
840
+ tools: true,
841
+ structuredOutput: true,
842
+ contextWindow: 1048576
843
+ }
844
+ }),
845
+ text("gemini-2.5-pro", "Gemini 2.5 Pro", "gemini-2.5-pro", {
846
+ display: {
847
+ provider: "google",
848
+ description: "Google Gemini 2.5 Pro thinking model for complex reasoning over code, math, and documents; text, image, video, audio, and PDF input, 1M-token context."
849
+ },
850
+ capabilities: {
851
+ vision: true,
852
+ tools: true,
853
+ structuredOutput: true,
854
+ contextWindow: 1048576
855
+ }
856
+ }),
857
+ text("gemini-3.1-pro-preview", "Gemini 3.1 Pro (preview)", "gemini-3.1-pro-preview", {
858
+ display: {
859
+ provider: "google",
860
+ description: "Google Gemini 3.1 Pro preview reasoning model for software engineering, tool use, and reliable agentic workflows; 1M-token context."
861
+ },
862
+ capabilities: {
863
+ vision: true,
864
+ tools: true,
865
+ structuredOutput: true,
866
+ contextWindow: 1048576
867
+ }
868
+ }),
869
+ text("gemini-3.5-flash", "Gemini 3.5 Flash", "gemini-3.5-flash", {
870
+ display: {
871
+ provider: "google",
872
+ description: "Google Gemini 3.5 Flash high-throughput model for frontier-level agentic tasks; text, image, video, audio, and PDF input, 1M-token context."
873
+ },
874
+ capabilities: {
875
+ vision: true,
876
+ tools: true,
877
+ structuredOutput: true,
878
+ contextWindow: 1048576
879
+ }
880
+ }),
881
+ text("gemini-3.6-flash", "Gemini 3.6 Flash", "gemini-3.6-flash", {
882
+ display: {
883
+ provider: "google",
884
+ description: "Google Gemini 3.6 Flash general-purpose multimodal agentic model balancing speed and capability; 1M-token context."
885
+ },
886
+ capabilities: {
887
+ vision: true,
888
+ tools: true,
889
+ structuredOutput: true,
890
+ contextWindow: 1048576
891
+ }
892
+ }),
893
+ text("gemini-3.7-flash", "Gemini 3.7 Flash", "gemini-3.7-flash", {
894
+ display: {
895
+ provider: "google",
896
+ description: "Google Gemini 3.7 Flash natively multimodal reasoning model for coding, agentic workflows, and multi-step execution; 1M-token context."
897
+ },
898
+ capabilities: {
899
+ vision: true,
900
+ tools: true,
901
+ structuredOutput: true,
902
+ contextWindow: 1048576
903
+ }
904
+ }),
905
+ text("gemini-3.8-flash", "Gemini 3.8 Flash", "gemini-3.8-flash", {
906
+ display: {
907
+ provider: "google",
908
+ description: "Google Gemini 3.8 Flash frontier model for long-horizon software engineering, autonomous agents, and enterprise workflows; 1M-token context."
909
+ },
910
+ capabilities: {
911
+ vision: true,
912
+ tools: true,
913
+ structuredOutput: true,
914
+ contextWindow: 1048576
915
+ }
557
916
  }),
558
- text("claude-fable-5", "Claude Fable 5", "claude-fable-5"),
559
- text("gemini-2.5-flash", "Gemini 2.5 Flash", "gemini-2.5-flash"),
560
- text("gemini-2.5-pro", "Gemini 2.5 Pro", "gemini-2.5-pro"),
561
- text("gemini-3.1-pro-preview", "Gemini 3.1 Pro (preview)", "gemini-3.1-pro-preview"),
562
- text("gemini-3.5-flash", "Gemini 3.5 Flash", "gemini-3.5-flash"),
563
- text("gemini-3.6-flash", "Gemini 3.6 Flash", "gemini-3.6-flash"),
564
- text("gemini-3.7-flash", "Gemini 3.7 Flash", "gemini-3.7-flash"),
565
- text("gemini-3.8-flash", "Gemini 3.8 Flash", "gemini-3.8-flash"),
566
917
  // Renamed to gemini-3.6-flash in vibe-shared model-migration.ts.
567
918
  text("gemini-3-flash-preview", "Gemini 3 Flash (preview)", "gemini-3-flash-preview", {
568
919
  lifecycle: "deprecated",
569
920
  replacedBy: "gemini-3.6-flash"
570
921
  }),
571
- text("gemini-3.1-flash-lite", "Gemini 3.1 Flash Lite", "gemini-3.1-flash-lite"),
572
- text("sonar", "Sonar", "sonar"),
573
- text("sonar-pro", "Sonar Pro", "sonar-pro"),
574
- text("sonar-reasoning-pro", "Sonar Reasoning Pro", "sonar-reasoning-pro"),
575
- text("deepseek-v4-flash", "DeepSeek V4 Flash", "deepseek-v4-flash"),
576
- text("deepseek-v4-pro", "DeepSeek V4 Pro", "deepseek-v4-pro"),
577
- text("grok-4.3", "Grok 4.3", "grok-4.3"),
578
- text("kimi-k2.5", "Kimi K2.5", "kimi-k2.5"),
579
- text("kimi-k3", "Kimi K3", "kimi-k3"),
922
+ text("gemini-3.1-flash-lite", "Gemini 3.1 Flash Lite", "gemini-3.1-flash-lite", {
923
+ display: {
924
+ provider: "google",
925
+ description: "Google Gemini 3.1 Flash-Lite low-latency multimodal model for high-volume agentic, translation, and data-extraction tasks; 1M-token context."
926
+ },
927
+ capabilities: {
928
+ vision: true,
929
+ tools: true,
930
+ structuredOutput: true,
931
+ contextWindow: 1048576
932
+ }
933
+ }),
934
+ text("sonar", "Sonar", "sonar", {
935
+ display: {
936
+ provider: "perplexity",
937
+ description: "Perplexity Sonar lightweight web-grounded search model returning cited answers; 128K-token context."
938
+ },
939
+ capabilities: {
940
+ vision: true,
941
+ tools: false,
942
+ structuredOutput: true,
943
+ contextWindow: 128e3
944
+ }
945
+ }),
946
+ text("sonar-pro", "Sonar Pro", "sonar-pro", {
947
+ display: {
948
+ provider: "perplexity",
949
+ description: "Perplexity Sonar Pro advanced web-grounded search model for complex multi-step queries; 200K-token context."
950
+ },
951
+ capabilities: {
952
+ vision: true,
953
+ tools: false,
954
+ structuredOutput: true,
955
+ contextWindow: 2e5
956
+ }
957
+ }),
958
+ text("sonar-reasoning-pro", "Sonar Reasoning Pro", "sonar-reasoning-pro", {
959
+ display: {
960
+ provider: "perplexity",
961
+ description: "Perplexity Sonar Reasoning Pro chain-of-thought web-grounded search model for complex problem-solving; 128K-token context."
962
+ },
963
+ capabilities: {
964
+ vision: true,
965
+ tools: false,
966
+ structuredOutput: true,
967
+ contextWindow: 128e3
968
+ }
969
+ }),
970
+ text("deepseek-v4-flash", "DeepSeek V4 Flash", "deepseek-v4-flash", {
971
+ display: {
972
+ provider: "deepseek",
973
+ description: "DeepSeek V4 Flash thinking/non-thinking model with vision, tool calls, and JSON output; 1M-token context and up to 384K output tokens."
974
+ },
975
+ capabilities: {
976
+ vision: true,
977
+ tools: true,
978
+ structuredOutput: true,
979
+ contextWindow: 1e6
980
+ }
981
+ }),
982
+ text("deepseek-v4-pro", "DeepSeek V4 Pro", "deepseek-v4-pro", {
983
+ display: {
984
+ provider: "deepseek",
985
+ description: "DeepSeek V4 Pro thinking/non-thinking text model with tool calls and JSON output (no vision); 1M-token context and up to 384K output tokens."
986
+ },
987
+ capabilities: {
988
+ vision: false,
989
+ tools: true,
990
+ structuredOutput: true,
991
+ contextWindow: 1e6
992
+ }
993
+ }),
994
+ // 1M-token context: https://docs.x.ai/docs/models/grok-4.3
995
+ text("grok-4.3", "Grok 4.3", "grok-4.3", {
996
+ display: {
997
+ provider: "xai",
998
+ description: "xAI Grok 4.3 reasoning model with tool calling and structured outputs; text and image input, 1M-token context."
999
+ },
1000
+ capabilities: {
1001
+ vision: true,
1002
+ tools: true,
1003
+ structuredOutput: true,
1004
+ contextWindow: 1e6
1005
+ }
1006
+ }),
1007
+ // Moonshot retired kimi-k2.5 on 2026-08-31 and no longer publishes its specs.
1008
+ text("kimi-k2.5", "Kimi K2.5", "kimi-k2.5", {
1009
+ display: {
1010
+ provider: "moonshot",
1011
+ description: "Moonshot Kimi K2.5; the provider retired the model on 2026-08-31 and calls now return a model-not-found error."
1012
+ },
1013
+ capabilities: {}
1014
+ }),
1015
+ text("kimi-k3", "Kimi K3", "kimi-k3", {
1016
+ display: {
1017
+ provider: "moonshot",
1018
+ description: "Moonshot Kimi K3 flagship model with native visual understanding and always-on thinking; text, image, and video input, 1M-token context."
1019
+ },
1020
+ capabilities: {
1021
+ vision: true,
1022
+ tools: true,
1023
+ structuredOutput: true,
1024
+ contextWindow: 1e6
1025
+ }
1026
+ }),
580
1027
  // Cowork calls the provider "MiniMax-M3"; the pricing key is lowercase.
581
- text("minimax-m3", "MiniMax M3", "MiniMax-M3", { aliases: ["MiniMax-M3"] })
1028
+ // 1M-token context: https://platform.minimax.io/docs/api-reference/text-openai-api
1029
+ text("minimax-m3", "MiniMax M3", "MiniMax-M3", {
1030
+ aliases: ["MiniMax-M3"],
1031
+ display: {
1032
+ provider: "minimax",
1033
+ description: "MiniMax M3 frontier multimodal model for agentic reasoning, tool use, and coding; text, image, and video input, 1M-token context."
1034
+ },
1035
+ capabilities: { vision: true, tools: true, contextWindow: 1e6 }
1036
+ })
582
1037
  ];
583
1038
 
584
1039
  // src/catalog/video.ts
@@ -590,6 +1045,8 @@ var GROK_RATIOS = ["1:1", "16:9", "9:16", "4:3", "3:4", "3:2", "2:3"];
590
1045
  var H3_RATIOS = ["21:9", "16:9", "4:3", "1:1", "3:4", "9:16"];
591
1046
  var H3_REFERENCE_RATIOS = ["adaptive", ...H3_RATIOS];
592
1047
  var NEGATIVE_PROMPT = "blur, distort, and low quality";
1048
+ var FRAMES_EXCLUDE_REFERENCES = { framesExcludeReferences: true };
1049
+ var GOOGLE_LOGO = "https://d31vvfpk2ppdyq.cloudfront.net/assets/image/192485d2-a76a-4657-9c06-930b2c70badd_compressed.png";
593
1050
  var VIDEO_MODELS = [
594
1051
  {
595
1052
  id: "veo-3.1",
@@ -613,7 +1070,13 @@ var VIDEO_MODELS = [
613
1070
  },
614
1071
  defaults: { durationSeconds: 8, resolution: "720p", aspectRatio: "16:9" },
615
1072
  usage: { ...VIDEO_USAGE },
616
- pricingKind: "video_per_second"
1073
+ pricingKind: "video_per_second",
1074
+ display: {
1075
+ provider: "google",
1076
+ description: "Google's highest-quality video generation model. Supports up to 4K resolution and 8-second clips with reference image conditioning.",
1077
+ logo: GOOGLE_LOGO
1078
+ },
1079
+ capabilities: { ...FRAMES_EXCLUDE_REFERENCES }
617
1080
  },
618
1081
  {
619
1082
  id: "veo-3.1-fast",
@@ -636,7 +1099,12 @@ var VIDEO_MODELS = [
636
1099
  },
637
1100
  defaults: { durationSeconds: 8, resolution: "720p", aspectRatio: "16:9" },
638
1101
  usage: { ...VIDEO_USAGE },
639
- pricingKind: "video_per_second"
1102
+ pricingKind: "video_per_second",
1103
+ display: {
1104
+ provider: "google",
1105
+ description: "Faster variant of Google Veo 3.1; text- and image-to-video with natively generated audio at 4 or 8 seconds."
1106
+ },
1107
+ capabilities: { ...FRAMES_EXCLUDE_REFERENCES }
640
1108
  },
641
1109
  {
642
1110
  id: "veo-3.1-lite",
@@ -659,7 +1127,12 @@ var VIDEO_MODELS = [
659
1127
  },
660
1128
  defaults: { durationSeconds: 8, resolution: "720p", aspectRatio: "16:9" },
661
1129
  usage: { ...VIDEO_USAGE },
662
- pricingKind: "video_per_second"
1130
+ pricingKind: "video_per_second",
1131
+ display: {
1132
+ provider: "google",
1133
+ description: "Efficiency-focused Google Veo 3.1 Lite for high-volume video generation and editing; text and image input, with no 4K output or video extension."
1134
+ },
1135
+ capabilities: { ...FRAMES_EXCLUDE_REFERENCES }
663
1136
  },
664
1137
  {
665
1138
  id: "gemini-omni-flash",
@@ -686,7 +1159,13 @@ var VIDEO_MODELS = [
686
1159
  },
687
1160
  defaults: {},
688
1161
  usage: { ...VIDEO_USAGE },
689
- pricingKind: "video_per_second"
1162
+ pricingKind: "video_per_second",
1163
+ display: {
1164
+ provider: "google",
1165
+ description: "Google's conversational video model with native audio, human support, and natural language editing. Aspect ratio only \u2014 resolution and duration are model-determined.",
1166
+ logo: GOOGLE_LOGO
1167
+ },
1168
+ capabilities: { acceptsSourceVideo: true }
690
1169
  },
691
1170
  {
692
1171
  id: "gemini-omni-1.1-flash",
@@ -705,7 +1184,12 @@ var VIDEO_MODELS = [
705
1184
  },
706
1185
  defaults: {},
707
1186
  usage: { ...VIDEO_USAGE },
708
- pricingKind: "video_per_second"
1187
+ pricingKind: "video_per_second",
1188
+ display: {
1189
+ provider: "google",
1190
+ description: "Google Gemini Omni Flash 1.1 conversational video generation and editing; text, image, and up to 10-second video input, 3-10 second output at up to 4K and 24 FPS."
1191
+ },
1192
+ capabilities: { acceptsSourceVideo: true }
709
1193
  },
710
1194
  {
711
1195
  // vibe-triggers and Space Builder drop the ratio when a frame is wired; Cowork always sends it.
@@ -722,7 +1206,7 @@ var VIDEO_MODELS = [
722
1206
  durations: [4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15],
723
1207
  references: { images: 9, videos: 3, audios: 3, audioOnly: false },
724
1208
  ratioIgnoredWith: ["firstFrame"],
725
- supports: ["generateAudio", "seed", "lastFrame"]
1209
+ supports: ["generateAudio", "seed", "lastFrame", "watermark"]
726
1210
  },
727
1211
  defaults: {
728
1212
  durationSeconds: 5,
@@ -731,7 +1215,13 @@ var VIDEO_MODELS = [
731
1215
  generateAudio: true
732
1216
  },
733
1217
  usage: { ...VIDEO_USAGE },
734
- pricingKind: "video_per_second"
1218
+ pricingKind: "video_per_second",
1219
+ display: {
1220
+ provider: "bytedance",
1221
+ description: "High-quality AI video generation with reference image conditioning. Supports 4-15 second clips with human preprocessing.",
1222
+ logo: "https://d31vvfpk2ppdyq.cloudfront.net/assets/image/3bba5d81-4e57-4e71-911b-cdfa05acca2c_compressed.png"
1223
+ },
1224
+ capabilities: { acceptsSourceVideo: true, ...FRAMES_EXCLUDE_REFERENCES }
735
1225
  },
736
1226
  {
737
1227
  id: "seedance-2.0-fast",
@@ -747,7 +1237,7 @@ var VIDEO_MODELS = [
747
1237
  durations: [4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15],
748
1238
  references: { images: 9, videos: 3, audios: 3, audioOnly: false },
749
1239
  ratioIgnoredWith: ["firstFrame"],
750
- supports: ["generateAudio", "seed", "lastFrame"]
1240
+ supports: ["generateAudio", "seed", "lastFrame", "watermark"]
751
1241
  },
752
1242
  defaults: {
753
1243
  durationSeconds: 5,
@@ -756,7 +1246,12 @@ var VIDEO_MODELS = [
756
1246
  generateAudio: true
757
1247
  },
758
1248
  usage: { ...VIDEO_USAGE },
759
- pricingKind: "video_per_second"
1249
+ pricingKind: "video_per_second",
1250
+ display: {
1251
+ provider: "bytedance",
1252
+ description: "Speed- and cost-optimized ByteDance Seedance 2.0 Fast; text plus image, video, and audio references to generate or edit 4-15 second clips."
1253
+ },
1254
+ capabilities: { acceptsSourceVideo: true, ...FRAMES_EXCLUDE_REFERENCES }
760
1255
  },
761
1256
  {
762
1257
  // generateAudio defaults to false here (trigger-workflows); Space Builder defaults it to true.
@@ -773,7 +1268,7 @@ var VIDEO_MODELS = [
773
1268
  durations: { min: 4, max: 30 },
774
1269
  references: { images: 30, videos: 10, audios: 10 },
775
1270
  ratioIgnoredWith: ["firstFrame"],
776
- supports: ["generateAudio", "seed", "lastFrame"]
1271
+ supports: ["generateAudio", "seed", "lastFrame", "watermark"]
777
1272
  },
778
1273
  defaults: {
779
1274
  durationSeconds: 5,
@@ -782,7 +1277,12 @@ var VIDEO_MODELS = [
782
1277
  generateAudio: false
783
1278
  },
784
1279
  usage: { ...VIDEO_USAGE },
785
- pricingKind: "video_per_second"
1280
+ pricingKind: "video_per_second",
1281
+ display: {
1282
+ provider: "bytedance",
1283
+ description: "ByteDance Seedance 2.5 for long-form storytelling and omni-reference generation/editing; up to 50 reference assets and 4-30 second clips at up to 1080p."
1284
+ },
1285
+ capabilities: { acceptsSourceVideo: true, ...FRAMES_EXCLUDE_REFERENCES }
786
1286
  },
787
1287
  {
788
1288
  // Cowork allows 7 reference images and durations 4/6/8/10; Space Builder allows 5 and 1-15s.
@@ -800,9 +1300,16 @@ var VIDEO_MODELS = [
800
1300
  references: { images: 7 },
801
1301
  supports: []
802
1302
  },
803
- defaults: { durationSeconds: 8, resolution: "720p", aspectRatio: "16:9" },
1303
+ // No resolution default: xAI picks its own unless the caller asks.
1304
+ defaults: { durationSeconds: 8, aspectRatio: "16:9" },
804
1305
  usage: { unit: "reported_cost_usd", modality: "VIDEO" },
805
- pricingKind: "video_per_second"
1306
+ pricingKind: "video_per_second",
1307
+ display: {
1308
+ provider: "xai",
1309
+ description: "xAI's reference-to-video model. Generates short clips conditioned on up to 7 reference images with flexible aspect ratios.",
1310
+ logo: "https://d31vvfpk2ppdyq.cloudfront.net/assets/image/858b7fcc-9bd5-411d-aea1-a7b35ecfffab_compressed.png"
1311
+ },
1312
+ capabilities: { ...FRAMES_EXCLUDE_REFERENCES }
806
1313
  },
807
1314
  {
808
1315
  id: "kling-2.5",
@@ -825,7 +1332,12 @@ var VIDEO_MODELS = [
825
1332
  negativePrompt: NEGATIVE_PROMPT
826
1333
  },
827
1334
  usage: { ...VIDEO_USAGE },
828
- pricingKind: "video_per_second"
1335
+ pricingKind: "video_per_second",
1336
+ display: {
1337
+ provider: "kuaishou",
1338
+ description: "Kuaishou Kling 2.5 Turbo Pro image-to-video; animates a single input image from a text prompt with an optional end image, 5- or 10-second clips."
1339
+ },
1340
+ capabilities: { ...FRAMES_EXCLUDE_REFERENCES }
829
1341
  },
830
1342
  {
831
1343
  // trigger-workflows validates 5/10 only; Space Builder allows 3-15s (Kling30Input).
@@ -849,7 +1361,12 @@ var VIDEO_MODELS = [
849
1361
  negativePrompt: NEGATIVE_PROMPT
850
1362
  },
851
1363
  usage: { ...VIDEO_USAGE },
852
- pricingKind: "video_per_second"
1364
+ pricingKind: "video_per_second",
1365
+ display: {
1366
+ provider: "kuaishou",
1367
+ description: "Kuaishou Kling 3.0 Pro image-to-video; a required start image plus a text prompt (or multi-shot prompts) with optional end image and native audio, 3-15 second clips."
1368
+ },
1369
+ capabilities: { ...FRAMES_EXCLUDE_REFERENCES }
853
1370
  },
854
1371
  {
855
1372
  // mode standard/pro selects 720p/1080p; the endpoint takes no separate resolution.
@@ -880,7 +1397,12 @@ var VIDEO_MODELS = [
880
1397
  },
881
1398
  defaults: { durationSeconds: 5, aspectRatio: "16:9", generateAudio: false, mode: "pro" },
882
1399
  usage: { unit: "compute_seconds", modality: "VIDEO" },
883
- pricingKind: "video_per_second"
1400
+ pricingKind: "video_per_second",
1401
+ display: {
1402
+ provider: "kuaishou",
1403
+ description: "Kuaishou Kling Video 3.0 Omni unified generation and editing from text, start/end images, up to 7 reference images, and reference video; up to 15 seconds at up to 1080p."
1404
+ },
1405
+ capabilities: {}
884
1406
  },
885
1407
  {
886
1408
  id: "minimax-h3",
@@ -894,7 +1416,7 @@ var VIDEO_MODELS = [
894
1416
  aspectRatios: [...H3_RATIOS],
895
1417
  resolutions: ["480p", "768p", "2k", "4k"],
896
1418
  durations: { min: 5, max: 15 },
897
- supports: ["seed", "promptExpansion"]
1419
+ supports: ["seed", "promptExpansion", "lastFrame", "safetyChecker"]
898
1420
  },
899
1421
  defaults: {
900
1422
  durationSeconds: 5,
@@ -903,7 +1425,12 @@ var VIDEO_MODELS = [
903
1425
  promptExpansion: "balanced"
904
1426
  },
905
1427
  usage: { ...VIDEO_USAGE },
906
- pricingKind: "video_per_second"
1428
+ pricingKind: "video_per_second",
1429
+ display: {
1430
+ provider: "minimax",
1431
+ description: "MiniMax H3 multimodal video model for text-to-video, image-to-video (first/last frame), and reference-to-video with native audio, up to 2K."
1432
+ },
1433
+ capabilities: { ...FRAMES_EXCLUDE_REFERENCES }
907
1434
  },
908
1435
  {
909
1436
  id: "minimax-h3-max",
@@ -917,7 +1444,7 @@ var VIDEO_MODELS = [
917
1444
  aspectRatios: [...H3_RATIOS],
918
1445
  resolutions: ["480p", "768p"],
919
1446
  durations: { min: 5, max: 15 },
920
- supports: ["seed", "promptExpansion"]
1447
+ supports: ["seed", "promptExpansion", "lastFrame", "safetyChecker"]
921
1448
  },
922
1449
  defaults: {
923
1450
  durationSeconds: 5,
@@ -926,7 +1453,12 @@ var VIDEO_MODELS = [
926
1453
  promptExpansion: "balanced"
927
1454
  },
928
1455
  usage: { ...VIDEO_USAGE },
929
- pricingKind: "video_per_second"
1456
+ pricingKind: "video_per_second",
1457
+ display: {
1458
+ provider: "minimax",
1459
+ description: "High-speed MiniMax H3 Max variant for text-to-video and image-to-video (first/last frame) only, up to 768p."
1460
+ },
1461
+ capabilities: { ...FRAMES_EXCLUDE_REFERENCES }
930
1462
  },
931
1463
  {
932
1464
  id: "minimax-h3-reference",
@@ -941,7 +1473,7 @@ var VIDEO_MODELS = [
941
1473
  resolutions: ["480p", "768p", "2k", "4k"],
942
1474
  durations: { min: 5, max: 15 },
943
1475
  references: { images: 9, videos: 3, audios: 3, total: 12, audioOnly: false },
944
- supports: ["seed", "promptExpansion"]
1476
+ supports: ["seed", "promptExpansion", "safetyChecker"]
945
1477
  },
946
1478
  defaults: {
947
1479
  durationSeconds: 5,
@@ -950,7 +1482,12 @@ var VIDEO_MODELS = [
950
1482
  promptExpansion: "balanced"
951
1483
  },
952
1484
  usage: { ...VIDEO_USAGE },
953
- pricingKind: "video_per_second"
1485
+ pricingKind: "video_per_second",
1486
+ display: {
1487
+ provider: "minimax",
1488
+ description: "MiniMax H3 reference-to-video mode guided by up to 9 reference images, 3 reference videos, and 3 audio clips; reference and image-to-video inputs cannot be mixed."
1489
+ },
1490
+ capabilities: {}
954
1491
  },
955
1492
  {
956
1493
  id: "minimax-h3-max-reference",
@@ -965,7 +1502,7 @@ var VIDEO_MODELS = [
965
1502
  resolutions: ["480p", "768p"],
966
1503
  durations: { min: 5, max: 15 },
967
1504
  references: { images: 9, videos: 3, audios: 3, total: 12, audioOnly: false },
968
- supports: ["seed", "promptExpansion"]
1505
+ supports: ["seed", "promptExpansion", "safetyChecker"]
969
1506
  },
970
1507
  defaults: {
971
1508
  durationSeconds: 5,
@@ -974,7 +1511,12 @@ var VIDEO_MODELS = [
974
1511
  promptExpansion: "balanced"
975
1512
  },
976
1513
  usage: { ...VIDEO_USAGE },
977
- pricingKind: "video_per_second"
1514
+ pricingKind: "video_per_second",
1515
+ display: {
1516
+ provider: "minimax",
1517
+ description: "MiniMax H3 Max reference-to-video on fal; conditions on reference images, videos, and audio. MiniMax's own docs do not list a reference mode for H3 Max."
1518
+ },
1519
+ capabilities: {}
978
1520
  },
979
1521
  {
980
1522
  // Duration 3-10s per trigger-workflows mapDuration; the endpoint takes no resolution.
@@ -993,7 +1535,12 @@ var VIDEO_MODELS = [
993
1535
  },
994
1536
  defaults: { durationSeconds: 5, aspectRatio: "16:9" },
995
1537
  usage: { ...VIDEO_USAGE },
996
- pricingKind: "video_per_second"
1538
+ pricingKind: "video_per_second",
1539
+ display: {
1540
+ provider: "kuaishou",
1541
+ description: "Kuaishou Kling O1 Reference Video-to-Video; a prompt plus a 3-10 second reference video (optional reference images) generates 3-10 second clips preserving motion and camera language."
1542
+ },
1543
+ capabilities: { acceptsSourceVideo: true }
997
1544
  },
998
1545
  {
999
1546
  id: "hailuo-02",
@@ -1010,7 +1557,12 @@ var VIDEO_MODELS = [
1010
1557
  },
1011
1558
  defaults: { durationSeconds: 6, resolution: "768p" },
1012
1559
  usage: { unit: "compute_seconds", modality: "VIDEO" },
1013
- pricingKind: "video_per_second"
1560
+ pricingKind: "video_per_second",
1561
+ display: {
1562
+ provider: "minimax",
1563
+ description: "MiniMax Hailuo 02 text-to-video and image-to-video model with realistic physics and strong prompt following; outputs 768p at 6 or 10 seconds."
1564
+ },
1565
+ capabilities: {}
1014
1566
  },
1015
1567
  {
1016
1568
  id: "higgsfield-dop-preview",
@@ -1026,7 +1578,12 @@ var VIDEO_MODELS = [
1026
1578
  },
1027
1579
  defaults: {},
1028
1580
  usage: { ...VIDEO_USAGE },
1029
- pricingKind: "video_per_second"
1581
+ pricingKind: "video_per_second",
1582
+ display: {
1583
+ provider: "higgsfield",
1584
+ description: "Higgsfield DOP image-to-video model; animates a required first-frame image from a text prompt with an optional last-frame image and motion controls."
1585
+ },
1586
+ capabilities: {}
1030
1587
  }
1031
1588
  ];
1032
1589
 
@@ -1093,19 +1650,22 @@ function resolveTier(field, code, requested, supported, order, model, onUnsuppor
1093
1650
  });
1094
1651
  return to;
1095
1652
  }
1096
- function resolveDuration(requested, durations, model, warnings) {
1653
+ function resolveDuration(requested, durations, model, onUnsupported, warnings) {
1097
1654
  if (requested === void 0 || !durations) return requested;
1098
- let to = requested;
1099
- if (Array.isArray(durations)) {
1100
- if (durations.includes(requested)) return requested;
1101
- to = durations.reduce(
1102
- (best, d) => Math.abs(d - requested) < Math.abs(best - requested) ? d : best,
1103
- durations[0] ?? requested
1104
- );
1105
- } else {
1106
- to = Math.min(Math.max(requested, durations.min), durations.max);
1107
- if (to === requested) return requested;
1655
+ const inSet = Array.isArray(durations) ? durations.includes(requested) : requested >= durations.min && requested <= durations.max;
1656
+ if (inSet) return requested;
1657
+ if (onUnsupported === "error") {
1658
+ const supported = Array.isArray(durations) ? `Supported: ${durations.join(", ")}.` : `Supported range: ${durations.min}-${durations.max}.`;
1659
+ throw new ModelKitError({
1660
+ code: "unsupported_option",
1661
+ message: `${model.id} does not support durationSeconds ${requested}. ${supported}`,
1662
+ model: model.id
1663
+ });
1108
1664
  }
1665
+ const to = Array.isArray(durations) ? durations.reduce(
1666
+ (best, d) => Math.abs(d - requested) < Math.abs(best - requested) ? d : best,
1667
+ durations[0] ?? requested
1668
+ ) : Math.min(Math.max(requested, durations.min), durations.max);
1109
1669
  warnings.push({
1110
1670
  code: "duration_coerced",
1111
1671
  field: "durationSeconds",
@@ -1115,6 +1675,26 @@ function resolveDuration(requested, durations, model, warnings) {
1115
1675
  });
1116
1676
  return to;
1117
1677
  }
1678
+ function resolveAspectRatio(requested, supported, model, onUnsupported, warnings) {
1679
+ if (requested === void 0 || requested === "adaptive" || !supported) return requested;
1680
+ if (supported.includes(requested)) return requested;
1681
+ if (onUnsupported === "error") {
1682
+ throw new ModelKitError({
1683
+ code: "unsupported_option",
1684
+ message: `${model.id} does not support aspectRatio "${requested}". Supported: ${supported.join(", ")}.`,
1685
+ model: model.id
1686
+ });
1687
+ }
1688
+ const to = nearestAspectRatio(requested, supported);
1689
+ warnings.push({
1690
+ code: "ratio_coerced",
1691
+ field: "aspectRatio",
1692
+ from: requested,
1693
+ to,
1694
+ message: `aspect ratio ${requested} coerced to ${to} for ${model.id}`
1695
+ });
1696
+ return to;
1697
+ }
1118
1698
  function compact(refs) {
1119
1699
  return (refs ?? []).filter((r) => Boolean(r));
1120
1700
  }
@@ -1133,17 +1713,14 @@ function resolveImageOptions(model, request, onUnsupported = "error") {
1133
1713
  const warnings = [];
1134
1714
  const c = model.constraints;
1135
1715
  const merged = { ...model.defaults, ...stripUndefined(request), model: model.id };
1136
- if (merged.aspectRatio !== void 0 && merged.aspectRatio !== "adaptive" && c.aspectRatios !== void 0 && !c.aspectRatios.includes(merged.aspectRatio)) {
1137
- const to = nearestAspectRatio(merged.aspectRatio, c.aspectRatios);
1138
- warnings.push({
1139
- code: "ratio_coerced",
1140
- field: "aspectRatio",
1141
- from: merged.aspectRatio,
1142
- to,
1143
- message: `aspect ratio ${merged.aspectRatio} coerced to ${to} for ${model.id}`
1144
- });
1145
- merged.aspectRatio = to;
1146
- }
1716
+ const aspectRatio = resolveAspectRatio(
1717
+ merged.aspectRatio,
1718
+ c.aspectRatios,
1719
+ model,
1720
+ onUnsupported,
1721
+ warnings
1722
+ );
1723
+ if (aspectRatio !== void 0) merged.aspectRatio = aspectRatio;
1147
1724
  const size = resolveTier(
1148
1725
  "size",
1149
1726
  "size_coerced",
@@ -1177,7 +1754,9 @@ function resolveImageOptions(model, request, onUnsupported = "error") {
1177
1754
  "count",
1178
1755
  "outputFormat",
1179
1756
  "scaleFactor",
1180
- "safetySettings"
1757
+ "safetySettings",
1758
+ "temperature",
1759
+ "upscale"
1181
1760
  ]);
1182
1761
  return { request: merged, warnings };
1183
1762
  }
@@ -1207,17 +1786,14 @@ function resolveVideoOptions(model, request, onUnsupported = "error") {
1207
1786
  delete merged.aspectRatio;
1208
1787
  }
1209
1788
  }
1210
- if (merged.aspectRatio !== void 0 && merged.aspectRatio !== "adaptive" && c.aspectRatios !== void 0 && !c.aspectRatios.includes(merged.aspectRatio)) {
1211
- const to = nearestAspectRatio(merged.aspectRatio, c.aspectRatios);
1212
- warnings.push({
1213
- code: "ratio_coerced",
1214
- field: "aspectRatio",
1215
- from: merged.aspectRatio,
1216
- to,
1217
- message: `aspect ratio ${merged.aspectRatio} coerced to ${to} for ${model.id}`
1218
- });
1219
- merged.aspectRatio = to;
1220
- }
1789
+ const aspectRatio = resolveAspectRatio(
1790
+ merged.aspectRatio,
1791
+ c.aspectRatios,
1792
+ model,
1793
+ onUnsupported,
1794
+ warnings
1795
+ );
1796
+ if (aspectRatio !== void 0) merged.aspectRatio = aspectRatio;
1221
1797
  const resolution = resolveTier(
1222
1798
  "resolution",
1223
1799
  "resolution_coerced",
@@ -1229,7 +1805,13 @@ function resolveVideoOptions(model, request, onUnsupported = "error") {
1229
1805
  warnings
1230
1806
  );
1231
1807
  if (resolution !== void 0) merged.resolution = resolution;
1232
- const duration = resolveDuration(merged.durationSeconds, c.durations, model, warnings);
1808
+ const duration = resolveDuration(
1809
+ merged.durationSeconds,
1810
+ c.durations,
1811
+ model,
1812
+ onUnsupported,
1813
+ warnings
1814
+ );
1233
1815
  if (duration !== void 0) merged.durationSeconds = duration;
1234
1816
  const limits = c.references ?? {};
1235
1817
  let images = trim(
@@ -1284,10 +1866,45 @@ function resolveVideoOptions(model, request, onUnsupported = "error") {
1284
1866
  "cfgScale",
1285
1867
  "generateAudio",
1286
1868
  "promptExpansion",
1287
- "lastFrame"
1869
+ "lastFrame",
1870
+ "watermark",
1871
+ "safetyChecker"
1288
1872
  ]);
1289
1873
  return { request: merged, warnings };
1290
1874
  }
1875
+ function resolveAudioOptions(model, request, onUnsupported = "error") {
1876
+ const warnings = [];
1877
+ const c = model.constraints;
1878
+ const merged = { ...model.defaults, ...stripUndefined(request), model: model.id };
1879
+ for (const input of c.requires ?? []) {
1880
+ const present = input === "references" ? compact(merged.references).length > 0 : input === "sourceVideo" && merged.sourceVideo !== void 0;
1881
+ if (!present)
1882
+ throw new ModelKitError({
1883
+ code: "missing_input",
1884
+ message: `${model.id} requires ${input}`,
1885
+ model: model.id
1886
+ });
1887
+ }
1888
+ const duration = resolveDuration(
1889
+ merged.durationSeconds,
1890
+ c.durations,
1891
+ model,
1892
+ onUnsupported,
1893
+ warnings
1894
+ );
1895
+ if (duration !== void 0) merged.durationSeconds = duration;
1896
+ const references = trim(
1897
+ "references",
1898
+ compact(merged.references),
1899
+ c.references?.images ?? c.references?.total,
1900
+ model,
1901
+ warnings
1902
+ );
1903
+ if (references.length > 0) merged.references = references;
1904
+ else delete merged.references;
1905
+ dropUnsupported(merged, model, warnings, ["seed", "negativePrompt", "instrumental"]);
1906
+ return { request: merged, warnings };
1907
+ }
1291
1908
  function dropUnsupported(req, model, warnings, fields) {
1292
1909
  const supports = model.constraints.supports ?? [];
1293
1910
  for (const field of fields) {
@@ -1329,6 +1946,9 @@ function hasAll(keys, names) {
1329
1946
  (n) => Boolean(keys[n]) && (n !== "higgsfield" || Boolean(keys.higgsfieldSecret))
1330
1947
  );
1331
1948
  }
1949
+ function missingEnterpriseKeys(config, names) {
1950
+ return names.filter((name) => !hasAll(config.keys.enterprise, [name]));
1951
+ }
1332
1952
  function resolveKeys(config, names, tier = "pro") {
1333
1953
  if (tier === "enterprise" && hasAll(config.keys.enterprise, names))
1334
1954
  return config.keys.enterprise;
@@ -1361,7 +1981,6 @@ function keysFromEnv(env, prefix = "") {
1361
1981
  set("prodia", pick("PRODIA_API_KEY", "PRODIA_TOKEN"));
1362
1982
  set("higgsfield", pick("HIGGSFIELD_API_KEY"));
1363
1983
  set("higgsfieldSecret", pick("HIGGSFIELD_API_SECRET"));
1364
- set("openrouter", pick("OPENROUTER_API_KEY"));
1365
1984
  set("cloudflare", pick("CLOUDFLARE_AI_GATEWAY_TOKEN", "CF_AIG_TOKEN"));
1366
1985
  return keys;
1367
1986
  }
@@ -1409,7 +2028,9 @@ var textBody = z2.string();
1409
2028
  function classifyHttpError(status, body, provider, model) {
1410
2029
  const asText = textBody.safeParse(body);
1411
2030
  const text2 = asText.success ? asText.data : JSON.stringify(body);
1412
- const blocked = /content_policy|safety|blockReason|PROHIBITED_CONTENT|moderation/i.test(text2);
2031
+ const blocked = /content_policy_violation|blockReason|PROHIBITED_CONTENT|moderation_blocked|safety(?![_A-Za-z])/i.test(
2032
+ text2
2033
+ );
1413
2034
  let code = "provider_rejected";
1414
2035
  if (blocked) code = "content_blocked";
1415
2036
  else if (status === 401 || status === 403) code = "auth";
@@ -1430,8 +2051,8 @@ function classifyHttpError(status, body, provider, model) {
1430
2051
  }
1431
2052
  function shouldRetry(err) {
1432
2053
  if (err instanceof ModelKitError) return err.retryable;
1433
- if (err.name === "AbortError" || /ECONNRESET|ETIMEDOUT|fetch failed|socket hang up/i.test(err.message))
1434
- return true;
2054
+ if (err.name === "AbortError") return false;
2055
+ if (/ECONNRESET|ETIMEDOUT|fetch failed|socket hang up/i.test(err.message)) return true;
1435
2056
  return false;
1436
2057
  }
1437
2058
  async function withRetry(fn, opts = {}) {
@@ -1623,6 +2244,12 @@ import { z as z4 } from "zod";
1623
2244
  function isUrlRef(ref) {
1624
2245
  return "url" in ref;
1625
2246
  }
2247
+ function abortedError() {
2248
+ return new ModelKitError({ code: "timeout", message: "aborted", retryable: false });
2249
+ }
2250
+ function throwIfAborted(signal) {
2251
+ if (signal?.aborted) throw abortedError();
2252
+ }
1626
2253
  function mediaRefToBase64(bytes) {
1627
2254
  let binary = "";
1628
2255
  const CHUNK = 32768;
@@ -1637,10 +2264,34 @@ function base64ToBytes(b64) {
1637
2264
  for (let i = 0; i < binary.length; i++) out[i] = binary.charCodeAt(i);
1638
2265
  return out;
1639
2266
  }
1640
- async function mediaRefToBytes(ref, fetch) {
2267
+ function mediaRefToDataUrl(ref) {
2268
+ if (isUrlRef(ref)) return ref.url;
2269
+ return `data:${ref.mime};base64,${mediaRefToBase64(ref.bytes)}`;
2270
+ }
2271
+ var refToUrl = mediaRefToDataUrl;
2272
+ async function readBody(res) {
2273
+ const text2 = await res.text().catch(() => "");
2274
+ try {
2275
+ const decoded = JsonValueSchema.safeParse(JSON.parse(text2));
2276
+ return decoded.success ? decoded.data : text2;
2277
+ } catch {
2278
+ return text2;
2279
+ }
2280
+ }
2281
+ async function mediaRefToBytes(ref, fetch, options = {}) {
1641
2282
  if (!isUrlRef(ref)) return { bytes: ref.bytes, mime: ref.mime };
1642
- const res = await fetch(ref.url);
1643
- if (!res.ok) throw new Error(`reference fetch failed (${res.status}): ${ref.url}`);
2283
+ const init = {};
2284
+ if (options.signal !== void 0) init.signal = options.signal;
2285
+ const res = await fetch(ref.url, init);
2286
+ if (!res.ok) {
2287
+ const text2 = await res.text().catch(() => "");
2288
+ throw classifyHttpError(
2289
+ res.status,
2290
+ text2,
2291
+ options.provider ?? "media-reference",
2292
+ options.model
2293
+ );
2294
+ }
1644
2295
  const mime = res.headers.get("content-type")?.split(";")[0]?.trim().toLowerCase() || "image/png";
1645
2296
  return { bytes: new Uint8Array(await res.arrayBuffer()), mime };
1646
2297
  }
@@ -1698,16 +2349,6 @@ var GATEWAY_SLUGS = {
1698
2349
  perplexity: "perplexity-ai",
1699
2350
  deepseek: "deepseek"
1700
2351
  };
1701
- var OPENROUTER_VENDORS = {
1702
- openai: "openai",
1703
- anthropic: "anthropic",
1704
- google: "google",
1705
- xai: "x-ai",
1706
- perplexity: "perplexity",
1707
- deepseek: "deepseek",
1708
- moonshot: "moonshotai",
1709
- minimax: "minimax"
1710
- };
1711
2352
  function detectTextProvider(modelId) {
1712
2353
  const id = modelId.toLowerCase();
1713
2354
  if (id.startsWith("claude") || id.includes("anthropic.")) return "anthropic";
@@ -1724,14 +2365,17 @@ function detectTextProvider(modelId) {
1724
2365
  message: `ai-sdk-text cannot detect a provider for "${modelId}". Set providerOptions.provider to one of ${TEXT_PROVIDERS.join(", ")}.`
1725
2366
  });
1726
2367
  }
1727
- function textKeyName(model, routing) {
1728
- if (routing.text === "openrouter") return "openrouter";
2368
+ function textKeyName(model, _routing) {
1729
2369
  return textProviderOf(model);
1730
2370
  }
1731
2371
  function textProviderOf(model) {
1732
2372
  const explicit = model.providerOptions?.["modelkit"]?.["provider"];
1733
2373
  return isTextProvider(explicit) ? explicit : detectTextProvider(model.providerModelId);
1734
2374
  }
2375
+ function textUsesCloudflareGateway(model, routing) {
2376
+ if (routing.text !== "cloudflare") return false;
2377
+ return GATEWAY_SLUGS[textProviderOf(model)] !== void 0;
2378
+ }
1735
2379
  function nativeClientFor(provider) {
1736
2380
  switch (provider) {
1737
2381
  case "openai":
@@ -1764,16 +2408,6 @@ function resolveTextRoute(ctx) {
1764
2408
  retryable: false
1765
2409
  });
1766
2410
  }
1767
- if (mode === "openrouter") {
1768
- const modelId = ctx.model.providerModelId.includes("/") ? ctx.model.providerModelId : `${OPENROUTER_VENDORS[provider]}/${ctx.model.providerModelId}`;
1769
- return {
1770
- provider,
1771
- client: "openai-compatible",
1772
- modelId,
1773
- keyName: "openrouter",
1774
- baseURL: "https://openrouter.ai/api/v1"
1775
- };
1776
- }
1777
2411
  const compatBase = OPENAI_COMPATIBLE_BASE_URLS[provider];
1778
2412
  const route2 = {
1779
2413
  provider,
@@ -1841,7 +2475,7 @@ var aiSdkTextAdapter = {
1841
2475
  case "openai-compatible":
1842
2476
  return createOpenAI({
1843
2477
  ...settings,
1844
- name: route2.provider === "openai" ? "openai" : ctx.routing.text === "openrouter" ? "openrouter" : route2.provider
2478
+ name: route2.provider
1845
2479
  }).chat(route2.modelId);
1846
2480
  case "openai":
1847
2481
  return createOpenAI(settings)(route2.modelId);
@@ -1854,8 +2488,9 @@ function requiredKeys(model, config) {
1854
2488
  if (model.adapter === "fal" && config.routing?.fal === "cloudflare-passthrough")
1855
2489
  return ["cloudflare"];
1856
2490
  if (model.adapter === "ai-sdk-text") {
1857
- const names = [textKeyName(model, config.routing ?? {})];
1858
- if (config.routing?.text === "cloudflare") names.push("cloudflare");
2491
+ const routing = config.routing ?? {};
2492
+ const names = [textKeyName(model, routing)];
2493
+ if (textUsesCloudflareGateway(model, routing)) names.push("cloudflare");
1859
2494
  return names;
1860
2495
  }
1861
2496
  return ADAPTER_KEYS[model.adapter];
@@ -1929,6 +2564,11 @@ function pushIgnored(warnings, field, from, modelId) {
1929
2564
  message: `${modelId} provider instance does not support ${field}; ignored`
1930
2565
  });
1931
2566
  }
2567
+ function withoutModelkit(options) {
2568
+ if (!options) return {};
2569
+ const { modelkit: _modelkit, ...rest } = options;
2570
+ return rest;
2571
+ }
1932
2572
  function logCoercions(logger, modelId, warnings) {
1933
2573
  if (warnings.length === 0) return;
1934
2574
  logger.warn(`[modelkit] ${modelId} request coerced`, { model: modelId, warnings });
@@ -1945,7 +2585,16 @@ function createModelKit(config, adapters) {
1945
2585
  message: `No adapter registered for ${model.adapter}`,
1946
2586
  model: model.id
1947
2587
  });
1948
- const keys = resolveKeys(config, requiredKeys(model, config), call.tier);
2588
+ const names = requiredKeys(model, config);
2589
+ if (call.tier === "enterprise") {
2590
+ const missing = missingEnterpriseKeys(config, names);
2591
+ if (missing.length > 0)
2592
+ logger.warn(
2593
+ `[modelkit] enterprise keys incomplete for ${model.adapter}; using pro`,
2594
+ { missing }
2595
+ );
2596
+ }
2597
+ const keys = resolveKeys(config, names, call.tier);
1949
2598
  const ctx = {
1950
2599
  model,
1951
2600
  keys,
@@ -2266,8 +2915,15 @@ function createModelKit(config, adapters) {
2266
2915
  model: model.id
2267
2916
  });
2268
2917
  }
2269
- const warnings = [];
2270
- const { adapter, ctx } = context(model, call, warnings);
2918
+ const { abortSignal: _signal, ...request } = req;
2919
+ const { request: resolved, warnings } = resolveAudioOptions(
2920
+ model,
2921
+ request,
2922
+ call.onUnsupported ?? "error"
2923
+ );
2924
+ logCoercions(logger, model.id, warnings);
2925
+ const signal = combineSignals(req.abortSignal, call.signal);
2926
+ const { adapter, ctx } = context(model, signal ? { ...call, signal } : call, warnings);
2271
2927
  if (!adapter.generateAudio) {
2272
2928
  throw new ModelKitError({
2273
2929
  code: "unsupported_option",
@@ -2275,11 +2931,7 @@ function createModelKit(config, adapters) {
2275
2931
  model: model.id
2276
2932
  });
2277
2933
  }
2278
- const result = await adapter.generateAudio(
2279
- { ...model.defaults, ...req, model: model.id },
2280
- ctx
2281
- );
2282
- logCoercions(logger, model.id, warnings);
2934
+ const result = await adapter.generateAudio(resolved, ctx);
2283
2935
  await emit({ ...result.usage, model: model.id, unit: model.usage.unit });
2284
2936
  return result;
2285
2937
  },
@@ -2300,7 +2952,7 @@ function createModelKit(config, adapters) {
2300
2952
  });
2301
2953
  return {
2302
2954
  model,
2303
- providerOptions: entry.providerOptions ?? {},
2955
+ providerOptions: withoutModelkit(entry.providerOptions),
2304
2956
  catalog: entry
2305
2957
  };
2306
2958
  },
@@ -2336,7 +2988,9 @@ function createModelKit(config, adapters) {
2336
2988
  ...textAbortOptions(options.abortSignal, call.signal),
2337
2989
  model: target.languageModel,
2338
2990
  providerOptions: {},
2339
- prepareStep: steps.wrap(options.prepareStep),
2991
+ prepareStep: steps.wrap(
2992
+ options.prepareStep ?? options.experimental_prepareStep
2993
+ ),
2340
2994
  onFinish: async (event) => {
2341
2995
  for (const usage2 of steps.usage(event.steps)) await emit(withRowUnit(usage2));
2342
2996
  await callerOnFinish?.(event);
@@ -2393,6 +3047,7 @@ var SEEDREAM_SIZES = {
2393
3047
  "3:2": "1248x832",
2394
3048
  "2:3": "832x1248",
2395
3049
  "21:9": "1568x672",
3050
+ "9:21": "672x1568",
2396
3051
  "4:5": "864x1080",
2397
3052
  "5:4": "1080x864"
2398
3053
  },
@@ -2405,16 +3060,34 @@ var SEEDREAM_SIZES = {
2405
3060
  "3:2": "2496x1664",
2406
3061
  "2:3": "1664x2496",
2407
3062
  "21:9": "3136x1344",
3063
+ "9:21": "1344x3136",
2408
3064
  "4:5": "1728x2160",
2409
3065
  "5:4": "2160x1728"
3066
+ },
3067
+ // 4K keeps the long edge at Ark's 4096 cap (a doubled 2K entry would exceed
3068
+ // it); sides are multiples of 16.
3069
+ "4K": {
3070
+ "1:1": "4096x4096",
3071
+ "4:3": "4096x3072",
3072
+ "3:4": "3072x4096",
3073
+ "16:9": "4096x2304",
3074
+ "9:16": "2304x4096",
3075
+ "3:2": "4096x2736",
3076
+ "2:3": "2736x4096",
3077
+ "21:9": "4096x1760",
3078
+ "9:21": "1760x4096",
3079
+ "4:5": "3280x4096",
3080
+ "5:4": "4096x3280"
2410
3081
  }
2411
3082
  };
2412
3083
  var arkAdapter = {
2413
3084
  id: "ark",
2414
3085
  async generateImage(req, ctx) {
3086
+ throwIfAborted(ctx.signal);
2415
3087
  return generateSeedreamImage(req, ctx);
2416
3088
  },
2417
3089
  async generateVideo(req, ctx) {
3090
+ throwIfAborted(ctx.signal);
2418
3091
  return generateSeedanceVideo(req, ctx);
2419
3092
  }
2420
3093
  };
@@ -2434,7 +3107,7 @@ function arkKey(ctx) {
2434
3107
  function throwArkError(status, body, model) {
2435
3108
  const parsed = z5.string().safeParse(body);
2436
3109
  const text2 = parsed.success ? parsed.data : JSON.stringify(body ?? "");
2437
- if (/sensitive.?content|inputimage|human.?face/i.test(text2)) {
3110
+ if (/sensitive.?content|human.?face/i.test(text2)) {
2438
3111
  const init = {
2439
3112
  code: "content_blocked",
2440
3113
  message: `ark rejected the request: ${text2.slice(0, 500)}`,
@@ -2459,7 +3132,7 @@ function throwArkError(status, body, model) {
2459
3132
  throw classifyHttpError(status, body, "ark", model);
2460
3133
  }
2461
3134
  function seedreamSize(aspect, size) {
2462
- const tier = size === "1K" ? "1K" : "2K";
3135
+ const tier = size === "4K" || size === "1K" ? size : "2K";
2463
3136
  const table = SEEDREAM_SIZES[tier];
2464
3137
  return table[aspect ?? ""] ?? table["1:1"] ?? "2048x2048";
2465
3138
  }
@@ -2470,10 +3143,6 @@ function sniffedImageMime(bytes) {
2470
3143
  return "image/webp";
2471
3144
  return "image/jpeg";
2472
3145
  }
2473
- function arkUrl(ref) {
2474
- if ("url" in ref) return ref.url;
2475
- return `data:${ref.mime};base64,${Buffer.from(ref.bytes).toString("base64")}`;
2476
- }
2477
3146
  var SeedreamImageSchema = z5.object({
2478
3147
  url: z5.string().optional(),
2479
3148
  b64_json: z5.string().optional()
@@ -2500,7 +3169,7 @@ async function generateSeedreamImage(req, ctx) {
2500
3169
  const key = arkKey(ctx);
2501
3170
  const modelId = ctx.model.id;
2502
3171
  const size = seedreamSize(req.aspectRatio, req.size);
2503
- const refs = (req.references ?? []).map(arkUrl);
3172
+ const refs = (req.references ?? []).map(refToUrl);
2504
3173
  const body = {
2505
3174
  model: ctx.model.providerModelId,
2506
3175
  prompt: req.prompt,
@@ -2552,10 +3221,18 @@ async function generateSeedreamImage(req, ctx) {
2552
3221
  if (req.quality !== void 0) usage2.imageQuality = req.quality;
2553
3222
  return { images: [{ bytes, mime: sniffedImageMime(bytes) }], usage: usage2, raw: data };
2554
3223
  }
3224
+ var SeedanceUsageSchema = z5.object({
3225
+ total_tokens: z5.number().optional(),
3226
+ completion_tokens: z5.number().optional()
3227
+ });
2555
3228
  var SeedanceResponseSchema = z5.object({
2556
3229
  id: z5.string().optional(),
2557
3230
  status: z5.string().optional(),
2558
3231
  content: z5.object({ video_url: z5.string().optional() }).optional(),
3232
+ usage: SeedanceUsageSchema.optional(),
3233
+ duration: z5.number().optional(),
3234
+ framespersecond: z5.number().optional(),
3235
+ resolution: z5.string().optional(),
2559
3236
  error: z5.union([z5.string(), z5.object({ message: z5.string().optional() })]).optional()
2560
3237
  });
2561
3238
  var SeedanceErrorMessageSchema = z5.object({ message: z5.string().optional() });
@@ -2587,42 +3264,42 @@ async function generateSeedanceVideo(req, ctx) {
2587
3264
  if (req.firstFrame) {
2588
3265
  content.push({
2589
3266
  type: "image_url",
2590
- image_url: { url: arkUrl(req.firstFrame) },
3267
+ image_url: { url: refToUrl(req.firstFrame) },
2591
3268
  role: "first_frame"
2592
3269
  });
2593
3270
  }
2594
3271
  if (req.lastFrame) {
2595
3272
  content.push({
2596
3273
  type: "image_url",
2597
- image_url: { url: arkUrl(req.lastFrame) },
3274
+ image_url: { url: refToUrl(req.lastFrame) },
2598
3275
  role: "last_frame"
2599
3276
  });
2600
3277
  }
2601
3278
  for (const ref of req.referenceImages ?? []) {
2602
3279
  content.push({
2603
3280
  type: "image_url",
2604
- image_url: { url: arkUrl(ref) },
3281
+ image_url: { url: refToUrl(ref) },
2605
3282
  role: "reference_image"
2606
3283
  });
2607
3284
  }
2608
3285
  if (req.sourceVideo) {
2609
3286
  content.push({
2610
3287
  type: "video_url",
2611
- video_url: { url: arkUrl(req.sourceVideo) },
3288
+ video_url: { url: refToUrl(req.sourceVideo) },
2612
3289
  role: "reference_video"
2613
3290
  });
2614
3291
  }
2615
3292
  for (const ref of req.referenceVideos ?? []) {
2616
3293
  content.push({
2617
3294
  type: "video_url",
2618
- video_url: { url: arkUrl(ref) },
3295
+ video_url: { url: refToUrl(ref) },
2619
3296
  role: "reference_video"
2620
3297
  });
2621
3298
  }
2622
3299
  for (const ref of req.referenceAudios ?? []) {
2623
3300
  content.push({
2624
3301
  type: "audio_url",
2625
- audio_url: { url: arkUrl(ref) },
3302
+ audio_url: { url: refToUrl(ref) },
2626
3303
  role: "reference_audio"
2627
3304
  });
2628
3305
  }
@@ -2636,6 +3313,7 @@ async function generateSeedanceVideo(req, ctx) {
2636
3313
  if (!hasFrameAnchor && req.aspectRatio !== void 0) body.ratio = req.aspectRatio;
2637
3314
  if (req.generateAudio !== void 0) body.generate_audio = req.generateAudio;
2638
3315
  if (req.seed !== void 0) body.seed = req.seed;
3316
+ if (req.watermark !== void 0) body.watermark = req.watermark;
2639
3317
  const submit = await withRetry(async () => {
2640
3318
  const res = await arkFetch(ctx, `${arkBaseUrl(ctx)}${SUBMIT_PATH}`, {
2641
3319
  method: "POST",
@@ -2699,6 +3377,11 @@ async function generateSeedanceVideo(req, ctx) {
2699
3377
  "Seedance task succeeded but returned no video_url",
2700
3378
  modelId
2701
3379
  );
3380
+ const tokens = polled.usage;
3381
+ if (tokens?.total_tokens !== void 0 && tokens.completion_tokens !== void 0) {
3382
+ usage2.inputTokens = tokens.total_tokens - tokens.completion_tokens;
3383
+ usage2.outputTokens = tokens.completion_tokens;
3384
+ }
2702
3385
  return {
2703
3386
  video: { url, expiresAt: new Date(Date.now() + VIDEO_URL_TTL_MS) },
2704
3387
  usage: usage2,
@@ -2762,24 +3445,17 @@ var defaultDeps = { createFalClient: defaultCreateFalClient };
2762
3445
  function createFalAdapter(deps = defaultDeps) {
2763
3446
  return {
2764
3447
  id: "fal",
2765
- generateImage: (req, ctx) => generateMuseImage(req, ctx, deps),
2766
- generateVideo: (req, ctx) => generateFalVideo(req, ctx, deps)
3448
+ generateImage: (req, ctx) => {
3449
+ throwIfAborted(ctx.signal);
3450
+ return generateMuseImage(req, ctx, deps);
3451
+ },
3452
+ generateVideo: (req, ctx) => {
3453
+ throwIfAborted(ctx.signal);
3454
+ return generateFalVideo(req, ctx, deps);
3455
+ }
2767
3456
  };
2768
3457
  }
2769
3458
  var falAdapter = createFalAdapter();
2770
- function refToUrl(ref) {
2771
- if ("url" in ref) return ref.url;
2772
- return `data:${ref.mime};base64,${Buffer.from(ref.bytes).toString("base64")}`;
2773
- }
2774
- async function readBody(res) {
2775
- const text2 = await res.text().catch(() => "");
2776
- try {
2777
- const decoded = JsonValueSchema.safeParse(JSON.parse(text2));
2778
- return decoded.success ? decoded.data : text2;
2779
- } catch {
2780
- return text2;
2781
- }
2782
- }
2783
3459
  var falDetailSchema = z6.object({ detail: JsonValueSchema });
2784
3460
  function liftDetail(body) {
2785
3461
  const parsed = falDetailSchema.safeParse(body);
@@ -3016,8 +3692,12 @@ async function generateMuseImage(req, ctx, deps) {
3016
3692
  raw: payload
3017
3693
  };
3018
3694
  }
3695
+ var H3_MAX_MODEL_IDS = /* @__PURE__ */ new Set([
3696
+ "minimax/h3-max",
3697
+ "minimax/h3-max/reference-to-video"
3698
+ ]);
3019
3699
  function minimaxFamily(providerModelId) {
3020
- return providerModelId.toLowerCase().includes("h3-max") ? "h3-max" : "h3";
3700
+ return H3_MAX_MODEL_IDS.has(providerModelId.toLowerCase()) ? "h3-max" : "h3";
3021
3701
  }
3022
3702
  function minimaxBase(req, family) {
3023
3703
  const resolution = (family === "h3-max" ? req.resolution ?? "768p" : req.resolution ?? "2k").toUpperCase();
@@ -3025,14 +3705,17 @@ function minimaxBase(req, family) {
3025
3705
  prompt: req.prompt.trim(),
3026
3706
  duration: req.durationSeconds ?? 5,
3027
3707
  resolution,
3028
- prompt_expansion_mode: req.promptExpansion ?? "balanced",
3029
- enable_safety_checker: true
3708
+ prompt_expansion_mode: req.promptExpansion ?? "balanced"
3030
3709
  };
3710
+ if (req.safetyChecker !== void 0) input.enable_safety_checker = req.safetyChecker;
3031
3711
  if (req.seed !== void 0) input.seed = req.seed;
3032
3712
  return input;
3033
3713
  }
3714
+ var KLING_MODEL_IDS = /* @__PURE__ */ new Set([
3715
+ "fal-ai/kling-video/o1/video-to-video/reference"
3716
+ ]);
3034
3717
  function isKling(providerModelId) {
3035
- return providerModelId.toLowerCase().includes("kling");
3718
+ return KLING_MODEL_IDS.has(providerModelId.toLowerCase());
3036
3719
  }
3037
3720
  function klingO1Input(req) {
3038
3721
  const source = req.sourceVideo ? refToUrl(req.sourceVideo) : void 0;
@@ -3105,7 +3788,9 @@ async function generateFalVideo(req, ctx, deps) {
3105
3788
  if (lastFrame) base.end_image_url = lastFrame;
3106
3789
  } else {
3107
3790
  endpoint = `minimax/${family}/text-to-video`;
3108
- base.aspect_ratio = req.aspectRatio ?? "adaptive";
3791
+ if (req.aspectRatio !== void 0 && req.aspectRatio !== "adaptive") {
3792
+ base.aspect_ratio = req.aspectRatio;
3793
+ }
3109
3794
  }
3110
3795
  if (ctx.routing.fal === "cloudflare-passthrough") {
3111
3796
  return await passthroughVideoJob(ctx, req, endpoint, base);
@@ -3593,6 +4278,14 @@ async function generateVeoVideo(req, ctx, createGenAIClient) {
3593
4278
  model: modelId
3594
4279
  });
3595
4280
  }
4281
+ if (req.lastFrame !== void 0 && req.firstFrame === void 0) {
4282
+ throw new ModelKitError({
4283
+ code: "missing_input",
4284
+ message: `${modelId} requires firstFrame when lastFrame is set`,
4285
+ provider: "google-genai",
4286
+ model: modelId
4287
+ });
4288
+ }
3596
4289
  const duration = req.durationSeconds ?? 8;
3597
4290
  const personGeneration = req.firstFrame ?? refs.length > 0 ? "allow_adult" : "allow_all";
3598
4291
  let image;
@@ -3659,7 +4352,7 @@ async function generateVeoVideo(req, ctx, createGenAIClient) {
3659
4352
  throwSdkError(cause, modelId);
3660
4353
  }
3661
4354
  };
3662
- const jobId = operation.name ?? "veo-operation";
4355
+ const jobId = operation.name ?? crypto.randomUUID();
3663
4356
  const usage2 = videoUsage2(ctx, req, duration);
3664
4357
  return {
3665
4358
  jobId,
@@ -3798,6 +4491,7 @@ async function generateOmniVideo(req, ctx, createGenAIClient) {
3798
4491
  });
3799
4492
  }
3800
4493
  for (const ref of videos) {
4494
+ if (ref.label !== void 0) parts.push({ type: "text", text: ref.label });
3801
4495
  const media = await resolveMedia(ref, ctx.fetch, ctx.signal, "video/mp4", modelId);
3802
4496
  parts.push({
3803
4497
  type: "video",
@@ -3806,6 +4500,8 @@ async function generateOmniVideo(req, ctx, createGenAIClient) {
3806
4500
  });
3807
4501
  }
3808
4502
  if (req.firstFrame) {
4503
+ if (req.firstFrame.label !== void 0)
4504
+ parts.push({ type: "text", text: req.firstFrame.label });
3809
4505
  const media = await resolveMedia(
3810
4506
  req.firstFrame,
3811
4507
  ctx.fetch,
@@ -3824,6 +4520,7 @@ async function generateOmniVideo(req, ctx, createGenAIClient) {
3824
4520
  }
3825
4521
  }
3826
4522
  for (const ref of images) {
4523
+ if (ref.label !== void 0) parts.push({ type: "text", text: ref.label });
3827
4524
  const media = await resolveMedia(ref, ctx.fetch, ctx.signal, "image/png", modelId);
3828
4525
  if (media.bytes.length > OMNI_INLINE_LIMIT_BYTES) {
3829
4526
  parts.push({
@@ -3867,7 +4564,7 @@ async function generateOmniVideo(req, ctx, createGenAIClient) {
3867
4564
  } finally {
3868
4565
  await cleanupUploads();
3869
4566
  }
3870
- const jobId = interaction.id ?? "gemini-omni-interaction";
4567
+ const jobId = interaction.id ?? crypto.randomUUID();
3871
4568
  const usage2 = videoUsage2(ctx, req, duration);
3872
4569
  const getInteraction = async () => {
3873
4570
  try {
@@ -4093,11 +4790,11 @@ function createGoogleImageAdapter(deps = defaultGoogleImageDeps) {
4093
4790
  const model = google(ctx.model.providerModelId);
4094
4791
  const content = [];
4095
4792
  for (const ref of req.references ?? []) {
4793
+ if (ref.label !== void 0) content.push({ type: "text", text: ref.label });
4096
4794
  if ("url" in ref) content.push({ type: "image", image: ref.url });
4097
4795
  else content.push({ type: "image", image: ref.bytes, mediaType: ref.mime });
4098
4796
  }
4099
4797
  content.push({ type: "text", text: req.prompt });
4100
- void ctx.call.postCropToAspect;
4101
4798
  const imageConfig = {};
4102
4799
  if (req.aspectRatio !== void 0 && req.aspectRatio !== "adaptive")
4103
4800
  imageConfig.aspectRatio = req.aspectRatio;
@@ -4113,7 +4810,7 @@ function createGoogleImageAdapter(deps = defaultGoogleImageDeps) {
4113
4810
  const timeout = AbortSignal.timeout(GEMINI_IMAGE_TIMEOUT_MS);
4114
4811
  const signal = ctx.signal !== void 0 ? AbortSignal.any([ctx.signal, timeout]) : timeout;
4115
4812
  try {
4116
- return await deps.runTextGeneration({
4813
+ const genOptions = {
4117
4814
  model,
4118
4815
  messages: [{ role: "user", content }],
4119
4816
  maxRetries: 0,
@@ -4121,7 +4818,9 @@ function createGoogleImageAdapter(deps = defaultGoogleImageDeps) {
4121
4818
  google: googleOptions
4122
4819
  },
4123
4820
  abortSignal: signal
4124
- });
4821
+ };
4822
+ if (req.temperature !== void 0) genOptions.temperature = req.temperature;
4823
+ return await deps.runTextGeneration(genOptions);
4125
4824
  } catch (cause) {
4126
4825
  throw toProviderError(cause, ctx);
4127
4826
  }
@@ -4325,26 +5024,14 @@ function headers(ctx) {
4325
5024
  const { key, secret } = credentials(ctx);
4326
5025
  return { "Content-Type": "application/json", "hf-api-key": key, "hf-secret": secret };
4327
5026
  }
4328
- function refToUrl2(ref) {
4329
- if ("url" in ref) return ref.url;
4330
- return `data:${ref.mime};base64,${Buffer.from(ref.bytes).toString("base64")}`;
4331
- }
4332
- async function readBody2(res) {
4333
- const text2 = await res.text().catch(() => "");
4334
- try {
4335
- const decoded = JsonValueSchema.safeParse(JSON.parse(text2));
4336
- return decoded.success ? decoded.data : text2;
4337
- } catch {
4338
- return text2;
4339
- }
4340
- }
4341
5027
  function klingEndpoint(providerModelId) {
4342
5028
  if (providerModelId.startsWith("http")) return providerModelId;
4343
5029
  const path = providerModelId.startsWith("/") ? providerModelId.slice(1) : providerModelId;
4344
5030
  return `${PLATFORM}/${path}`;
4345
5031
  }
4346
- function isDop(providerModelId) {
4347
- return providerModelId.toLowerCase().includes("dop");
5032
+ var DOP_MODEL_IDS = /* @__PURE__ */ new Set(["dop", "dop-preview"]);
5033
+ function higgsfieldVideoKind(providerModelId) {
5034
+ return DOP_MODEL_IDS.has(providerModelId.toLowerCase()) ? "dop" : "kling";
4348
5035
  }
4349
5036
  function videoUsage3(ctx, req, duration) {
4350
5037
  const event = {
@@ -4388,12 +5075,12 @@ async function createRequest(ctx, url, init) {
4388
5075
  throw classifyThrown("higgsfield", cause, ctx.model.id);
4389
5076
  }
4390
5077
  if (!res.ok) {
4391
- throw classifyHttpError(res.status, await readBody2(res), "higgsfield", ctx.model.id);
5078
+ throw classifyHttpError(res.status, await readBody(res), "higgsfield", ctx.model.id);
4392
5079
  }
4393
5080
  return res;
4394
5081
  }
4395
5082
  async function generateKlingVideo(req, ctx) {
4396
- const firstFrame = req.firstFrame ? refToUrl2(req.firstFrame) : void 0;
5083
+ const firstFrame = req.firstFrame ? refToUrl(req.firstFrame) : void 0;
4397
5084
  if (!firstFrame) {
4398
5085
  throw new ModelKitError({
4399
5086
  code: "missing_input",
@@ -4412,7 +5099,7 @@ async function generateKlingVideo(req, ctx) {
4412
5099
  if (req.durationSeconds !== void 0) body.duration = req.durationSeconds;
4413
5100
  if (req.aspectRatio !== void 0 && req.aspectRatio !== "adaptive")
4414
5101
  body.aspect_ratio = req.aspectRatio;
4415
- if (req.lastFrame) body.last_image_url = refToUrl2(req.lastFrame);
5102
+ if (req.lastFrame) body.last_image_url = refToUrl(req.lastFrame);
4416
5103
  const endpoint = klingEndpoint(ctx.model.providerModelId);
4417
5104
  const retryOptions = {};
4418
5105
  if (ctx.signal !== void 0) retryOptions.signal = ctx.signal;
@@ -4438,9 +5125,9 @@ async function generateKlingVideo(req, ctx) {
4438
5125
  } catch {
4439
5126
  return void 0;
4440
5127
  }
4441
- if (res.status >= 500) return void 0;
5128
+ if (res.status === 429 || res.status >= 500) return void 0;
4442
5129
  if (!res.ok) {
4443
- throw classifyHttpError(res.status, await readBody2(res), "higgsfield", ctx.model.id);
5130
+ throw classifyHttpError(res.status, await readBody(res), "higgsfield", ctx.model.id);
4444
5131
  }
4445
5132
  const payload = JsonValueSchema.parse(await res.json());
4446
5133
  const parsed = klingStatusSchema.safeParse(payload);
@@ -4547,7 +5234,7 @@ function dopUrl(body) {
4547
5234
  var dopCreateSchema = z10.object({ id: z10.string() });
4548
5235
  var dopStatusSchema = z10.object({ status: z10.string().optional() });
4549
5236
  async function generateDopVideo(req, ctx) {
4550
- const firstFrame = req.firstFrame ? refToUrl2(req.firstFrame) : void 0;
5237
+ const firstFrame = req.firstFrame ? refToUrl(req.firstFrame) : void 0;
4551
5238
  if (!firstFrame) {
4552
5239
  throw new ModelKitError({
4553
5240
  code: "missing_input",
@@ -4567,7 +5254,7 @@ async function generateDopVideo(req, ctx) {
4567
5254
  webhook: null
4568
5255
  };
4569
5256
  if (req.lastFrame) {
4570
- params.input_images_end = [{ type: "image_url", image_url: refToUrl2(req.lastFrame) }];
5257
+ params.input_images_end = [{ type: "image_url", image_url: refToUrl(req.lastFrame) }];
4571
5258
  }
4572
5259
  if (req.seed !== void 0) params.seed = req.seed;
4573
5260
  const retryOptions = {};
@@ -4657,7 +5344,10 @@ async function generateDopVideo(req, ctx) {
4657
5344
  }
4658
5345
  var higgsfieldAdapter = {
4659
5346
  id: "higgsfield",
4660
- generateVideo: (req, ctx) => isDop(ctx.model.providerModelId) ? generateDopVideo(req, ctx) : generateKlingVideo(req, ctx)
5347
+ generateVideo: (req, ctx) => {
5348
+ throwIfAborted(ctx.signal);
5349
+ return higgsfieldVideoKind(ctx.model.providerModelId) === "dop" ? generateDopVideo(req, ctx) : generateKlingVideo(req, ctx);
5350
+ }
4661
5351
  };
4662
5352
 
4663
5353
  // src/adapters/openai-image.ts
@@ -4752,6 +5442,7 @@ var openaiUsageSchema = z11.object({
4752
5442
  var openaiImageAdapter = {
4753
5443
  id: "openai-image",
4754
5444
  async generateImage(req, ctx) {
5445
+ throwIfAborted(ctx.signal);
4755
5446
  const apiKey = ctx.keys.openai;
4756
5447
  if (!apiKey)
4757
5448
  throw new ModelKitError({
@@ -4761,6 +5452,13 @@ var openaiImageAdapter = {
4761
5452
  model: ctx.model.id,
4762
5453
  retryable: false
4763
5454
  });
5455
+ if ((req.references ?? []).some((ref) => ref.label !== void 0)) {
5456
+ ctx.warnings.push({
5457
+ code: "option_ignored",
5458
+ field: "references.label",
5459
+ message: "openai-image cannot express reference labels; ignoring them"
5460
+ });
5461
+ }
4764
5462
  const client2 = new OpenAI({ apiKey, fetch: ctx.fetch, maxRetries: 0 });
4765
5463
  const size = req.exactSize !== void 0 ? req.exactSize : req.aspectRatio === void 0 || req.aspectRatio === "adaptive" ? void 0 : aspectRatioToSize(req.aspectRatio, req.size ?? "1K");
4766
5464
  const refs = req.references ?? [];
@@ -4774,24 +5472,34 @@ var openaiImageAdapter = {
4774
5472
  try {
4775
5473
  if (!isEdit) {
4776
5474
  if (size !== void 0) {
4777
- return await client2.images.generate({
5475
+ return await client2.images.generate(
5476
+ {
5477
+ model: ctx.model.providerModelId,
5478
+ prompt: req.prompt,
5479
+ size,
5480
+ quality: sdkQuality,
5481
+ ...count
5482
+ },
5483
+ { signal: ctx.signal }
5484
+ );
5485
+ }
5486
+ return await client2.images.generate(
5487
+ {
4778
5488
  model: ctx.model.providerModelId,
4779
5489
  prompt: req.prompt,
4780
- size,
4781
5490
  quality: sdkQuality,
4782
5491
  ...count
4783
- });
4784
- }
4785
- return await client2.images.generate({
4786
- model: ctx.model.providerModelId,
4787
- prompt: req.prompt,
4788
- quality: sdkQuality,
4789
- ...count
4790
- });
5492
+ },
5493
+ { signal: ctx.signal }
5494
+ );
4791
5495
  }
4792
5496
  const files = await Promise.all(
4793
5497
  refs.slice(0, MAX_EDIT_REFERENCES).map(async (ref, i) => {
4794
- const { bytes, mime } = await mediaRefToBytes(ref, ctx.fetch);
5498
+ const { bytes, mime } = await mediaRefToBytes(ref, ctx.fetch, {
5499
+ signal: ctx.signal,
5500
+ provider: "openai-image",
5501
+ model: ctx.model.id
5502
+ });
4795
5503
  const ext = mime.includes("jpeg") ? "jpg" : mime.includes("webp") ? "webp" : "png";
4796
5504
  return new File([bytes], `ref-${i}.${ext}`, { type: mime });
4797
5505
  })
@@ -4799,22 +5507,28 @@ var openaiImageAdapter = {
4799
5507
  const single = files.length === 1 ? files[0] : void 0;
4800
5508
  const imageInput = single ?? files;
4801
5509
  if (size !== void 0) {
4802
- return await client2.images.edit({
5510
+ return await client2.images.edit(
5511
+ {
5512
+ model: ctx.model.providerModelId,
5513
+ image: imageInput,
5514
+ prompt: req.prompt,
5515
+ size,
5516
+ quality: sdkQuality,
5517
+ ...count
5518
+ },
5519
+ { signal: ctx.signal }
5520
+ );
5521
+ }
5522
+ return await client2.images.edit(
5523
+ {
4803
5524
  model: ctx.model.providerModelId,
4804
5525
  image: imageInput,
4805
5526
  prompt: req.prompt,
4806
- size,
4807
5527
  quality: sdkQuality,
4808
5528
  ...count
4809
- });
4810
- }
4811
- return await client2.images.edit({
4812
- model: ctx.model.providerModelId,
4813
- image: imageInput,
4814
- prompt: req.prompt,
4815
- quality: sdkQuality,
4816
- ...count
4817
- });
5529
+ },
5530
+ { signal: ctx.signal }
5531
+ );
4818
5532
  } catch (cause) {
4819
5533
  throw toProviderError2(cause, ctx);
4820
5534
  }
@@ -4896,7 +5610,7 @@ async function refToBytes(ctx, ref) {
4896
5610
  if ("bytes" in ref) return { bytes: ref.bytes, mime: ref.mime };
4897
5611
  let res;
4898
5612
  try {
4899
- res = await ctx.fetch(ref.url);
5613
+ res = await ctx.fetch(ref.url, ctx.signal ? { signal: ctx.signal } : {});
4900
5614
  } catch (cause) {
4901
5615
  throw classifyThrown("prodia", cause, ctx.model.id);
4902
5616
  }
@@ -4942,6 +5656,12 @@ function emptyImage() {
4942
5656
  retryable: false
4943
5657
  });
4944
5658
  }
5659
+ function inputExtension(mime) {
5660
+ if (mime.includes("jpeg") || mime.includes("jpg")) return "jpg";
5661
+ if (mime.includes("webp")) return "webp";
5662
+ if (mime.includes("gif")) return "gif";
5663
+ return "png";
5664
+ }
4945
5665
  function isBgRemove(providerModelId) {
4946
5666
  const id = providerModelId.toLowerCase();
4947
5667
  return id.includes("remove-background") || id.includes("bg-remove");
@@ -4950,6 +5670,7 @@ function isKlein(providerModelId) {
4950
5670
  return providerModelId.toLowerCase().includes("klein");
4951
5671
  }
4952
5672
  async function generateProdiaImage(req, ctx) {
5673
+ throwIfAborted(ctx.signal);
4953
5674
  const key = ctx.keys.prodia;
4954
5675
  if (!key) {
4955
5676
  throw new ModelKitError({
@@ -5048,7 +5769,9 @@ async function generateProdiaImage(req, ctx) {
5048
5769
  "input",
5049
5770
  // Uint8Array<ArrayBufferLike> is not a valid BlobPart under the
5050
5771
  // strict lib types; copy into a fresh ArrayBuffer-backed view.
5051
- new File([new Uint8Array(input.bytes)], `input-${i}.jpg`, { type: "image/jpeg" })
5772
+ new File([new Uint8Array(input.bytes)], `input-${i}.${inputExtension(input.mime)}`, {
5773
+ type: input.mime
5774
+ })
5052
5775
  );
5053
5776
  });
5054
5777
  const retryOpts = {
@@ -5063,11 +5786,13 @@ async function generateProdiaImage(req, ctx) {
5063
5786
  res = await withRetry(async () => {
5064
5787
  let attemptRes;
5065
5788
  try {
5066
- attemptRes = await ctx.fetch(PRODIA_JOB_URL, {
5789
+ const init = {
5067
5790
  method: "POST",
5068
5791
  headers: { Authorization: `Bearer ${key}`, Accept: accept },
5069
5792
  body: form
5070
- });
5793
+ };
5794
+ if (ctx.signal !== void 0) init.signal = ctx.signal;
5795
+ attemptRes = await ctx.fetch(PRODIA_JOB_URL, init);
5071
5796
  } catch (err) {
5072
5797
  throw classifyThrown("prodia", err, ctx.model.id);
5073
5798
  }
@@ -5128,11 +5853,7 @@ function client(ctx, deps) {
5128
5853
  retryable: false
5129
5854
  });
5130
5855
  }
5131
- return deps.createReplicateClient({ auth: token });
5132
- }
5133
- function refToUrl3(ref) {
5134
- if ("url" in ref) return ref.url;
5135
- return `data:${ref.mime};base64,${Buffer.from(ref.bytes).toString("base64")}`;
5856
+ return deps.createReplicateClient({ auth: token, fetch: ctx.fetch });
5136
5857
  }
5137
5858
  function route(providerModelId) {
5138
5859
  if (providerModelId.includes(":")) {
@@ -5152,8 +5873,9 @@ function replicateError(cause, ctx) {
5152
5873
  const status = info.status ?? (nested.success ? nested.data.response?.status : void 0);
5153
5874
  return classifyThrown("replicate", cause, ctx.model.id, { status, body: info.body });
5154
5875
  }
5155
- function isLower(id) {
5156
- return id.toLowerCase();
5876
+ function baseModelId(providerModelId) {
5877
+ const colon = providerModelId.indexOf(":");
5878
+ return colon === -1 ? providerModelId : providerModelId.slice(0, colon);
5157
5879
  }
5158
5880
  function predictionErrorBody(cause) {
5159
5881
  const parsed = JsonValueSchema.safeParse(cause);
@@ -5201,6 +5923,7 @@ function noOutput() {
5201
5923
  });
5202
5924
  }
5203
5925
  async function runPrediction(ctx, input, deps) {
5926
+ throwIfAborted(ctx.signal);
5204
5927
  const api = client(ctx, deps);
5205
5928
  const createOptions = { ...route(ctx.model.providerModelId), input };
5206
5929
  let prediction;
@@ -5208,12 +5931,14 @@ async function runPrediction(ctx, input, deps) {
5208
5931
  prediction = await withRetry(
5209
5932
  async () => {
5210
5933
  try {
5211
- return await api.predictions.create(createOptions);
5934
+ return await api.predictions.create(
5935
+ ctx.signal ? { ...createOptions, signal: ctx.signal } : createOptions
5936
+ );
5212
5937
  } catch (cause) {
5213
5938
  throw replicateError(cause, ctx);
5214
5939
  }
5215
5940
  },
5216
- ctx.signal ? { signal: ctx.signal } : {}
5941
+ { attempts: 1 }
5217
5942
  );
5218
5943
  } catch (cause) {
5219
5944
  throw replicateError(cause, ctx);
@@ -5226,7 +5951,10 @@ async function runPrediction(ctx, input, deps) {
5226
5951
  const done = await pollUntil(async () => {
5227
5952
  let current;
5228
5953
  try {
5229
- current = await api.predictions.get(prediction.id);
5954
+ current = await api.predictions.get(
5955
+ prediction.id,
5956
+ ctx.signal ? { signal: ctx.signal } : void 0
5957
+ );
5230
5958
  } catch (cause) {
5231
5959
  throw replicateError(cause, ctx);
5232
5960
  }
@@ -5293,21 +6021,32 @@ async function download(ctx, url) {
5293
6021
  const mime = res.headers.get("content-type") ?? "image/png";
5294
6022
  return { bytes: new Uint8Array(await res.arrayBuffer()), mime };
5295
6023
  }
5296
- function isUpscaler(providerModelId) {
5297
- const id = isLower(providerModelId);
5298
- return id.includes("upscal") || id.includes("crystal") || id.includes("topaz");
5299
- }
5300
- function isTopazUpscaler(providerModelId) {
5301
- return isLower(providerModelId).includes("topazlabs/image-upscale");
5302
- }
5303
- function isQwen(providerModelId) {
5304
- return isLower(providerModelId).includes("qwen");
5305
- }
5306
- function isWanImage(providerModelId) {
5307
- return isLower(providerModelId).includes("wan-2.7");
6024
+ var REPLICATE_IMAGE_KINDS = {
6025
+ "topazlabs/image-upscale": "topaz",
6026
+ "philz1337x/crystal-upscaler": "upscaler",
6027
+ "wan-video/wan-2.7-image-pro": "wan",
6028
+ "qwen/qwen-image-2-pro": "qwen-image",
6029
+ "qwen-edit-apps/qwen-image-edit-plus-lora-skin": "qwen-edit"
6030
+ };
6031
+ function imageKind(providerModelId) {
6032
+ return REPLICATE_IMAGE_KINDS[baseModelId(providerModelId)] ?? "generic";
6033
+ }
6034
+ var REPLICATE_VIDEO_KINDS = {
6035
+ "kling-video/v2.5-turbo/pro/image-to-video": "kling",
6036
+ "kling-video/v3.0/pro/image-to-video": "kling",
6037
+ "kwaivgi/kling-v3-video": "kling",
6038
+ "kwaivgi/kling-v3-omni-video": "kling-omni",
6039
+ "minimax/hailuo-02": "hailuo"
6040
+ };
6041
+ function videoKind(providerModelId) {
6042
+ return REPLICATE_VIDEO_KINDS[baseModelId(providerModelId)] ?? "wan";
5308
6043
  }
5309
- function isQwenEditPlus(providerModelId) {
5310
- return isLower(providerModelId).includes("edit");
6044
+ var REPLICATE_AUDIO_KINDS = {
6045
+ "zsxkib/mmaudio": "mmaudio",
6046
+ "elevenlabs/music": "elevenlabs"
6047
+ };
6048
+ function audioKind(providerModelId) {
6049
+ return REPLICATE_AUDIO_KINDS[baseModelId(providerModelId)] ?? "lyria";
5311
6050
  }
5312
6051
  var QWEN_IMAGE_RATIOS = /* @__PURE__ */ new Set([
5313
6052
  "1:1",
@@ -5350,87 +6089,113 @@ function wanImageSize(aspectRatio, size) {
5350
6089
  async function generateReplicateImage(req, ctx, deps) {
5351
6090
  const id = ctx.model.providerModelId;
5352
6091
  let input;
5353
- if (isTopazUpscaler(id)) {
5354
- const first = (req.references ?? [])[0];
5355
- if (!first) {
5356
- throw new ModelKitError({
5357
- code: "missing_input",
5358
- message: `${ctx.model.id} requires a reference image to upscale`,
5359
- provider: "replicate",
5360
- model: ctx.model.id,
5361
- retryable: false
5362
- });
6092
+ switch (imageKind(id)) {
6093
+ case "topaz": {
6094
+ const first = (req.references ?? [])[0];
6095
+ if (!first) {
6096
+ throw new ModelKitError({
6097
+ code: "missing_input",
6098
+ message: `${ctx.model.id} requires a reference image to upscale`,
6099
+ provider: "replicate",
6100
+ model: ctx.model.id,
6101
+ retryable: false
6102
+ });
6103
+ }
6104
+ let format = req.outputFormat === "jpeg" ? "jpg" : req.outputFormat ?? "png";
6105
+ if (format !== "png" && format !== "jpg") {
6106
+ ctx.warnings.push({
6107
+ code: "option_ignored",
6108
+ field: "outputFormat",
6109
+ from: format,
6110
+ message: "topazlabs/image-upscale supports only png and jpg; using png"
6111
+ });
6112
+ format = "png";
6113
+ }
6114
+ const upscale = req.upscale;
6115
+ const topaz = {
6116
+ image: refToUrl(first),
6117
+ enhance_model: upscale?.enhanceModel ?? "Standard V2",
6118
+ output_format: format,
6119
+ upscale_factor: `${req.scaleFactor ?? 2}x`,
6120
+ face_enhancement: upscale?.faceEnhancement ?? false
6121
+ };
6122
+ if (upscale?.subjectDetection !== void 0)
6123
+ topaz.subject_detection = upscale.subjectDetection;
6124
+ if (upscale?.faceEnhancementCreativity !== void 0)
6125
+ topaz.face_enhancement_creativity = upscale.faceEnhancementCreativity;
6126
+ if (upscale?.faceEnhancementStrength !== void 0)
6127
+ topaz.face_enhancement_strength = upscale.faceEnhancementStrength;
6128
+ input = topaz;
6129
+ break;
5363
6130
  }
5364
- let format = req.outputFormat === "jpeg" ? "jpg" : req.outputFormat ?? "png";
5365
- if (format !== "png" && format !== "jpg") {
5366
- ctx.warnings.push({
5367
- code: "option_ignored",
5368
- field: "outputFormat",
5369
- from: format,
5370
- message: "topazlabs/image-upscale supports only png and jpg; using png"
5371
- });
5372
- format = "png";
6131
+ case "upscaler": {
6132
+ const first = (req.references ?? [])[0];
6133
+ if (!first) {
6134
+ throw new ModelKitError({
6135
+ code: "missing_input",
6136
+ message: `${ctx.model.id} requires a reference image to upscale`,
6137
+ provider: "replicate",
6138
+ model: ctx.model.id,
6139
+ retryable: false
6140
+ });
6141
+ }
6142
+ const imageInput = { prompt: req.prompt };
6143
+ imageInput.image = refToUrl(first);
6144
+ imageInput.creativity = 0;
6145
+ imageInput.scale_factor = 2;
6146
+ imageInput.output_format = "png";
6147
+ input = imageInput;
6148
+ break;
5373
6149
  }
5374
- input = {
5375
- image: refToUrl3(first),
5376
- enhance_model: "Standard V2",
5377
- output_format: format,
5378
- upscale_factor: `${req.scaleFactor ?? 2}x`,
5379
- face_enhancement: true
5380
- };
5381
- } else if (isUpscaler(id)) {
5382
- const refs = req.references ?? [];
5383
- const first = refs[0];
5384
- if (!first) {
5385
- throw new ModelKitError({
5386
- code: "missing_input",
5387
- message: `${ctx.model.id} requires a reference image to upscale`,
5388
- provider: "replicate",
5389
- model: ctx.model.id,
5390
- retryable: false
5391
- });
6150
+ case "wan": {
6151
+ const refs = (req.references ?? []).map(refToUrl);
6152
+ const imageInput = { prompt: req.prompt };
6153
+ if (refs.length > 0) imageInput.images = refs;
6154
+ imageInput.size = wanImageSize(req.aspectRatio, req.size);
6155
+ input = imageInput;
6156
+ break;
5392
6157
  }
5393
- const imageInput = { prompt: req.prompt };
5394
- imageInput.image = refToUrl3(first);
5395
- imageInput.creativity = 0;
5396
- imageInput.scale_factor = 2;
5397
- imageInput.output_format = "png";
5398
- input = imageInput;
5399
- } else if (isWanImage(id)) {
5400
- const refs = (req.references ?? []).map(refToUrl3);
5401
- const imageInput = { prompt: req.prompt };
5402
- if (refs.length > 0) imageInput.images = refs;
5403
- imageInput.size = wanImageSize(req.aspectRatio, req.size);
5404
- input = imageInput;
5405
- } else if (isQwen(id)) {
5406
- const refs = (req.references ?? []).map(refToUrl3);
5407
- const firstUrl = refs[0];
5408
- const imageInput = { prompt: req.prompt };
5409
- if (firstUrl !== void 0) {
5410
- imageInput.image = firstUrl;
5411
- if (!isQwenEditPlus(id)) imageInput.match_input_image = true;
6158
+ case "qwen-image": {
6159
+ const refs = (req.references ?? []).map(refToUrl);
6160
+ const firstUrl = refs[0];
6161
+ const imageInput = { prompt: req.prompt };
6162
+ if (firstUrl !== void 0) {
6163
+ imageInput.image = firstUrl;
6164
+ imageInput.match_input_image = true;
6165
+ } else if (req.aspectRatio !== void 0 && QWEN_IMAGE_RATIOS.has(req.aspectRatio)) {
6166
+ imageInput.aspect_ratio = req.aspectRatio;
6167
+ }
6168
+ input = imageInput;
6169
+ break;
5412
6170
  }
5413
- const ratios = isQwenEditPlus(id) ? QWEN_EDIT_RATIOS : QWEN_IMAGE_RATIOS;
5414
- if (req.aspectRatio !== void 0) {
5415
- if (ratios.has(req.aspectRatio)) imageInput.aspect_ratio = req.aspectRatio;
5416
- else
5417
- ctx.warnings.push({
5418
- code: "option_ignored",
5419
- field: "aspectRatio",
5420
- from: req.aspectRatio,
5421
- message: `${id} has no ${req.aspectRatio} aspect_ratio; using the provider default`
5422
- });
6171
+ case "qwen-edit": {
6172
+ const refs = (req.references ?? []).map(refToUrl);
6173
+ const firstUrl = refs[0];
6174
+ const imageInput = { prompt: req.prompt };
6175
+ if (firstUrl !== void 0) imageInput.image = firstUrl;
6176
+ if (req.aspectRatio !== void 0) {
6177
+ if (QWEN_EDIT_RATIOS.has(req.aspectRatio))
6178
+ imageInput.aspect_ratio = req.aspectRatio;
6179
+ else
6180
+ ctx.warnings.push({
6181
+ code: "option_ignored",
6182
+ field: "aspectRatio",
6183
+ from: req.aspectRatio,
6184
+ message: `${id} has no ${req.aspectRatio} aspect_ratio; using the provider default`
6185
+ });
6186
+ }
6187
+ if (req.outputFormat !== void 0) {
6188
+ imageInput.output_format = req.outputFormat === "jpeg" ? "jpg" : req.outputFormat;
6189
+ }
6190
+ input = imageInput;
6191
+ break;
5423
6192
  }
5424
- if (isQwenEditPlus(id) && req.outputFormat !== void 0) {
5425
- imageInput.output_format = req.outputFormat === "jpeg" ? "jpg" : req.outputFormat;
6193
+ default: {
6194
+ const first = (req.references ?? [])[0];
6195
+ const imageInput = { prompt: req.prompt };
6196
+ if (first) imageInput.image = refToUrl(first);
6197
+ input = imageInput;
5426
6198
  }
5427
- input = imageInput;
5428
- } else {
5429
- const refs = req.references ?? [];
5430
- const first = refs[0];
5431
- const imageInput = { prompt: req.prompt };
5432
- if (first) imageInput.image = refToUrl3(first);
5433
- input = imageInput;
5434
6199
  }
5435
6200
  const { url, predictTime, raw } = await runPrediction(ctx, input, deps);
5436
6201
  const file = await download(ctx, url);
@@ -5447,8 +6212,8 @@ function wanSize(aspectRatio) {
5447
6212
  return w > h ? "2048*1152" : "1536*2048";
5448
6213
  }
5449
6214
  function klingInput(req) {
5450
- const images = (req.firstFrame ? [req.firstFrame] : []).map(refToUrl3);
5451
- if (req.lastFrame) images.push(refToUrl3(req.lastFrame));
6215
+ const images = (req.firstFrame ? [req.firstFrame] : []).map(refToUrl);
6216
+ if (req.lastFrame) images.push(refToUrl(req.lastFrame));
5452
6217
  const input = {
5453
6218
  prompt: req.prompt,
5454
6219
  duration: req.durationSeconds ?? 10,
@@ -5466,13 +6231,18 @@ function klingInput(req) {
5466
6231
  if (req.negativePrompt !== void 0) input.negative_prompt = req.negativePrompt;
5467
6232
  return input;
5468
6233
  }
6234
+ function klingOmniMode(req) {
6235
+ if (req.resolution === "1080p") return "pro";
6236
+ if (req.resolution === "720p") return "standard";
6237
+ return req.mode ?? "standard";
6238
+ }
5469
6239
  function klingOmniInput(req) {
5470
- const images = (req.firstFrame ? [req.firstFrame] : []).map(refToUrl3);
5471
- if (req.lastFrame) images.push(refToUrl3(req.lastFrame));
6240
+ const images = (req.firstFrame ? [req.firstFrame] : []).map(refToUrl);
6241
+ if (req.lastFrame) images.push(refToUrl(req.lastFrame));
5472
6242
  const input = {
5473
6243
  prompt: req.prompt,
5474
6244
  duration: req.durationSeconds ?? 10,
5475
- mode: req.mode ?? "standard",
6245
+ mode: klingOmniMode(req),
5476
6246
  generate_audio: req.generateAudio ?? false
5477
6247
  };
5478
6248
  const startImage = images[0];
@@ -5483,7 +6253,7 @@ function klingOmniInput(req) {
5483
6253
  } else if (req.aspectRatio !== "adaptive") {
5484
6254
  input.aspect_ratio = req.aspectRatio ?? "16:9";
5485
6255
  }
5486
- const refs = (req.referenceImages ?? []).map(refToUrl3);
6256
+ const refs = (req.referenceImages ?? []).map(refToUrl);
5487
6257
  if (refs.length > 0) input.reference_images = refs;
5488
6258
  return input;
5489
6259
  }
@@ -5494,8 +6264,8 @@ function hailuoInput(req) {
5494
6264
  resolution: req.resolution ?? "768p",
5495
6265
  prompt_optimizer: true
5496
6266
  };
5497
- if (req.firstFrame) input.first_frame_image = refToUrl3(req.firstFrame);
5498
- if (req.lastFrame) input.last_frame_image = refToUrl3(req.lastFrame);
6267
+ if (req.firstFrame) input.first_frame_image = refToUrl(req.firstFrame);
6268
+ if (req.lastFrame) input.last_frame_image = refToUrl(req.lastFrame);
5499
6269
  return input;
5500
6270
  }
5501
6271
  function wanInput(req) {
@@ -5504,12 +6274,13 @@ function wanInput(req) {
5504
6274
  duration: req.durationSeconds ?? 5,
5505
6275
  size: wanSize(req.aspectRatio)
5506
6276
  };
5507
- if (req.firstFrame) input.image = refToUrl3(req.firstFrame);
6277
+ if (req.firstFrame) input.image = refToUrl(req.firstFrame);
5508
6278
  return input;
5509
6279
  }
5510
6280
  async function generateReplicateVideo(req, ctx, deps) {
5511
- const id = isLower(ctx.model.providerModelId);
5512
- const input = id.includes("kling") ? id.includes("omni") ? klingOmniInput(req) : klingInput(req) : id.includes("hailuo") ? hailuoInput(req) : wanInput(req);
6281
+ throwIfAborted(ctx.signal);
6282
+ const kind = videoKind(ctx.model.providerModelId);
6283
+ const input = kind === "kling" ? klingInput(req) : kind === "kling-omni" ? klingOmniInput(req) : kind === "hailuo" ? hailuoInput(req) : wanInput(req);
5513
6284
  const api = client(ctx, deps);
5514
6285
  const createOptions = { ...route(ctx.model.providerModelId), input };
5515
6286
  let prediction;
@@ -5517,12 +6288,14 @@ async function generateReplicateVideo(req, ctx, deps) {
5517
6288
  prediction = await withRetry(
5518
6289
  async () => {
5519
6290
  try {
5520
- return await api.predictions.create(createOptions);
6291
+ return await api.predictions.create(
6292
+ ctx.signal ? { ...createOptions, signal: ctx.signal } : createOptions
6293
+ );
5521
6294
  } catch (cause) {
5522
6295
  throw replicateError(cause, ctx);
5523
6296
  }
5524
6297
  },
5525
- ctx.signal ? { signal: ctx.signal } : {}
6298
+ { attempts: 1 }
5526
6299
  );
5527
6300
  } catch (cause) {
5528
6301
  throw replicateError(cause, ctx);
@@ -5531,7 +6304,10 @@ async function generateReplicateVideo(req, ctx, deps) {
5531
6304
  const check = async () => {
5532
6305
  let current;
5533
6306
  try {
5534
- current = await api.predictions.get(jobId);
6307
+ current = await api.predictions.get(
6308
+ jobId,
6309
+ ctx.signal ? { signal: ctx.signal } : void 0
6310
+ );
5535
6311
  } catch (cause) {
5536
6312
  throw replicateError(cause, ctx);
5537
6313
  }
@@ -5632,7 +6408,7 @@ function mmaudioInput(req, ctx) {
5632
6408
  });
5633
6409
  }
5634
6410
  const input = {
5635
- video: refToUrl3(video),
6411
+ video: refToUrl(video),
5636
6412
  duration: req.durationSeconds ?? 8,
5637
6413
  seed: req.seed ?? -1
5638
6414
  };
@@ -5651,8 +6427,8 @@ function predictionRaw(prediction) {
5651
6427
  return raw;
5652
6428
  }
5653
6429
  async function generateReplicateAudio(req, ctx, deps) {
5654
- const id = isLower(ctx.model.providerModelId);
5655
- const input = id.includes("mmaudio") ? mmaudioInput(req, ctx) : id.includes("elevenlabs") ? elevenLabsMusicInput(req, ctx) : lyriaAudioInput(req, ctx);
6430
+ const kind = audioKind(ctx.model.providerModelId);
6431
+ const input = kind === "mmaudio" ? mmaudioInput(req, ctx) : kind === "elevenlabs" ? elevenLabsMusicInput(req, ctx) : lyriaAudioInput(req, ctx);
5656
6432
  const { url, predictTime, raw } = await runPrediction(ctx, input, deps);
5657
6433
  const usage2 = audioUsage(ctx, req);
5658
6434
  if (ctx.model.usage.unit === "seconds") {
@@ -5681,10 +6457,6 @@ function createXaiVideoAdapter(deps = defaultXaiDeps) {
5681
6457
  };
5682
6458
  }
5683
6459
  var xaiVideoAdapter = createXaiVideoAdapter();
5684
- function refToUrl4(ref) {
5685
- if ("url" in ref) return ref.url;
5686
- return `data:${ref.mime};base64,${Buffer.from(ref.bytes).toString("base64")}`;
5687
- }
5688
6460
  var xaiResponseStatusSchema = z13.object({
5689
6461
  response: z13.object({ status: z13.number() }).partial()
5690
6462
  });
@@ -5716,13 +6488,14 @@ async function generateXaiVideo(req, ctx, deps) {
5716
6488
  retryable: false
5717
6489
  });
5718
6490
  }
5719
- const refUrls = (req.referenceImages ?? []).map(refToUrl4).slice(0, 7);
5720
- const resolution = req.resolution === "480p" ? "480p" : "720p";
6491
+ const refUrls = (req.referenceImages ?? []).map(refToUrl).slice(0, 7);
5721
6492
  const xaiOptions = {
5722
- resolution,
5723
6493
  pollIntervalMs: POLL_INTERVAL_MS2,
5724
6494
  pollTimeoutMs: POLL_TIMEOUT_MS2
5725
6495
  };
6496
+ if (req.resolution === "480p" || req.resolution === "720p" || req.resolution === "1080p") {
6497
+ xaiOptions.resolution = req.resolution;
6498
+ }
5726
6499
  if (refUrls.length > 0) {
5727
6500
  xaiOptions.mode = "reference-to-video";
5728
6501
  xaiOptions.referenceImageUrls = refUrls;
@@ -5742,7 +6515,7 @@ async function generateXaiVideo(req, ctx, deps) {
5742
6515
  providerOptions: { xai: xaiOptions }
5743
6516
  };
5744
6517
  if (req.firstFrame) {
5745
- callBody.frameImages = [{ image: refToUrl4(req.firstFrame), frameType: "first_frame" }];
6518
+ callBody.frameImages = [{ image: refToUrl(req.firstFrame), frameType: "first_frame" }];
5746
6519
  }
5747
6520
  if (req.aspectRatio !== void 0 && req.aspectRatio !== "adaptive") {
5748
6521
  callBody.aspectRatio = req.aspectRatio;
@@ -5778,7 +6551,8 @@ async function generateXaiVideo(req, ctx, deps) {
5778
6551
  return settled;
5779
6552
  };
5780
6553
  return {
5781
- jobId: `${ctx.model.id}-grok`,
6554
+ // xAI exposes no request id on the synchronous handle, so each job gets a fresh one.
6555
+ jobId: crypto.randomUUID(),
5782
6556
  async poll() {
5783
6557
  if (failed !== void 0) return { status: "failed" };
5784
6558
  return settled ? { status: "succeeded" } : { status: "pending" };
@@ -5912,6 +6686,7 @@ export {
5912
6686
  prodiaAdapter,
5913
6687
  readErrorBody,
5914
6688
  replicateAdapter,
6689
+ resolveAudioOptions,
5915
6690
  resolveImageOptions,
5916
6691
  resolveKeys,
5917
6692
  resolveVideoOptions,