@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.cjs CHANGED
@@ -68,6 +68,7 @@ __export(index_exports, {
68
68
  prodiaAdapter: () => prodiaAdapter,
69
69
  readErrorBody: () => readErrorBody,
70
70
  replicateAdapter: () => replicateAdapter,
71
+ resolveAudioOptions: () => resolveAudioOptions,
71
72
  resolveImageOptions: () => resolveImageOptions,
72
73
  resolveKeys: () => resolveKeys,
73
74
  resolveVideoOptions: () => resolveVideoOptions,
@@ -165,6 +166,7 @@ var Catalog = class _Catalog {
165
166
 
166
167
  // src/catalog/image.ts
167
168
  var IMAGE_USAGE = { unit: "images", modality: "IMAGE" };
169
+ var IMAGE_GENERATE_EDIT = { imageGenerate: true, imageEdit: true };
168
170
  var GEMINI_RATIOS = [
169
171
  "1:1",
170
172
  "2:3",
@@ -178,7 +180,7 @@ var GEMINI_RATIOS = [
178
180
  "5:4",
179
181
  "adaptive"
180
182
  ];
181
- function geminiImage(id, label, aliases, sizes) {
183
+ function geminiImage(id, label, aliases, sizes, display) {
182
184
  const model = {
183
185
  id,
184
186
  aliases,
@@ -187,25 +189,47 @@ function geminiImage(id, label, aliases, sizes) {
187
189
  adapter: "google-image",
188
190
  providerModelId: id,
189
191
  lifecycle: "active",
190
- constraints: { aspectRatios: [...GEMINI_RATIOS], sizes, supports: ["safetySettings"] },
192
+ constraints: {
193
+ aspectRatios: [...GEMINI_RATIOS],
194
+ sizes,
195
+ supports: ["safetySettings", "temperature"]
196
+ },
191
197
  defaults: { size: "1K", aspectRatio: "1:1" },
192
198
  usage: { ...IMAGE_USAGE },
193
- pricingKind: "image_size"
199
+ pricingKind: "image_size",
200
+ display,
201
+ capabilities: { ...IMAGE_GENERATE_EDIT }
194
202
  };
195
203
  return model;
196
204
  }
197
205
  var IMAGE_MODELS = [
198
- geminiImage("gemini-3-pro-image-preview", "Nano Banana Pro", ["nb-pro"], ["1K", "2K", "4K"]),
206
+ geminiImage("gemini-3-pro-image-preview", "Nano Banana Pro", ["nb-pro"], ["1K", "2K", "4K"], {
207
+ provider: "google",
208
+ description: "Create stunning, production-quality visuals with exceptional realism, detail, and typography. Best for campaigns, e-commerce, product photography, and hero creatives.",
209
+ logo: "https://d31vvfpk2ppdyq.cloudfront.net/assets/image/192485d2-a76a-4657-9c06-930b2c70badd_compressed.png"
210
+ }),
199
211
  geminiImage(
200
212
  "gemini-3.1-flash-image-preview",
201
213
  "Nano Banana Flash",
202
214
  ["nb-2"],
203
215
  // Flash 3.1 is the only Gemini image model with the 512 tier.
204
- ["512", "1K", "2K", "4K"]
216
+ ["512", "1K", "2K", "4K"],
217
+ {
218
+ provider: "google",
219
+ description: "Generate high-quality images at speed. Perfect for brainstorming, moodboards, concept testing, creative exploration, and large batch workflows.",
220
+ logo: "https://d31vvfpk2ppdyq.cloudfront.net/assets/image/192485d2-a76a-4657-9c06-930b2c70badd_compressed.png"
221
+ }
205
222
  ),
206
- geminiImage("gemini-3.1-flash-lite-image", "Nano Banana Lite", ["nb-lite"], ["1K"]),
223
+ geminiImage("gemini-3.1-flash-lite-image", "Nano Banana Lite", ["nb-lite"], ["1K"], {
224
+ provider: "google",
225
+ description: "The fastest and most affordable image model. Ideal for rapid drafts, bulk exploration, and iterative concept testing at 1K resolution.",
226
+ logo: "https://d31vvfpk2ppdyq.cloudfront.net/assets/image/192485d2-a76a-4657-9c06-930b2c70badd_compressed.png"
227
+ }),
207
228
  // trigger-workflows validates 1K only; pricing seeds 2K/4K rows so allow all three.
208
- geminiImage("gemini-2.5-flash-image", "Gemini 2.5 Flash Image", [], ["1K", "2K", "4K"]),
229
+ geminiImage("gemini-2.5-flash-image", "Gemini 2.5 Flash Image", [], ["1K", "2K", "4K"], {
230
+ provider: "google",
231
+ description: "Nano Banana (legacy Gemini 2.5 Flash Image) native image generation and editing model for high-volume, low-latency creative workflows."
232
+ }),
209
233
  {
210
234
  // Cowork "auto" lets OpenAI pick the size; the canonical equivalent is adaptive.
211
235
  id: "gpt-image-2",
@@ -223,7 +247,13 @@ var IMAGE_MODELS = [
223
247
  },
224
248
  defaults: { size: "1K", aspectRatio: "adaptive", quality: "auto" },
225
249
  usage: { ...IMAGE_USAGE },
226
- pricingKind: "image_size"
250
+ pricingKind: "image_size",
251
+ display: {
252
+ provider: "openai",
253
+ description: "The most capable model for image editing and complex creative instructions. Excels at text rendering, visual consistency, design generation, and iterative refinement.",
254
+ logo: "https://d31vvfpk2ppdyq.cloudfront.net/assets/image/1204b494-8962-4f7f-af5e-699a8bdc98e3_compressed.png"
255
+ },
256
+ capabilities: { ...IMAGE_GENERATE_EDIT }
227
257
  },
228
258
  {
229
259
  // Same Images API surface as gpt-image-2 plus the "xhigh" and "max" quality tiers.
@@ -242,7 +272,12 @@ var IMAGE_MODELS = [
242
272
  },
243
273
  defaults: { size: "1K", aspectRatio: "adaptive", quality: "auto" },
244
274
  usage: { ...IMAGE_USAGE },
245
- pricingKind: "image_size"
275
+ pricingKind: "image_size",
276
+ display: {
277
+ provider: "openai",
278
+ 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."
279
+ },
280
+ capabilities: { ...IMAGE_GENERATE_EDIT }
246
281
  },
247
282
  {
248
283
  // Same Images API surface as gpt-image-2 plus the "xhigh" and "max" quality tiers.
@@ -261,7 +296,12 @@ var IMAGE_MODELS = [
261
296
  },
262
297
  defaults: { size: "1K", aspectRatio: "adaptive", quality: "auto" },
263
298
  usage: { ...IMAGE_USAGE },
264
- pricingKind: "image_size"
299
+ pricingKind: "image_size",
300
+ display: {
301
+ provider: "openai",
302
+ 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."
303
+ },
304
+ capabilities: { ...IMAGE_GENERATE_EDIT }
265
305
  },
266
306
  {
267
307
  // Cowork allows 7 ratios; trigger-workflows allows 9 (adds 3:2, 9:21, drops 4:5/5:4).
@@ -293,7 +333,13 @@ var IMAGE_MODELS = [
293
333
  },
294
334
  defaults: { size: "1K", aspectRatio: "1:1", outputFormat: "png", count: 1 },
295
335
  usage: { ...IMAGE_USAGE },
296
- pricingKind: "image_size"
336
+ pricingKind: "image_size",
337
+ display: {
338
+ provider: "meta",
339
+ 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.",
340
+ logo: "https://d31vvfpk2ppdyq.cloudfront.net/assets/characters/1787909065794-k9a7zz.png"
341
+ },
342
+ capabilities: { ...IMAGE_GENERATE_EDIT }
297
343
  },
298
344
  {
299
345
  id: "seedream-5-0-pro",
@@ -313,7 +359,12 @@ var IMAGE_MODELS = [
313
359
  },
314
360
  defaults: { size: "1K", aspectRatio: "1:1" },
315
361
  usage: { ...IMAGE_USAGE },
316
- pricingKind: "image_size"
362
+ pricingKind: "image_size",
363
+ display: {
364
+ provider: "bytedance",
365
+ 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."
366
+ },
367
+ capabilities: { ...IMAGE_GENERATE_EDIT }
317
368
  },
318
369
  {
319
370
  id: "seedream-4.5",
@@ -326,14 +377,31 @@ var IMAGE_MODELS = [
326
377
  constraints: {
327
378
  // 97:30 has no Ark size spelling; the A+ flow pads inputs to 97:30 and
328
379
  // omits the ratio instead. Advertise only sizes the adapter can send.
329
- aspectRatios: ["1:1", "2:3", "3:2", "4:3", "3:4", "4:5", "5:4", "9:16", "16:9", "21:9"],
330
- sizes: ["1K", "2K"],
380
+ aspectRatios: [
381
+ "1:1",
382
+ "2:3",
383
+ "3:2",
384
+ "4:3",
385
+ "3:4",
386
+ "4:5",
387
+ "5:4",
388
+ "9:16",
389
+ "16:9",
390
+ "21:9",
391
+ "9:21"
392
+ ],
393
+ sizes: ["1K", "2K", "4K"],
331
394
  references: { images: 10 },
332
395
  supports: ["seed"]
333
396
  },
334
397
  defaults: { size: "1K", aspectRatio: "1:1" },
335
398
  usage: { ...IMAGE_USAGE },
336
- pricingKind: "image_size"
399
+ pricingKind: "image_size",
400
+ display: {
401
+ provider: "bytedance",
402
+ 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."
403
+ },
404
+ capabilities: { ...IMAGE_GENERATE_EDIT }
337
405
  },
338
406
  {
339
407
  id: "flux-2-pro",
@@ -351,7 +419,12 @@ var IMAGE_MODELS = [
351
419
  },
352
420
  defaults: { size: "1K", aspectRatio: "1:1" },
353
421
  usage: { ...IMAGE_USAGE },
354
- pricingKind: "image_size"
422
+ pricingKind: "image_size",
423
+ display: {
424
+ provider: "black-forest-labs",
425
+ 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."
426
+ },
427
+ capabilities: { ...IMAGE_GENERATE_EDIT }
355
428
  },
356
429
  {
357
430
  // Prodia's gemini-3-pro jobs take aspect_ratio + resolution (1K/2K/4K), not
@@ -372,7 +445,12 @@ var IMAGE_MODELS = [
372
445
  },
373
446
  defaults: { size: "1K", aspectRatio: "1:1" },
374
447
  usage: { ...IMAGE_USAGE },
375
- pricingKind: "image_size"
448
+ pricingKind: "image_size",
449
+ display: {
450
+ provider: "google",
451
+ 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."
452
+ },
453
+ capabilities: { ...IMAGE_GENERATE_EDIT }
376
454
  },
377
455
  {
378
456
  // Nano Banana (img2img.v2) is flat-rate with aspect_ratio only; 3:2, 4:5,
@@ -392,7 +470,12 @@ var IMAGE_MODELS = [
392
470
  },
393
471
  defaults: { size: "1K", aspectRatio: "1:1" },
394
472
  usage: { ...IMAGE_USAGE },
395
- pricingKind: "image_size"
473
+ pricingKind: "image_size",
474
+ display: {
475
+ provider: "google",
476
+ 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."
477
+ },
478
+ capabilities: { ...IMAGE_GENERATE_EDIT }
396
479
  },
397
480
  {
398
481
  // WAN 2.7 takes `images` (up to 9) and a `size` enum whose exact values
@@ -413,7 +496,12 @@ var IMAGE_MODELS = [
413
496
  },
414
497
  defaults: { size: "1K", aspectRatio: "1:1" },
415
498
  usage: { unit: "compute_seconds", modality: "IMAGE" },
416
- pricingKind: "image_size"
499
+ pricingKind: "image_size",
500
+ display: {
501
+ provider: "alibaba",
502
+ 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)."
503
+ },
504
+ capabilities: { ...IMAGE_GENERATE_EDIT }
417
505
  },
418
506
  {
419
507
  // Qwen Image 2 Pro has aspect_ratio but no size input; resolution follows
@@ -435,7 +523,12 @@ var IMAGE_MODELS = [
435
523
  },
436
524
  defaults: { aspectRatio: "1:1" },
437
525
  usage: { unit: "compute_seconds", modality: "IMAGE" },
438
- pricingKind: "image_size"
526
+ pricingKind: "image_size",
527
+ display: {
528
+ provider: "alibaba",
529
+ 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."
530
+ },
531
+ capabilities: { ...IMAGE_GENERATE_EDIT }
439
532
  },
440
533
  {
441
534
  // Qwen Image Edit Plus takes aspect_ratio (or match_input_image) and
@@ -457,7 +550,12 @@ var IMAGE_MODELS = [
457
550
  },
458
551
  defaults: { outputFormat: "png" },
459
552
  usage: { unit: "compute_seconds", modality: "IMAGE" },
460
- pricingKind: "image_size"
553
+ pricingKind: "image_size",
554
+ display: {
555
+ provider: "alibaba",
556
+ 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."
557
+ },
558
+ capabilities: { ...IMAGE_GENERATE_EDIT }
461
559
  }
462
560
  ];
463
561
 
@@ -478,7 +576,11 @@ var OTHER_MODELS = [
478
576
  },
479
577
  defaults: {},
480
578
  usage: { unit: "images", modality: "AUDIO" },
481
- pricingKind: "flat"
579
+ pricingKind: "flat",
580
+ display: {
581
+ provider: "google",
582
+ 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."
583
+ }
482
584
  },
483
585
  {
484
586
  id: "lyria-3-pro-preview",
@@ -494,7 +596,11 @@ var OTHER_MODELS = [
494
596
  },
495
597
  defaults: {},
496
598
  usage: { unit: "images", modality: "AUDIO" },
497
- pricingKind: "flat"
599
+ pricingKind: "flat",
600
+ display: {
601
+ provider: "google",
602
+ 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."
603
+ }
498
604
  },
499
605
  {
500
606
  id: "google/lyria-2",
@@ -510,7 +616,11 @@ var OTHER_MODELS = [
510
616
  },
511
617
  defaults: {},
512
618
  usage: { unit: "compute_seconds", modality: "AUDIO" },
513
- pricingKind: "flat"
619
+ pricingKind: "flat",
620
+ display: {
621
+ provider: "google",
622
+ description: "Lyria 2 music generation on Replicate; produces 30 seconds of 48 kHz stereo audio from a text prompt."
623
+ }
514
624
  },
515
625
  {
516
626
  id: "mmaudio",
@@ -527,7 +637,11 @@ var OTHER_MODELS = [
527
637
  },
528
638
  defaults: { durationSeconds: 8 },
529
639
  usage: { unit: "compute_seconds", modality: "AUDIO" },
530
- pricingKind: "flat"
640
+ pricingKind: "flat",
641
+ display: {
642
+ provider: "mmaudio",
643
+ description: "MMAudio V2 video-to-audio model on Replicate; takes a source video and prompt and synthesizes synchronized environmental, action, and speech-like audio."
644
+ }
531
645
  },
532
646
  {
533
647
  id: "elevenlabs-music",
@@ -544,7 +658,11 @@ var OTHER_MODELS = [
544
658
  defaults: { durationSeconds: 15, instrumental: true },
545
659
  // Replicate's run() does not report predict_time; billed by generated length.
546
660
  usage: { unit: "seconds", modality: "AUDIO" },
547
- pricingKind: "flat"
661
+ pricingKind: "flat",
662
+ display: {
663
+ provider: "elevenlabs",
664
+ 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."
665
+ }
548
666
  },
549
667
  {
550
668
  id: "topaz",
@@ -557,11 +675,16 @@ var OTHER_MODELS = [
557
675
  constraints: {
558
676
  references: { images: 1 },
559
677
  requires: ["references"],
560
- supports: ["scaleFactor", "outputFormat"]
678
+ supports: ["scaleFactor", "outputFormat", "upscale"]
561
679
  },
562
680
  defaults: { scaleFactor: 2, outputFormat: "png" },
563
681
  usage: { unit: "compute_seconds", modality: "IMAGE" },
564
- pricingKind: "flat"
682
+ pricingKind: "flat",
683
+ display: {
684
+ provider: "topaz",
685
+ 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."
686
+ },
687
+ capabilities: { imageEdit: true }
565
688
  },
566
689
  {
567
690
  id: "bg-remove",
@@ -578,7 +701,12 @@ var OTHER_MODELS = [
578
701
  },
579
702
  defaults: {},
580
703
  usage: { unit: "images", modality: "IMAGE" },
581
- pricingKind: "flat"
704
+ pricingKind: "flat",
705
+ display: {
706
+ provider: "prodia",
707
+ description: "Prodia background removal; takes one input image and returns a PNG cut-out of the subject plus an optional black-and-white mask."
708
+ },
709
+ capabilities: { imageEdit: true }
582
710
  }
583
711
  ];
584
712
 
@@ -601,22 +729,112 @@ function text(id, label, providerModelId, extra) {
601
729
  };
602
730
  }
603
731
  var TEXT_MODELS = [
604
- text("gpt-4o", "GPT-4o", "gpt-4o"),
605
- text("gpt-4o-mini", "GPT-4o mini", "gpt-4o-mini"),
606
- text("gpt-5.1", "GPT-5.1", "gpt-5.1"),
607
- text("gpt-5.2", "GPT-5.2", "gpt-5.2"),
608
- text("gpt-5.4", "GPT-5.4", "gpt-5.4"),
732
+ text("gpt-4o", "GPT-4o", "gpt-4o", {
733
+ display: {
734
+ provider: "openai",
735
+ description: "OpenAI GPT-4o flagship multimodal model; text and image input, 128K-token context."
736
+ },
737
+ capabilities: { vision: true, tools: true, structuredOutput: true, contextWindow: 128e3 }
738
+ }),
739
+ text("gpt-4o-mini", "GPT-4o mini", "gpt-4o-mini", {
740
+ display: {
741
+ provider: "openai",
742
+ description: "OpenAI GPT-4o mini fast, low-cost multimodal model; text and image input, 128K-token context."
743
+ },
744
+ capabilities: { vision: true, tools: true, structuredOutput: true, contextWindow: 128e3 }
745
+ }),
746
+ text("gpt-5.1", "GPT-5.1", "gpt-5.1", {
747
+ display: {
748
+ provider: "openai",
749
+ description: "OpenAI GPT-5.1 reasoning model for coding and agentic tasks; text and image input, 400K-token context."
750
+ },
751
+ capabilities: { vision: true, tools: true, structuredOutput: true, contextWindow: 4e5 }
752
+ }),
753
+ text("gpt-5.2", "GPT-5.2", "gpt-5.2", {
754
+ display: {
755
+ provider: "openai",
756
+ description: "OpenAI GPT-5.2 reasoning model for complex professional work; text and image input, 400K-token context."
757
+ },
758
+ capabilities: { vision: true, tools: true, structuredOutput: true, contextWindow: 4e5 }
759
+ }),
760
+ // 1.05M-token context: https://platform.openai.com/docs/models/gpt-5.4
761
+ text("gpt-5.4", "GPT-5.4", "gpt-5.4", {
762
+ display: {
763
+ provider: "openai",
764
+ description: "OpenAI GPT-5.4 flagship reasoning model; text and image input, 1.05M-token context."
765
+ },
766
+ capabilities: {
767
+ vision: true,
768
+ tools: true,
769
+ structuredOutput: true,
770
+ contextWindow: 105e4
771
+ }
772
+ }),
609
773
  // OpenAI never shipped gpt-5.1-mini or gpt-5.2-mini; callers that still send them get 5.4 mini.
610
774
  text("gpt-5.4-mini", "GPT-5.4 mini", "gpt-5.4-mini", {
611
- aliases: ["gpt-5.1-mini", "gpt-5.2-mini"]
775
+ aliases: ["gpt-5.1-mini", "gpt-5.2-mini"],
776
+ display: {
777
+ provider: "openai",
778
+ description: "OpenAI GPT-5.4 mini efficient reasoning model for high-volume workloads; text and image input, 400K-token context."
779
+ },
780
+ capabilities: { vision: true, tools: true, structuredOutput: true, contextWindow: 4e5 }
781
+ }),
782
+ text("gpt-5.5", "GPT-5.5", "gpt-5.5", {
783
+ display: {
784
+ provider: "openai",
785
+ description: "OpenAI GPT-5.5 flagship reasoning model for the most complex professional and coding work; text and image input, 1.05M-token context."
786
+ },
787
+ capabilities: {
788
+ vision: true,
789
+ tools: true,
790
+ structuredOutput: true,
791
+ contextWindow: 105e4
792
+ }
793
+ }),
794
+ text("gpt-5.6-luna", "GPT-5.6 Luna", "gpt-5.6-luna", {
795
+ display: {
796
+ provider: "openai",
797
+ description: "OpenAI GPT-5.6 Luna cost-optimized tier of the GPT-5.6 reasoning family; text and image input, 1.05M-token context."
798
+ },
799
+ capabilities: {
800
+ vision: true,
801
+ tools: true,
802
+ structuredOutput: true,
803
+ contextWindow: 105e4
804
+ }
805
+ }),
806
+ text("gpt-5.6-terra", "GPT-5.6 Terra", "gpt-5.6-terra", {
807
+ display: {
808
+ provider: "openai",
809
+ description: "OpenAI GPT-5.6 Terra balanced tier of the GPT-5.6 reasoning family; text and image input, 1.05M-token context."
810
+ },
811
+ capabilities: {
812
+ vision: true,
813
+ tools: true,
814
+ structuredOutput: true,
815
+ contextWindow: 105e4
816
+ }
817
+ }),
818
+ text("gpt-5.6-sol", "GPT-5.6 Sol", "gpt-5.6-sol", {
819
+ display: {
820
+ provider: "openai",
821
+ description: "OpenAI GPT-5.6 Sol flagship tier of the GPT-5.6 reasoning family; text and image input, 1.05M-token context."
822
+ },
823
+ capabilities: {
824
+ vision: true,
825
+ tools: true,
826
+ structuredOutput: true,
827
+ contextWindow: 105e4
828
+ }
612
829
  }),
613
- text("gpt-5.5", "GPT-5.5", "gpt-5.5"),
614
- text("gpt-5.6-luna", "GPT-5.6 Luna", "gpt-5.6-luna"),
615
- text("gpt-5.6-terra", "GPT-5.6 Terra", "gpt-5.6-terra"),
616
- text("gpt-5.6-sol", "GPT-5.6 Sol", "gpt-5.6-sol"),
617
830
  // Library key "claude-sonnet-4.5" maps to the SDK id "claude-sonnet-4-5".
618
831
  text("claude-sonnet-4-5", "Claude Sonnet 4.5", "claude-sonnet-4-5", {
619
- aliases: ["claude-sonnet-4.5", "claude-sonnet-4-5-20250929"]
832
+ aliases: ["claude-sonnet-4.5", "claude-sonnet-4-5-20250929"],
833
+ display: {
834
+ provider: "anthropic",
835
+ description: "Anthropic Claude Sonnet 4.5 (legacy) balanced model; text and image input, 200K-token context."
836
+ },
837
+ capabilities: { vision: true, tools: true, structuredOutput: true, contextWindow: 2e5 }
620
838
  }),
621
839
  // Anthropic retired the dated 4.0 ids; callers that still send them get 4.6.
622
840
  text("claude-sonnet-4-6", "Claude Sonnet 4.6", "claude-sonnet-4-6", {
@@ -625,42 +843,280 @@ var TEXT_MODELS = [
625
843
  "claude-sonnet-4",
626
844
  "claude-sonnet-4.0",
627
845
  "claude-sonnet-4-20250514"
628
- ]
846
+ ],
847
+ display: {
848
+ provider: "anthropic",
849
+ description: "Anthropic Claude Sonnet 4.6 (legacy) balanced model; text and image input, 1M-token context."
850
+ },
851
+ capabilities: {
852
+ vision: true,
853
+ tools: true,
854
+ structuredOutput: true,
855
+ contextWindow: 1e6
856
+ }
857
+ }),
858
+ text("claude-sonnet-5", "Claude Sonnet 5", "claude-sonnet-5", {
859
+ display: {
860
+ provider: "anthropic",
861
+ description: "Anthropic Claude Sonnet 5 current balanced model; text and image input, 1M-token context."
862
+ },
863
+ capabilities: {
864
+ vision: true,
865
+ tools: true,
866
+ structuredOutput: true,
867
+ contextWindow: 1e6
868
+ }
629
869
  }),
630
- text("claude-sonnet-5", "Claude Sonnet 5", "claude-sonnet-5"),
631
870
  text("claude-opus-4-8", "Claude Opus 4.8", "claude-opus-4-8", {
632
- aliases: ["claude-opus-4.8", "claude-opus-4", "claude-opus-4.0", "claude-opus-4-20250514"]
871
+ aliases: ["claude-opus-4.8", "claude-opus-4", "claude-opus-4.0", "claude-opus-4-20250514"],
872
+ display: {
873
+ provider: "anthropic",
874
+ description: "Anthropic Claude Opus 4.8 (legacy) frontier model; text and image input, 1M-token context."
875
+ },
876
+ capabilities: {
877
+ vision: true,
878
+ tools: true,
879
+ structuredOutput: true,
880
+ contextWindow: 1e6
881
+ }
633
882
  }),
634
883
  text("claude-haiku-4-5", "Claude Haiku 4.5", "claude-haiku-4-5", {
635
- aliases: ["claude-haiku-4.5", "claude-haiku-4-5-20251001", "claude-haiku-4"]
884
+ aliases: ["claude-haiku-4.5", "claude-haiku-4-5-20251001", "claude-haiku-4"],
885
+ display: {
886
+ provider: "anthropic",
887
+ description: "Anthropic Claude Haiku 4.5 fast model with near-frontier intelligence; text and image input, 200K-token context."
888
+ },
889
+ capabilities: { vision: true, tools: true, structuredOutput: true, contextWindow: 2e5 }
636
890
  }),
637
891
  text("claude-opus-5", "Claude Opus 5", "claude-opus-5", {
638
- aliases: ["claude-opus-5.0"]
892
+ aliases: ["claude-opus-5.0"],
893
+ display: {
894
+ provider: "anthropic",
895
+ description: "Anthropic Claude Opus 5 current frontier model for agentic coding and enterprise work; text and image input, 1M-token context."
896
+ },
897
+ capabilities: {
898
+ vision: true,
899
+ tools: true,
900
+ structuredOutput: true,
901
+ contextWindow: 1e6
902
+ }
903
+ }),
904
+ text("claude-fable-5", "Claude Fable 5", "claude-fable-5", {
905
+ display: {
906
+ provider: "anthropic",
907
+ description: "Anthropic Claude Fable 5 (legacy) model for demanding reasoning and long-horizon agentic work; text and image input, 1M-token context."
908
+ },
909
+ capabilities: {
910
+ vision: true,
911
+ tools: true,
912
+ structuredOutput: true,
913
+ contextWindow: 1e6
914
+ }
915
+ }),
916
+ text("gemini-2.5-flash", "Gemini 2.5 Flash", "gemini-2.5-flash", {
917
+ display: {
918
+ provider: "google",
919
+ description: "Google Gemini 2.5 Flash low-latency multimodal model for high-volume reasoning over text, images, video, and audio; 1M-token context."
920
+ },
921
+ capabilities: {
922
+ vision: true,
923
+ tools: true,
924
+ structuredOutput: true,
925
+ contextWindow: 1048576
926
+ }
927
+ }),
928
+ text("gemini-2.5-pro", "Gemini 2.5 Pro", "gemini-2.5-pro", {
929
+ display: {
930
+ provider: "google",
931
+ 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."
932
+ },
933
+ capabilities: {
934
+ vision: true,
935
+ tools: true,
936
+ structuredOutput: true,
937
+ contextWindow: 1048576
938
+ }
939
+ }),
940
+ text("gemini-3.1-pro-preview", "Gemini 3.1 Pro (preview)", "gemini-3.1-pro-preview", {
941
+ display: {
942
+ provider: "google",
943
+ description: "Google Gemini 3.1 Pro preview reasoning model for software engineering, tool use, and reliable agentic workflows; 1M-token context."
944
+ },
945
+ capabilities: {
946
+ vision: true,
947
+ tools: true,
948
+ structuredOutput: true,
949
+ contextWindow: 1048576
950
+ }
951
+ }),
952
+ text("gemini-3.5-flash", "Gemini 3.5 Flash", "gemini-3.5-flash", {
953
+ display: {
954
+ provider: "google",
955
+ description: "Google Gemini 3.5 Flash high-throughput model for frontier-level agentic tasks; text, image, video, audio, and PDF input, 1M-token context."
956
+ },
957
+ capabilities: {
958
+ vision: true,
959
+ tools: true,
960
+ structuredOutput: true,
961
+ contextWindow: 1048576
962
+ }
963
+ }),
964
+ text("gemini-3.6-flash", "Gemini 3.6 Flash", "gemini-3.6-flash", {
965
+ display: {
966
+ provider: "google",
967
+ description: "Google Gemini 3.6 Flash general-purpose multimodal agentic model balancing speed and capability; 1M-token context."
968
+ },
969
+ capabilities: {
970
+ vision: true,
971
+ tools: true,
972
+ structuredOutput: true,
973
+ contextWindow: 1048576
974
+ }
975
+ }),
976
+ text("gemini-3.7-flash", "Gemini 3.7 Flash", "gemini-3.7-flash", {
977
+ display: {
978
+ provider: "google",
979
+ description: "Google Gemini 3.7 Flash natively multimodal reasoning model for coding, agentic workflows, and multi-step execution; 1M-token context."
980
+ },
981
+ capabilities: {
982
+ vision: true,
983
+ tools: true,
984
+ structuredOutput: true,
985
+ contextWindow: 1048576
986
+ }
987
+ }),
988
+ text("gemini-3.8-flash", "Gemini 3.8 Flash", "gemini-3.8-flash", {
989
+ display: {
990
+ provider: "google",
991
+ description: "Google Gemini 3.8 Flash frontier model for long-horizon software engineering, autonomous agents, and enterprise workflows; 1M-token context."
992
+ },
993
+ capabilities: {
994
+ vision: true,
995
+ tools: true,
996
+ structuredOutput: true,
997
+ contextWindow: 1048576
998
+ }
639
999
  }),
640
- text("claude-fable-5", "Claude Fable 5", "claude-fable-5"),
641
- text("gemini-2.5-flash", "Gemini 2.5 Flash", "gemini-2.5-flash"),
642
- text("gemini-2.5-pro", "Gemini 2.5 Pro", "gemini-2.5-pro"),
643
- text("gemini-3.1-pro-preview", "Gemini 3.1 Pro (preview)", "gemini-3.1-pro-preview"),
644
- text("gemini-3.5-flash", "Gemini 3.5 Flash", "gemini-3.5-flash"),
645
- text("gemini-3.6-flash", "Gemini 3.6 Flash", "gemini-3.6-flash"),
646
- text("gemini-3.7-flash", "Gemini 3.7 Flash", "gemini-3.7-flash"),
647
- text("gemini-3.8-flash", "Gemini 3.8 Flash", "gemini-3.8-flash"),
648
1000
  // Renamed to gemini-3.6-flash in vibe-shared model-migration.ts.
649
1001
  text("gemini-3-flash-preview", "Gemini 3 Flash (preview)", "gemini-3-flash-preview", {
650
1002
  lifecycle: "deprecated",
651
1003
  replacedBy: "gemini-3.6-flash"
652
1004
  }),
653
- text("gemini-3.1-flash-lite", "Gemini 3.1 Flash Lite", "gemini-3.1-flash-lite"),
654
- text("sonar", "Sonar", "sonar"),
655
- text("sonar-pro", "Sonar Pro", "sonar-pro"),
656
- text("sonar-reasoning-pro", "Sonar Reasoning Pro", "sonar-reasoning-pro"),
657
- text("deepseek-v4-flash", "DeepSeek V4 Flash", "deepseek-v4-flash"),
658
- text("deepseek-v4-pro", "DeepSeek V4 Pro", "deepseek-v4-pro"),
659
- text("grok-4.3", "Grok 4.3", "grok-4.3"),
660
- text("kimi-k2.5", "Kimi K2.5", "kimi-k2.5"),
661
- text("kimi-k3", "Kimi K3", "kimi-k3"),
1005
+ text("gemini-3.1-flash-lite", "Gemini 3.1 Flash Lite", "gemini-3.1-flash-lite", {
1006
+ display: {
1007
+ provider: "google",
1008
+ description: "Google Gemini 3.1 Flash-Lite low-latency multimodal model for high-volume agentic, translation, and data-extraction tasks; 1M-token context."
1009
+ },
1010
+ capabilities: {
1011
+ vision: true,
1012
+ tools: true,
1013
+ structuredOutput: true,
1014
+ contextWindow: 1048576
1015
+ }
1016
+ }),
1017
+ text("sonar", "Sonar", "sonar", {
1018
+ display: {
1019
+ provider: "perplexity",
1020
+ description: "Perplexity Sonar lightweight web-grounded search model returning cited answers; 128K-token context."
1021
+ },
1022
+ capabilities: {
1023
+ vision: true,
1024
+ tools: false,
1025
+ structuredOutput: true,
1026
+ contextWindow: 128e3
1027
+ }
1028
+ }),
1029
+ text("sonar-pro", "Sonar Pro", "sonar-pro", {
1030
+ display: {
1031
+ provider: "perplexity",
1032
+ description: "Perplexity Sonar Pro advanced web-grounded search model for complex multi-step queries; 200K-token context."
1033
+ },
1034
+ capabilities: {
1035
+ vision: true,
1036
+ tools: false,
1037
+ structuredOutput: true,
1038
+ contextWindow: 2e5
1039
+ }
1040
+ }),
1041
+ text("sonar-reasoning-pro", "Sonar Reasoning Pro", "sonar-reasoning-pro", {
1042
+ display: {
1043
+ provider: "perplexity",
1044
+ description: "Perplexity Sonar Reasoning Pro chain-of-thought web-grounded search model for complex problem-solving; 128K-token context."
1045
+ },
1046
+ capabilities: {
1047
+ vision: true,
1048
+ tools: false,
1049
+ structuredOutput: true,
1050
+ contextWindow: 128e3
1051
+ }
1052
+ }),
1053
+ text("deepseek-v4-flash", "DeepSeek V4 Flash", "deepseek-v4-flash", {
1054
+ display: {
1055
+ provider: "deepseek",
1056
+ description: "DeepSeek V4 Flash thinking/non-thinking model with vision, tool calls, and JSON output; 1M-token context and up to 384K output tokens."
1057
+ },
1058
+ capabilities: {
1059
+ vision: true,
1060
+ tools: true,
1061
+ structuredOutput: true,
1062
+ contextWindow: 1e6
1063
+ }
1064
+ }),
1065
+ text("deepseek-v4-pro", "DeepSeek V4 Pro", "deepseek-v4-pro", {
1066
+ display: {
1067
+ provider: "deepseek",
1068
+ 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."
1069
+ },
1070
+ capabilities: {
1071
+ vision: false,
1072
+ tools: true,
1073
+ structuredOutput: true,
1074
+ contextWindow: 1e6
1075
+ }
1076
+ }),
1077
+ // 1M-token context: https://docs.x.ai/docs/models/grok-4.3
1078
+ text("grok-4.3", "Grok 4.3", "grok-4.3", {
1079
+ display: {
1080
+ provider: "xai",
1081
+ description: "xAI Grok 4.3 reasoning model with tool calling and structured outputs; text and image input, 1M-token context."
1082
+ },
1083
+ capabilities: {
1084
+ vision: true,
1085
+ tools: true,
1086
+ structuredOutput: true,
1087
+ contextWindow: 1e6
1088
+ }
1089
+ }),
1090
+ // Moonshot retired kimi-k2.5 on 2026-08-31 and no longer publishes its specs.
1091
+ text("kimi-k2.5", "Kimi K2.5", "kimi-k2.5", {
1092
+ display: {
1093
+ provider: "moonshot",
1094
+ description: "Moonshot Kimi K2.5; the provider retired the model on 2026-08-31 and calls now return a model-not-found error."
1095
+ },
1096
+ capabilities: {}
1097
+ }),
1098
+ text("kimi-k3", "Kimi K3", "kimi-k3", {
1099
+ display: {
1100
+ provider: "moonshot",
1101
+ description: "Moonshot Kimi K3 flagship model with native visual understanding and always-on thinking; text, image, and video input, 1M-token context."
1102
+ },
1103
+ capabilities: {
1104
+ vision: true,
1105
+ tools: true,
1106
+ structuredOutput: true,
1107
+ contextWindow: 1e6
1108
+ }
1109
+ }),
662
1110
  // Cowork calls the provider "MiniMax-M3"; the pricing key is lowercase.
663
- text("minimax-m3", "MiniMax M3", "MiniMax-M3", { aliases: ["MiniMax-M3"] })
1111
+ // 1M-token context: https://platform.minimax.io/docs/api-reference/text-openai-api
1112
+ text("minimax-m3", "MiniMax M3", "MiniMax-M3", {
1113
+ aliases: ["MiniMax-M3"],
1114
+ display: {
1115
+ provider: "minimax",
1116
+ description: "MiniMax M3 frontier multimodal model for agentic reasoning, tool use, and coding; text, image, and video input, 1M-token context."
1117
+ },
1118
+ capabilities: { vision: true, tools: true, contextWindow: 1e6 }
1119
+ })
664
1120
  ];
665
1121
 
666
1122
  // src/catalog/video.ts
@@ -672,6 +1128,8 @@ var GROK_RATIOS = ["1:1", "16:9", "9:16", "4:3", "3:4", "3:2", "2:3"];
672
1128
  var H3_RATIOS = ["21:9", "16:9", "4:3", "1:1", "3:4", "9:16"];
673
1129
  var H3_REFERENCE_RATIOS = ["adaptive", ...H3_RATIOS];
674
1130
  var NEGATIVE_PROMPT = "blur, distort, and low quality";
1131
+ var FRAMES_EXCLUDE_REFERENCES = { framesExcludeReferences: true };
1132
+ var GOOGLE_LOGO = "https://d31vvfpk2ppdyq.cloudfront.net/assets/image/192485d2-a76a-4657-9c06-930b2c70badd_compressed.png";
675
1133
  var VIDEO_MODELS = [
676
1134
  {
677
1135
  id: "veo-3.1",
@@ -695,7 +1153,13 @@ var VIDEO_MODELS = [
695
1153
  },
696
1154
  defaults: { durationSeconds: 8, resolution: "720p", aspectRatio: "16:9" },
697
1155
  usage: { ...VIDEO_USAGE },
698
- pricingKind: "video_per_second"
1156
+ pricingKind: "video_per_second",
1157
+ display: {
1158
+ provider: "google",
1159
+ description: "Google's highest-quality video generation model. Supports up to 4K resolution and 8-second clips with reference image conditioning.",
1160
+ logo: GOOGLE_LOGO
1161
+ },
1162
+ capabilities: { ...FRAMES_EXCLUDE_REFERENCES }
699
1163
  },
700
1164
  {
701
1165
  id: "veo-3.1-fast",
@@ -718,7 +1182,12 @@ var VIDEO_MODELS = [
718
1182
  },
719
1183
  defaults: { durationSeconds: 8, resolution: "720p", aspectRatio: "16:9" },
720
1184
  usage: { ...VIDEO_USAGE },
721
- pricingKind: "video_per_second"
1185
+ pricingKind: "video_per_second",
1186
+ display: {
1187
+ provider: "google",
1188
+ description: "Faster variant of Google Veo 3.1; text- and image-to-video with natively generated audio at 4 or 8 seconds."
1189
+ },
1190
+ capabilities: { ...FRAMES_EXCLUDE_REFERENCES }
722
1191
  },
723
1192
  {
724
1193
  id: "veo-3.1-lite",
@@ -741,7 +1210,12 @@ var VIDEO_MODELS = [
741
1210
  },
742
1211
  defaults: { durationSeconds: 8, resolution: "720p", aspectRatio: "16:9" },
743
1212
  usage: { ...VIDEO_USAGE },
744
- pricingKind: "video_per_second"
1213
+ pricingKind: "video_per_second",
1214
+ display: {
1215
+ provider: "google",
1216
+ 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."
1217
+ },
1218
+ capabilities: { ...FRAMES_EXCLUDE_REFERENCES }
745
1219
  },
746
1220
  {
747
1221
  id: "gemini-omni-flash",
@@ -768,7 +1242,13 @@ var VIDEO_MODELS = [
768
1242
  },
769
1243
  defaults: {},
770
1244
  usage: { ...VIDEO_USAGE },
771
- pricingKind: "video_per_second"
1245
+ pricingKind: "video_per_second",
1246
+ display: {
1247
+ provider: "google",
1248
+ 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.",
1249
+ logo: GOOGLE_LOGO
1250
+ },
1251
+ capabilities: { acceptsSourceVideo: true }
772
1252
  },
773
1253
  {
774
1254
  id: "gemini-omni-1.1-flash",
@@ -787,7 +1267,12 @@ var VIDEO_MODELS = [
787
1267
  },
788
1268
  defaults: {},
789
1269
  usage: { ...VIDEO_USAGE },
790
- pricingKind: "video_per_second"
1270
+ pricingKind: "video_per_second",
1271
+ display: {
1272
+ provider: "google",
1273
+ 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."
1274
+ },
1275
+ capabilities: { acceptsSourceVideo: true }
791
1276
  },
792
1277
  {
793
1278
  // vibe-triggers and Space Builder drop the ratio when a frame is wired; Cowork always sends it.
@@ -804,7 +1289,7 @@ var VIDEO_MODELS = [
804
1289
  durations: [4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15],
805
1290
  references: { images: 9, videos: 3, audios: 3, audioOnly: false },
806
1291
  ratioIgnoredWith: ["firstFrame"],
807
- supports: ["generateAudio", "seed", "lastFrame"]
1292
+ supports: ["generateAudio", "seed", "lastFrame", "watermark"]
808
1293
  },
809
1294
  defaults: {
810
1295
  durationSeconds: 5,
@@ -813,7 +1298,13 @@ var VIDEO_MODELS = [
813
1298
  generateAudio: true
814
1299
  },
815
1300
  usage: { ...VIDEO_USAGE },
816
- pricingKind: "video_per_second"
1301
+ pricingKind: "video_per_second",
1302
+ display: {
1303
+ provider: "bytedance",
1304
+ description: "High-quality AI video generation with reference image conditioning. Supports 4-15 second clips with human preprocessing.",
1305
+ logo: "https://d31vvfpk2ppdyq.cloudfront.net/assets/image/3bba5d81-4e57-4e71-911b-cdfa05acca2c_compressed.png"
1306
+ },
1307
+ capabilities: { acceptsSourceVideo: true, ...FRAMES_EXCLUDE_REFERENCES }
817
1308
  },
818
1309
  {
819
1310
  id: "seedance-2.0-fast",
@@ -829,7 +1320,7 @@ var VIDEO_MODELS = [
829
1320
  durations: [4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15],
830
1321
  references: { images: 9, videos: 3, audios: 3, audioOnly: false },
831
1322
  ratioIgnoredWith: ["firstFrame"],
832
- supports: ["generateAudio", "seed", "lastFrame"]
1323
+ supports: ["generateAudio", "seed", "lastFrame", "watermark"]
833
1324
  },
834
1325
  defaults: {
835
1326
  durationSeconds: 5,
@@ -838,7 +1329,12 @@ var VIDEO_MODELS = [
838
1329
  generateAudio: true
839
1330
  },
840
1331
  usage: { ...VIDEO_USAGE },
841
- pricingKind: "video_per_second"
1332
+ pricingKind: "video_per_second",
1333
+ display: {
1334
+ provider: "bytedance",
1335
+ 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."
1336
+ },
1337
+ capabilities: { acceptsSourceVideo: true, ...FRAMES_EXCLUDE_REFERENCES }
842
1338
  },
843
1339
  {
844
1340
  // generateAudio defaults to false here (trigger-workflows); Space Builder defaults it to true.
@@ -855,7 +1351,7 @@ var VIDEO_MODELS = [
855
1351
  durations: { min: 4, max: 30 },
856
1352
  references: { images: 30, videos: 10, audios: 10 },
857
1353
  ratioIgnoredWith: ["firstFrame"],
858
- supports: ["generateAudio", "seed", "lastFrame"]
1354
+ supports: ["generateAudio", "seed", "lastFrame", "watermark"]
859
1355
  },
860
1356
  defaults: {
861
1357
  durationSeconds: 5,
@@ -864,7 +1360,12 @@ var VIDEO_MODELS = [
864
1360
  generateAudio: false
865
1361
  },
866
1362
  usage: { ...VIDEO_USAGE },
867
- pricingKind: "video_per_second"
1363
+ pricingKind: "video_per_second",
1364
+ display: {
1365
+ provider: "bytedance",
1366
+ 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."
1367
+ },
1368
+ capabilities: { acceptsSourceVideo: true, ...FRAMES_EXCLUDE_REFERENCES }
868
1369
  },
869
1370
  {
870
1371
  // Cowork allows 7 reference images and durations 4/6/8/10; Space Builder allows 5 and 1-15s.
@@ -882,9 +1383,16 @@ var VIDEO_MODELS = [
882
1383
  references: { images: 7 },
883
1384
  supports: []
884
1385
  },
885
- defaults: { durationSeconds: 8, resolution: "720p", aspectRatio: "16:9" },
1386
+ // No resolution default: xAI picks its own unless the caller asks.
1387
+ defaults: { durationSeconds: 8, aspectRatio: "16:9" },
886
1388
  usage: { unit: "reported_cost_usd", modality: "VIDEO" },
887
- pricingKind: "video_per_second"
1389
+ pricingKind: "video_per_second",
1390
+ display: {
1391
+ provider: "xai",
1392
+ description: "xAI's reference-to-video model. Generates short clips conditioned on up to 7 reference images with flexible aspect ratios.",
1393
+ logo: "https://d31vvfpk2ppdyq.cloudfront.net/assets/image/858b7fcc-9bd5-411d-aea1-a7b35ecfffab_compressed.png"
1394
+ },
1395
+ capabilities: { ...FRAMES_EXCLUDE_REFERENCES }
888
1396
  },
889
1397
  {
890
1398
  id: "kling-2.5",
@@ -907,7 +1415,12 @@ var VIDEO_MODELS = [
907
1415
  negativePrompt: NEGATIVE_PROMPT
908
1416
  },
909
1417
  usage: { ...VIDEO_USAGE },
910
- pricingKind: "video_per_second"
1418
+ pricingKind: "video_per_second",
1419
+ display: {
1420
+ provider: "kuaishou",
1421
+ 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."
1422
+ },
1423
+ capabilities: { ...FRAMES_EXCLUDE_REFERENCES }
911
1424
  },
912
1425
  {
913
1426
  // trigger-workflows validates 5/10 only; Space Builder allows 3-15s (Kling30Input).
@@ -931,7 +1444,12 @@ var VIDEO_MODELS = [
931
1444
  negativePrompt: NEGATIVE_PROMPT
932
1445
  },
933
1446
  usage: { ...VIDEO_USAGE },
934
- pricingKind: "video_per_second"
1447
+ pricingKind: "video_per_second",
1448
+ display: {
1449
+ provider: "kuaishou",
1450
+ 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."
1451
+ },
1452
+ capabilities: { ...FRAMES_EXCLUDE_REFERENCES }
935
1453
  },
936
1454
  {
937
1455
  // mode standard/pro selects 720p/1080p; the endpoint takes no separate resolution.
@@ -962,7 +1480,12 @@ var VIDEO_MODELS = [
962
1480
  },
963
1481
  defaults: { durationSeconds: 5, aspectRatio: "16:9", generateAudio: false, mode: "pro" },
964
1482
  usage: { unit: "compute_seconds", modality: "VIDEO" },
965
- pricingKind: "video_per_second"
1483
+ pricingKind: "video_per_second",
1484
+ display: {
1485
+ provider: "kuaishou",
1486
+ 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."
1487
+ },
1488
+ capabilities: {}
966
1489
  },
967
1490
  {
968
1491
  id: "minimax-h3",
@@ -976,7 +1499,7 @@ var VIDEO_MODELS = [
976
1499
  aspectRatios: [...H3_RATIOS],
977
1500
  resolutions: ["480p", "768p", "2k", "4k"],
978
1501
  durations: { min: 5, max: 15 },
979
- supports: ["seed", "promptExpansion"]
1502
+ supports: ["seed", "promptExpansion", "lastFrame", "safetyChecker"]
980
1503
  },
981
1504
  defaults: {
982
1505
  durationSeconds: 5,
@@ -985,7 +1508,12 @@ var VIDEO_MODELS = [
985
1508
  promptExpansion: "balanced"
986
1509
  },
987
1510
  usage: { ...VIDEO_USAGE },
988
- pricingKind: "video_per_second"
1511
+ pricingKind: "video_per_second",
1512
+ display: {
1513
+ provider: "minimax",
1514
+ 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."
1515
+ },
1516
+ capabilities: { ...FRAMES_EXCLUDE_REFERENCES }
989
1517
  },
990
1518
  {
991
1519
  id: "minimax-h3-max",
@@ -999,7 +1527,7 @@ var VIDEO_MODELS = [
999
1527
  aspectRatios: [...H3_RATIOS],
1000
1528
  resolutions: ["480p", "768p"],
1001
1529
  durations: { min: 5, max: 15 },
1002
- supports: ["seed", "promptExpansion"]
1530
+ supports: ["seed", "promptExpansion", "lastFrame", "safetyChecker"]
1003
1531
  },
1004
1532
  defaults: {
1005
1533
  durationSeconds: 5,
@@ -1008,7 +1536,12 @@ var VIDEO_MODELS = [
1008
1536
  promptExpansion: "balanced"
1009
1537
  },
1010
1538
  usage: { ...VIDEO_USAGE },
1011
- pricingKind: "video_per_second"
1539
+ pricingKind: "video_per_second",
1540
+ display: {
1541
+ provider: "minimax",
1542
+ description: "High-speed MiniMax H3 Max variant for text-to-video and image-to-video (first/last frame) only, up to 768p."
1543
+ },
1544
+ capabilities: { ...FRAMES_EXCLUDE_REFERENCES }
1012
1545
  },
1013
1546
  {
1014
1547
  id: "minimax-h3-reference",
@@ -1023,7 +1556,7 @@ var VIDEO_MODELS = [
1023
1556
  resolutions: ["480p", "768p", "2k", "4k"],
1024
1557
  durations: { min: 5, max: 15 },
1025
1558
  references: { images: 9, videos: 3, audios: 3, total: 12, audioOnly: false },
1026
- supports: ["seed", "promptExpansion"]
1559
+ supports: ["seed", "promptExpansion", "safetyChecker"]
1027
1560
  },
1028
1561
  defaults: {
1029
1562
  durationSeconds: 5,
@@ -1032,7 +1565,12 @@ var VIDEO_MODELS = [
1032
1565
  promptExpansion: "balanced"
1033
1566
  },
1034
1567
  usage: { ...VIDEO_USAGE },
1035
- pricingKind: "video_per_second"
1568
+ pricingKind: "video_per_second",
1569
+ display: {
1570
+ provider: "minimax",
1571
+ 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."
1572
+ },
1573
+ capabilities: {}
1036
1574
  },
1037
1575
  {
1038
1576
  id: "minimax-h3-max-reference",
@@ -1047,7 +1585,7 @@ var VIDEO_MODELS = [
1047
1585
  resolutions: ["480p", "768p"],
1048
1586
  durations: { min: 5, max: 15 },
1049
1587
  references: { images: 9, videos: 3, audios: 3, total: 12, audioOnly: false },
1050
- supports: ["seed", "promptExpansion"]
1588
+ supports: ["seed", "promptExpansion", "safetyChecker"]
1051
1589
  },
1052
1590
  defaults: {
1053
1591
  durationSeconds: 5,
@@ -1056,7 +1594,12 @@ var VIDEO_MODELS = [
1056
1594
  promptExpansion: "balanced"
1057
1595
  },
1058
1596
  usage: { ...VIDEO_USAGE },
1059
- pricingKind: "video_per_second"
1597
+ pricingKind: "video_per_second",
1598
+ display: {
1599
+ provider: "minimax",
1600
+ 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."
1601
+ },
1602
+ capabilities: {}
1060
1603
  },
1061
1604
  {
1062
1605
  // Duration 3-10s per trigger-workflows mapDuration; the endpoint takes no resolution.
@@ -1075,7 +1618,12 @@ var VIDEO_MODELS = [
1075
1618
  },
1076
1619
  defaults: { durationSeconds: 5, aspectRatio: "16:9" },
1077
1620
  usage: { ...VIDEO_USAGE },
1078
- pricingKind: "video_per_second"
1621
+ pricingKind: "video_per_second",
1622
+ display: {
1623
+ provider: "kuaishou",
1624
+ 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."
1625
+ },
1626
+ capabilities: { acceptsSourceVideo: true }
1079
1627
  },
1080
1628
  {
1081
1629
  id: "hailuo-02",
@@ -1092,7 +1640,12 @@ var VIDEO_MODELS = [
1092
1640
  },
1093
1641
  defaults: { durationSeconds: 6, resolution: "768p" },
1094
1642
  usage: { unit: "compute_seconds", modality: "VIDEO" },
1095
- pricingKind: "video_per_second"
1643
+ pricingKind: "video_per_second",
1644
+ display: {
1645
+ provider: "minimax",
1646
+ 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."
1647
+ },
1648
+ capabilities: {}
1096
1649
  },
1097
1650
  {
1098
1651
  id: "higgsfield-dop-preview",
@@ -1108,7 +1661,12 @@ var VIDEO_MODELS = [
1108
1661
  },
1109
1662
  defaults: {},
1110
1663
  usage: { ...VIDEO_USAGE },
1111
- pricingKind: "video_per_second"
1664
+ pricingKind: "video_per_second",
1665
+ display: {
1666
+ provider: "higgsfield",
1667
+ 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."
1668
+ },
1669
+ capabilities: {}
1112
1670
  }
1113
1671
  ];
1114
1672
 
@@ -1175,19 +1733,22 @@ function resolveTier(field, code, requested, supported, order, model, onUnsuppor
1175
1733
  });
1176
1734
  return to;
1177
1735
  }
1178
- function resolveDuration(requested, durations, model, warnings) {
1736
+ function resolveDuration(requested, durations, model, onUnsupported, warnings) {
1179
1737
  if (requested === void 0 || !durations) return requested;
1180
- let to = requested;
1181
- if (Array.isArray(durations)) {
1182
- if (durations.includes(requested)) return requested;
1183
- to = durations.reduce(
1184
- (best, d) => Math.abs(d - requested) < Math.abs(best - requested) ? d : best,
1185
- durations[0] ?? requested
1186
- );
1187
- } else {
1188
- to = Math.min(Math.max(requested, durations.min), durations.max);
1189
- if (to === requested) return requested;
1738
+ const inSet = Array.isArray(durations) ? durations.includes(requested) : requested >= durations.min && requested <= durations.max;
1739
+ if (inSet) return requested;
1740
+ if (onUnsupported === "error") {
1741
+ const supported = Array.isArray(durations) ? `Supported: ${durations.join(", ")}.` : `Supported range: ${durations.min}-${durations.max}.`;
1742
+ throw new ModelKitError({
1743
+ code: "unsupported_option",
1744
+ message: `${model.id} does not support durationSeconds ${requested}. ${supported}`,
1745
+ model: model.id
1746
+ });
1190
1747
  }
1748
+ const to = Array.isArray(durations) ? durations.reduce(
1749
+ (best, d) => Math.abs(d - requested) < Math.abs(best - requested) ? d : best,
1750
+ durations[0] ?? requested
1751
+ ) : Math.min(Math.max(requested, durations.min), durations.max);
1191
1752
  warnings.push({
1192
1753
  code: "duration_coerced",
1193
1754
  field: "durationSeconds",
@@ -1197,6 +1758,26 @@ function resolveDuration(requested, durations, model, warnings) {
1197
1758
  });
1198
1759
  return to;
1199
1760
  }
1761
+ function resolveAspectRatio(requested, supported, model, onUnsupported, warnings) {
1762
+ if (requested === void 0 || requested === "adaptive" || !supported) return requested;
1763
+ if (supported.includes(requested)) return requested;
1764
+ if (onUnsupported === "error") {
1765
+ throw new ModelKitError({
1766
+ code: "unsupported_option",
1767
+ message: `${model.id} does not support aspectRatio "${requested}". Supported: ${supported.join(", ")}.`,
1768
+ model: model.id
1769
+ });
1770
+ }
1771
+ const to = nearestAspectRatio(requested, supported);
1772
+ warnings.push({
1773
+ code: "ratio_coerced",
1774
+ field: "aspectRatio",
1775
+ from: requested,
1776
+ to,
1777
+ message: `aspect ratio ${requested} coerced to ${to} for ${model.id}`
1778
+ });
1779
+ return to;
1780
+ }
1200
1781
  function compact(refs) {
1201
1782
  return (refs ?? []).filter((r) => Boolean(r));
1202
1783
  }
@@ -1215,17 +1796,14 @@ function resolveImageOptions(model, request, onUnsupported = "error") {
1215
1796
  const warnings = [];
1216
1797
  const c = model.constraints;
1217
1798
  const merged = { ...model.defaults, ...stripUndefined(request), model: model.id };
1218
- if (merged.aspectRatio !== void 0 && merged.aspectRatio !== "adaptive" && c.aspectRatios !== void 0 && !c.aspectRatios.includes(merged.aspectRatio)) {
1219
- const to = nearestAspectRatio(merged.aspectRatio, c.aspectRatios);
1220
- warnings.push({
1221
- code: "ratio_coerced",
1222
- field: "aspectRatio",
1223
- from: merged.aspectRatio,
1224
- to,
1225
- message: `aspect ratio ${merged.aspectRatio} coerced to ${to} for ${model.id}`
1226
- });
1227
- merged.aspectRatio = to;
1228
- }
1799
+ const aspectRatio = resolveAspectRatio(
1800
+ merged.aspectRatio,
1801
+ c.aspectRatios,
1802
+ model,
1803
+ onUnsupported,
1804
+ warnings
1805
+ );
1806
+ if (aspectRatio !== void 0) merged.aspectRatio = aspectRatio;
1229
1807
  const size = resolveTier(
1230
1808
  "size",
1231
1809
  "size_coerced",
@@ -1259,7 +1837,9 @@ function resolveImageOptions(model, request, onUnsupported = "error") {
1259
1837
  "count",
1260
1838
  "outputFormat",
1261
1839
  "scaleFactor",
1262
- "safetySettings"
1840
+ "safetySettings",
1841
+ "temperature",
1842
+ "upscale"
1263
1843
  ]);
1264
1844
  return { request: merged, warnings };
1265
1845
  }
@@ -1289,17 +1869,14 @@ function resolveVideoOptions(model, request, onUnsupported = "error") {
1289
1869
  delete merged.aspectRatio;
1290
1870
  }
1291
1871
  }
1292
- if (merged.aspectRatio !== void 0 && merged.aspectRatio !== "adaptive" && c.aspectRatios !== void 0 && !c.aspectRatios.includes(merged.aspectRatio)) {
1293
- const to = nearestAspectRatio(merged.aspectRatio, c.aspectRatios);
1294
- warnings.push({
1295
- code: "ratio_coerced",
1296
- field: "aspectRatio",
1297
- from: merged.aspectRatio,
1298
- to,
1299
- message: `aspect ratio ${merged.aspectRatio} coerced to ${to} for ${model.id}`
1300
- });
1301
- merged.aspectRatio = to;
1302
- }
1872
+ const aspectRatio = resolveAspectRatio(
1873
+ merged.aspectRatio,
1874
+ c.aspectRatios,
1875
+ model,
1876
+ onUnsupported,
1877
+ warnings
1878
+ );
1879
+ if (aspectRatio !== void 0) merged.aspectRatio = aspectRatio;
1303
1880
  const resolution = resolveTier(
1304
1881
  "resolution",
1305
1882
  "resolution_coerced",
@@ -1311,7 +1888,13 @@ function resolveVideoOptions(model, request, onUnsupported = "error") {
1311
1888
  warnings
1312
1889
  );
1313
1890
  if (resolution !== void 0) merged.resolution = resolution;
1314
- const duration = resolveDuration(merged.durationSeconds, c.durations, model, warnings);
1891
+ const duration = resolveDuration(
1892
+ merged.durationSeconds,
1893
+ c.durations,
1894
+ model,
1895
+ onUnsupported,
1896
+ warnings
1897
+ );
1315
1898
  if (duration !== void 0) merged.durationSeconds = duration;
1316
1899
  const limits = c.references ?? {};
1317
1900
  let images = trim(
@@ -1366,10 +1949,45 @@ function resolveVideoOptions(model, request, onUnsupported = "error") {
1366
1949
  "cfgScale",
1367
1950
  "generateAudio",
1368
1951
  "promptExpansion",
1369
- "lastFrame"
1952
+ "lastFrame",
1953
+ "watermark",
1954
+ "safetyChecker"
1370
1955
  ]);
1371
1956
  return { request: merged, warnings };
1372
1957
  }
1958
+ function resolveAudioOptions(model, request, onUnsupported = "error") {
1959
+ const warnings = [];
1960
+ const c = model.constraints;
1961
+ const merged = { ...model.defaults, ...stripUndefined(request), model: model.id };
1962
+ for (const input of c.requires ?? []) {
1963
+ const present = input === "references" ? compact(merged.references).length > 0 : input === "sourceVideo" && merged.sourceVideo !== void 0;
1964
+ if (!present)
1965
+ throw new ModelKitError({
1966
+ code: "missing_input",
1967
+ message: `${model.id} requires ${input}`,
1968
+ model: model.id
1969
+ });
1970
+ }
1971
+ const duration = resolveDuration(
1972
+ merged.durationSeconds,
1973
+ c.durations,
1974
+ model,
1975
+ onUnsupported,
1976
+ warnings
1977
+ );
1978
+ if (duration !== void 0) merged.durationSeconds = duration;
1979
+ const references = trim(
1980
+ "references",
1981
+ compact(merged.references),
1982
+ c.references?.images ?? c.references?.total,
1983
+ model,
1984
+ warnings
1985
+ );
1986
+ if (references.length > 0) merged.references = references;
1987
+ else delete merged.references;
1988
+ dropUnsupported(merged, model, warnings, ["seed", "negativePrompt", "instrumental"]);
1989
+ return { request: merged, warnings };
1990
+ }
1373
1991
  function dropUnsupported(req, model, warnings, fields) {
1374
1992
  const supports = model.constraints.supports ?? [];
1375
1993
  for (const field of fields) {
@@ -1411,6 +2029,9 @@ function hasAll(keys, names) {
1411
2029
  (n) => Boolean(keys[n]) && (n !== "higgsfield" || Boolean(keys.higgsfieldSecret))
1412
2030
  );
1413
2031
  }
2032
+ function missingEnterpriseKeys(config, names) {
2033
+ return names.filter((name) => !hasAll(config.keys.enterprise, [name]));
2034
+ }
1414
2035
  function resolveKeys(config, names, tier = "pro") {
1415
2036
  if (tier === "enterprise" && hasAll(config.keys.enterprise, names))
1416
2037
  return config.keys.enterprise;
@@ -1443,7 +2064,6 @@ function keysFromEnv(env, prefix = "") {
1443
2064
  set("prodia", pick("PRODIA_API_KEY", "PRODIA_TOKEN"));
1444
2065
  set("higgsfield", pick("HIGGSFIELD_API_KEY"));
1445
2066
  set("higgsfieldSecret", pick("HIGGSFIELD_API_SECRET"));
1446
- set("openrouter", pick("OPENROUTER_API_KEY"));
1447
2067
  set("cloudflare", pick("CLOUDFLARE_AI_GATEWAY_TOKEN", "CF_AIG_TOKEN"));
1448
2068
  return keys;
1449
2069
  }
@@ -1491,7 +2111,9 @@ var textBody = import_zod2.z.string();
1491
2111
  function classifyHttpError(status, body, provider, model) {
1492
2112
  const asText = textBody.safeParse(body);
1493
2113
  const text2 = asText.success ? asText.data : JSON.stringify(body);
1494
- const blocked = /content_policy|safety|blockReason|PROHIBITED_CONTENT|moderation/i.test(text2);
2114
+ const blocked = /content_policy_violation|blockReason|PROHIBITED_CONTENT|moderation_blocked|safety(?![_A-Za-z])/i.test(
2115
+ text2
2116
+ );
1495
2117
  let code = "provider_rejected";
1496
2118
  if (blocked) code = "content_blocked";
1497
2119
  else if (status === 401 || status === 403) code = "auth";
@@ -1512,8 +2134,8 @@ function classifyHttpError(status, body, provider, model) {
1512
2134
  }
1513
2135
  function shouldRetry(err) {
1514
2136
  if (err instanceof ModelKitError) return err.retryable;
1515
- if (err.name === "AbortError" || /ECONNRESET|ETIMEDOUT|fetch failed|socket hang up/i.test(err.message))
1516
- return true;
2137
+ if (err.name === "AbortError") return false;
2138
+ if (/ECONNRESET|ETIMEDOUT|fetch failed|socket hang up/i.test(err.message)) return true;
1517
2139
  return false;
1518
2140
  }
1519
2141
  async function withRetry(fn, opts = {}) {
@@ -1698,6 +2320,12 @@ var import_zod4 = require("zod");
1698
2320
  function isUrlRef(ref) {
1699
2321
  return "url" in ref;
1700
2322
  }
2323
+ function abortedError() {
2324
+ return new ModelKitError({ code: "timeout", message: "aborted", retryable: false });
2325
+ }
2326
+ function throwIfAborted(signal) {
2327
+ if (signal?.aborted) throw abortedError();
2328
+ }
1701
2329
  function mediaRefToBase64(bytes) {
1702
2330
  let binary = "";
1703
2331
  const CHUNK = 32768;
@@ -1712,10 +2340,34 @@ function base64ToBytes(b64) {
1712
2340
  for (let i = 0; i < binary.length; i++) out[i] = binary.charCodeAt(i);
1713
2341
  return out;
1714
2342
  }
1715
- async function mediaRefToBytes(ref, fetch) {
2343
+ function mediaRefToDataUrl(ref) {
2344
+ if (isUrlRef(ref)) return ref.url;
2345
+ return `data:${ref.mime};base64,${mediaRefToBase64(ref.bytes)}`;
2346
+ }
2347
+ var refToUrl = mediaRefToDataUrl;
2348
+ async function readBody(res) {
2349
+ const text2 = await res.text().catch(() => "");
2350
+ try {
2351
+ const decoded = JsonValueSchema.safeParse(JSON.parse(text2));
2352
+ return decoded.success ? decoded.data : text2;
2353
+ } catch {
2354
+ return text2;
2355
+ }
2356
+ }
2357
+ async function mediaRefToBytes(ref, fetch, options = {}) {
1716
2358
  if (!isUrlRef(ref)) return { bytes: ref.bytes, mime: ref.mime };
1717
- const res = await fetch(ref.url);
1718
- if (!res.ok) throw new Error(`reference fetch failed (${res.status}): ${ref.url}`);
2359
+ const init = {};
2360
+ if (options.signal !== void 0) init.signal = options.signal;
2361
+ const res = await fetch(ref.url, init);
2362
+ if (!res.ok) {
2363
+ const text2 = await res.text().catch(() => "");
2364
+ throw classifyHttpError(
2365
+ res.status,
2366
+ text2,
2367
+ options.provider ?? "media-reference",
2368
+ options.model
2369
+ );
2370
+ }
1719
2371
  const mime = res.headers.get("content-type")?.split(";")[0]?.trim().toLowerCase() || "image/png";
1720
2372
  return { bytes: new Uint8Array(await res.arrayBuffer()), mime };
1721
2373
  }
@@ -1773,16 +2425,6 @@ var GATEWAY_SLUGS = {
1773
2425
  perplexity: "perplexity-ai",
1774
2426
  deepseek: "deepseek"
1775
2427
  };
1776
- var OPENROUTER_VENDORS = {
1777
- openai: "openai",
1778
- anthropic: "anthropic",
1779
- google: "google",
1780
- xai: "x-ai",
1781
- perplexity: "perplexity",
1782
- deepseek: "deepseek",
1783
- moonshot: "moonshotai",
1784
- minimax: "minimax"
1785
- };
1786
2428
  function detectTextProvider(modelId) {
1787
2429
  const id = modelId.toLowerCase();
1788
2430
  if (id.startsWith("claude") || id.includes("anthropic.")) return "anthropic";
@@ -1799,14 +2441,17 @@ function detectTextProvider(modelId) {
1799
2441
  message: `ai-sdk-text cannot detect a provider for "${modelId}". Set providerOptions.provider to one of ${TEXT_PROVIDERS.join(", ")}.`
1800
2442
  });
1801
2443
  }
1802
- function textKeyName(model, routing) {
1803
- if (routing.text === "openrouter") return "openrouter";
2444
+ function textKeyName(model, _routing) {
1804
2445
  return textProviderOf(model);
1805
2446
  }
1806
2447
  function textProviderOf(model) {
1807
2448
  const explicit = model.providerOptions?.["modelkit"]?.["provider"];
1808
2449
  return isTextProvider(explicit) ? explicit : detectTextProvider(model.providerModelId);
1809
2450
  }
2451
+ function textUsesCloudflareGateway(model, routing) {
2452
+ if (routing.text !== "cloudflare") return false;
2453
+ return GATEWAY_SLUGS[textProviderOf(model)] !== void 0;
2454
+ }
1810
2455
  function nativeClientFor(provider) {
1811
2456
  switch (provider) {
1812
2457
  case "openai":
@@ -1839,16 +2484,6 @@ function resolveTextRoute(ctx) {
1839
2484
  retryable: false
1840
2485
  });
1841
2486
  }
1842
- if (mode === "openrouter") {
1843
- const modelId = ctx.model.providerModelId.includes("/") ? ctx.model.providerModelId : `${OPENROUTER_VENDORS[provider]}/${ctx.model.providerModelId}`;
1844
- return {
1845
- provider,
1846
- client: "openai-compatible",
1847
- modelId,
1848
- keyName: "openrouter",
1849
- baseURL: "https://openrouter.ai/api/v1"
1850
- };
1851
- }
1852
2487
  const compatBase = OPENAI_COMPATIBLE_BASE_URLS[provider];
1853
2488
  const route2 = {
1854
2489
  provider,
@@ -1916,7 +2551,7 @@ var aiSdkTextAdapter = {
1916
2551
  case "openai-compatible":
1917
2552
  return (0, import_openai.createOpenAI)({
1918
2553
  ...settings,
1919
- name: route2.provider === "openai" ? "openai" : ctx.routing.text === "openrouter" ? "openrouter" : route2.provider
2554
+ name: route2.provider
1920
2555
  }).chat(route2.modelId);
1921
2556
  case "openai":
1922
2557
  return (0, import_openai.createOpenAI)(settings)(route2.modelId);
@@ -1929,8 +2564,9 @@ function requiredKeys(model, config) {
1929
2564
  if (model.adapter === "fal" && config.routing?.fal === "cloudflare-passthrough")
1930
2565
  return ["cloudflare"];
1931
2566
  if (model.adapter === "ai-sdk-text") {
1932
- const names = [textKeyName(model, config.routing ?? {})];
1933
- if (config.routing?.text === "cloudflare") names.push("cloudflare");
2567
+ const routing = config.routing ?? {};
2568
+ const names = [textKeyName(model, routing)];
2569
+ if (textUsesCloudflareGateway(model, routing)) names.push("cloudflare");
1934
2570
  return names;
1935
2571
  }
1936
2572
  return ADAPTER_KEYS[model.adapter];
@@ -2004,6 +2640,11 @@ function pushIgnored(warnings, field, from, modelId) {
2004
2640
  message: `${modelId} provider instance does not support ${field}; ignored`
2005
2641
  });
2006
2642
  }
2643
+ function withoutModelkit(options) {
2644
+ if (!options) return {};
2645
+ const { modelkit: _modelkit, ...rest } = options;
2646
+ return rest;
2647
+ }
2007
2648
  function logCoercions(logger, modelId, warnings) {
2008
2649
  if (warnings.length === 0) return;
2009
2650
  logger.warn(`[modelkit] ${modelId} request coerced`, { model: modelId, warnings });
@@ -2020,7 +2661,16 @@ function createModelKit(config, adapters) {
2020
2661
  message: `No adapter registered for ${model.adapter}`,
2021
2662
  model: model.id
2022
2663
  });
2023
- const keys = resolveKeys(config, requiredKeys(model, config), call.tier);
2664
+ const names = requiredKeys(model, config);
2665
+ if (call.tier === "enterprise") {
2666
+ const missing = missingEnterpriseKeys(config, names);
2667
+ if (missing.length > 0)
2668
+ logger.warn(
2669
+ `[modelkit] enterprise keys incomplete for ${model.adapter}; using pro`,
2670
+ { missing }
2671
+ );
2672
+ }
2673
+ const keys = resolveKeys(config, names, call.tier);
2024
2674
  const ctx = {
2025
2675
  model,
2026
2676
  keys,
@@ -2341,8 +2991,15 @@ function createModelKit(config, adapters) {
2341
2991
  model: model.id
2342
2992
  });
2343
2993
  }
2344
- const warnings = [];
2345
- const { adapter, ctx } = context(model, call, warnings);
2994
+ const { abortSignal: _signal, ...request } = req;
2995
+ const { request: resolved, warnings } = resolveAudioOptions(
2996
+ model,
2997
+ request,
2998
+ call.onUnsupported ?? "error"
2999
+ );
3000
+ logCoercions(logger, model.id, warnings);
3001
+ const signal = combineSignals(req.abortSignal, call.signal);
3002
+ const { adapter, ctx } = context(model, signal ? { ...call, signal } : call, warnings);
2346
3003
  if (!adapter.generateAudio) {
2347
3004
  throw new ModelKitError({
2348
3005
  code: "unsupported_option",
@@ -2350,11 +3007,7 @@ function createModelKit(config, adapters) {
2350
3007
  model: model.id
2351
3008
  });
2352
3009
  }
2353
- const result = await adapter.generateAudio(
2354
- { ...model.defaults, ...req, model: model.id },
2355
- ctx
2356
- );
2357
- logCoercions(logger, model.id, warnings);
3010
+ const result = await adapter.generateAudio(resolved, ctx);
2358
3011
  await emit({ ...result.usage, model: model.id, unit: model.usage.unit });
2359
3012
  return result;
2360
3013
  },
@@ -2375,7 +3028,7 @@ function createModelKit(config, adapters) {
2375
3028
  });
2376
3029
  return {
2377
3030
  model,
2378
- providerOptions: entry.providerOptions ?? {},
3031
+ providerOptions: withoutModelkit(entry.providerOptions),
2379
3032
  catalog: entry
2380
3033
  };
2381
3034
  },
@@ -2411,7 +3064,9 @@ function createModelKit(config, adapters) {
2411
3064
  ...textAbortOptions(options.abortSignal, call.signal),
2412
3065
  model: target.languageModel,
2413
3066
  providerOptions: {},
2414
- prepareStep: steps.wrap(options.prepareStep),
3067
+ prepareStep: steps.wrap(
3068
+ options.prepareStep ?? options.experimental_prepareStep
3069
+ ),
2415
3070
  onFinish: async (event) => {
2416
3071
  for (const usage2 of steps.usage(event.steps)) await emit(withRowUnit(usage2));
2417
3072
  await callerOnFinish?.(event);
@@ -2468,6 +3123,7 @@ var SEEDREAM_SIZES = {
2468
3123
  "3:2": "1248x832",
2469
3124
  "2:3": "832x1248",
2470
3125
  "21:9": "1568x672",
3126
+ "9:21": "672x1568",
2471
3127
  "4:5": "864x1080",
2472
3128
  "5:4": "1080x864"
2473
3129
  },
@@ -2480,16 +3136,34 @@ var SEEDREAM_SIZES = {
2480
3136
  "3:2": "2496x1664",
2481
3137
  "2:3": "1664x2496",
2482
3138
  "21:9": "3136x1344",
3139
+ "9:21": "1344x3136",
2483
3140
  "4:5": "1728x2160",
2484
3141
  "5:4": "2160x1728"
3142
+ },
3143
+ // 4K keeps the long edge at Ark's 4096 cap (a doubled 2K entry would exceed
3144
+ // it); sides are multiples of 16.
3145
+ "4K": {
3146
+ "1:1": "4096x4096",
3147
+ "4:3": "4096x3072",
3148
+ "3:4": "3072x4096",
3149
+ "16:9": "4096x2304",
3150
+ "9:16": "2304x4096",
3151
+ "3:2": "4096x2736",
3152
+ "2:3": "2736x4096",
3153
+ "21:9": "4096x1760",
3154
+ "9:21": "1760x4096",
3155
+ "4:5": "3280x4096",
3156
+ "5:4": "4096x3280"
2485
3157
  }
2486
3158
  };
2487
3159
  var arkAdapter = {
2488
3160
  id: "ark",
2489
3161
  async generateImage(req, ctx) {
3162
+ throwIfAborted(ctx.signal);
2490
3163
  return generateSeedreamImage(req, ctx);
2491
3164
  },
2492
3165
  async generateVideo(req, ctx) {
3166
+ throwIfAborted(ctx.signal);
2493
3167
  return generateSeedanceVideo(req, ctx);
2494
3168
  }
2495
3169
  };
@@ -2509,7 +3183,7 @@ function arkKey(ctx) {
2509
3183
  function throwArkError(status, body, model) {
2510
3184
  const parsed = import_zod5.z.string().safeParse(body);
2511
3185
  const text2 = parsed.success ? parsed.data : JSON.stringify(body ?? "");
2512
- if (/sensitive.?content|inputimage|human.?face/i.test(text2)) {
3186
+ if (/sensitive.?content|human.?face/i.test(text2)) {
2513
3187
  const init = {
2514
3188
  code: "content_blocked",
2515
3189
  message: `ark rejected the request: ${text2.slice(0, 500)}`,
@@ -2534,7 +3208,7 @@ function throwArkError(status, body, model) {
2534
3208
  throw classifyHttpError(status, body, "ark", model);
2535
3209
  }
2536
3210
  function seedreamSize(aspect, size) {
2537
- const tier = size === "1K" ? "1K" : "2K";
3211
+ const tier = size === "4K" || size === "1K" ? size : "2K";
2538
3212
  const table = SEEDREAM_SIZES[tier];
2539
3213
  return table[aspect ?? ""] ?? table["1:1"] ?? "2048x2048";
2540
3214
  }
@@ -2545,10 +3219,6 @@ function sniffedImageMime(bytes) {
2545
3219
  return "image/webp";
2546
3220
  return "image/jpeg";
2547
3221
  }
2548
- function arkUrl(ref) {
2549
- if ("url" in ref) return ref.url;
2550
- return `data:${ref.mime};base64,${Buffer.from(ref.bytes).toString("base64")}`;
2551
- }
2552
3222
  var SeedreamImageSchema = import_zod5.z.object({
2553
3223
  url: import_zod5.z.string().optional(),
2554
3224
  b64_json: import_zod5.z.string().optional()
@@ -2575,7 +3245,7 @@ async function generateSeedreamImage(req, ctx) {
2575
3245
  const key = arkKey(ctx);
2576
3246
  const modelId = ctx.model.id;
2577
3247
  const size = seedreamSize(req.aspectRatio, req.size);
2578
- const refs = (req.references ?? []).map(arkUrl);
3248
+ const refs = (req.references ?? []).map(refToUrl);
2579
3249
  const body = {
2580
3250
  model: ctx.model.providerModelId,
2581
3251
  prompt: req.prompt,
@@ -2627,10 +3297,18 @@ async function generateSeedreamImage(req, ctx) {
2627
3297
  if (req.quality !== void 0) usage2.imageQuality = req.quality;
2628
3298
  return { images: [{ bytes, mime: sniffedImageMime(bytes) }], usage: usage2, raw: data };
2629
3299
  }
3300
+ var SeedanceUsageSchema = import_zod5.z.object({
3301
+ total_tokens: import_zod5.z.number().optional(),
3302
+ completion_tokens: import_zod5.z.number().optional()
3303
+ });
2630
3304
  var SeedanceResponseSchema = import_zod5.z.object({
2631
3305
  id: import_zod5.z.string().optional(),
2632
3306
  status: import_zod5.z.string().optional(),
2633
3307
  content: import_zod5.z.object({ video_url: import_zod5.z.string().optional() }).optional(),
3308
+ usage: SeedanceUsageSchema.optional(),
3309
+ duration: import_zod5.z.number().optional(),
3310
+ framespersecond: import_zod5.z.number().optional(),
3311
+ resolution: import_zod5.z.string().optional(),
2634
3312
  error: import_zod5.z.union([import_zod5.z.string(), import_zod5.z.object({ message: import_zod5.z.string().optional() })]).optional()
2635
3313
  });
2636
3314
  var SeedanceErrorMessageSchema = import_zod5.z.object({ message: import_zod5.z.string().optional() });
@@ -2662,42 +3340,42 @@ async function generateSeedanceVideo(req, ctx) {
2662
3340
  if (req.firstFrame) {
2663
3341
  content.push({
2664
3342
  type: "image_url",
2665
- image_url: { url: arkUrl(req.firstFrame) },
3343
+ image_url: { url: refToUrl(req.firstFrame) },
2666
3344
  role: "first_frame"
2667
3345
  });
2668
3346
  }
2669
3347
  if (req.lastFrame) {
2670
3348
  content.push({
2671
3349
  type: "image_url",
2672
- image_url: { url: arkUrl(req.lastFrame) },
3350
+ image_url: { url: refToUrl(req.lastFrame) },
2673
3351
  role: "last_frame"
2674
3352
  });
2675
3353
  }
2676
3354
  for (const ref of req.referenceImages ?? []) {
2677
3355
  content.push({
2678
3356
  type: "image_url",
2679
- image_url: { url: arkUrl(ref) },
3357
+ image_url: { url: refToUrl(ref) },
2680
3358
  role: "reference_image"
2681
3359
  });
2682
3360
  }
2683
3361
  if (req.sourceVideo) {
2684
3362
  content.push({
2685
3363
  type: "video_url",
2686
- video_url: { url: arkUrl(req.sourceVideo) },
3364
+ video_url: { url: refToUrl(req.sourceVideo) },
2687
3365
  role: "reference_video"
2688
3366
  });
2689
3367
  }
2690
3368
  for (const ref of req.referenceVideos ?? []) {
2691
3369
  content.push({
2692
3370
  type: "video_url",
2693
- video_url: { url: arkUrl(ref) },
3371
+ video_url: { url: refToUrl(ref) },
2694
3372
  role: "reference_video"
2695
3373
  });
2696
3374
  }
2697
3375
  for (const ref of req.referenceAudios ?? []) {
2698
3376
  content.push({
2699
3377
  type: "audio_url",
2700
- audio_url: { url: arkUrl(ref) },
3378
+ audio_url: { url: refToUrl(ref) },
2701
3379
  role: "reference_audio"
2702
3380
  });
2703
3381
  }
@@ -2711,6 +3389,7 @@ async function generateSeedanceVideo(req, ctx) {
2711
3389
  if (!hasFrameAnchor && req.aspectRatio !== void 0) body.ratio = req.aspectRatio;
2712
3390
  if (req.generateAudio !== void 0) body.generate_audio = req.generateAudio;
2713
3391
  if (req.seed !== void 0) body.seed = req.seed;
3392
+ if (req.watermark !== void 0) body.watermark = req.watermark;
2714
3393
  const submit = await withRetry(async () => {
2715
3394
  const res = await arkFetch(ctx, `${arkBaseUrl(ctx)}${SUBMIT_PATH}`, {
2716
3395
  method: "POST",
@@ -2774,6 +3453,11 @@ async function generateSeedanceVideo(req, ctx) {
2774
3453
  "Seedance task succeeded but returned no video_url",
2775
3454
  modelId
2776
3455
  );
3456
+ const tokens = polled.usage;
3457
+ if (tokens?.total_tokens !== void 0 && tokens.completion_tokens !== void 0) {
3458
+ usage2.inputTokens = tokens.total_tokens - tokens.completion_tokens;
3459
+ usage2.outputTokens = tokens.completion_tokens;
3460
+ }
2777
3461
  return {
2778
3462
  video: { url, expiresAt: new Date(Date.now() + VIDEO_URL_TTL_MS) },
2779
3463
  usage: usage2,
@@ -2837,24 +3521,17 @@ var defaultDeps = { createFalClient: defaultCreateFalClient };
2837
3521
  function createFalAdapter(deps = defaultDeps) {
2838
3522
  return {
2839
3523
  id: "fal",
2840
- generateImage: (req, ctx) => generateMuseImage(req, ctx, deps),
2841
- generateVideo: (req, ctx) => generateFalVideo(req, ctx, deps)
3524
+ generateImage: (req, ctx) => {
3525
+ throwIfAborted(ctx.signal);
3526
+ return generateMuseImage(req, ctx, deps);
3527
+ },
3528
+ generateVideo: (req, ctx) => {
3529
+ throwIfAborted(ctx.signal);
3530
+ return generateFalVideo(req, ctx, deps);
3531
+ }
2842
3532
  };
2843
3533
  }
2844
3534
  var falAdapter = createFalAdapter();
2845
- function refToUrl(ref) {
2846
- if ("url" in ref) return ref.url;
2847
- return `data:${ref.mime};base64,${Buffer.from(ref.bytes).toString("base64")}`;
2848
- }
2849
- async function readBody(res) {
2850
- const text2 = await res.text().catch(() => "");
2851
- try {
2852
- const decoded = JsonValueSchema.safeParse(JSON.parse(text2));
2853
- return decoded.success ? decoded.data : text2;
2854
- } catch {
2855
- return text2;
2856
- }
2857
- }
2858
3535
  var falDetailSchema = import_zod6.z.object({ detail: JsonValueSchema });
2859
3536
  function liftDetail(body) {
2860
3537
  const parsed = falDetailSchema.safeParse(body);
@@ -3091,8 +3768,12 @@ async function generateMuseImage(req, ctx, deps) {
3091
3768
  raw: payload
3092
3769
  };
3093
3770
  }
3771
+ var H3_MAX_MODEL_IDS = /* @__PURE__ */ new Set([
3772
+ "minimax/h3-max",
3773
+ "minimax/h3-max/reference-to-video"
3774
+ ]);
3094
3775
  function minimaxFamily(providerModelId) {
3095
- return providerModelId.toLowerCase().includes("h3-max") ? "h3-max" : "h3";
3776
+ return H3_MAX_MODEL_IDS.has(providerModelId.toLowerCase()) ? "h3-max" : "h3";
3096
3777
  }
3097
3778
  function minimaxBase(req, family) {
3098
3779
  const resolution = (family === "h3-max" ? req.resolution ?? "768p" : req.resolution ?? "2k").toUpperCase();
@@ -3100,14 +3781,17 @@ function minimaxBase(req, family) {
3100
3781
  prompt: req.prompt.trim(),
3101
3782
  duration: req.durationSeconds ?? 5,
3102
3783
  resolution,
3103
- prompt_expansion_mode: req.promptExpansion ?? "balanced",
3104
- enable_safety_checker: true
3784
+ prompt_expansion_mode: req.promptExpansion ?? "balanced"
3105
3785
  };
3786
+ if (req.safetyChecker !== void 0) input.enable_safety_checker = req.safetyChecker;
3106
3787
  if (req.seed !== void 0) input.seed = req.seed;
3107
3788
  return input;
3108
3789
  }
3790
+ var KLING_MODEL_IDS = /* @__PURE__ */ new Set([
3791
+ "fal-ai/kling-video/o1/video-to-video/reference"
3792
+ ]);
3109
3793
  function isKling(providerModelId) {
3110
- return providerModelId.toLowerCase().includes("kling");
3794
+ return KLING_MODEL_IDS.has(providerModelId.toLowerCase());
3111
3795
  }
3112
3796
  function klingO1Input(req) {
3113
3797
  const source = req.sourceVideo ? refToUrl(req.sourceVideo) : void 0;
@@ -3180,7 +3864,9 @@ async function generateFalVideo(req, ctx, deps) {
3180
3864
  if (lastFrame) base.end_image_url = lastFrame;
3181
3865
  } else {
3182
3866
  endpoint = `minimax/${family}/text-to-video`;
3183
- base.aspect_ratio = req.aspectRatio ?? "adaptive";
3867
+ if (req.aspectRatio !== void 0 && req.aspectRatio !== "adaptive") {
3868
+ base.aspect_ratio = req.aspectRatio;
3869
+ }
3184
3870
  }
3185
3871
  if (ctx.routing.fal === "cloudflare-passthrough") {
3186
3872
  return await passthroughVideoJob(ctx, req, endpoint, base);
@@ -3666,6 +4352,14 @@ async function generateVeoVideo(req, ctx, createGenAIClient) {
3666
4352
  model: modelId
3667
4353
  });
3668
4354
  }
4355
+ if (req.lastFrame !== void 0 && req.firstFrame === void 0) {
4356
+ throw new ModelKitError({
4357
+ code: "missing_input",
4358
+ message: `${modelId} requires firstFrame when lastFrame is set`,
4359
+ provider: "google-genai",
4360
+ model: modelId
4361
+ });
4362
+ }
3669
4363
  const duration = req.durationSeconds ?? 8;
3670
4364
  const personGeneration = req.firstFrame ?? refs.length > 0 ? "allow_adult" : "allow_all";
3671
4365
  let image;
@@ -3732,7 +4426,7 @@ async function generateVeoVideo(req, ctx, createGenAIClient) {
3732
4426
  throwSdkError(cause, modelId);
3733
4427
  }
3734
4428
  };
3735
- const jobId = operation.name ?? "veo-operation";
4429
+ const jobId = operation.name ?? crypto.randomUUID();
3736
4430
  const usage2 = videoUsage2(ctx, req, duration);
3737
4431
  return {
3738
4432
  jobId,
@@ -3871,6 +4565,7 @@ async function generateOmniVideo(req, ctx, createGenAIClient) {
3871
4565
  });
3872
4566
  }
3873
4567
  for (const ref of videos) {
4568
+ if (ref.label !== void 0) parts.push({ type: "text", text: ref.label });
3874
4569
  const media = await resolveMedia(ref, ctx.fetch, ctx.signal, "video/mp4", modelId);
3875
4570
  parts.push({
3876
4571
  type: "video",
@@ -3879,6 +4574,8 @@ async function generateOmniVideo(req, ctx, createGenAIClient) {
3879
4574
  });
3880
4575
  }
3881
4576
  if (req.firstFrame) {
4577
+ if (req.firstFrame.label !== void 0)
4578
+ parts.push({ type: "text", text: req.firstFrame.label });
3882
4579
  const media = await resolveMedia(
3883
4580
  req.firstFrame,
3884
4581
  ctx.fetch,
@@ -3897,6 +4594,7 @@ async function generateOmniVideo(req, ctx, createGenAIClient) {
3897
4594
  }
3898
4595
  }
3899
4596
  for (const ref of images) {
4597
+ if (ref.label !== void 0) parts.push({ type: "text", text: ref.label });
3900
4598
  const media = await resolveMedia(ref, ctx.fetch, ctx.signal, "image/png", modelId);
3901
4599
  if (media.bytes.length > OMNI_INLINE_LIMIT_BYTES) {
3902
4600
  parts.push({
@@ -3940,7 +4638,7 @@ async function generateOmniVideo(req, ctx, createGenAIClient) {
3940
4638
  } finally {
3941
4639
  await cleanupUploads();
3942
4640
  }
3943
- const jobId = interaction.id ?? "gemini-omni-interaction";
4641
+ const jobId = interaction.id ?? crypto.randomUUID();
3944
4642
  const usage2 = videoUsage2(ctx, req, duration);
3945
4643
  const getInteraction = async () => {
3946
4644
  try {
@@ -4166,11 +4864,11 @@ function createGoogleImageAdapter(deps = defaultGoogleImageDeps) {
4166
4864
  const model = google(ctx.model.providerModelId);
4167
4865
  const content = [];
4168
4866
  for (const ref of req.references ?? []) {
4867
+ if (ref.label !== void 0) content.push({ type: "text", text: ref.label });
4169
4868
  if ("url" in ref) content.push({ type: "image", image: ref.url });
4170
4869
  else content.push({ type: "image", image: ref.bytes, mediaType: ref.mime });
4171
4870
  }
4172
4871
  content.push({ type: "text", text: req.prompt });
4173
- void ctx.call.postCropToAspect;
4174
4872
  const imageConfig = {};
4175
4873
  if (req.aspectRatio !== void 0 && req.aspectRatio !== "adaptive")
4176
4874
  imageConfig.aspectRatio = req.aspectRatio;
@@ -4186,7 +4884,7 @@ function createGoogleImageAdapter(deps = defaultGoogleImageDeps) {
4186
4884
  const timeout = AbortSignal.timeout(GEMINI_IMAGE_TIMEOUT_MS);
4187
4885
  const signal = ctx.signal !== void 0 ? AbortSignal.any([ctx.signal, timeout]) : timeout;
4188
4886
  try {
4189
- return await deps.runTextGeneration({
4887
+ const genOptions = {
4190
4888
  model,
4191
4889
  messages: [{ role: "user", content }],
4192
4890
  maxRetries: 0,
@@ -4194,7 +4892,9 @@ function createGoogleImageAdapter(deps = defaultGoogleImageDeps) {
4194
4892
  google: googleOptions
4195
4893
  },
4196
4894
  abortSignal: signal
4197
- });
4895
+ };
4896
+ if (req.temperature !== void 0) genOptions.temperature = req.temperature;
4897
+ return await deps.runTextGeneration(genOptions);
4198
4898
  } catch (cause) {
4199
4899
  throw toProviderError(cause, ctx);
4200
4900
  }
@@ -4398,26 +5098,14 @@ function headers(ctx) {
4398
5098
  const { key, secret } = credentials(ctx);
4399
5099
  return { "Content-Type": "application/json", "hf-api-key": key, "hf-secret": secret };
4400
5100
  }
4401
- function refToUrl2(ref) {
4402
- if ("url" in ref) return ref.url;
4403
- return `data:${ref.mime};base64,${Buffer.from(ref.bytes).toString("base64")}`;
4404
- }
4405
- async function readBody2(res) {
4406
- const text2 = await res.text().catch(() => "");
4407
- try {
4408
- const decoded = JsonValueSchema.safeParse(JSON.parse(text2));
4409
- return decoded.success ? decoded.data : text2;
4410
- } catch {
4411
- return text2;
4412
- }
4413
- }
4414
5101
  function klingEndpoint(providerModelId) {
4415
5102
  if (providerModelId.startsWith("http")) return providerModelId;
4416
5103
  const path = providerModelId.startsWith("/") ? providerModelId.slice(1) : providerModelId;
4417
5104
  return `${PLATFORM}/${path}`;
4418
5105
  }
4419
- function isDop(providerModelId) {
4420
- return providerModelId.toLowerCase().includes("dop");
5106
+ var DOP_MODEL_IDS = /* @__PURE__ */ new Set(["dop", "dop-preview"]);
5107
+ function higgsfieldVideoKind(providerModelId) {
5108
+ return DOP_MODEL_IDS.has(providerModelId.toLowerCase()) ? "dop" : "kling";
4421
5109
  }
4422
5110
  function videoUsage3(ctx, req, duration) {
4423
5111
  const event = {
@@ -4461,12 +5149,12 @@ async function createRequest(ctx, url, init) {
4461
5149
  throw classifyThrown("higgsfield", cause, ctx.model.id);
4462
5150
  }
4463
5151
  if (!res.ok) {
4464
- throw classifyHttpError(res.status, await readBody2(res), "higgsfield", ctx.model.id);
5152
+ throw classifyHttpError(res.status, await readBody(res), "higgsfield", ctx.model.id);
4465
5153
  }
4466
5154
  return res;
4467
5155
  }
4468
5156
  async function generateKlingVideo(req, ctx) {
4469
- const firstFrame = req.firstFrame ? refToUrl2(req.firstFrame) : void 0;
5157
+ const firstFrame = req.firstFrame ? refToUrl(req.firstFrame) : void 0;
4470
5158
  if (!firstFrame) {
4471
5159
  throw new ModelKitError({
4472
5160
  code: "missing_input",
@@ -4485,7 +5173,7 @@ async function generateKlingVideo(req, ctx) {
4485
5173
  if (req.durationSeconds !== void 0) body.duration = req.durationSeconds;
4486
5174
  if (req.aspectRatio !== void 0 && req.aspectRatio !== "adaptive")
4487
5175
  body.aspect_ratio = req.aspectRatio;
4488
- if (req.lastFrame) body.last_image_url = refToUrl2(req.lastFrame);
5176
+ if (req.lastFrame) body.last_image_url = refToUrl(req.lastFrame);
4489
5177
  const endpoint = klingEndpoint(ctx.model.providerModelId);
4490
5178
  const retryOptions = {};
4491
5179
  if (ctx.signal !== void 0) retryOptions.signal = ctx.signal;
@@ -4511,9 +5199,9 @@ async function generateKlingVideo(req, ctx) {
4511
5199
  } catch {
4512
5200
  return void 0;
4513
5201
  }
4514
- if (res.status >= 500) return void 0;
5202
+ if (res.status === 429 || res.status >= 500) return void 0;
4515
5203
  if (!res.ok) {
4516
- throw classifyHttpError(res.status, await readBody2(res), "higgsfield", ctx.model.id);
5204
+ throw classifyHttpError(res.status, await readBody(res), "higgsfield", ctx.model.id);
4517
5205
  }
4518
5206
  const payload = JsonValueSchema.parse(await res.json());
4519
5207
  const parsed = klingStatusSchema.safeParse(payload);
@@ -4620,7 +5308,7 @@ function dopUrl(body) {
4620
5308
  var dopCreateSchema = import_zod10.z.object({ id: import_zod10.z.string() });
4621
5309
  var dopStatusSchema = import_zod10.z.object({ status: import_zod10.z.string().optional() });
4622
5310
  async function generateDopVideo(req, ctx) {
4623
- const firstFrame = req.firstFrame ? refToUrl2(req.firstFrame) : void 0;
5311
+ const firstFrame = req.firstFrame ? refToUrl(req.firstFrame) : void 0;
4624
5312
  if (!firstFrame) {
4625
5313
  throw new ModelKitError({
4626
5314
  code: "missing_input",
@@ -4640,7 +5328,7 @@ async function generateDopVideo(req, ctx) {
4640
5328
  webhook: null
4641
5329
  };
4642
5330
  if (req.lastFrame) {
4643
- params.input_images_end = [{ type: "image_url", image_url: refToUrl2(req.lastFrame) }];
5331
+ params.input_images_end = [{ type: "image_url", image_url: refToUrl(req.lastFrame) }];
4644
5332
  }
4645
5333
  if (req.seed !== void 0) params.seed = req.seed;
4646
5334
  const retryOptions = {};
@@ -4730,7 +5418,10 @@ async function generateDopVideo(req, ctx) {
4730
5418
  }
4731
5419
  var higgsfieldAdapter = {
4732
5420
  id: "higgsfield",
4733
- generateVideo: (req, ctx) => isDop(ctx.model.providerModelId) ? generateDopVideo(req, ctx) : generateKlingVideo(req, ctx)
5421
+ generateVideo: (req, ctx) => {
5422
+ throwIfAborted(ctx.signal);
5423
+ return higgsfieldVideoKind(ctx.model.providerModelId) === "dop" ? generateDopVideo(req, ctx) : generateKlingVideo(req, ctx);
5424
+ }
4734
5425
  };
4735
5426
 
4736
5427
  // src/adapters/openai-image.ts
@@ -4825,6 +5516,7 @@ var openaiUsageSchema = import_zod11.z.object({
4825
5516
  var openaiImageAdapter = {
4826
5517
  id: "openai-image",
4827
5518
  async generateImage(req, ctx) {
5519
+ throwIfAborted(ctx.signal);
4828
5520
  const apiKey = ctx.keys.openai;
4829
5521
  if (!apiKey)
4830
5522
  throw new ModelKitError({
@@ -4834,6 +5526,13 @@ var openaiImageAdapter = {
4834
5526
  model: ctx.model.id,
4835
5527
  retryable: false
4836
5528
  });
5529
+ if ((req.references ?? []).some((ref) => ref.label !== void 0)) {
5530
+ ctx.warnings.push({
5531
+ code: "option_ignored",
5532
+ field: "references.label",
5533
+ message: "openai-image cannot express reference labels; ignoring them"
5534
+ });
5535
+ }
4837
5536
  const client2 = new import_openai2.default({ apiKey, fetch: ctx.fetch, maxRetries: 0 });
4838
5537
  const size = req.exactSize !== void 0 ? req.exactSize : req.aspectRatio === void 0 || req.aspectRatio === "adaptive" ? void 0 : aspectRatioToSize(req.aspectRatio, req.size ?? "1K");
4839
5538
  const refs = req.references ?? [];
@@ -4847,24 +5546,34 @@ var openaiImageAdapter = {
4847
5546
  try {
4848
5547
  if (!isEdit) {
4849
5548
  if (size !== void 0) {
4850
- return await client2.images.generate({
5549
+ return await client2.images.generate(
5550
+ {
5551
+ model: ctx.model.providerModelId,
5552
+ prompt: req.prompt,
5553
+ size,
5554
+ quality: sdkQuality,
5555
+ ...count
5556
+ },
5557
+ { signal: ctx.signal }
5558
+ );
5559
+ }
5560
+ return await client2.images.generate(
5561
+ {
4851
5562
  model: ctx.model.providerModelId,
4852
5563
  prompt: req.prompt,
4853
- size,
4854
5564
  quality: sdkQuality,
4855
5565
  ...count
4856
- });
4857
- }
4858
- return await client2.images.generate({
4859
- model: ctx.model.providerModelId,
4860
- prompt: req.prompt,
4861
- quality: sdkQuality,
4862
- ...count
4863
- });
5566
+ },
5567
+ { signal: ctx.signal }
5568
+ );
4864
5569
  }
4865
5570
  const files = await Promise.all(
4866
5571
  refs.slice(0, MAX_EDIT_REFERENCES).map(async (ref, i) => {
4867
- const { bytes, mime } = await mediaRefToBytes(ref, ctx.fetch);
5572
+ const { bytes, mime } = await mediaRefToBytes(ref, ctx.fetch, {
5573
+ signal: ctx.signal,
5574
+ provider: "openai-image",
5575
+ model: ctx.model.id
5576
+ });
4868
5577
  const ext = mime.includes("jpeg") ? "jpg" : mime.includes("webp") ? "webp" : "png";
4869
5578
  return new File([bytes], `ref-${i}.${ext}`, { type: mime });
4870
5579
  })
@@ -4872,22 +5581,28 @@ var openaiImageAdapter = {
4872
5581
  const single = files.length === 1 ? files[0] : void 0;
4873
5582
  const imageInput = single ?? files;
4874
5583
  if (size !== void 0) {
4875
- return await client2.images.edit({
5584
+ return await client2.images.edit(
5585
+ {
5586
+ model: ctx.model.providerModelId,
5587
+ image: imageInput,
5588
+ prompt: req.prompt,
5589
+ size,
5590
+ quality: sdkQuality,
5591
+ ...count
5592
+ },
5593
+ { signal: ctx.signal }
5594
+ );
5595
+ }
5596
+ return await client2.images.edit(
5597
+ {
4876
5598
  model: ctx.model.providerModelId,
4877
5599
  image: imageInput,
4878
5600
  prompt: req.prompt,
4879
- size,
4880
5601
  quality: sdkQuality,
4881
5602
  ...count
4882
- });
4883
- }
4884
- return await client2.images.edit({
4885
- model: ctx.model.providerModelId,
4886
- image: imageInput,
4887
- prompt: req.prompt,
4888
- quality: sdkQuality,
4889
- ...count
4890
- });
5603
+ },
5604
+ { signal: ctx.signal }
5605
+ );
4891
5606
  } catch (cause) {
4892
5607
  throw toProviderError2(cause, ctx);
4893
5608
  }
@@ -4969,7 +5684,7 @@ async function refToBytes(ctx, ref) {
4969
5684
  if ("bytes" in ref) return { bytes: ref.bytes, mime: ref.mime };
4970
5685
  let res;
4971
5686
  try {
4972
- res = await ctx.fetch(ref.url);
5687
+ res = await ctx.fetch(ref.url, ctx.signal ? { signal: ctx.signal } : {});
4973
5688
  } catch (cause) {
4974
5689
  throw classifyThrown("prodia", cause, ctx.model.id);
4975
5690
  }
@@ -5015,6 +5730,12 @@ function emptyImage() {
5015
5730
  retryable: false
5016
5731
  });
5017
5732
  }
5733
+ function inputExtension(mime) {
5734
+ if (mime.includes("jpeg") || mime.includes("jpg")) return "jpg";
5735
+ if (mime.includes("webp")) return "webp";
5736
+ if (mime.includes("gif")) return "gif";
5737
+ return "png";
5738
+ }
5018
5739
  function isBgRemove(providerModelId) {
5019
5740
  const id = providerModelId.toLowerCase();
5020
5741
  return id.includes("remove-background") || id.includes("bg-remove");
@@ -5023,6 +5744,7 @@ function isKlein(providerModelId) {
5023
5744
  return providerModelId.toLowerCase().includes("klein");
5024
5745
  }
5025
5746
  async function generateProdiaImage(req, ctx) {
5747
+ throwIfAborted(ctx.signal);
5026
5748
  const key = ctx.keys.prodia;
5027
5749
  if (!key) {
5028
5750
  throw new ModelKitError({
@@ -5121,7 +5843,9 @@ async function generateProdiaImage(req, ctx) {
5121
5843
  "input",
5122
5844
  // Uint8Array<ArrayBufferLike> is not a valid BlobPart under the
5123
5845
  // strict lib types; copy into a fresh ArrayBuffer-backed view.
5124
- new File([new Uint8Array(input.bytes)], `input-${i}.jpg`, { type: "image/jpeg" })
5846
+ new File([new Uint8Array(input.bytes)], `input-${i}.${inputExtension(input.mime)}`, {
5847
+ type: input.mime
5848
+ })
5125
5849
  );
5126
5850
  });
5127
5851
  const retryOpts = {
@@ -5136,11 +5860,13 @@ async function generateProdiaImage(req, ctx) {
5136
5860
  res = await withRetry(async () => {
5137
5861
  let attemptRes;
5138
5862
  try {
5139
- attemptRes = await ctx.fetch(PRODIA_JOB_URL, {
5863
+ const init = {
5140
5864
  method: "POST",
5141
5865
  headers: { Authorization: `Bearer ${key}`, Accept: accept },
5142
5866
  body: form
5143
- });
5867
+ };
5868
+ if (ctx.signal !== void 0) init.signal = ctx.signal;
5869
+ attemptRes = await ctx.fetch(PRODIA_JOB_URL, init);
5144
5870
  } catch (err) {
5145
5871
  throw classifyThrown("prodia", err, ctx.model.id);
5146
5872
  }
@@ -5201,11 +5927,7 @@ function client(ctx, deps) {
5201
5927
  retryable: false
5202
5928
  });
5203
5929
  }
5204
- return deps.createReplicateClient({ auth: token });
5205
- }
5206
- function refToUrl3(ref) {
5207
- if ("url" in ref) return ref.url;
5208
- return `data:${ref.mime};base64,${Buffer.from(ref.bytes).toString("base64")}`;
5930
+ return deps.createReplicateClient({ auth: token, fetch: ctx.fetch });
5209
5931
  }
5210
5932
  function route(providerModelId) {
5211
5933
  if (providerModelId.includes(":")) {
@@ -5225,8 +5947,9 @@ function replicateError(cause, ctx) {
5225
5947
  const status = info.status ?? (nested.success ? nested.data.response?.status : void 0);
5226
5948
  return classifyThrown("replicate", cause, ctx.model.id, { status, body: info.body });
5227
5949
  }
5228
- function isLower(id) {
5229
- return id.toLowerCase();
5950
+ function baseModelId(providerModelId) {
5951
+ const colon = providerModelId.indexOf(":");
5952
+ return colon === -1 ? providerModelId : providerModelId.slice(0, colon);
5230
5953
  }
5231
5954
  function predictionErrorBody(cause) {
5232
5955
  const parsed = JsonValueSchema.safeParse(cause);
@@ -5274,6 +5997,7 @@ function noOutput() {
5274
5997
  });
5275
5998
  }
5276
5999
  async function runPrediction(ctx, input, deps) {
6000
+ throwIfAborted(ctx.signal);
5277
6001
  const api = client(ctx, deps);
5278
6002
  const createOptions = { ...route(ctx.model.providerModelId), input };
5279
6003
  let prediction;
@@ -5281,12 +6005,14 @@ async function runPrediction(ctx, input, deps) {
5281
6005
  prediction = await withRetry(
5282
6006
  async () => {
5283
6007
  try {
5284
- return await api.predictions.create(createOptions);
6008
+ return await api.predictions.create(
6009
+ ctx.signal ? { ...createOptions, signal: ctx.signal } : createOptions
6010
+ );
5285
6011
  } catch (cause) {
5286
6012
  throw replicateError(cause, ctx);
5287
6013
  }
5288
6014
  },
5289
- ctx.signal ? { signal: ctx.signal } : {}
6015
+ { attempts: 1 }
5290
6016
  );
5291
6017
  } catch (cause) {
5292
6018
  throw replicateError(cause, ctx);
@@ -5299,7 +6025,10 @@ async function runPrediction(ctx, input, deps) {
5299
6025
  const done = await pollUntil(async () => {
5300
6026
  let current;
5301
6027
  try {
5302
- current = await api.predictions.get(prediction.id);
6028
+ current = await api.predictions.get(
6029
+ prediction.id,
6030
+ ctx.signal ? { signal: ctx.signal } : void 0
6031
+ );
5303
6032
  } catch (cause) {
5304
6033
  throw replicateError(cause, ctx);
5305
6034
  }
@@ -5366,21 +6095,32 @@ async function download(ctx, url) {
5366
6095
  const mime = res.headers.get("content-type") ?? "image/png";
5367
6096
  return { bytes: new Uint8Array(await res.arrayBuffer()), mime };
5368
6097
  }
5369
- function isUpscaler(providerModelId) {
5370
- const id = isLower(providerModelId);
5371
- return id.includes("upscal") || id.includes("crystal") || id.includes("topaz");
5372
- }
5373
- function isTopazUpscaler(providerModelId) {
5374
- return isLower(providerModelId).includes("topazlabs/image-upscale");
5375
- }
5376
- function isQwen(providerModelId) {
5377
- return isLower(providerModelId).includes("qwen");
5378
- }
5379
- function isWanImage(providerModelId) {
5380
- return isLower(providerModelId).includes("wan-2.7");
6098
+ var REPLICATE_IMAGE_KINDS = {
6099
+ "topazlabs/image-upscale": "topaz",
6100
+ "philz1337x/crystal-upscaler": "upscaler",
6101
+ "wan-video/wan-2.7-image-pro": "wan",
6102
+ "qwen/qwen-image-2-pro": "qwen-image",
6103
+ "qwen-edit-apps/qwen-image-edit-plus-lora-skin": "qwen-edit"
6104
+ };
6105
+ function imageKind(providerModelId) {
6106
+ return REPLICATE_IMAGE_KINDS[baseModelId(providerModelId)] ?? "generic";
6107
+ }
6108
+ var REPLICATE_VIDEO_KINDS = {
6109
+ "kling-video/v2.5-turbo/pro/image-to-video": "kling",
6110
+ "kling-video/v3.0/pro/image-to-video": "kling",
6111
+ "kwaivgi/kling-v3-video": "kling",
6112
+ "kwaivgi/kling-v3-omni-video": "kling-omni",
6113
+ "minimax/hailuo-02": "hailuo"
6114
+ };
6115
+ function videoKind(providerModelId) {
6116
+ return REPLICATE_VIDEO_KINDS[baseModelId(providerModelId)] ?? "wan";
5381
6117
  }
5382
- function isQwenEditPlus(providerModelId) {
5383
- return isLower(providerModelId).includes("edit");
6118
+ var REPLICATE_AUDIO_KINDS = {
6119
+ "zsxkib/mmaudio": "mmaudio",
6120
+ "elevenlabs/music": "elevenlabs"
6121
+ };
6122
+ function audioKind(providerModelId) {
6123
+ return REPLICATE_AUDIO_KINDS[baseModelId(providerModelId)] ?? "lyria";
5384
6124
  }
5385
6125
  var QWEN_IMAGE_RATIOS = /* @__PURE__ */ new Set([
5386
6126
  "1:1",
@@ -5423,87 +6163,113 @@ function wanImageSize(aspectRatio, size) {
5423
6163
  async function generateReplicateImage(req, ctx, deps) {
5424
6164
  const id = ctx.model.providerModelId;
5425
6165
  let input;
5426
- if (isTopazUpscaler(id)) {
5427
- const first = (req.references ?? [])[0];
5428
- if (!first) {
5429
- throw new ModelKitError({
5430
- code: "missing_input",
5431
- message: `${ctx.model.id} requires a reference image to upscale`,
5432
- provider: "replicate",
5433
- model: ctx.model.id,
5434
- retryable: false
5435
- });
6166
+ switch (imageKind(id)) {
6167
+ case "topaz": {
6168
+ const first = (req.references ?? [])[0];
6169
+ if (!first) {
6170
+ throw new ModelKitError({
6171
+ code: "missing_input",
6172
+ message: `${ctx.model.id} requires a reference image to upscale`,
6173
+ provider: "replicate",
6174
+ model: ctx.model.id,
6175
+ retryable: false
6176
+ });
6177
+ }
6178
+ let format = req.outputFormat === "jpeg" ? "jpg" : req.outputFormat ?? "png";
6179
+ if (format !== "png" && format !== "jpg") {
6180
+ ctx.warnings.push({
6181
+ code: "option_ignored",
6182
+ field: "outputFormat",
6183
+ from: format,
6184
+ message: "topazlabs/image-upscale supports only png and jpg; using png"
6185
+ });
6186
+ format = "png";
6187
+ }
6188
+ const upscale = req.upscale;
6189
+ const topaz = {
6190
+ image: refToUrl(first),
6191
+ enhance_model: upscale?.enhanceModel ?? "Standard V2",
6192
+ output_format: format,
6193
+ upscale_factor: `${req.scaleFactor ?? 2}x`,
6194
+ face_enhancement: upscale?.faceEnhancement ?? false
6195
+ };
6196
+ if (upscale?.subjectDetection !== void 0)
6197
+ topaz.subject_detection = upscale.subjectDetection;
6198
+ if (upscale?.faceEnhancementCreativity !== void 0)
6199
+ topaz.face_enhancement_creativity = upscale.faceEnhancementCreativity;
6200
+ if (upscale?.faceEnhancementStrength !== void 0)
6201
+ topaz.face_enhancement_strength = upscale.faceEnhancementStrength;
6202
+ input = topaz;
6203
+ break;
5436
6204
  }
5437
- let format = req.outputFormat === "jpeg" ? "jpg" : req.outputFormat ?? "png";
5438
- if (format !== "png" && format !== "jpg") {
5439
- ctx.warnings.push({
5440
- code: "option_ignored",
5441
- field: "outputFormat",
5442
- from: format,
5443
- message: "topazlabs/image-upscale supports only png and jpg; using png"
5444
- });
5445
- format = "png";
6205
+ case "upscaler": {
6206
+ const first = (req.references ?? [])[0];
6207
+ if (!first) {
6208
+ throw new ModelKitError({
6209
+ code: "missing_input",
6210
+ message: `${ctx.model.id} requires a reference image to upscale`,
6211
+ provider: "replicate",
6212
+ model: ctx.model.id,
6213
+ retryable: false
6214
+ });
6215
+ }
6216
+ const imageInput = { prompt: req.prompt };
6217
+ imageInput.image = refToUrl(first);
6218
+ imageInput.creativity = 0;
6219
+ imageInput.scale_factor = 2;
6220
+ imageInput.output_format = "png";
6221
+ input = imageInput;
6222
+ break;
5446
6223
  }
5447
- input = {
5448
- image: refToUrl3(first),
5449
- enhance_model: "Standard V2",
5450
- output_format: format,
5451
- upscale_factor: `${req.scaleFactor ?? 2}x`,
5452
- face_enhancement: true
5453
- };
5454
- } else if (isUpscaler(id)) {
5455
- const refs = req.references ?? [];
5456
- const first = refs[0];
5457
- if (!first) {
5458
- throw new ModelKitError({
5459
- code: "missing_input",
5460
- message: `${ctx.model.id} requires a reference image to upscale`,
5461
- provider: "replicate",
5462
- model: ctx.model.id,
5463
- retryable: false
5464
- });
6224
+ case "wan": {
6225
+ const refs = (req.references ?? []).map(refToUrl);
6226
+ const imageInput = { prompt: req.prompt };
6227
+ if (refs.length > 0) imageInput.images = refs;
6228
+ imageInput.size = wanImageSize(req.aspectRatio, req.size);
6229
+ input = imageInput;
6230
+ break;
5465
6231
  }
5466
- const imageInput = { prompt: req.prompt };
5467
- imageInput.image = refToUrl3(first);
5468
- imageInput.creativity = 0;
5469
- imageInput.scale_factor = 2;
5470
- imageInput.output_format = "png";
5471
- input = imageInput;
5472
- } else if (isWanImage(id)) {
5473
- const refs = (req.references ?? []).map(refToUrl3);
5474
- const imageInput = { prompt: req.prompt };
5475
- if (refs.length > 0) imageInput.images = refs;
5476
- imageInput.size = wanImageSize(req.aspectRatio, req.size);
5477
- input = imageInput;
5478
- } else if (isQwen(id)) {
5479
- const refs = (req.references ?? []).map(refToUrl3);
5480
- const firstUrl = refs[0];
5481
- const imageInput = { prompt: req.prompt };
5482
- if (firstUrl !== void 0) {
5483
- imageInput.image = firstUrl;
5484
- if (!isQwenEditPlus(id)) imageInput.match_input_image = true;
6232
+ case "qwen-image": {
6233
+ const refs = (req.references ?? []).map(refToUrl);
6234
+ const firstUrl = refs[0];
6235
+ const imageInput = { prompt: req.prompt };
6236
+ if (firstUrl !== void 0) {
6237
+ imageInput.image = firstUrl;
6238
+ imageInput.match_input_image = true;
6239
+ } else if (req.aspectRatio !== void 0 && QWEN_IMAGE_RATIOS.has(req.aspectRatio)) {
6240
+ imageInput.aspect_ratio = req.aspectRatio;
6241
+ }
6242
+ input = imageInput;
6243
+ break;
5485
6244
  }
5486
- const ratios = isQwenEditPlus(id) ? QWEN_EDIT_RATIOS : QWEN_IMAGE_RATIOS;
5487
- if (req.aspectRatio !== void 0) {
5488
- if (ratios.has(req.aspectRatio)) imageInput.aspect_ratio = req.aspectRatio;
5489
- else
5490
- ctx.warnings.push({
5491
- code: "option_ignored",
5492
- field: "aspectRatio",
5493
- from: req.aspectRatio,
5494
- message: `${id} has no ${req.aspectRatio} aspect_ratio; using the provider default`
5495
- });
6245
+ case "qwen-edit": {
6246
+ const refs = (req.references ?? []).map(refToUrl);
6247
+ const firstUrl = refs[0];
6248
+ const imageInput = { prompt: req.prompt };
6249
+ if (firstUrl !== void 0) imageInput.image = firstUrl;
6250
+ if (req.aspectRatio !== void 0) {
6251
+ if (QWEN_EDIT_RATIOS.has(req.aspectRatio))
6252
+ imageInput.aspect_ratio = req.aspectRatio;
6253
+ else
6254
+ ctx.warnings.push({
6255
+ code: "option_ignored",
6256
+ field: "aspectRatio",
6257
+ from: req.aspectRatio,
6258
+ message: `${id} has no ${req.aspectRatio} aspect_ratio; using the provider default`
6259
+ });
6260
+ }
6261
+ if (req.outputFormat !== void 0) {
6262
+ imageInput.output_format = req.outputFormat === "jpeg" ? "jpg" : req.outputFormat;
6263
+ }
6264
+ input = imageInput;
6265
+ break;
5496
6266
  }
5497
- if (isQwenEditPlus(id) && req.outputFormat !== void 0) {
5498
- imageInput.output_format = req.outputFormat === "jpeg" ? "jpg" : req.outputFormat;
6267
+ default: {
6268
+ const first = (req.references ?? [])[0];
6269
+ const imageInput = { prompt: req.prompt };
6270
+ if (first) imageInput.image = refToUrl(first);
6271
+ input = imageInput;
5499
6272
  }
5500
- input = imageInput;
5501
- } else {
5502
- const refs = req.references ?? [];
5503
- const first = refs[0];
5504
- const imageInput = { prompt: req.prompt };
5505
- if (first) imageInput.image = refToUrl3(first);
5506
- input = imageInput;
5507
6273
  }
5508
6274
  const { url, predictTime, raw } = await runPrediction(ctx, input, deps);
5509
6275
  const file = await download(ctx, url);
@@ -5520,8 +6286,8 @@ function wanSize(aspectRatio) {
5520
6286
  return w > h ? "2048*1152" : "1536*2048";
5521
6287
  }
5522
6288
  function klingInput(req) {
5523
- const images = (req.firstFrame ? [req.firstFrame] : []).map(refToUrl3);
5524
- if (req.lastFrame) images.push(refToUrl3(req.lastFrame));
6289
+ const images = (req.firstFrame ? [req.firstFrame] : []).map(refToUrl);
6290
+ if (req.lastFrame) images.push(refToUrl(req.lastFrame));
5525
6291
  const input = {
5526
6292
  prompt: req.prompt,
5527
6293
  duration: req.durationSeconds ?? 10,
@@ -5539,13 +6305,18 @@ function klingInput(req) {
5539
6305
  if (req.negativePrompt !== void 0) input.negative_prompt = req.negativePrompt;
5540
6306
  return input;
5541
6307
  }
6308
+ function klingOmniMode(req) {
6309
+ if (req.resolution === "1080p") return "pro";
6310
+ if (req.resolution === "720p") return "standard";
6311
+ return req.mode ?? "standard";
6312
+ }
5542
6313
  function klingOmniInput(req) {
5543
- const images = (req.firstFrame ? [req.firstFrame] : []).map(refToUrl3);
5544
- if (req.lastFrame) images.push(refToUrl3(req.lastFrame));
6314
+ const images = (req.firstFrame ? [req.firstFrame] : []).map(refToUrl);
6315
+ if (req.lastFrame) images.push(refToUrl(req.lastFrame));
5545
6316
  const input = {
5546
6317
  prompt: req.prompt,
5547
6318
  duration: req.durationSeconds ?? 10,
5548
- mode: req.mode ?? "standard",
6319
+ mode: klingOmniMode(req),
5549
6320
  generate_audio: req.generateAudio ?? false
5550
6321
  };
5551
6322
  const startImage = images[0];
@@ -5556,7 +6327,7 @@ function klingOmniInput(req) {
5556
6327
  } else if (req.aspectRatio !== "adaptive") {
5557
6328
  input.aspect_ratio = req.aspectRatio ?? "16:9";
5558
6329
  }
5559
- const refs = (req.referenceImages ?? []).map(refToUrl3);
6330
+ const refs = (req.referenceImages ?? []).map(refToUrl);
5560
6331
  if (refs.length > 0) input.reference_images = refs;
5561
6332
  return input;
5562
6333
  }
@@ -5567,8 +6338,8 @@ function hailuoInput(req) {
5567
6338
  resolution: req.resolution ?? "768p",
5568
6339
  prompt_optimizer: true
5569
6340
  };
5570
- if (req.firstFrame) input.first_frame_image = refToUrl3(req.firstFrame);
5571
- if (req.lastFrame) input.last_frame_image = refToUrl3(req.lastFrame);
6341
+ if (req.firstFrame) input.first_frame_image = refToUrl(req.firstFrame);
6342
+ if (req.lastFrame) input.last_frame_image = refToUrl(req.lastFrame);
5572
6343
  return input;
5573
6344
  }
5574
6345
  function wanInput(req) {
@@ -5577,12 +6348,13 @@ function wanInput(req) {
5577
6348
  duration: req.durationSeconds ?? 5,
5578
6349
  size: wanSize(req.aspectRatio)
5579
6350
  };
5580
- if (req.firstFrame) input.image = refToUrl3(req.firstFrame);
6351
+ if (req.firstFrame) input.image = refToUrl(req.firstFrame);
5581
6352
  return input;
5582
6353
  }
5583
6354
  async function generateReplicateVideo(req, ctx, deps) {
5584
- const id = isLower(ctx.model.providerModelId);
5585
- const input = id.includes("kling") ? id.includes("omni") ? klingOmniInput(req) : klingInput(req) : id.includes("hailuo") ? hailuoInput(req) : wanInput(req);
6355
+ throwIfAborted(ctx.signal);
6356
+ const kind = videoKind(ctx.model.providerModelId);
6357
+ const input = kind === "kling" ? klingInput(req) : kind === "kling-omni" ? klingOmniInput(req) : kind === "hailuo" ? hailuoInput(req) : wanInput(req);
5586
6358
  const api = client(ctx, deps);
5587
6359
  const createOptions = { ...route(ctx.model.providerModelId), input };
5588
6360
  let prediction;
@@ -5590,12 +6362,14 @@ async function generateReplicateVideo(req, ctx, deps) {
5590
6362
  prediction = await withRetry(
5591
6363
  async () => {
5592
6364
  try {
5593
- return await api.predictions.create(createOptions);
6365
+ return await api.predictions.create(
6366
+ ctx.signal ? { ...createOptions, signal: ctx.signal } : createOptions
6367
+ );
5594
6368
  } catch (cause) {
5595
6369
  throw replicateError(cause, ctx);
5596
6370
  }
5597
6371
  },
5598
- ctx.signal ? { signal: ctx.signal } : {}
6372
+ { attempts: 1 }
5599
6373
  );
5600
6374
  } catch (cause) {
5601
6375
  throw replicateError(cause, ctx);
@@ -5604,7 +6378,10 @@ async function generateReplicateVideo(req, ctx, deps) {
5604
6378
  const check = async () => {
5605
6379
  let current;
5606
6380
  try {
5607
- current = await api.predictions.get(jobId);
6381
+ current = await api.predictions.get(
6382
+ jobId,
6383
+ ctx.signal ? { signal: ctx.signal } : void 0
6384
+ );
5608
6385
  } catch (cause) {
5609
6386
  throw replicateError(cause, ctx);
5610
6387
  }
@@ -5705,7 +6482,7 @@ function mmaudioInput(req, ctx) {
5705
6482
  });
5706
6483
  }
5707
6484
  const input = {
5708
- video: refToUrl3(video),
6485
+ video: refToUrl(video),
5709
6486
  duration: req.durationSeconds ?? 8,
5710
6487
  seed: req.seed ?? -1
5711
6488
  };
@@ -5724,8 +6501,8 @@ function predictionRaw(prediction) {
5724
6501
  return raw;
5725
6502
  }
5726
6503
  async function generateReplicateAudio(req, ctx, deps) {
5727
- const id = isLower(ctx.model.providerModelId);
5728
- const input = id.includes("mmaudio") ? mmaudioInput(req, ctx) : id.includes("elevenlabs") ? elevenLabsMusicInput(req, ctx) : lyriaAudioInput(req, ctx);
6504
+ const kind = audioKind(ctx.model.providerModelId);
6505
+ const input = kind === "mmaudio" ? mmaudioInput(req, ctx) : kind === "elevenlabs" ? elevenLabsMusicInput(req, ctx) : lyriaAudioInput(req, ctx);
5729
6506
  const { url, predictTime, raw } = await runPrediction(ctx, input, deps);
5730
6507
  const usage2 = audioUsage(ctx, req);
5731
6508
  if (ctx.model.usage.unit === "seconds") {
@@ -5754,10 +6531,6 @@ function createXaiVideoAdapter(deps = defaultXaiDeps) {
5754
6531
  };
5755
6532
  }
5756
6533
  var xaiVideoAdapter = createXaiVideoAdapter();
5757
- function refToUrl4(ref) {
5758
- if ("url" in ref) return ref.url;
5759
- return `data:${ref.mime};base64,${Buffer.from(ref.bytes).toString("base64")}`;
5760
- }
5761
6534
  var xaiResponseStatusSchema = import_zod13.z.object({
5762
6535
  response: import_zod13.z.object({ status: import_zod13.z.number() }).partial()
5763
6536
  });
@@ -5789,13 +6562,14 @@ async function generateXaiVideo(req, ctx, deps) {
5789
6562
  retryable: false
5790
6563
  });
5791
6564
  }
5792
- const refUrls = (req.referenceImages ?? []).map(refToUrl4).slice(0, 7);
5793
- const resolution = req.resolution === "480p" ? "480p" : "720p";
6565
+ const refUrls = (req.referenceImages ?? []).map(refToUrl).slice(0, 7);
5794
6566
  const xaiOptions = {
5795
- resolution,
5796
6567
  pollIntervalMs: POLL_INTERVAL_MS2,
5797
6568
  pollTimeoutMs: POLL_TIMEOUT_MS2
5798
6569
  };
6570
+ if (req.resolution === "480p" || req.resolution === "720p" || req.resolution === "1080p") {
6571
+ xaiOptions.resolution = req.resolution;
6572
+ }
5799
6573
  if (refUrls.length > 0) {
5800
6574
  xaiOptions.mode = "reference-to-video";
5801
6575
  xaiOptions.referenceImageUrls = refUrls;
@@ -5815,7 +6589,7 @@ async function generateXaiVideo(req, ctx, deps) {
5815
6589
  providerOptions: { xai: xaiOptions }
5816
6590
  };
5817
6591
  if (req.firstFrame) {
5818
- callBody.frameImages = [{ image: refToUrl4(req.firstFrame), frameType: "first_frame" }];
6592
+ callBody.frameImages = [{ image: refToUrl(req.firstFrame), frameType: "first_frame" }];
5819
6593
  }
5820
6594
  if (req.aspectRatio !== void 0 && req.aspectRatio !== "adaptive") {
5821
6595
  callBody.aspectRatio = req.aspectRatio;
@@ -5851,7 +6625,8 @@ async function generateXaiVideo(req, ctx, deps) {
5851
6625
  return settled;
5852
6626
  };
5853
6627
  return {
5854
- jobId: `${ctx.model.id}-grok`,
6628
+ // xAI exposes no request id on the synchronous handle, so each job gets a fresh one.
6629
+ jobId: crypto.randomUUID(),
5855
6630
  async poll() {
5856
6631
  if (failed !== void 0) return { status: "failed" };
5857
6632
  return settled ? { status: "succeeded" } : { status: "pending" };
@@ -5986,6 +6761,7 @@ var describe = (modelId) => createDefaultModelKit().describe(modelId);
5986
6761
  prodiaAdapter,
5987
6762
  readErrorBody,
5988
6763
  replicateAdapter,
6764
+ resolveAudioOptions,
5989
6765
  resolveImageOptions,
5990
6766
  resolveKeys,
5991
6767
  resolveVideoOptions,