@shop-os/modelkit 0.6.0 → 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -375,8 +375,59 @@ var FAL_IMAGE_MODELS = [
375
375
  }
376
376
  ];
377
377
 
378
- // src/catalog/image.ts
378
+ // src/catalog/image-luma.ts
379
379
  var IMAGE_USAGE2 = { unit: "images" };
380
+ var EXPOSURE2 = ["cowork", "space_builder"];
381
+ var LOBE2 = "https://cdn.jsdelivr.net/npm/@lobehub/icons-static-png@1.97.0/light";
382
+ var LUMA_LOGO = `${LOBE2}/luma-color.png`;
383
+ var UNI_RATIOS = ["adaptive", "16:9", "3:2", "1:1", "2:3", "9:16"];
384
+ function uniImage(id, label, providerModelId, description) {
385
+ return {
386
+ // https://docs.agents.lumalabs.ai: type "image" without references, else
387
+ // "image_edit" with the first reference as source and up to 8 more as
388
+ // image_ref. The output is always 2048 px on the long side.
389
+ id,
390
+ aliases: [],
391
+ label,
392
+ type: "image",
393
+ adapter: "luma",
394
+ providerModelId,
395
+ lifecycle: "active",
396
+ exposure: EXPOSURE2,
397
+ constraints: {
398
+ aspectRatios: [...UNI_RATIOS],
399
+ sizes: ["2K"],
400
+ references: { images: 9 },
401
+ supports: ["outputFormat"],
402
+ prompt: { referenceSyntax: "numbered", maxChars: 6e3 }
403
+ },
404
+ defaults: { size: "2K", aspectRatio: "adaptive" },
405
+ usage: { ...IMAGE_USAGE2 },
406
+ pricingKind: "image_size",
407
+ display: {
408
+ provider: "luma",
409
+ description,
410
+ logo: LUMA_LOGO
411
+ }
412
+ };
413
+ }
414
+ var LUMA_IMAGE_MODELS = [
415
+ uniImage(
416
+ "uni-1",
417
+ "Uni-1",
418
+ "uni-1",
419
+ "Luma Uni-1 fast image generation and editing; takes a prompt and up to 9 reference images and outputs 2K."
420
+ ),
421
+ uniImage(
422
+ "uni-1-max",
423
+ "Uni-1 Max",
424
+ "uni-1-max",
425
+ "Luma Uni-1 Max higher-quality image generation and editing; takes a prompt and up to 9 reference images and outputs 2K."
426
+ )
427
+ ];
428
+
429
+ // src/catalog/image.ts
430
+ var IMAGE_USAGE3 = { unit: "images" };
380
431
  var GEMINI_RATIOS = [
381
432
  "1:1",
382
433
  "2:3",
@@ -421,7 +472,7 @@ function geminiImage(id, label, aliases, sizes, display, exposure, references, p
421
472
  exposure,
422
473
  constraints,
423
474
  defaults: { size: "1K", aspectRatio: "1:1" },
424
- usage: { ...IMAGE_USAGE2, slug: "gemini" },
475
+ usage: { ...IMAGE_USAGE3, slug: "gemini" },
425
476
  pricingKind: "image_size",
426
477
  display
427
478
  };
@@ -534,7 +585,7 @@ var IMAGE_MODELS = [
534
585
  prompt: GPT_IMAGE_PROMPT
535
586
  },
536
587
  defaults: { size: "1K", aspectRatio: "adaptive", quality: "auto" },
537
- usage: { ...IMAGE_USAGE2, slug: "gpt" },
588
+ usage: { ...IMAGE_USAGE3, slug: "gpt" },
538
589
  pricingKind: "image_size",
539
590
  display: {
540
591
  provider: "openai",
@@ -570,7 +621,7 @@ var IMAGE_MODELS = [
570
621
  prompt: GPT_IMAGE_PROMPT
571
622
  },
572
623
  defaults: { size: "1K", aspectRatio: "adaptive", quality: "auto" },
573
- usage: { ...IMAGE_USAGE2 },
624
+ usage: { ...IMAGE_USAGE3 },
574
625
  pricingKind: "image_size",
575
626
  display: {
576
627
  provider: "openai",
@@ -605,7 +656,7 @@ var IMAGE_MODELS = [
605
656
  prompt: GPT_IMAGE_PROMPT
606
657
  },
607
658
  defaults: { size: "1K", aspectRatio: "adaptive", quality: "auto" },
608
- usage: { ...IMAGE_USAGE2 },
659
+ usage: { ...IMAGE_USAGE3 },
609
660
  pricingKind: "image_size",
610
661
  display: {
611
662
  provider: "openai",
@@ -640,7 +691,7 @@ var IMAGE_MODELS = [
640
691
  supports: ["count", "outputFormat"]
641
692
  },
642
693
  defaults: { size: "1K", aspectRatio: "1:1", outputFormat: "png", count: 1 },
643
- usage: { ...IMAGE_USAGE2, slug: "muse" },
694
+ usage: { ...IMAGE_USAGE3, slug: "muse" },
644
695
  pricingKind: "image_size",
645
696
  display: {
646
697
  provider: "meta",
@@ -667,7 +718,7 @@ var IMAGE_MODELS = [
667
718
  supports: ["quality", "count", "seed"]
668
719
  },
669
720
  defaults: { size: "2K", aspectRatio: "1:1", quality: "medium", count: 1 },
670
- usage: { ...IMAGE_USAGE2, slug: "ideogram" },
721
+ usage: { ...IMAGE_USAGE3, slug: "ideogram" },
671
722
  pricingKind: "image_size",
672
723
  display: {
673
724
  provider: "ideogram",
@@ -692,7 +743,7 @@ var IMAGE_MODELS = [
692
743
  supports: ["seed", "outputFormat"]
693
744
  },
694
745
  defaults: { size: "1K", aspectRatio: "1:1" },
695
- usage: { ...IMAGE_USAGE2 },
746
+ usage: { ...IMAGE_USAGE3 },
696
747
  pricingKind: "image_size",
697
748
  display: {
698
749
  provider: "bytedance",
@@ -730,7 +781,7 @@ var IMAGE_MODELS = [
730
781
  supports: ["seed"]
731
782
  },
732
783
  defaults: { size: "1K", aspectRatio: "1:1" },
733
- usage: { ...IMAGE_USAGE2 },
784
+ usage: { ...IMAGE_USAGE3 },
734
785
  pricingKind: "image_size",
735
786
  display: {
736
787
  provider: "bytedance",
@@ -756,7 +807,7 @@ var IMAGE_MODELS = [
756
807
  prompt: { referenceSyntax: "numbered", maxChars: 32768 }
757
808
  },
758
809
  defaults: { size: "1K", aspectRatio: "1:1" },
759
- usage: { ...IMAGE_USAGE2 },
810
+ usage: { ...IMAGE_USAGE3 },
760
811
  pricingKind: "image_size",
761
812
  display: {
762
813
  provider: "black-forest-labs",
@@ -783,7 +834,7 @@ var IMAGE_MODELS = [
783
834
  supports: []
784
835
  },
785
836
  defaults: { size: "1K", aspectRatio: "1:1" },
786
- usage: { ...IMAGE_USAGE2 },
837
+ usage: { ...IMAGE_USAGE3 },
787
838
  pricingKind: "image_size",
788
839
  display: {
789
840
  provider: "google",
@@ -808,7 +859,7 @@ var IMAGE_MODELS = [
808
859
  supports: []
809
860
  },
810
861
  defaults: { size: "1K", aspectRatio: "1:1" },
811
- usage: { ...IMAGE_USAGE2 },
862
+ usage: { ...IMAGE_USAGE3 },
812
863
  pricingKind: "image_size",
813
864
  display: {
814
865
  provider: "google",
@@ -894,7 +945,8 @@ var IMAGE_MODELS = [
894
945
  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."
895
946
  }
896
947
  },
897
- ...FAL_IMAGE_MODELS
948
+ ...FAL_IMAGE_MODELS,
949
+ ...LUMA_IMAGE_MODELS
898
950
  ];
899
951
 
900
952
  // src/catalog/audio-formats.ts
@@ -1090,7 +1142,7 @@ var VEED_SUBTITLE_LANGUAGES = [
1090
1142
  ];
1091
1143
 
1092
1144
  // src/catalog/canvas-tools.ts
1093
- var EXPOSURE2 = ["cowork", "space_builder"];
1145
+ var EXPOSURE3 = ["cowork", "space_builder"];
1094
1146
  var META_LOGO = "https://d31vvfpk2ppdyq.cloudfront.net/assets/characters/1787909065794-k9a7zz.png";
1095
1147
  var VEED_SUBTITLE_PRESETS = [
1096
1148
  "simple",
@@ -1132,7 +1184,7 @@ var SAM_3_IMAGE = {
1132
1184
  adapter: "fal",
1133
1185
  providerModelId: "fal-ai/sam-3/image",
1134
1186
  lifecycle: "active",
1135
- exposure: EXPOSURE2,
1187
+ exposure: EXPOSURE3,
1136
1188
  constraints: {
1137
1189
  references: { images: 1 },
1138
1190
  requires: ["references"],
@@ -1155,7 +1207,7 @@ var VEED_SUBTITLES = {
1155
1207
  adapter: "fal",
1156
1208
  providerModelId: "veed/subtitles",
1157
1209
  lifecycle: "active",
1158
- exposure: EXPOSURE2,
1210
+ exposure: EXPOSURE3,
1159
1211
  constraints: {
1160
1212
  supports: [],
1161
1213
  // fal: up to 2 hours at 1080p or below, 1 hour above.
@@ -3260,7 +3312,7 @@ var GOOGLE_LOGO = "https://d31vvfpk2ppdyq.cloudfront.net/assets/image/192485d2-a
3260
3312
  var MINIMAX_LOGO = "https://d31vvfpk2ppdyq.cloudfront.net/assets/image/f4c2e906-28b7-4a4a-b491-df8fef7808ed.png";
3261
3313
  var SYNC_LOGO = "https://cdn.jsdelivr.net/npm/@lobehub/icons-static-png@1.97.0/light/sync.png";
3262
3314
  var PER_CALL = { unit: "images" };
3263
- var EXPOSURE3 = ["cowork", "space_builder"];
3315
+ var EXPOSURE4 = ["cowork", "space_builder"];
3264
3316
  var LIPSYNC_AUDIO = { audioSeconds: { max: 30 } };
3265
3317
  function elevenLabsTts(tts) {
3266
3318
  return {
@@ -3271,7 +3323,7 @@ function elevenLabsTts(tts) {
3271
3323
  adapter: "fal",
3272
3324
  providerModelId: tts.providerModelId,
3273
3325
  lifecycle: "active",
3274
- exposure: EXPOSURE3,
3326
+ exposure: EXPOSURE4,
3275
3327
  constraints: {
3276
3328
  voices: ELEVENLABS_VOICES,
3277
3329
  languages: tts.languages,
@@ -3296,7 +3348,7 @@ function geminiTts(id, label, description) {
3296
3348
  adapter: "fal",
3297
3349
  providerModelId,
3298
3350
  lifecycle: "active",
3299
- exposure: EXPOSURE3,
3351
+ exposure: EXPOSURE4,
3300
3352
  constraints: {
3301
3353
  voices: GEMINI_TTS_VOICES,
3302
3354
  supports: ["style"],
@@ -3319,7 +3371,7 @@ function syncLipsync(id, label, providerModelId, syncModel, description) {
3319
3371
  adapter: "fal",
3320
3372
  providerModelId,
3321
3373
  lifecycle: "active",
3322
- exposure: EXPOSURE3,
3374
+ exposure: EXPOSURE4,
3323
3375
  constraints: {
3324
3376
  requires: ["sourceVideo", "sourceAudio"],
3325
3377
  references: LIPSYNC_AUDIO,
@@ -3380,7 +3432,7 @@ var MEDIA_NODE_MODELS = [
3380
3432
  adapter: "fal",
3381
3433
  providerModelId: "fal-ai/minimax/speech-2.8-hd",
3382
3434
  lifecycle: "active",
3383
- exposure: EXPOSURE3,
3435
+ exposure: EXPOSURE4,
3384
3436
  constraints: {
3385
3437
  voices: MINIMAX_VOICES,
3386
3438
  languages: MINIMAX_SPEECH_LANGUAGES,
@@ -3405,7 +3457,7 @@ var MEDIA_NODE_MODELS = [
3405
3457
  adapter: "fal",
3406
3458
  providerModelId: "fal-ai/elevenlabs/voice-changer",
3407
3459
  lifecycle: "active",
3408
- exposure: EXPOSURE3,
3460
+ exposure: EXPOSURE4,
3409
3461
  constraints: {
3410
3462
  requires: ["sourceAudio"],
3411
3463
  // The flat price assumes at most a minute of speech.
@@ -3432,7 +3484,7 @@ var MEDIA_NODE_MODELS = [
3432
3484
  adapter: "fal",
3433
3485
  providerModelId: "fal-ai/elevenlabs/dubbing",
3434
3486
  lifecycle: "active",
3435
- exposure: EXPOSURE3,
3487
+ exposure: EXPOSURE4,
3436
3488
  constraints: {
3437
3489
  requiresOneOf: ["sourceVideo", "sourceAudio"],
3438
3490
  // The flat price covers 60 s of source.
@@ -3480,7 +3532,7 @@ var MEDIA_NODE_MODELS = [
3480
3532
  adapter: "fal",
3481
3533
  providerModelId: "veed/lipsync/v2",
3482
3534
  lifecycle: "active",
3483
- exposure: EXPOSURE3,
3535
+ exposure: EXPOSURE4,
3484
3536
  constraints: {
3485
3537
  requires: ["sourceVideo", "sourceAudio"],
3486
3538
  references: LIPSYNC_AUDIO,
@@ -3506,7 +3558,7 @@ var MEDIA_NODE_MODELS = [
3506
3558
  adapter: "fal",
3507
3559
  providerModelId: "minimax/h3-max/lip-sync/image-to-video",
3508
3560
  lifecycle: "active",
3509
- exposure: EXPOSURE3,
3561
+ exposure: EXPOSURE4,
3510
3562
  constraints: {
3511
3563
  requires: ["firstFrame", "sourceAudio"],
3512
3564
  resolutions: ["480p", "768p", "1080p", "2k"],
@@ -3532,7 +3584,7 @@ var MEDIA_NODE_MODELS = [
3532
3584
  adapter: "fal",
3533
3585
  providerModelId: "fal-ai/elevenlabs/speech-to-text/scribe-v2",
3534
3586
  lifecycle: "active",
3535
- exposure: EXPOSURE3,
3587
+ exposure: EXPOSURE4,
3536
3588
  constraints: {
3537
3589
  requires: ["sourceAudio"],
3538
3590
  // The flat price covers 10 minutes of audio.
@@ -5068,8 +5120,113 @@ var VIDEO_FAMILY_MODELS = [
5068
5120
  }
5069
5121
  ];
5070
5122
 
5071
- // src/catalog/video.ts
5123
+ // src/catalog/video-luma.ts
5072
5124
  var VIDEO_USAGE2 = { unit: "seconds" };
5125
+ var EXPOSURE5 = ["cowork", "space_builder"];
5126
+ var LUMA_LOGO2 = "https://cdn.jsdelivr.net/npm/@lobehub/icons-static-png@1.97.0/light/luma-color.png";
5127
+ var RAY_RATIOS = ["9:16", "3:4", "1:1", "4:3", "16:9", "21:9"];
5128
+ var RAY_RESOLUTIONS = ["360p", "720p", "1080p"];
5129
+ var RAY_PROMPT = { referenceSyntax: "numbered", maxChars: 6e3 };
5130
+ var LUMA_VIDEO_MODELS = [
5131
+ {
5132
+ // https://docs.agents.lumalabs.ai: type "video" with a fixed 5 s duration;
5133
+ // firstFrame and lastFrame ride as start_frame and end_frame.
5134
+ id: "ray-3.2",
5135
+ aliases: [],
5136
+ label: "Ray 3.2",
5137
+ type: "video",
5138
+ adapter: "luma",
5139
+ providerModelId: "ray-3.2",
5140
+ lifecycle: "active",
5141
+ exposure: EXPOSURE5,
5142
+ constraints: {
5143
+ aspectRatios: ["adaptive", ...RAY_RATIOS],
5144
+ resolutions: [...RAY_RESOLUTIONS],
5145
+ durations: [5],
5146
+ supports: ["lastFrame"],
5147
+ prompt: { ...RAY_PROMPT },
5148
+ audio: "never"
5149
+ },
5150
+ defaults: { durationSeconds: 5, resolution: "720p", aspectRatio: "adaptive" },
5151
+ usage: { ...VIDEO_USAGE2 },
5152
+ pricingKind: "video_per_second",
5153
+ display: {
5154
+ provider: "luma",
5155
+ description: "Luma Ray 3.2 text- and image-to-video with optional first and last frames, 5-second clips at up to 1080p.",
5156
+ logo: LUMA_LOGO2
5157
+ },
5158
+ capabilities: {}
5159
+ },
5160
+ {
5161
+ // https://docs.agents.lumalabs.ai: type "video_edit" with a source video of
5162
+ // up to 18 s; the output keeps the source length, so there is no durations
5163
+ // knob, no aspect_ratio and no end frame. Luma bills its 5 s price for any
5164
+ // source (live, 2026-10-21) and the row is priced per source second, so a
5165
+ // source under 5 s is refused: it would be billed below Luma's charge.
5166
+ id: "ray-3.2-edit",
5167
+ aliases: [],
5168
+ label: "Ray 3.2 Edit",
5169
+ type: "video",
5170
+ adapter: "luma",
5171
+ providerModelId: "ray-3.2",
5172
+ lifecycle: "active",
5173
+ exposure: EXPOSURE5,
5174
+ constraints: {
5175
+ resolutions: [...RAY_RESOLUTIONS],
5176
+ requires: ["sourceVideo"],
5177
+ sourceVideoSeconds: { min: 5, max: 18 },
5178
+ supports: [],
5179
+ prompt: { ...RAY_PROMPT },
5180
+ audio: "never"
5181
+ },
5182
+ defaults: { resolution: "720p" },
5183
+ usage: { ...VIDEO_USAGE2 },
5184
+ pricingKind: "video_per_second",
5185
+ display: {
5186
+ provider: "luma",
5187
+ description: "Luma Ray 3.2 video edit: changes a 5-18 second clip from a prompt, keeping its length.",
5188
+ logo: LUMA_LOGO2
5189
+ },
5190
+ capabilities: { acceptsSourceVideo: true, videoEdit: true }
5191
+ },
5192
+ {
5193
+ // https://docs.agents.lumalabs.ai: type "video_reframe" with a required
5194
+ // target aspect_ratio; the source is up to 10 s and the output keeps its
5195
+ // length whatever video.duration says, while Luma bills that duration
5196
+ // (live, 2026-10-21). So there is no durations knob, and a source under
5197
+ // 5 s is refused, as on ray-3.2-edit. 1080p is not offered for the
5198
+ // vertical targets 9:16 and 3:4.
5199
+ id: "ray-3.2-reframe",
5200
+ aliases: [],
5201
+ label: "Ray 3.2 Reframe",
5202
+ type: "video",
5203
+ adapter: "luma",
5204
+ providerModelId: "ray-3.2",
5205
+ lifecycle: "active",
5206
+ exposure: EXPOSURE5,
5207
+ constraints: {
5208
+ aspectRatios: [...RAY_RATIOS],
5209
+ resolutions: [...RAY_RESOLUTIONS],
5210
+ requires: ["sourceVideo"],
5211
+ sourceVideoSeconds: { min: 5, max: 10 },
5212
+ supports: [],
5213
+ prompt: { ...RAY_PROMPT },
5214
+ audio: "never"
5215
+ },
5216
+ defaults: { resolution: "720p", aspectRatio: "16:9" },
5217
+ usage: { ...VIDEO_USAGE2 },
5218
+ pricingKind: "video_per_second",
5219
+ display: {
5220
+ provider: "luma",
5221
+ description: "Luma Ray 3.2 Reframe: changes a 5-10 second clip to a new aspect ratio and fills the new area from the prompt.",
5222
+ logo: LUMA_LOGO2
5223
+ },
5224
+ capabilities: { acceptsSourceVideo: true, videoEdit: true }
5225
+ }
5226
+ ];
5227
+
5228
+ // src/catalog/video.ts
5229
+ var VIDEO_USAGE3 = { unit: "seconds" };
5073
5230
  var SEEDANCE_RATIOS = ["21:9", "16:9", "4:3", "1:1", "3:4", "9:16", "adaptive"];
5074
5231
  var KLING_RATIOS2 = ["16:9", "9:16", "1:1", "adaptive"];
5075
5232
  var KLING_OMNI_RATIOS = ["16:9", "9:16", "1:1"];
@@ -5122,7 +5279,7 @@ var VIDEO_MODELS = [
5122
5279
  audio: "always"
5123
5280
  },
5124
5281
  defaults: { durationSeconds: 8, resolution: "720p", aspectRatio: "16:9" },
5125
- usage: { ...VIDEO_USAGE2, slug: "veo" },
5282
+ usage: { ...VIDEO_USAGE3, slug: "veo" },
5126
5283
  pricingKind: "video_per_second",
5127
5284
  display: {
5128
5285
  provider: "google",
@@ -5155,7 +5312,7 @@ var VIDEO_MODELS = [
5155
5312
  audio: "always"
5156
5313
  },
5157
5314
  defaults: { durationSeconds: 8, resolution: "720p", aspectRatio: "16:9" },
5158
- usage: { ...VIDEO_USAGE2 },
5315
+ usage: { ...VIDEO_USAGE3 },
5159
5316
  pricingKind: "video_per_second",
5160
5317
  display: {
5161
5318
  provider: "google",
@@ -5185,7 +5342,7 @@ var VIDEO_MODELS = [
5185
5342
  audio: "always"
5186
5343
  },
5187
5344
  defaults: { durationSeconds: 8, resolution: "720p", aspectRatio: "16:9" },
5188
- usage: { ...VIDEO_USAGE2 },
5345
+ usage: { ...VIDEO_USAGE3 },
5189
5346
  pricingKind: "video_per_second",
5190
5347
  display: {
5191
5348
  provider: "google",
@@ -5222,7 +5379,7 @@ var VIDEO_MODELS = [
5222
5379
  audio: "always"
5223
5380
  },
5224
5381
  defaults: {},
5225
- usage: { ...VIDEO_USAGE2, slug: "gemini_omni" },
5382
+ usage: { ...VIDEO_USAGE3, slug: "gemini_omni" },
5226
5383
  pricingKind: "video_per_second",
5227
5384
  display: {
5228
5385
  provider: "google",
@@ -5259,7 +5416,7 @@ var VIDEO_MODELS = [
5259
5416
  audio: "always"
5260
5417
  },
5261
5418
  defaults: {},
5262
- usage: { ...VIDEO_USAGE2 },
5419
+ usage: { ...VIDEO_USAGE3 },
5263
5420
  pricingKind: "video_per_second",
5264
5421
  display: {
5265
5422
  provider: "google",
@@ -5308,7 +5465,7 @@ var VIDEO_MODELS = [
5308
5465
  aspectRatio: "16:9",
5309
5466
  generateAudio: true
5310
5467
  },
5311
- usage: { ...VIDEO_USAGE2, slug: "seedance2", providerLabel: "byteplus" },
5468
+ usage: { ...VIDEO_USAGE3, slug: "seedance2", providerLabel: "byteplus" },
5312
5469
  pricingKind: "video_per_second",
5313
5470
  display: {
5314
5471
  provider: "bytedance",
@@ -5351,7 +5508,7 @@ var VIDEO_MODELS = [
5351
5508
  aspectRatio: "16:9",
5352
5509
  generateAudio: true
5353
5510
  },
5354
- usage: { ...VIDEO_USAGE2 },
5511
+ usage: { ...VIDEO_USAGE3 },
5355
5512
  pricingKind: "video_per_second",
5356
5513
  display: {
5357
5514
  provider: "bytedance",
@@ -5401,7 +5558,7 @@ var VIDEO_MODELS = [
5401
5558
  aspectRatio: "16:9",
5402
5559
  generateAudio: true
5403
5560
  },
5404
- usage: { ...VIDEO_USAGE2 },
5561
+ usage: { ...VIDEO_USAGE3 },
5405
5562
  pricingKind: "video_per_second",
5406
5563
  display: {
5407
5564
  provider: "bytedance",
@@ -5463,7 +5620,7 @@ var VIDEO_MODELS = [
5463
5620
  cfgScale: 0.5,
5464
5621
  negativePrompt: NEGATIVE_PROMPT
5465
5622
  },
5466
- usage: { ...VIDEO_USAGE2 },
5623
+ usage: { ...VIDEO_USAGE3 },
5467
5624
  pricingKind: "video_per_second",
5468
5625
  display: {
5469
5626
  provider: "kuaishou",
@@ -5495,7 +5652,7 @@ var VIDEO_MODELS = [
5495
5652
  cfgScale: 0.5,
5496
5653
  generateAudio: true
5497
5654
  },
5498
- usage: { ...VIDEO_USAGE2 },
5655
+ usage: { ...VIDEO_USAGE3 },
5499
5656
  pricingKind: "video_per_second",
5500
5657
  display: {
5501
5658
  provider: "kuaishou",
@@ -5563,7 +5720,7 @@ var VIDEO_MODELS = [
5563
5720
  aspectRatio: "16:9",
5564
5721
  promptExpansion: "balanced"
5565
5722
  },
5566
- usage: { ...VIDEO_USAGE2 },
5723
+ usage: { ...VIDEO_USAGE3 },
5567
5724
  pricingKind: "video_per_second",
5568
5725
  display: {
5569
5726
  provider: "minimax",
@@ -5597,7 +5754,7 @@ var VIDEO_MODELS = [
5597
5754
  aspectRatio: "16:9",
5598
5755
  promptExpansion: "balanced"
5599
5756
  },
5600
- usage: { ...VIDEO_USAGE2 },
5757
+ usage: { ...VIDEO_USAGE3 },
5601
5758
  pricingKind: "video_per_second",
5602
5759
  display: {
5603
5760
  provider: "minimax",
@@ -5639,7 +5796,7 @@ var VIDEO_MODELS = [
5639
5796
  aspectRatio: "adaptive",
5640
5797
  promptExpansion: "balanced"
5641
5798
  },
5642
- usage: { ...VIDEO_USAGE2 },
5799
+ usage: { ...VIDEO_USAGE3 },
5643
5800
  pricingKind: "video_per_second",
5644
5801
  display: {
5645
5802
  provider: "minimax",
@@ -5683,7 +5840,7 @@ var VIDEO_MODELS = [
5683
5840
  aspectRatio: "adaptive",
5684
5841
  promptExpansion: "balanced"
5685
5842
  },
5686
- usage: { ...VIDEO_USAGE2 },
5843
+ usage: { ...VIDEO_USAGE3 },
5687
5844
  pricingKind: "video_per_second",
5688
5845
  display: {
5689
5846
  provider: "minimax",
@@ -5728,7 +5885,7 @@ var VIDEO_MODELS = [
5728
5885
  aspectRatio: "adaptive",
5729
5886
  generateAudio: true
5730
5887
  },
5731
- usage: { ...VIDEO_USAGE2 },
5888
+ usage: { ...VIDEO_USAGE3 },
5732
5889
  pricingKind: "video_per_second",
5733
5890
  display: {
5734
5891
  provider: "alibaba",
@@ -5755,7 +5912,7 @@ var VIDEO_MODELS = [
5755
5912
  audio: "never"
5756
5913
  },
5757
5914
  defaults: { durationSeconds: 5, aspectRatio: "16:9" },
5758
- usage: { ...VIDEO_USAGE2 },
5915
+ usage: { ...VIDEO_USAGE3 },
5759
5916
  pricingKind: "video_per_second",
5760
5917
  display: {
5761
5918
  provider: "kuaishou",
@@ -5804,7 +5961,7 @@ var VIDEO_MODELS = [
5804
5961
  audio: "never"
5805
5962
  },
5806
5963
  defaults: {},
5807
- usage: { ...VIDEO_USAGE2 },
5964
+ usage: { ...VIDEO_USAGE3 },
5808
5965
  // Higgsfield bills DoP per generation: a fixed-length clip with no duration knob.
5809
5966
  pricingKind: "flat",
5810
5967
  display: {
@@ -5813,7 +5970,8 @@ var VIDEO_MODELS = [
5813
5970
  },
5814
5971
  capabilities: {}
5815
5972
  },
5816
- ...VIDEO_FAMILY_MODELS
5973
+ ...VIDEO_FAMILY_MODELS,
5974
+ ...LUMA_VIDEO_MODELS
5817
5975
  ];
5818
5976
 
5819
5977
  // src/catalog/video-tools.ts
@@ -6787,7 +6945,8 @@ var ADAPTER_IDS = [
6787
6945
  "replicate",
6788
6946
  "prodia",
6789
6947
  "higgsfield",
6790
- "xai-video"
6948
+ "xai-video",
6949
+ "luma"
6791
6950
  ];
6792
6951
  var ModelTypeSchema = z3.enum([
6793
6952
  "text",
@@ -10917,6 +11076,30 @@ var klingV3Family = {
10917
11076
  }
10918
11077
  };
10919
11078
 
11079
+ // src/replicate-video-kinds.ts
11080
+ function replicateBaseModelId(providerModelId) {
11081
+ const colon = providerModelId.indexOf(":");
11082
+ return colon === -1 ? providerModelId : providerModelId.slice(0, colon);
11083
+ }
11084
+ var REPLICATE_VIDEO_KINDS = /* @__PURE__ */ new Map([
11085
+ ["kling-video/v2.5-turbo/pro/image-to-video", "kling"],
11086
+ ["kling-video/v3.0/pro/image-to-video", "kling"],
11087
+ ["kwaivgi/kling-v3-video", "kling"],
11088
+ ["kwaivgi/kling-v3-omni-video", "kling-omni"],
11089
+ ["minimax/hailuo-02", "hailuo"]
11090
+ ]);
11091
+ function videoKind(providerModelId) {
11092
+ return REPLICATE_VIDEO_KINDS.get(replicateBaseModelId(providerModelId));
11093
+ }
11094
+ var KLING_TIERS = [
11095
+ { mode: "standard", resolution: "720p" },
11096
+ { mode: "pro", resolution: "1080p" },
11097
+ { mode: "4k", resolution: "4k" }
11098
+ ];
11099
+ function klingTier(req) {
11100
+ return KLING_TIERS.find((tier) => tier.resolution === req.resolution) ?? KLING_TIERS.find((tier) => tier.mode === req.mode) ?? KLING_TIERS[0];
11101
+ }
11102
+
10920
11103
  // src/resolve-video-rules.ts
10921
11104
  var MOTION_PRO2 = "fal-ai/kling-video/v3/pro/motion-control";
10922
11105
  var MOTION_STANDARD2 = "fal-ai/kling-video/v3/standard/motion-control";
@@ -11090,6 +11273,16 @@ function h3EndFrameRatioWarning(model, ratio) {
11090
11273
  message: `${model.id} follows the end frame's aspect ratio`
11091
11274
  };
11092
11275
  }
11276
+ function isReplicateKling(model) {
11277
+ if (model.adapter !== "replicate") return false;
11278
+ const kind = videoKind(model.providerModelId);
11279
+ return kind === "kling" || kind === "kling-omni";
11280
+ }
11281
+ function fillReplicateKlingResolution(model, req) {
11282
+ if (isReplicateKling(model) && req.resolution === void 0) {
11283
+ req.resolution = klingTier(req).resolution;
11284
+ }
11285
+ }
11093
11286
  function applyFalVideoRules(model, req, callerRatio, warnings) {
11094
11287
  if (model.adapter !== "fal") return;
11095
11288
  const problem = falVideoProblem(model.providerModelId, withPromptForResolve(req));
@@ -13418,9 +13611,608 @@ var higgsfieldAdapter = {
13418
13611
  }
13419
13612
  };
13420
13613
 
13614
+ // src/adapters/luma.ts
13615
+ import { z as z25 } from "zod";
13616
+
13617
+ // src/adapters/mp4-duration.ts
13618
+ function mp4DurationSeconds(bytes) {
13619
+ const moov = findBox(bytes, 0, bytes.length, "moov");
13620
+ if (!moov) return void 0;
13621
+ const mvhd = findBox(bytes, moov.start + 8, moov.end, "mvhd");
13622
+ if (!mvhd) return void 0;
13623
+ const view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);
13624
+ const size = mvhd.end - mvhd.start;
13625
+ if (size < 8 + 4) return void 0;
13626
+ const version = view.getUint8(mvhd.start + 8);
13627
+ if (version === 0) {
13628
+ if (size < 8 + 4 + 4 + 4 + 4 + 4) return void 0;
13629
+ const timescale = view.getUint32(mvhd.start + 8 + 4 + 4 + 4);
13630
+ const duration = view.getUint32(mvhd.start + 8 + 4 + 4 + 4 + 4);
13631
+ if (timescale === 0) return void 0;
13632
+ return duration / timescale;
13633
+ }
13634
+ if (version === 1) {
13635
+ if (size < 8 + 4 + 8 + 8 + 4 + 8) return void 0;
13636
+ const timescale = view.getUint32(mvhd.start + 8 + 4 + 8 + 8);
13637
+ const high = view.getUint32(mvhd.start + 8 + 4 + 8 + 8 + 4);
13638
+ const low = view.getUint32(mvhd.start + 8 + 4 + 8 + 8 + 4 + 4);
13639
+ if (timescale === 0) return void 0;
13640
+ return (high * 2 ** 32 + low) / timescale;
13641
+ }
13642
+ return void 0;
13643
+ }
13644
+ function findBox(bytes, from, to, type) {
13645
+ const view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);
13646
+ let offset = from;
13647
+ while (offset + 8 <= to) {
13648
+ const size = view.getUint32(offset);
13649
+ const name = String.fromCharCode(
13650
+ view.getUint8(offset + 4),
13651
+ view.getUint8(offset + 5),
13652
+ view.getUint8(offset + 6),
13653
+ view.getUint8(offset + 7)
13654
+ );
13655
+ if (size < 8 || offset + size > to) return void 0;
13656
+ if (name === type) return { start: offset, end: offset + size };
13657
+ offset += size;
13658
+ }
13659
+ return void 0;
13660
+ }
13661
+
13662
+ // src/adapters/luma.ts
13663
+ var DEFAULT_BASE_URL = "https://agents.lumalabs.ai";
13664
+ var IMAGE_POLL_INTERVAL_MS = 3e3;
13665
+ var IMAGE_TIMEOUT_MS = 5 * 6e4;
13666
+ var VIDEO_POLL_INTERVAL_MS = 5e3;
13667
+ var VIDEO_TIMEOUT_MS = 20 * 6e4;
13668
+ var PROMPT_MAX_CHARS = 6e3;
13669
+ var LUMA_IMAGE_MODEL_IDS = /* @__PURE__ */ new Set(["uni-1", "uni-1-max"]);
13670
+ var LUMA_VIDEO_MODEL_ID = "ray-3.2";
13671
+ var LUMA_VIDEO_MODES = /* @__PURE__ */ new Map([
13672
+ ["ray-3.2", "generate"],
13673
+ ["ray-3.2-edit", "edit"],
13674
+ ["ray-3.2-reframe", "reframe"]
13675
+ ]);
13676
+ function lumaRowRefusal(row2) {
13677
+ const provider = row2.providerModelId.toLowerCase();
13678
+ if (row2.type === "image") {
13679
+ return LUMA_IMAGE_MODEL_IDS.has(provider) ? void 0 : "luma image rows need providerModelId uni-1 or uni-1-max";
13680
+ }
13681
+ if (row2.type === "video") {
13682
+ if (provider !== LUMA_VIDEO_MODEL_ID) return "luma video rows need providerModelId ray-3.2";
13683
+ return !LUMA_VIDEO_MODES.has(row2.id) ? `luma routes ray-3.2 rows by catalog id; it knows ${[...LUMA_VIDEO_MODES.keys()].join(", ")}` : void 0;
13684
+ }
13685
+ return `luma has no ${row2.type} rows`;
13686
+ }
13687
+ function lumaBase(ctx) {
13688
+ return ctx.endpoints?.luma ?? DEFAULT_BASE_URL;
13689
+ }
13690
+ function lumaKey(ctx) {
13691
+ const key = ctx.keys.luma;
13692
+ if (!key) {
13693
+ throw new ModelKitError({
13694
+ code: "auth",
13695
+ message: "Missing API key for luma",
13696
+ provider: "luma",
13697
+ model: ctx.model.id,
13698
+ retryable: false
13699
+ });
13700
+ }
13701
+ return key;
13702
+ }
13703
+ function checkPrompt(req, ctx) {
13704
+ const prompt = req.prompt.trim();
13705
+ if (prompt.length === 0) {
13706
+ throw new ModelKitError({
13707
+ code: "missing_input",
13708
+ message: `${ctx.model.id} needs a prompt`,
13709
+ provider: "luma",
13710
+ model: ctx.model.id,
13711
+ retryable: false
13712
+ });
13713
+ }
13714
+ if (req.prompt.length > PROMPT_MAX_CHARS) {
13715
+ throw new ModelKitError({
13716
+ code: "unsupported_option",
13717
+ message: `${ctx.model.id} takes a prompt of at most ${PROMPT_MAX_CHARS} characters`,
13718
+ provider: "luma",
13719
+ model: ctx.model.id,
13720
+ retryable: false
13721
+ });
13722
+ }
13723
+ return req.prompt;
13724
+ }
13725
+ function imageRef(ref) {
13726
+ if ("url" in ref) return { url: ref.url };
13727
+ return { data: mediaRefToBase64(ref.bytes), media_type: ref.mime };
13728
+ }
13729
+ function videoRef(ref) {
13730
+ if ("url" in ref) return { url: ref.url, media_type: "video/mp4" };
13731
+ const mediaType = ref.mime.startsWith("video/") ? ref.mime : "video/mp4";
13732
+ return { data: mediaRefToBase64(ref.bytes), media_type: mediaType };
13733
+ }
13734
+ function lumaOutputFormat(req, ctx) {
13735
+ const format = req.outputFormat;
13736
+ if (format === void 0 || format === "png" || format === "jpeg") return format;
13737
+ if ((ctx.call.onUnsupported ?? "error") === "error") {
13738
+ throw new ModelKitError({
13739
+ code: "unsupported_option",
13740
+ message: `${ctx.model.id} has no outputFormat "${format}". Supported: png, jpeg.`,
13741
+ provider: "luma",
13742
+ model: ctx.model.id,
13743
+ retryable: false
13744
+ });
13745
+ }
13746
+ ctx.warnings.push({
13747
+ code: "option_ignored",
13748
+ field: "outputFormat",
13749
+ from: format,
13750
+ to: "png",
13751
+ message: `${ctx.model.id} has no outputFormat "${format}"; using png`
13752
+ });
13753
+ return "png";
13754
+ }
13755
+ function refuseOrDrop(ctx, field, message) {
13756
+ if ((ctx.call.onUnsupported ?? "error") === "error") {
13757
+ throw new ModelKitError({
13758
+ code: "unsupported_option",
13759
+ message,
13760
+ provider: "luma",
13761
+ model: ctx.model.id,
13762
+ retryable: false
13763
+ });
13764
+ }
13765
+ ctx.warnings.push({ code: "option_ignored", field, message });
13766
+ }
13767
+ function lumaVideoMode(ctx) {
13768
+ const mode = LUMA_VIDEO_MODES.get(ctx.model.id);
13769
+ if (mode === void 0) {
13770
+ throw new ModelKitError({
13771
+ code: "unsupported_option",
13772
+ message: `luma routes ray-3.2 rows by catalog id; "${ctx.model.id}" is not a luma video row`,
13773
+ provider: "luma",
13774
+ model: ctx.model.id,
13775
+ retryable: false
13776
+ });
13777
+ }
13778
+ return mode;
13779
+ }
13780
+ function reframeTooTall(req) {
13781
+ const vertical = req.aspectRatio === "9:16" || req.aspectRatio === "3:4";
13782
+ return vertical && req.resolution === "1080p";
13783
+ }
13784
+ function sentResolution(req, mode) {
13785
+ return mode === "reframe" && reframeTooTall(req) ? "720p" : req.resolution;
13786
+ }
13787
+ function lumaReframeResolution(req, ctx) {
13788
+ const requested = req.resolution;
13789
+ if (requested === void 0 || !reframeTooTall(req)) return requested;
13790
+ const message = `${ctx.model.id} renders 9:16 and 3:4 reframes at up to 720p`;
13791
+ if ((ctx.call.onUnsupported ?? "error") === "error") {
13792
+ throw new ModelKitError({
13793
+ code: "unsupported_option",
13794
+ message,
13795
+ provider: "luma",
13796
+ model: ctx.model.id,
13797
+ retryable: false
13798
+ });
13799
+ }
13800
+ ctx.warnings.push({
13801
+ code: "resolution_coerced",
13802
+ field: "resolution",
13803
+ from: requested,
13804
+ to: "720p",
13805
+ message
13806
+ });
13807
+ return "720p";
13808
+ }
13809
+ var lumaGenerationSchema = z25.looseObject({
13810
+ id: z25.string(),
13811
+ state: z25.string().optional(),
13812
+ output: z25.array(
13813
+ z25.looseObject({
13814
+ type: z25.string().optional(),
13815
+ url: z25.string().optional()
13816
+ })
13817
+ ).optional(),
13818
+ failure_reason: z25.string().optional().nullable(),
13819
+ failure_code: z25.string().optional().nullable()
13820
+ });
13821
+ function lumaFailure(ctx, code, reason) {
13822
+ const detail = reason ?? code ?? "unknown_error";
13823
+ const message = `Luma generation failed: ${detail}`;
13824
+ switch (code) {
13825
+ case "content_moderated":
13826
+ return new ModelKitError({
13827
+ code: "content_blocked",
13828
+ message,
13829
+ provider: "luma",
13830
+ model: ctx.model.id,
13831
+ retryable: false
13832
+ });
13833
+ case "rate_limited":
13834
+ return new ModelKitError({
13835
+ code: "rate_limited",
13836
+ message,
13837
+ provider: "luma",
13838
+ model: ctx.model.id
13839
+ });
13840
+ case "generation_failed":
13841
+ case "output_not_found":
13842
+ return new ModelKitError({
13843
+ code: "provider_rejected",
13844
+ message,
13845
+ provider: "luma",
13846
+ model: ctx.model.id,
13847
+ retryable: true
13848
+ });
13849
+ case "budget_exhausted":
13850
+ return new ModelKitError({
13851
+ code: "provider_billing",
13852
+ message,
13853
+ provider: "luma",
13854
+ model: ctx.model.id,
13855
+ retryable: false
13856
+ });
13857
+ case "image_too_large":
13858
+ case "unsupported_format":
13859
+ return new ModelKitError({
13860
+ code: "unsupported_option",
13861
+ message,
13862
+ provider: "luma",
13863
+ model: ctx.model.id,
13864
+ retryable: false
13865
+ });
13866
+ default:
13867
+ return new ModelKitError({
13868
+ code: "provider_rejected",
13869
+ message,
13870
+ provider: "luma",
13871
+ model: ctx.model.id,
13872
+ retryable: false
13873
+ });
13874
+ }
13875
+ }
13876
+ function requestInit2(ctx, key, body) {
13877
+ const init = {
13878
+ method: body !== void 0 ? "POST" : "GET",
13879
+ headers: { Authorization: `Bearer ${key}`, "Content-Type": "application/json" }
13880
+ };
13881
+ if (body !== void 0) init.body = body;
13882
+ if (ctx.signal !== void 0) init.signal = ctx.signal;
13883
+ return init;
13884
+ }
13885
+ async function lumaRequest(ctx, url, body) {
13886
+ const key = lumaKey(ctx);
13887
+ let res;
13888
+ try {
13889
+ res = await ctx.fetch(url, requestInit2(ctx, key, body));
13890
+ } catch (cause) {
13891
+ throw classifyThrown("luma", cause, ctx.model.id);
13892
+ }
13893
+ if (!res.ok) {
13894
+ throw classifyHttpError(res.status, await readBody(res), "luma", ctx.model.id);
13895
+ }
13896
+ return res;
13897
+ }
13898
+ async function submitGeneration(ctx, body) {
13899
+ const retryOptions = mediaRetryOptions(ctx.call, ctx.signal);
13900
+ const res = await withRetry(
13901
+ () => lumaRequest(ctx, `${lumaBase(ctx)}/v1/generations`, JSON.stringify(body)),
13902
+ retryOptions
13903
+ );
13904
+ const parsed = lumaGenerationSchema.safeParse(await res.json());
13905
+ if (!parsed.success) {
13906
+ throw new ModelKitError({
13907
+ code: "provider_rejected",
13908
+ message: "Luma submit returned no generation id",
13909
+ provider: "luma",
13910
+ model: ctx.model.id,
13911
+ retryable: false
13912
+ });
13913
+ }
13914
+ return parsed.data;
13915
+ }
13916
+ async function readGeneration(ctx, id) {
13917
+ const res = await lumaRequest(ctx, `${lumaBase(ctx)}/v1/generations/${id}`);
13918
+ const payload = JsonValueSchema.parse(await res.json());
13919
+ const parsed = lumaGenerationSchema.safeParse(payload);
13920
+ if (!parsed.success) {
13921
+ throw new ModelKitError({
13922
+ code: "provider_rejected",
13923
+ message: "Luma returned an unreadable generation",
13924
+ provider: "luma",
13925
+ model: ctx.model.id,
13926
+ retryable: false
13927
+ });
13928
+ }
13929
+ return parsed.data;
13930
+ }
13931
+ function outputUrl(generation, type) {
13932
+ const outputs = generation.output ?? [];
13933
+ const match = outputs.find((item) => item.type === type && item.url);
13934
+ if (match?.url) return match.url;
13935
+ const fallback = outputs.find((item) => item.url);
13936
+ return fallback?.url;
13937
+ }
13938
+ async function readGenerationOnce(ctx, id) {
13939
+ try {
13940
+ return await readGeneration(ctx, id);
13941
+ } catch (cause) {
13942
+ if (ctx.call.maxRetries === 0) throw cause;
13943
+ if (isModelKitError(cause) && (cause.code === "network" || cause.status === 429 || (cause.status ?? 0) >= 500)) {
13944
+ return void 0;
13945
+ }
13946
+ throw cause;
13947
+ }
13948
+ }
13949
+ async function checkGeneration(ctx, id, type) {
13950
+ const generation = await readGenerationOnce(ctx, id);
13951
+ if (generation === void 0) return void 0;
13952
+ if (generation.state === "completed") {
13953
+ const url = outputUrl(generation, type);
13954
+ if (!url) {
13955
+ throw new ModelKitError({
13956
+ code: "provider_rejected",
13957
+ message: "Luma completed without an output URL",
13958
+ provider: "luma",
13959
+ model: ctx.model.id,
13960
+ retryable: false
13961
+ });
13962
+ }
13963
+ return url;
13964
+ }
13965
+ if (generation.state === "failed") {
13966
+ throw lumaFailure(
13967
+ ctx,
13968
+ generation.failure_code ?? void 0,
13969
+ generation.failure_reason ?? void 0
13970
+ );
13971
+ }
13972
+ return void 0;
13973
+ }
13974
+ async function downloadBytes(ctx, url, fallback) {
13975
+ let bytes;
13976
+ let header;
13977
+ try {
13978
+ const out = await mediaRefToBytes({ url }, ctx.fetch, {
13979
+ ...ctx.signal !== void 0 && { signal: ctx.signal },
13980
+ provider: "luma",
13981
+ model: ctx.model.id
13982
+ });
13983
+ bytes = out.bytes;
13984
+ header = out.mime;
13985
+ } catch (cause) {
13986
+ throw classifyThrown("luma", cause, ctx.model.id);
13987
+ }
13988
+ const kind = fallback.split("/")[0];
13989
+ const usable = header !== void 0 && header.startsWith(`${kind}/`);
13990
+ return { bytes, mime: usable ? header : fallback };
13991
+ }
13992
+ function imageUsage2(ctx) {
13993
+ return {
13994
+ model: ctx.model.id,
13995
+ provider: "luma",
13996
+ modality: "IMAGE",
13997
+ unit: "images",
13998
+ quantity: 1,
13999
+ imageSize: "2K"
14000
+ };
14001
+ }
14002
+ var GENERATE_SECONDS = 5;
14003
+ var MAX_PLAUSIBLE_SECONDS = 60;
14004
+ function billedSeconds(req, mode, bytes) {
14005
+ if (mode === "generate") return GENERATE_SECONDS;
14006
+ const measured = mp4DurationSeconds(bytes);
14007
+ const source2 = req.sourceVideo;
14008
+ const length = measured !== void 0 && measured > 0 && measured <= MAX_PLAUSIBLE_SECONDS ? measured : source2 !== void 0 && "url" in source2 ? source2.durationSeconds : void 0;
14009
+ return Math.max(length ?? 0, GENERATE_SECONDS);
14010
+ }
14011
+ function videoUsage4(ctx, req, mode, bytes) {
14012
+ const seconds3 = billedSeconds(req, mode, bytes);
14013
+ const event = {
14014
+ model: ctx.model.id,
14015
+ provider: "luma",
14016
+ modality: "VIDEO",
14017
+ unit: "seconds",
14018
+ quantity: seconds3,
14019
+ durationSeconds: seconds3
14020
+ };
14021
+ const resolution = sentResolution(req, mode);
14022
+ if (resolution !== void 0) event.resolution = resolution;
14023
+ return event;
14024
+ }
14025
+ function imageBody(req, ctx, outputFormat) {
14026
+ const prompt = checkPrompt(req, ctx);
14027
+ const model = ctx.model.providerModelId;
14028
+ const ratio = req.aspectRatio !== "adaptive" ? req.aspectRatio : void 0;
14029
+ const [source2, ...rest] = req.references ?? [];
14030
+ if (source2 === void 0) {
14031
+ const body2 = { model, type: "image", prompt };
14032
+ if (ratio !== void 0) body2["aspect_ratio"] = ratio;
14033
+ if (outputFormat !== void 0) body2["output_format"] = outputFormat;
14034
+ return body2;
14035
+ }
14036
+ if (ratio !== void 0) {
14037
+ ctx.warnings.push({
14038
+ code: "option_ignored",
14039
+ field: "aspectRatio",
14040
+ message: `${ctx.model.id} keeps the source image's aspect ratio on an edit`
14041
+ });
14042
+ }
14043
+ const body = {
14044
+ model,
14045
+ type: "image_edit",
14046
+ prompt,
14047
+ source: imageRef(source2)
14048
+ };
14049
+ if (rest.length > 0) body["image_ref"] = rest.map(imageRef);
14050
+ if (outputFormat !== void 0) body["output_format"] = outputFormat;
14051
+ return body;
14052
+ }
14053
+ function videoBody(req, ctx, mode) {
14054
+ const prompt = checkPrompt(req, ctx);
14055
+ const model = ctx.model.providerModelId;
14056
+ const hasReferences = (req.referenceImages ?? []).length + (req.referenceVideos ?? []).length + (req.referenceAudios ?? []).length > 0;
14057
+ if (hasReferences) {
14058
+ refuseOrDrop(
14059
+ ctx,
14060
+ "referenceImages",
14061
+ `${ctx.model.id} takes no reference images, videos or audio`
14062
+ );
14063
+ }
14064
+ if (mode === "generate") {
14065
+ const body = {
14066
+ model,
14067
+ type: "video",
14068
+ prompt,
14069
+ video: {
14070
+ ...req.resolution !== void 0 && { resolution: req.resolution },
14071
+ duration: "5s",
14072
+ ...req.firstFrame !== void 0 && { start_frame: imageRef(req.firstFrame) },
14073
+ ...req.lastFrame !== void 0 && { end_frame: imageRef(req.lastFrame) }
14074
+ }
14075
+ };
14076
+ if (req.aspectRatio !== void 0 && req.aspectRatio !== "adaptive") {
14077
+ body["aspect_ratio"] = req.aspectRatio;
14078
+ }
14079
+ return body;
14080
+ }
14081
+ const sourceVideo = req.sourceVideo;
14082
+ if (!sourceVideo) {
14083
+ throw new ModelKitError({
14084
+ code: "missing_input",
14085
+ message: `${ctx.model.id} requires sourceVideo`,
14086
+ provider: "luma",
14087
+ model: ctx.model.id,
14088
+ retryable: false
14089
+ });
14090
+ }
14091
+ if (mode === "edit") {
14092
+ if (req.lastFrame !== void 0) {
14093
+ refuseOrDrop(ctx, "lastFrame", `${ctx.model.id} takes no end frame`);
14094
+ }
14095
+ const body = {
14096
+ model,
14097
+ type: "video_edit",
14098
+ prompt,
14099
+ source: videoRef(sourceVideo),
14100
+ video: {
14101
+ ...req.resolution !== void 0 && { resolution: req.resolution },
14102
+ ...req.firstFrame !== void 0 && { start_frame: imageRef(req.firstFrame) },
14103
+ edit: { auto_controls: true }
14104
+ }
14105
+ };
14106
+ return body;
14107
+ }
14108
+ if (req.firstFrame !== void 0) {
14109
+ refuseOrDrop(ctx, "firstFrame", `${ctx.model.id} takes no start frame`);
14110
+ }
14111
+ if (req.lastFrame !== void 0) {
14112
+ refuseOrDrop(ctx, "lastFrame", `${ctx.model.id} takes no end frame`);
14113
+ }
14114
+ const aspectRatio = req.aspectRatio;
14115
+ if (aspectRatio === void 0 || aspectRatio === "adaptive") {
14116
+ throw new ModelKitError({
14117
+ code: "missing_input",
14118
+ message: `${ctx.model.id} needs an aspectRatio target`,
14119
+ provider: "luma",
14120
+ model: ctx.model.id,
14121
+ retryable: false
14122
+ });
14123
+ }
14124
+ const resolution = lumaReframeResolution(req, ctx);
14125
+ return {
14126
+ model,
14127
+ type: "video_reframe",
14128
+ prompt,
14129
+ aspect_ratio: aspectRatio,
14130
+ source: videoRef(sourceVideo),
14131
+ video: { ...resolution !== void 0 && { resolution } }
14132
+ };
14133
+ }
14134
+ async function generateLumaImage(req, ctx) {
14135
+ throwIfAborted(ctx.signal);
14136
+ lumaKey(ctx);
14137
+ if (!LUMA_IMAGE_MODEL_IDS.has(ctx.model.providerModelId.toLowerCase())) {
14138
+ throw new ModelKitError({
14139
+ code: "unsupported_option",
14140
+ message: `luma has no image mapping for "${ctx.model.providerModelId}"`,
14141
+ provider: "luma",
14142
+ model: ctx.model.id,
14143
+ retryable: false
14144
+ });
14145
+ }
14146
+ const format = lumaOutputFormat(req, ctx);
14147
+ const created = await submitGeneration(ctx, imageBody(req, ctx, format));
14148
+ const url = await pollUntil(checkGeneration.bind(null, ctx, created.id, "image"), {
14149
+ intervalMs: IMAGE_POLL_INTERVAL_MS,
14150
+ timeoutMs: IMAGE_TIMEOUT_MS,
14151
+ ...ctx.signal !== void 0 && { signal: ctx.signal }
14152
+ });
14153
+ const image = await downloadBytes(ctx, url, format === "jpeg" ? "image/jpeg" : "image/png");
14154
+ return {
14155
+ images: [image],
14156
+ usage: imageUsage2(ctx),
14157
+ raw: created
14158
+ };
14159
+ }
14160
+ function lumaVideoJob(ctx, req, mode, id) {
14161
+ return {
14162
+ jobId: id,
14163
+ async poll() {
14164
+ const generation = await readGenerationOnce(ctx, id);
14165
+ if (generation?.state === "failed") return { status: "failed" };
14166
+ if (generation?.state === "completed") return { status: "succeeded" };
14167
+ return { status: "pending" };
14168
+ },
14169
+ async result(opts) {
14170
+ const signal = opts?.signal ?? ctx.signal;
14171
+ const url = await pollUntil(checkGeneration.bind(null, ctx, id, "video"), {
14172
+ intervalMs: opts?.pollIntervalMs ?? VIDEO_POLL_INTERVAL_MS,
14173
+ timeoutMs: opts?.timeoutMs ?? VIDEO_TIMEOUT_MS,
14174
+ ...signal !== void 0 && { signal }
14175
+ });
14176
+ const video = await downloadBytes(ctx, url, "video/mp4");
14177
+ const result = {
14178
+ video,
14179
+ usage: videoUsage4(ctx, req, mode, video.bytes),
14180
+ raw: { id }
14181
+ };
14182
+ return result;
14183
+ }
14184
+ };
14185
+ }
14186
+ async function generateLumaVideo(req, ctx) {
14187
+ throwIfAborted(ctx.signal);
14188
+ lumaKey(ctx);
14189
+ if (ctx.model.providerModelId.toLowerCase() !== LUMA_VIDEO_MODEL_ID) {
14190
+ throw new ModelKitError({
14191
+ code: "unsupported_option",
14192
+ message: `luma has no video mapping for "${ctx.model.providerModelId}"`,
14193
+ provider: "luma",
14194
+ model: ctx.model.id,
14195
+ retryable: false
14196
+ });
14197
+ }
14198
+ const mode = lumaVideoMode(ctx);
14199
+ const created = await submitGeneration(ctx, videoBody(req, ctx, mode));
14200
+ return lumaVideoJob(ctx, req, mode, created.id);
14201
+ }
14202
+ var lumaAdapter = {
14203
+ id: "luma",
14204
+ generateImage: generateLumaImage,
14205
+ generateVideo: generateLumaVideo,
14206
+ resumeVideo: (req, jobId, ctx) => {
14207
+ throwIfAborted(ctx.signal);
14208
+ lumaKey(ctx);
14209
+ return lumaVideoJob(ctx, req, lumaVideoMode(ctx), jobId);
14210
+ }
14211
+ };
14212
+
13421
14213
  // src/adapters/openai-image.ts
13422
14214
  import OpenAI from "openai";
13423
- import { z as z25 } from "zod";
14215
+ import { z as z26 } from "zod";
13424
14216
  var MAX_EDIT_REFERENCES = 16;
13425
14217
  function aspectRatioToSize(ratio, tier) {
13426
14218
  const TIER_LONG_EDGE = {
@@ -13503,9 +14295,9 @@ function toProviderError2(cause, ctx) {
13503
14295
  ctx.model.id
13504
14296
  );
13505
14297
  }
13506
- var openaiUsageSchema = z25.object({
13507
- input_tokens: z25.number().optional(),
13508
- output_tokens: z25.number().optional()
14298
+ var openaiUsageSchema = z26.object({
14299
+ input_tokens: z26.number().optional(),
14300
+ output_tokens: z26.number().optional()
13509
14301
  });
13510
14302
  var openaiImageAdapter = {
13511
14303
  id: "openai-image",
@@ -13917,13 +14709,13 @@ var prodiaAdapter = {
13917
14709
  import Replicate from "replicate";
13918
14710
 
13919
14711
  // src/adapters/replicate-http.ts
13920
- import { z as z26 } from "zod";
13921
- var predictionSchema = z26.object({
13922
- id: z26.string(),
13923
- status: z26.string(),
14712
+ import { z as z27 } from "zod";
14713
+ var predictionSchema = z27.object({
14714
+ id: z27.string(),
14715
+ status: z27.string(),
13924
14716
  output: JsonValueSchema.optional(),
13925
14717
  error: JsonValueSchema.optional(),
13926
- metrics: z26.object({ predict_time: z26.number().optional() }).optional()
14718
+ metrics: z27.object({ predict_time: z27.number().optional() }).optional()
13927
14719
  });
13928
14720
  function createReplicateHttpClient(opts) {
13929
14721
  async function request(path, body, signal) {
@@ -13974,7 +14766,7 @@ function createReplicateHttpClient(opts) {
13974
14766
  }
13975
14767
 
13976
14768
  // src/adapters/replicate.ts
13977
- import { z as z27 } from "zod";
14769
+ import { z as z28 } from "zod";
13978
14770
  var POLL_INTERVAL_MS = 1e3;
13979
14771
  var POLL_TIMEOUT_MS = 15 * 6e4;
13980
14772
  var defaultReplicateDeps = {
@@ -14014,8 +14806,8 @@ function route(providerModelId) {
14014
14806
  if (!providerModelId.includes("/")) return { version: providerModelId };
14015
14807
  return { model: providerModelId };
14016
14808
  }
14017
- var replicateResponseStatusSchema = z27.object({
14018
- response: z27.object({ status: z27.number() }).partial()
14809
+ var replicateResponseStatusSchema = z28.object({
14810
+ response: z28.object({ status: z28.number() }).partial()
14019
14811
  });
14020
14812
  function replicateError(cause, ctx) {
14021
14813
  if (isModelKitError(cause)) return cause;
@@ -14024,10 +14816,6 @@ function replicateError(cause, ctx) {
14024
14816
  const status = info.status ?? (nested.success ? nested.data.response?.status : void 0);
14025
14817
  return classifyThrown("replicate", cause, ctx.model.id, { status, body: info.body });
14026
14818
  }
14027
- function baseModelId(providerModelId) {
14028
- const colon = providerModelId.indexOf(":");
14029
- return colon === -1 ? providerModelId : providerModelId.slice(0, colon);
14030
- }
14031
14819
  function predictionErrorBody(cause) {
14032
14820
  const parsed = JsonValueSchema.safeParse(cause);
14033
14821
  if (parsed.success) return parsed.data;
@@ -14042,7 +14830,7 @@ function isUrlFn(value) {
14042
14830
  function extractOutputUrl(output) {
14043
14831
  const root = Array.isArray(output) ? output[0] : output;
14044
14832
  if (root === void 0) throw noOutput();
14045
- const asString = z27.string().safeParse(root);
14833
+ const asString = z28.string().safeParse(root);
14046
14834
  if (asString.success && asString.data) return asString.data;
14047
14835
  if (root instanceof URL) return root.href;
14048
14836
  if (isObjectOutput(root)) {
@@ -14051,15 +14839,15 @@ function extractOutputUrl(output) {
14051
14839
  if (urlValue !== void 0 && isUrlFn(urlValue)) {
14052
14840
  const called = urlValue();
14053
14841
  if (called instanceof URL) return called.href;
14054
- const calledString = z27.string().safeParse(called);
14842
+ const calledString = z28.string().safeParse(called);
14055
14843
  if (calledString.success && calledString.data) return calledString.data;
14056
14844
  } else {
14057
- const direct = z27.string().safeParse(urlValue);
14845
+ const direct = z28.string().safeParse(urlValue);
14058
14846
  if (direct.success && direct.data) return direct.data;
14059
14847
  }
14060
14848
  }
14061
14849
  if ("default" in root) {
14062
- const fallback = z27.string().safeParse(root.default);
14850
+ const fallback = z28.string().safeParse(root.default);
14063
14851
  if (fallback.success && fallback.data) return fallback.data;
14064
14852
  }
14065
14853
  }
@@ -14120,7 +14908,7 @@ async function runPrediction(ctx, input, deps) {
14120
14908
  }
14121
14909
  return void 0;
14122
14910
  }, pollOpts);
14123
- const predictTime = z27.number().safeParse(done.metrics?.predict_time);
14911
+ const predictTime = z28.number().safeParse(done.metrics?.predict_time);
14124
14912
  if (done.output === void 0) throw noOutput();
14125
14913
  return {
14126
14914
  url: extractOutputUrl(done.output),
@@ -14128,7 +14916,7 @@ async function runPrediction(ctx, input, deps) {
14128
14916
  raw: done
14129
14917
  };
14130
14918
  }
14131
- function imageUsage2(ctx, req) {
14919
+ function imageUsage3(ctx, req) {
14132
14920
  const event = {
14133
14921
  model: ctx.model.id,
14134
14922
  provider: "replicate",
@@ -14140,7 +14928,7 @@ function imageUsage2(ctx, req) {
14140
14928
  if (req.quality !== void 0) event.imageQuality = req.quality;
14141
14929
  return event;
14142
14930
  }
14143
- function videoUsage4(ctx, req) {
14931
+ function videoUsage5(ctx, req) {
14144
14932
  const event = {
14145
14933
  model: ctx.model.id,
14146
14934
  provider: "replicate",
@@ -14181,17 +14969,7 @@ var REPLICATE_IMAGE_KINDS = {
14181
14969
  "qwen-edit-apps/qwen-image-edit-plus-lora-skin": "qwen-edit"
14182
14970
  };
14183
14971
  function imageKind(providerModelId) {
14184
- return REPLICATE_IMAGE_KINDS[baseModelId(providerModelId)] ?? "generic";
14185
- }
14186
- var REPLICATE_VIDEO_KINDS = {
14187
- "kling-video/v2.5-turbo/pro/image-to-video": "kling",
14188
- "kling-video/v3.0/pro/image-to-video": "kling",
14189
- "kwaivgi/kling-v3-video": "kling",
14190
- "kwaivgi/kling-v3-omni-video": "kling-omni",
14191
- "minimax/hailuo-02": "hailuo"
14192
- };
14193
- function videoKind(providerModelId) {
14194
- return REPLICATE_VIDEO_KINDS[baseModelId(providerModelId)];
14972
+ return REPLICATE_IMAGE_KINDS[replicateBaseModelId(providerModelId)] ?? "generic";
14195
14973
  }
14196
14974
  function isReplicateVideoKnown(providerModelId) {
14197
14975
  return videoKind(providerModelId) !== void 0;
@@ -14202,7 +14980,7 @@ var REPLICATE_AUDIO_KINDS = {
14202
14980
  "google/lyria-2": "lyria"
14203
14981
  };
14204
14982
  function audioKind(providerModelId) {
14205
- return REPLICATE_AUDIO_KINDS[baseModelId(providerModelId)];
14983
+ return REPLICATE_AUDIO_KINDS[replicateBaseModelId(providerModelId)];
14206
14984
  }
14207
14985
  function isReplicateAudioKnown(providerModelId) {
14208
14986
  return audioKind(providerModelId) !== void 0;
@@ -14376,7 +15154,7 @@ async function generateReplicateImage(req, ctx, deps) {
14376
15154
  }
14377
15155
  const { url, predictTime, raw } = await runPrediction(ctx, input, deps);
14378
15156
  const file = await download(ctx, url);
14379
- const usage3 = imageUsage2(ctx, req);
15157
+ const usage3 = imageUsage3(ctx, req);
14380
15158
  usage3.quantity = predictTime;
14381
15159
  return { images: [{ bytes: file.bytes, mime: file.mime }], usage: usage3, raw, outputUrl: url };
14382
15160
  }
@@ -14406,14 +15184,6 @@ function replicateVideoResolution(providerModelId, req) {
14406
15184
  if (kind === "hailuo") return hailuoResolution(req);
14407
15185
  return req.resolution;
14408
15186
  }
14409
- var KLING_TIERS = [
14410
- { mode: "standard", resolution: "720p" },
14411
- { mode: "pro", resolution: "1080p" },
14412
- { mode: "4k", resolution: "4k" }
14413
- ];
14414
- function klingTier(req) {
14415
- return KLING_TIERS.find((tier) => tier.resolution === req.resolution) ?? KLING_TIERS.find((tier) => tier.mode === req.mode) ?? KLING_TIERS[0];
14416
- }
14417
15187
  function klingOmniInput(req) {
14418
15188
  const input = {
14419
15189
  prompt: req.prompt,
@@ -14491,7 +15261,7 @@ async function generateReplicateVideo(req, ctx, deps) {
14491
15261
  throw replicateError(cause, ctx);
14492
15262
  }
14493
15263
  if (current.status === "succeeded") {
14494
- const predictTime = z27.number().safeParse(current.metrics?.predict_time);
15264
+ const predictTime = z28.number().safeParse(current.metrics?.predict_time);
14495
15265
  if (current.output === void 0) throw noOutput();
14496
15266
  return {
14497
15267
  url: extractOutputUrl(current.output),
@@ -14510,7 +15280,7 @@ async function generateReplicateVideo(req, ctx, deps) {
14510
15280
  return void 0;
14511
15281
  };
14512
15282
  const toResult = (done) => {
14513
- const usage3 = videoUsage4(ctx, req);
15283
+ const usage3 = videoUsage5(ctx, req);
14514
15284
  usage3.quantity = done.predictTime;
14515
15285
  return { video: { url: done.url }, usage: usage3, raw: done.raw };
14516
15286
  };
@@ -14633,7 +15403,7 @@ async function generateReplicateAudio(req, ctx, deps) {
14633
15403
  // src/adapters/xai-video.ts
14634
15404
  import { experimental_generateVideo } from "ai";
14635
15405
  import { createXai as createXai2 } from "@ai-sdk/xai";
14636
- import { z as z28 } from "zod";
15406
+ import { z as z29 } from "zod";
14637
15407
  var POLL_INTERVAL_MS2 = 1e4;
14638
15408
  var POLL_TIMEOUT_MS2 = 15 * 6e4;
14639
15409
  var defaultXaiDeps = {
@@ -14646,8 +15416,8 @@ function createXaiVideoAdapter(deps = defaultXaiDeps) {
14646
15416
  };
14647
15417
  }
14648
15418
  var xaiVideoAdapter = createXaiVideoAdapter();
14649
- var xaiResponseStatusSchema = z28.object({
14650
- response: z28.object({ status: z28.number() }).partial()
15419
+ var xaiResponseStatusSchema = z29.object({
15420
+ response: z29.object({ status: z29.number() }).partial()
14651
15421
  });
14652
15422
  function xaiError(cause, ctx) {
14653
15423
  const info = describeThrown(cause);
@@ -14655,12 +15425,12 @@ function xaiError(cause, ctx) {
14655
15425
  const status = info.status ?? (nested.success ? nested.data.response?.status : void 0);
14656
15426
  return classifyThrown("xai-video", cause, ctx.model.id, { status, body: info.body });
14657
15427
  }
14658
- var xaiMetaSchema = z28.object({
15428
+ var xaiMetaSchema = z29.object({
14659
15429
  videoUrl: JsonValueSchema.optional(),
14660
15430
  costInUsdTicks: JsonValueSchema.optional(),
14661
15431
  duration: JsonValueSchema.optional()
14662
15432
  });
14663
- var xaiProviderMetadataSchema = z28.object({ xai: xaiMetaSchema });
15433
+ var xaiProviderMetadataSchema = z29.object({ xai: xaiMetaSchema });
14664
15434
  function metaOf(result) {
14665
15435
  const parsed = xaiProviderMetadataSchema.partial().safeParse(result.providerMetadata);
14666
15436
  if (!parsed.success) return {};
@@ -14776,7 +15546,7 @@ async function mapResult(ctx, req, result) {
14776
15546
  };
14777
15547
  }
14778
15548
  const meta = metaOf(result);
14779
- const videoUrl = z28.string().safeParse(meta.videoUrl);
15549
+ const videoUrl = z29.string().safeParse(meta.videoUrl);
14780
15550
  if (videoUrl.success && videoUrl.data) {
14781
15551
  const res = await ctx.fetch(videoUrl.data);
14782
15552
  if (!res.ok) {
@@ -14799,7 +15569,7 @@ async function mapResult(ctx, req, result) {
14799
15569
  });
14800
15570
  }
14801
15571
  function usage2(ctx, req, meta) {
14802
- const costTicks = z28.number().safeParse(meta.costInUsdTicks);
15572
+ const costTicks = z29.number().safeParse(meta.costInUsdTicks);
14803
15573
  const cost = costTicks.success ? costTicks.data / 1e10 : void 0;
14804
15574
  const event = {
14805
15575
  model: ctx.model.id,
@@ -14847,7 +15617,8 @@ var DEFAULT_ADAPTERS = {
14847
15617
  replicate: replicateAdapter,
14848
15618
  prodia: prodiaAdapter,
14849
15619
  higgsfield: higgsfieldAdapter,
14850
- "xai-video": xaiVideoAdapter
15620
+ "xai-video": xaiVideoAdapter,
15621
+ luma: lumaAdapter
14851
15622
  };
14852
15623
 
14853
15624
  // src/catalog/data-only.ts
@@ -14963,6 +15734,10 @@ function dataOnlyCapable(row2) {
14963
15734
  reason: "higgsfield providerModelId must be a platform path, not a URL"
14964
15735
  };
14965
15736
  }
15737
+ case "luma": {
15738
+ const reason = lumaRowRefusal(row2);
15739
+ return reason === void 0 ? { ok: true } : { ok: false, reason };
15740
+ }
14966
15741
  default: {
14967
15742
  return assertNever(row2.adapter);
14968
15743
  }
@@ -14982,22 +15757,22 @@ var liveState = store[KEY] ??= {
14982
15757
  var liveCatalog = liveState.catalog;
14983
15758
 
14984
15759
  // src/catalog/load.ts
14985
- import { z as z29 } from "zod";
15760
+ import { z as z30 } from "zod";
14986
15761
  function rowCount() {
14987
15762
  return liveCatalog.list({ lifecycle: ["active", "deprecated", "retired"] }).length;
14988
15763
  }
14989
15764
  function authHeaders(auth) {
14990
15765
  return "bearer" in auth ? { Authorization: `Bearer ${auth.bearer}` } : { "x-api-key": auth.apiKey };
14991
15766
  }
14992
- var EnvelopeSchema = z29.object({
14993
- rows: z29.array(JsonValueSchema),
14994
- revision: z29.string().optional()
15767
+ var EnvelopeSchema = z30.object({
15768
+ rows: z30.array(JsonValueSchema),
15769
+ revision: z30.string().optional()
14995
15770
  });
14996
15771
  function dropReason(path, message) {
14997
15772
  return path.length === 0 ? message : `${path.map((part) => String(part)).join(".")} ${message}`;
14998
15773
  }
14999
15774
  function rowId(raw) {
15000
- const parsed = z29.object({ id: z29.string().min(1) }).loose().safeParse(raw);
15775
+ const parsed = z30.object({ id: z30.string().min(1) }).loose().safeParse(raw);
15001
15776
  if (!parsed.success) return "?";
15002
15777
  return parsed.data.id;
15003
15778
  }
@@ -15641,8 +16416,10 @@ function resolveVideoOptions(model, request, onUnsupported = "error") {
15641
16416
  warnings
15642
16417
  );
15643
16418
  if (aspectRatio !== void 0) merged.aspectRatio = aspectRatio;
16419
+ fillReplicateKlingResolution(model, merged);
15644
16420
  const resolution = resolveVideoResolution(merged.resolution, model, onUnsupported, warnings);
15645
16421
  if (resolution !== void 0) merged.resolution = resolution;
16422
+ if (isReplicateKling(model)) delete merged.mode;
15646
16423
  const duration = resolveDuration(
15647
16424
  merged.durationSeconds,
15648
16425
  c.durations,
@@ -16044,7 +16821,8 @@ var ADAPTER_KEYS = {
16044
16821
  replicate: ["replicate"],
16045
16822
  prodia: ["prodia"],
16046
16823
  higgsfield: ["higgsfield"],
16047
- "xai-video": ["xai"]
16824
+ "xai-video": ["xai"],
16825
+ luma: ["luma"]
16048
16826
  };
16049
16827
  function hasAll(keys, names) {
16050
16828
  if (!keys) return false;
@@ -16102,6 +16880,7 @@ function keysFromEnv(env, prefix = "") {
16102
16880
  set("prodia", pick2("PRODIA_API_KEY", "PRODIA_TOKEN"));
16103
16881
  set("higgsfield", pick2("HIGGSFIELD_API_KEY"));
16104
16882
  set("higgsfieldSecret", pick2("HIGGSFIELD_API_SECRET"));
16883
+ set("luma", pick2("LUMA_API_KEY", "LUMA_AGENTS_API_KEY"));
16105
16884
  set("cloudflare", pick2("CLOUDFLARE_AI_GATEWAY_TOKEN", "CF_AIG_TOKEN"));
16106
16885
  const bearer = pick2("AWS_BEARER_TOKEN_BEDROCK");
16107
16886
  const accessKeyId = pick2("AWS_ACCESS_KEY_ID");
@@ -16124,7 +16903,7 @@ function keysFromEnv(env, prefix = "") {
16124
16903
 
16125
16904
  // src/text.ts
16126
16905
  import "ai";
16127
- import { z as z30 } from "zod";
16906
+ import { z as z31 } from "zod";
16128
16907
  function mergeTextProviderOptions(catalog, caller) {
16129
16908
  const merged = {};
16130
16909
  const catalogOptions = catalog.providerOptions ?? {};
@@ -16163,8 +16942,8 @@ function passthroughTextModel(model) {
16163
16942
  pricingKind: "token"
16164
16943
  };
16165
16944
  }
16166
- var perplexityCostSchema = z30.object({
16167
- perplexity: z30.object({ cost: z30.object({ totalCost: z30.number().finite().nonnegative() }) })
16945
+ var perplexityCostSchema = z31.object({
16946
+ perplexity: z31.object({ cost: z31.object({ totalCost: z31.number().finite().nonnegative() }) })
16168
16947
  });
16169
16948
  function reportedCostUsd(metadata) {
16170
16949
  const parsed = perplexityCostSchema.safeParse(metadata);
@@ -16244,7 +17023,7 @@ import {
16244
17023
  streamObject as aiStreamObject2,
16245
17024
  streamText as aiStreamText2
16246
17025
  } from "ai";
16247
- import { z as z31 } from "zod";
17026
+ import { z as z32 } from "zod";
16248
17027
 
16249
17028
  // src/generated-image.ts
16250
17029
  function toGeneratedImage(image) {
@@ -16593,10 +17372,10 @@ function textAbortOptions(sdk, context) {
16593
17372
  if (abortSignal) return { abortSignal };
16594
17373
  return {};
16595
17374
  }
16596
- var catalogId = z31.string();
16597
- var exactSize = z31.templateLiteral([z31.number(), "x", z31.number()]);
16598
- var sdkRatio = z31.templateLiteral([z31.number(), ":", z31.number()]);
16599
- var sizeTier = z31.enum(["512", "1K", "2K", "4K"]);
17375
+ var catalogId = z32.string();
17376
+ var exactSize = z32.templateLiteral([z32.number(), "x", z32.number()]);
17377
+ var sdkRatio = z32.templateLiteral([z32.number(), ":", z32.number()]);
17378
+ var sizeTier = z32.enum(["512", "1K", "2K", "4K"]);
16600
17379
  function isCatalogId(model) {
16601
17380
  return catalogId.safeParse(model).success;
16602
17381
  }
@@ -17340,6 +18119,7 @@ export {
17340
18119
  languageModel,
17341
18120
  liveCatalog,
17342
18121
  liveState,
18122
+ lumaAdapter,
17343
18123
  mergeTextProviderOptions,
17344
18124
  missingTierKeys,
17345
18125
  model3dCall,