@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.cjs CHANGED
@@ -88,6 +88,7 @@ __export(index_exports, {
88
88
  languageModel: () => languageModel,
89
89
  liveCatalog: () => liveCatalog,
90
90
  liveState: () => liveState,
91
+ lumaAdapter: () => lumaAdapter,
91
92
  mergeTextProviderOptions: () => mergeTextProviderOptions,
92
93
  missingTierKeys: () => missingTierKeys,
93
94
  model3dCall: () => model3dCall,
@@ -510,8 +511,59 @@ var FAL_IMAGE_MODELS = [
510
511
  }
511
512
  ];
512
513
 
513
- // src/catalog/image.ts
514
+ // src/catalog/image-luma.ts
514
515
  var IMAGE_USAGE2 = { unit: "images" };
516
+ var EXPOSURE2 = ["cowork", "space_builder"];
517
+ var LOBE2 = "https://cdn.jsdelivr.net/npm/@lobehub/icons-static-png@1.97.0/light";
518
+ var LUMA_LOGO = `${LOBE2}/luma-color.png`;
519
+ var UNI_RATIOS = ["adaptive", "16:9", "3:2", "1:1", "2:3", "9:16"];
520
+ function uniImage(id, label, providerModelId, description) {
521
+ return {
522
+ // https://docs.agents.lumalabs.ai: type "image" without references, else
523
+ // "image_edit" with the first reference as source and up to 8 more as
524
+ // image_ref. The output is always 2048 px on the long side.
525
+ id,
526
+ aliases: [],
527
+ label,
528
+ type: "image",
529
+ adapter: "luma",
530
+ providerModelId,
531
+ lifecycle: "active",
532
+ exposure: EXPOSURE2,
533
+ constraints: {
534
+ aspectRatios: [...UNI_RATIOS],
535
+ sizes: ["2K"],
536
+ references: { images: 9 },
537
+ supports: ["outputFormat"],
538
+ prompt: { referenceSyntax: "numbered", maxChars: 6e3 }
539
+ },
540
+ defaults: { size: "2K", aspectRatio: "adaptive" },
541
+ usage: { ...IMAGE_USAGE2 },
542
+ pricingKind: "image_size",
543
+ display: {
544
+ provider: "luma",
545
+ description,
546
+ logo: LUMA_LOGO
547
+ }
548
+ };
549
+ }
550
+ var LUMA_IMAGE_MODELS = [
551
+ uniImage(
552
+ "uni-1",
553
+ "Uni-1",
554
+ "uni-1",
555
+ "Luma Uni-1 fast image generation and editing; takes a prompt and up to 9 reference images and outputs 2K."
556
+ ),
557
+ uniImage(
558
+ "uni-1-max",
559
+ "Uni-1 Max",
560
+ "uni-1-max",
561
+ "Luma Uni-1 Max higher-quality image generation and editing; takes a prompt and up to 9 reference images and outputs 2K."
562
+ )
563
+ ];
564
+
565
+ // src/catalog/image.ts
566
+ var IMAGE_USAGE3 = { unit: "images" };
515
567
  var GEMINI_RATIOS = [
516
568
  "1:1",
517
569
  "2:3",
@@ -556,7 +608,7 @@ function geminiImage(id, label, aliases, sizes, display, exposure, references, p
556
608
  exposure,
557
609
  constraints,
558
610
  defaults: { size: "1K", aspectRatio: "1:1" },
559
- usage: { ...IMAGE_USAGE2, slug: "gemini" },
611
+ usage: { ...IMAGE_USAGE3, slug: "gemini" },
560
612
  pricingKind: "image_size",
561
613
  display
562
614
  };
@@ -669,7 +721,7 @@ var IMAGE_MODELS = [
669
721
  prompt: GPT_IMAGE_PROMPT
670
722
  },
671
723
  defaults: { size: "1K", aspectRatio: "adaptive", quality: "auto" },
672
- usage: { ...IMAGE_USAGE2, slug: "gpt" },
724
+ usage: { ...IMAGE_USAGE3, slug: "gpt" },
673
725
  pricingKind: "image_size",
674
726
  display: {
675
727
  provider: "openai",
@@ -705,7 +757,7 @@ var IMAGE_MODELS = [
705
757
  prompt: GPT_IMAGE_PROMPT
706
758
  },
707
759
  defaults: { size: "1K", aspectRatio: "adaptive", quality: "auto" },
708
- usage: { ...IMAGE_USAGE2 },
760
+ usage: { ...IMAGE_USAGE3 },
709
761
  pricingKind: "image_size",
710
762
  display: {
711
763
  provider: "openai",
@@ -740,7 +792,7 @@ var IMAGE_MODELS = [
740
792
  prompt: GPT_IMAGE_PROMPT
741
793
  },
742
794
  defaults: { size: "1K", aspectRatio: "adaptive", quality: "auto" },
743
- usage: { ...IMAGE_USAGE2 },
795
+ usage: { ...IMAGE_USAGE3 },
744
796
  pricingKind: "image_size",
745
797
  display: {
746
798
  provider: "openai",
@@ -775,7 +827,7 @@ var IMAGE_MODELS = [
775
827
  supports: ["count", "outputFormat"]
776
828
  },
777
829
  defaults: { size: "1K", aspectRatio: "1:1", outputFormat: "png", count: 1 },
778
- usage: { ...IMAGE_USAGE2, slug: "muse" },
830
+ usage: { ...IMAGE_USAGE3, slug: "muse" },
779
831
  pricingKind: "image_size",
780
832
  display: {
781
833
  provider: "meta",
@@ -802,7 +854,7 @@ var IMAGE_MODELS = [
802
854
  supports: ["quality", "count", "seed"]
803
855
  },
804
856
  defaults: { size: "2K", aspectRatio: "1:1", quality: "medium", count: 1 },
805
- usage: { ...IMAGE_USAGE2, slug: "ideogram" },
857
+ usage: { ...IMAGE_USAGE3, slug: "ideogram" },
806
858
  pricingKind: "image_size",
807
859
  display: {
808
860
  provider: "ideogram",
@@ -827,7 +879,7 @@ var IMAGE_MODELS = [
827
879
  supports: ["seed", "outputFormat"]
828
880
  },
829
881
  defaults: { size: "1K", aspectRatio: "1:1" },
830
- usage: { ...IMAGE_USAGE2 },
882
+ usage: { ...IMAGE_USAGE3 },
831
883
  pricingKind: "image_size",
832
884
  display: {
833
885
  provider: "bytedance",
@@ -865,7 +917,7 @@ var IMAGE_MODELS = [
865
917
  supports: ["seed"]
866
918
  },
867
919
  defaults: { size: "1K", aspectRatio: "1:1" },
868
- usage: { ...IMAGE_USAGE2 },
920
+ usage: { ...IMAGE_USAGE3 },
869
921
  pricingKind: "image_size",
870
922
  display: {
871
923
  provider: "bytedance",
@@ -891,7 +943,7 @@ var IMAGE_MODELS = [
891
943
  prompt: { referenceSyntax: "numbered", maxChars: 32768 }
892
944
  },
893
945
  defaults: { size: "1K", aspectRatio: "1:1" },
894
- usage: { ...IMAGE_USAGE2 },
946
+ usage: { ...IMAGE_USAGE3 },
895
947
  pricingKind: "image_size",
896
948
  display: {
897
949
  provider: "black-forest-labs",
@@ -918,7 +970,7 @@ var IMAGE_MODELS = [
918
970
  supports: []
919
971
  },
920
972
  defaults: { size: "1K", aspectRatio: "1:1" },
921
- usage: { ...IMAGE_USAGE2 },
973
+ usage: { ...IMAGE_USAGE3 },
922
974
  pricingKind: "image_size",
923
975
  display: {
924
976
  provider: "google",
@@ -943,7 +995,7 @@ var IMAGE_MODELS = [
943
995
  supports: []
944
996
  },
945
997
  defaults: { size: "1K", aspectRatio: "1:1" },
946
- usage: { ...IMAGE_USAGE2 },
998
+ usage: { ...IMAGE_USAGE3 },
947
999
  pricingKind: "image_size",
948
1000
  display: {
949
1001
  provider: "google",
@@ -1029,7 +1081,8 @@ var IMAGE_MODELS = [
1029
1081
  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."
1030
1082
  }
1031
1083
  },
1032
- ...FAL_IMAGE_MODELS
1084
+ ...FAL_IMAGE_MODELS,
1085
+ ...LUMA_IMAGE_MODELS
1033
1086
  ];
1034
1087
 
1035
1088
  // src/catalog/audio-formats.ts
@@ -1225,7 +1278,7 @@ var VEED_SUBTITLE_LANGUAGES = [
1225
1278
  ];
1226
1279
 
1227
1280
  // src/catalog/canvas-tools.ts
1228
- var EXPOSURE2 = ["cowork", "space_builder"];
1281
+ var EXPOSURE3 = ["cowork", "space_builder"];
1229
1282
  var META_LOGO = "https://d31vvfpk2ppdyq.cloudfront.net/assets/characters/1787909065794-k9a7zz.png";
1230
1283
  var VEED_SUBTITLE_PRESETS = [
1231
1284
  "simple",
@@ -1267,7 +1320,7 @@ var SAM_3_IMAGE = {
1267
1320
  adapter: "fal",
1268
1321
  providerModelId: "fal-ai/sam-3/image",
1269
1322
  lifecycle: "active",
1270
- exposure: EXPOSURE2,
1323
+ exposure: EXPOSURE3,
1271
1324
  constraints: {
1272
1325
  references: { images: 1 },
1273
1326
  requires: ["references"],
@@ -1290,7 +1343,7 @@ var VEED_SUBTITLES = {
1290
1343
  adapter: "fal",
1291
1344
  providerModelId: "veed/subtitles",
1292
1345
  lifecycle: "active",
1293
- exposure: EXPOSURE2,
1346
+ exposure: EXPOSURE3,
1294
1347
  constraints: {
1295
1348
  supports: [],
1296
1349
  // fal: up to 2 hours at 1080p or below, 1 hour above.
@@ -3395,7 +3448,7 @@ var GOOGLE_LOGO = "https://d31vvfpk2ppdyq.cloudfront.net/assets/image/192485d2-a
3395
3448
  var MINIMAX_LOGO = "https://d31vvfpk2ppdyq.cloudfront.net/assets/image/f4c2e906-28b7-4a4a-b491-df8fef7808ed.png";
3396
3449
  var SYNC_LOGO = "https://cdn.jsdelivr.net/npm/@lobehub/icons-static-png@1.97.0/light/sync.png";
3397
3450
  var PER_CALL = { unit: "images" };
3398
- var EXPOSURE3 = ["cowork", "space_builder"];
3451
+ var EXPOSURE4 = ["cowork", "space_builder"];
3399
3452
  var LIPSYNC_AUDIO = { audioSeconds: { max: 30 } };
3400
3453
  function elevenLabsTts(tts) {
3401
3454
  return {
@@ -3406,7 +3459,7 @@ function elevenLabsTts(tts) {
3406
3459
  adapter: "fal",
3407
3460
  providerModelId: tts.providerModelId,
3408
3461
  lifecycle: "active",
3409
- exposure: EXPOSURE3,
3462
+ exposure: EXPOSURE4,
3410
3463
  constraints: {
3411
3464
  voices: ELEVENLABS_VOICES,
3412
3465
  languages: tts.languages,
@@ -3431,7 +3484,7 @@ function geminiTts(id, label, description) {
3431
3484
  adapter: "fal",
3432
3485
  providerModelId,
3433
3486
  lifecycle: "active",
3434
- exposure: EXPOSURE3,
3487
+ exposure: EXPOSURE4,
3435
3488
  constraints: {
3436
3489
  voices: GEMINI_TTS_VOICES,
3437
3490
  supports: ["style"],
@@ -3454,7 +3507,7 @@ function syncLipsync(id, label, providerModelId, syncModel, description) {
3454
3507
  adapter: "fal",
3455
3508
  providerModelId,
3456
3509
  lifecycle: "active",
3457
- exposure: EXPOSURE3,
3510
+ exposure: EXPOSURE4,
3458
3511
  constraints: {
3459
3512
  requires: ["sourceVideo", "sourceAudio"],
3460
3513
  references: LIPSYNC_AUDIO,
@@ -3515,7 +3568,7 @@ var MEDIA_NODE_MODELS = [
3515
3568
  adapter: "fal",
3516
3569
  providerModelId: "fal-ai/minimax/speech-2.8-hd",
3517
3570
  lifecycle: "active",
3518
- exposure: EXPOSURE3,
3571
+ exposure: EXPOSURE4,
3519
3572
  constraints: {
3520
3573
  voices: MINIMAX_VOICES,
3521
3574
  languages: MINIMAX_SPEECH_LANGUAGES,
@@ -3540,7 +3593,7 @@ var MEDIA_NODE_MODELS = [
3540
3593
  adapter: "fal",
3541
3594
  providerModelId: "fal-ai/elevenlabs/voice-changer",
3542
3595
  lifecycle: "active",
3543
- exposure: EXPOSURE3,
3596
+ exposure: EXPOSURE4,
3544
3597
  constraints: {
3545
3598
  requires: ["sourceAudio"],
3546
3599
  // The flat price assumes at most a minute of speech.
@@ -3567,7 +3620,7 @@ var MEDIA_NODE_MODELS = [
3567
3620
  adapter: "fal",
3568
3621
  providerModelId: "fal-ai/elevenlabs/dubbing",
3569
3622
  lifecycle: "active",
3570
- exposure: EXPOSURE3,
3623
+ exposure: EXPOSURE4,
3571
3624
  constraints: {
3572
3625
  requiresOneOf: ["sourceVideo", "sourceAudio"],
3573
3626
  // The flat price covers 60 s of source.
@@ -3615,7 +3668,7 @@ var MEDIA_NODE_MODELS = [
3615
3668
  adapter: "fal",
3616
3669
  providerModelId: "veed/lipsync/v2",
3617
3670
  lifecycle: "active",
3618
- exposure: EXPOSURE3,
3671
+ exposure: EXPOSURE4,
3619
3672
  constraints: {
3620
3673
  requires: ["sourceVideo", "sourceAudio"],
3621
3674
  references: LIPSYNC_AUDIO,
@@ -3641,7 +3694,7 @@ var MEDIA_NODE_MODELS = [
3641
3694
  adapter: "fal",
3642
3695
  providerModelId: "minimax/h3-max/lip-sync/image-to-video",
3643
3696
  lifecycle: "active",
3644
- exposure: EXPOSURE3,
3697
+ exposure: EXPOSURE4,
3645
3698
  constraints: {
3646
3699
  requires: ["firstFrame", "sourceAudio"],
3647
3700
  resolutions: ["480p", "768p", "1080p", "2k"],
@@ -3667,7 +3720,7 @@ var MEDIA_NODE_MODELS = [
3667
3720
  adapter: "fal",
3668
3721
  providerModelId: "fal-ai/elevenlabs/speech-to-text/scribe-v2",
3669
3722
  lifecycle: "active",
3670
- exposure: EXPOSURE3,
3723
+ exposure: EXPOSURE4,
3671
3724
  constraints: {
3672
3725
  requires: ["sourceAudio"],
3673
3726
  // The flat price covers 10 minutes of audio.
@@ -5203,8 +5256,113 @@ var VIDEO_FAMILY_MODELS = [
5203
5256
  }
5204
5257
  ];
5205
5258
 
5206
- // src/catalog/video.ts
5259
+ // src/catalog/video-luma.ts
5207
5260
  var VIDEO_USAGE2 = { unit: "seconds" };
5261
+ var EXPOSURE5 = ["cowork", "space_builder"];
5262
+ var LUMA_LOGO2 = "https://cdn.jsdelivr.net/npm/@lobehub/icons-static-png@1.97.0/light/luma-color.png";
5263
+ var RAY_RATIOS = ["9:16", "3:4", "1:1", "4:3", "16:9", "21:9"];
5264
+ var RAY_RESOLUTIONS = ["360p", "720p", "1080p"];
5265
+ var RAY_PROMPT = { referenceSyntax: "numbered", maxChars: 6e3 };
5266
+ var LUMA_VIDEO_MODELS = [
5267
+ {
5268
+ // https://docs.agents.lumalabs.ai: type "video" with a fixed 5 s duration;
5269
+ // firstFrame and lastFrame ride as start_frame and end_frame.
5270
+ id: "ray-3.2",
5271
+ aliases: [],
5272
+ label: "Ray 3.2",
5273
+ type: "video",
5274
+ adapter: "luma",
5275
+ providerModelId: "ray-3.2",
5276
+ lifecycle: "active",
5277
+ exposure: EXPOSURE5,
5278
+ constraints: {
5279
+ aspectRatios: ["adaptive", ...RAY_RATIOS],
5280
+ resolutions: [...RAY_RESOLUTIONS],
5281
+ durations: [5],
5282
+ supports: ["lastFrame"],
5283
+ prompt: { ...RAY_PROMPT },
5284
+ audio: "never"
5285
+ },
5286
+ defaults: { durationSeconds: 5, resolution: "720p", aspectRatio: "adaptive" },
5287
+ usage: { ...VIDEO_USAGE2 },
5288
+ pricingKind: "video_per_second",
5289
+ display: {
5290
+ provider: "luma",
5291
+ description: "Luma Ray 3.2 text- and image-to-video with optional first and last frames, 5-second clips at up to 1080p.",
5292
+ logo: LUMA_LOGO2
5293
+ },
5294
+ capabilities: {}
5295
+ },
5296
+ {
5297
+ // https://docs.agents.lumalabs.ai: type "video_edit" with a source video of
5298
+ // up to 18 s; the output keeps the source length, so there is no durations
5299
+ // knob, no aspect_ratio and no end frame. Luma bills its 5 s price for any
5300
+ // source (live, 2026-10-21) and the row is priced per source second, so a
5301
+ // source under 5 s is refused: it would be billed below Luma's charge.
5302
+ id: "ray-3.2-edit",
5303
+ aliases: [],
5304
+ label: "Ray 3.2 Edit",
5305
+ type: "video",
5306
+ adapter: "luma",
5307
+ providerModelId: "ray-3.2",
5308
+ lifecycle: "active",
5309
+ exposure: EXPOSURE5,
5310
+ constraints: {
5311
+ resolutions: [...RAY_RESOLUTIONS],
5312
+ requires: ["sourceVideo"],
5313
+ sourceVideoSeconds: { min: 5, max: 18 },
5314
+ supports: [],
5315
+ prompt: { ...RAY_PROMPT },
5316
+ audio: "never"
5317
+ },
5318
+ defaults: { resolution: "720p" },
5319
+ usage: { ...VIDEO_USAGE2 },
5320
+ pricingKind: "video_per_second",
5321
+ display: {
5322
+ provider: "luma",
5323
+ description: "Luma Ray 3.2 video edit: changes a 5-18 second clip from a prompt, keeping its length.",
5324
+ logo: LUMA_LOGO2
5325
+ },
5326
+ capabilities: { acceptsSourceVideo: true, videoEdit: true }
5327
+ },
5328
+ {
5329
+ // https://docs.agents.lumalabs.ai: type "video_reframe" with a required
5330
+ // target aspect_ratio; the source is up to 10 s and the output keeps its
5331
+ // length whatever video.duration says, while Luma bills that duration
5332
+ // (live, 2026-10-21). So there is no durations knob, and a source under
5333
+ // 5 s is refused, as on ray-3.2-edit. 1080p is not offered for the
5334
+ // vertical targets 9:16 and 3:4.
5335
+ id: "ray-3.2-reframe",
5336
+ aliases: [],
5337
+ label: "Ray 3.2 Reframe",
5338
+ type: "video",
5339
+ adapter: "luma",
5340
+ providerModelId: "ray-3.2",
5341
+ lifecycle: "active",
5342
+ exposure: EXPOSURE5,
5343
+ constraints: {
5344
+ aspectRatios: [...RAY_RATIOS],
5345
+ resolutions: [...RAY_RESOLUTIONS],
5346
+ requires: ["sourceVideo"],
5347
+ sourceVideoSeconds: { min: 5, max: 10 },
5348
+ supports: [],
5349
+ prompt: { ...RAY_PROMPT },
5350
+ audio: "never"
5351
+ },
5352
+ defaults: { resolution: "720p", aspectRatio: "16:9" },
5353
+ usage: { ...VIDEO_USAGE2 },
5354
+ pricingKind: "video_per_second",
5355
+ display: {
5356
+ provider: "luma",
5357
+ 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.",
5358
+ logo: LUMA_LOGO2
5359
+ },
5360
+ capabilities: { acceptsSourceVideo: true, videoEdit: true }
5361
+ }
5362
+ ];
5363
+
5364
+ // src/catalog/video.ts
5365
+ var VIDEO_USAGE3 = { unit: "seconds" };
5208
5366
  var SEEDANCE_RATIOS = ["21:9", "16:9", "4:3", "1:1", "3:4", "9:16", "adaptive"];
5209
5367
  var KLING_RATIOS2 = ["16:9", "9:16", "1:1", "adaptive"];
5210
5368
  var KLING_OMNI_RATIOS = ["16:9", "9:16", "1:1"];
@@ -5257,7 +5415,7 @@ var VIDEO_MODELS = [
5257
5415
  audio: "always"
5258
5416
  },
5259
5417
  defaults: { durationSeconds: 8, resolution: "720p", aspectRatio: "16:9" },
5260
- usage: { ...VIDEO_USAGE2, slug: "veo" },
5418
+ usage: { ...VIDEO_USAGE3, slug: "veo" },
5261
5419
  pricingKind: "video_per_second",
5262
5420
  display: {
5263
5421
  provider: "google",
@@ -5290,7 +5448,7 @@ var VIDEO_MODELS = [
5290
5448
  audio: "always"
5291
5449
  },
5292
5450
  defaults: { durationSeconds: 8, resolution: "720p", aspectRatio: "16:9" },
5293
- usage: { ...VIDEO_USAGE2 },
5451
+ usage: { ...VIDEO_USAGE3 },
5294
5452
  pricingKind: "video_per_second",
5295
5453
  display: {
5296
5454
  provider: "google",
@@ -5320,7 +5478,7 @@ var VIDEO_MODELS = [
5320
5478
  audio: "always"
5321
5479
  },
5322
5480
  defaults: { durationSeconds: 8, resolution: "720p", aspectRatio: "16:9" },
5323
- usage: { ...VIDEO_USAGE2 },
5481
+ usage: { ...VIDEO_USAGE3 },
5324
5482
  pricingKind: "video_per_second",
5325
5483
  display: {
5326
5484
  provider: "google",
@@ -5357,7 +5515,7 @@ var VIDEO_MODELS = [
5357
5515
  audio: "always"
5358
5516
  },
5359
5517
  defaults: {},
5360
- usage: { ...VIDEO_USAGE2, slug: "gemini_omni" },
5518
+ usage: { ...VIDEO_USAGE3, slug: "gemini_omni" },
5361
5519
  pricingKind: "video_per_second",
5362
5520
  display: {
5363
5521
  provider: "google",
@@ -5394,7 +5552,7 @@ var VIDEO_MODELS = [
5394
5552
  audio: "always"
5395
5553
  },
5396
5554
  defaults: {},
5397
- usage: { ...VIDEO_USAGE2 },
5555
+ usage: { ...VIDEO_USAGE3 },
5398
5556
  pricingKind: "video_per_second",
5399
5557
  display: {
5400
5558
  provider: "google",
@@ -5443,7 +5601,7 @@ var VIDEO_MODELS = [
5443
5601
  aspectRatio: "16:9",
5444
5602
  generateAudio: true
5445
5603
  },
5446
- usage: { ...VIDEO_USAGE2, slug: "seedance2", providerLabel: "byteplus" },
5604
+ usage: { ...VIDEO_USAGE3, slug: "seedance2", providerLabel: "byteplus" },
5447
5605
  pricingKind: "video_per_second",
5448
5606
  display: {
5449
5607
  provider: "bytedance",
@@ -5486,7 +5644,7 @@ var VIDEO_MODELS = [
5486
5644
  aspectRatio: "16:9",
5487
5645
  generateAudio: true
5488
5646
  },
5489
- usage: { ...VIDEO_USAGE2 },
5647
+ usage: { ...VIDEO_USAGE3 },
5490
5648
  pricingKind: "video_per_second",
5491
5649
  display: {
5492
5650
  provider: "bytedance",
@@ -5536,7 +5694,7 @@ var VIDEO_MODELS = [
5536
5694
  aspectRatio: "16:9",
5537
5695
  generateAudio: true
5538
5696
  },
5539
- usage: { ...VIDEO_USAGE2 },
5697
+ usage: { ...VIDEO_USAGE3 },
5540
5698
  pricingKind: "video_per_second",
5541
5699
  display: {
5542
5700
  provider: "bytedance",
@@ -5598,7 +5756,7 @@ var VIDEO_MODELS = [
5598
5756
  cfgScale: 0.5,
5599
5757
  negativePrompt: NEGATIVE_PROMPT
5600
5758
  },
5601
- usage: { ...VIDEO_USAGE2 },
5759
+ usage: { ...VIDEO_USAGE3 },
5602
5760
  pricingKind: "video_per_second",
5603
5761
  display: {
5604
5762
  provider: "kuaishou",
@@ -5630,7 +5788,7 @@ var VIDEO_MODELS = [
5630
5788
  cfgScale: 0.5,
5631
5789
  generateAudio: true
5632
5790
  },
5633
- usage: { ...VIDEO_USAGE2 },
5791
+ usage: { ...VIDEO_USAGE3 },
5634
5792
  pricingKind: "video_per_second",
5635
5793
  display: {
5636
5794
  provider: "kuaishou",
@@ -5698,7 +5856,7 @@ var VIDEO_MODELS = [
5698
5856
  aspectRatio: "16:9",
5699
5857
  promptExpansion: "balanced"
5700
5858
  },
5701
- usage: { ...VIDEO_USAGE2 },
5859
+ usage: { ...VIDEO_USAGE3 },
5702
5860
  pricingKind: "video_per_second",
5703
5861
  display: {
5704
5862
  provider: "minimax",
@@ -5732,7 +5890,7 @@ var VIDEO_MODELS = [
5732
5890
  aspectRatio: "16:9",
5733
5891
  promptExpansion: "balanced"
5734
5892
  },
5735
- usage: { ...VIDEO_USAGE2 },
5893
+ usage: { ...VIDEO_USAGE3 },
5736
5894
  pricingKind: "video_per_second",
5737
5895
  display: {
5738
5896
  provider: "minimax",
@@ -5774,7 +5932,7 @@ var VIDEO_MODELS = [
5774
5932
  aspectRatio: "adaptive",
5775
5933
  promptExpansion: "balanced"
5776
5934
  },
5777
- usage: { ...VIDEO_USAGE2 },
5935
+ usage: { ...VIDEO_USAGE3 },
5778
5936
  pricingKind: "video_per_second",
5779
5937
  display: {
5780
5938
  provider: "minimax",
@@ -5818,7 +5976,7 @@ var VIDEO_MODELS = [
5818
5976
  aspectRatio: "adaptive",
5819
5977
  promptExpansion: "balanced"
5820
5978
  },
5821
- usage: { ...VIDEO_USAGE2 },
5979
+ usage: { ...VIDEO_USAGE3 },
5822
5980
  pricingKind: "video_per_second",
5823
5981
  display: {
5824
5982
  provider: "minimax",
@@ -5863,7 +6021,7 @@ var VIDEO_MODELS = [
5863
6021
  aspectRatio: "adaptive",
5864
6022
  generateAudio: true
5865
6023
  },
5866
- usage: { ...VIDEO_USAGE2 },
6024
+ usage: { ...VIDEO_USAGE3 },
5867
6025
  pricingKind: "video_per_second",
5868
6026
  display: {
5869
6027
  provider: "alibaba",
@@ -5890,7 +6048,7 @@ var VIDEO_MODELS = [
5890
6048
  audio: "never"
5891
6049
  },
5892
6050
  defaults: { durationSeconds: 5, aspectRatio: "16:9" },
5893
- usage: { ...VIDEO_USAGE2 },
6051
+ usage: { ...VIDEO_USAGE3 },
5894
6052
  pricingKind: "video_per_second",
5895
6053
  display: {
5896
6054
  provider: "kuaishou",
@@ -5939,7 +6097,7 @@ var VIDEO_MODELS = [
5939
6097
  audio: "never"
5940
6098
  },
5941
6099
  defaults: {},
5942
- usage: { ...VIDEO_USAGE2 },
6100
+ usage: { ...VIDEO_USAGE3 },
5943
6101
  // Higgsfield bills DoP per generation: a fixed-length clip with no duration knob.
5944
6102
  pricingKind: "flat",
5945
6103
  display: {
@@ -5948,7 +6106,8 @@ var VIDEO_MODELS = [
5948
6106
  },
5949
6107
  capabilities: {}
5950
6108
  },
5951
- ...VIDEO_FAMILY_MODELS
6109
+ ...VIDEO_FAMILY_MODELS,
6110
+ ...LUMA_VIDEO_MODELS
5952
6111
  ];
5953
6112
 
5954
6113
  // src/catalog/video-tools.ts
@@ -6922,7 +7081,8 @@ var ADAPTER_IDS = [
6922
7081
  "replicate",
6923
7082
  "prodia",
6924
7083
  "higgsfield",
6925
- "xai-video"
7084
+ "xai-video",
7085
+ "luma"
6926
7086
  ];
6927
7087
  var ModelTypeSchema = import_zod3.z.enum([
6928
7088
  "text",
@@ -11052,6 +11212,30 @@ var klingV3Family = {
11052
11212
  }
11053
11213
  };
11054
11214
 
11215
+ // src/replicate-video-kinds.ts
11216
+ function replicateBaseModelId(providerModelId) {
11217
+ const colon = providerModelId.indexOf(":");
11218
+ return colon === -1 ? providerModelId : providerModelId.slice(0, colon);
11219
+ }
11220
+ var REPLICATE_VIDEO_KINDS = /* @__PURE__ */ new Map([
11221
+ ["kling-video/v2.5-turbo/pro/image-to-video", "kling"],
11222
+ ["kling-video/v3.0/pro/image-to-video", "kling"],
11223
+ ["kwaivgi/kling-v3-video", "kling"],
11224
+ ["kwaivgi/kling-v3-omni-video", "kling-omni"],
11225
+ ["minimax/hailuo-02", "hailuo"]
11226
+ ]);
11227
+ function videoKind(providerModelId) {
11228
+ return REPLICATE_VIDEO_KINDS.get(replicateBaseModelId(providerModelId));
11229
+ }
11230
+ var KLING_TIERS = [
11231
+ { mode: "standard", resolution: "720p" },
11232
+ { mode: "pro", resolution: "1080p" },
11233
+ { mode: "4k", resolution: "4k" }
11234
+ ];
11235
+ function klingTier(req) {
11236
+ return KLING_TIERS.find((tier) => tier.resolution === req.resolution) ?? KLING_TIERS.find((tier) => tier.mode === req.mode) ?? KLING_TIERS[0];
11237
+ }
11238
+
11055
11239
  // src/resolve-video-rules.ts
11056
11240
  var MOTION_PRO2 = "fal-ai/kling-video/v3/pro/motion-control";
11057
11241
  var MOTION_STANDARD2 = "fal-ai/kling-video/v3/standard/motion-control";
@@ -11225,6 +11409,16 @@ function h3EndFrameRatioWarning(model, ratio) {
11225
11409
  message: `${model.id} follows the end frame's aspect ratio`
11226
11410
  };
11227
11411
  }
11412
+ function isReplicateKling(model) {
11413
+ if (model.adapter !== "replicate") return false;
11414
+ const kind = videoKind(model.providerModelId);
11415
+ return kind === "kling" || kind === "kling-omni";
11416
+ }
11417
+ function fillReplicateKlingResolution(model, req) {
11418
+ if (isReplicateKling(model) && req.resolution === void 0) {
11419
+ req.resolution = klingTier(req).resolution;
11420
+ }
11421
+ }
11228
11422
  function applyFalVideoRules(model, req, callerRatio, warnings) {
11229
11423
  if (model.adapter !== "fal") return;
11230
11424
  const problem = falVideoProblem(model.providerModelId, withPromptForResolve(req));
@@ -13551,9 +13745,608 @@ var higgsfieldAdapter = {
13551
13745
  }
13552
13746
  };
13553
13747
 
13748
+ // src/adapters/luma.ts
13749
+ var import_zod25 = require("zod");
13750
+
13751
+ // src/adapters/mp4-duration.ts
13752
+ function mp4DurationSeconds(bytes) {
13753
+ const moov = findBox(bytes, 0, bytes.length, "moov");
13754
+ if (!moov) return void 0;
13755
+ const mvhd = findBox(bytes, moov.start + 8, moov.end, "mvhd");
13756
+ if (!mvhd) return void 0;
13757
+ const view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);
13758
+ const size = mvhd.end - mvhd.start;
13759
+ if (size < 8 + 4) return void 0;
13760
+ const version = view.getUint8(mvhd.start + 8);
13761
+ if (version === 0) {
13762
+ if (size < 8 + 4 + 4 + 4 + 4 + 4) return void 0;
13763
+ const timescale = view.getUint32(mvhd.start + 8 + 4 + 4 + 4);
13764
+ const duration = view.getUint32(mvhd.start + 8 + 4 + 4 + 4 + 4);
13765
+ if (timescale === 0) return void 0;
13766
+ return duration / timescale;
13767
+ }
13768
+ if (version === 1) {
13769
+ if (size < 8 + 4 + 8 + 8 + 4 + 8) return void 0;
13770
+ const timescale = view.getUint32(mvhd.start + 8 + 4 + 8 + 8);
13771
+ const high = view.getUint32(mvhd.start + 8 + 4 + 8 + 8 + 4);
13772
+ const low = view.getUint32(mvhd.start + 8 + 4 + 8 + 8 + 4 + 4);
13773
+ if (timescale === 0) return void 0;
13774
+ return (high * 2 ** 32 + low) / timescale;
13775
+ }
13776
+ return void 0;
13777
+ }
13778
+ function findBox(bytes, from, to, type) {
13779
+ const view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);
13780
+ let offset = from;
13781
+ while (offset + 8 <= to) {
13782
+ const size = view.getUint32(offset);
13783
+ const name = String.fromCharCode(
13784
+ view.getUint8(offset + 4),
13785
+ view.getUint8(offset + 5),
13786
+ view.getUint8(offset + 6),
13787
+ view.getUint8(offset + 7)
13788
+ );
13789
+ if (size < 8 || offset + size > to) return void 0;
13790
+ if (name === type) return { start: offset, end: offset + size };
13791
+ offset += size;
13792
+ }
13793
+ return void 0;
13794
+ }
13795
+
13796
+ // src/adapters/luma.ts
13797
+ var DEFAULT_BASE_URL = "https://agents.lumalabs.ai";
13798
+ var IMAGE_POLL_INTERVAL_MS = 3e3;
13799
+ var IMAGE_TIMEOUT_MS = 5 * 6e4;
13800
+ var VIDEO_POLL_INTERVAL_MS = 5e3;
13801
+ var VIDEO_TIMEOUT_MS = 20 * 6e4;
13802
+ var PROMPT_MAX_CHARS = 6e3;
13803
+ var LUMA_IMAGE_MODEL_IDS = /* @__PURE__ */ new Set(["uni-1", "uni-1-max"]);
13804
+ var LUMA_VIDEO_MODEL_ID = "ray-3.2";
13805
+ var LUMA_VIDEO_MODES = /* @__PURE__ */ new Map([
13806
+ ["ray-3.2", "generate"],
13807
+ ["ray-3.2-edit", "edit"],
13808
+ ["ray-3.2-reframe", "reframe"]
13809
+ ]);
13810
+ function lumaRowRefusal(row2) {
13811
+ const provider = row2.providerModelId.toLowerCase();
13812
+ if (row2.type === "image") {
13813
+ return LUMA_IMAGE_MODEL_IDS.has(provider) ? void 0 : "luma image rows need providerModelId uni-1 or uni-1-max";
13814
+ }
13815
+ if (row2.type === "video") {
13816
+ if (provider !== LUMA_VIDEO_MODEL_ID) return "luma video rows need providerModelId ray-3.2";
13817
+ 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;
13818
+ }
13819
+ return `luma has no ${row2.type} rows`;
13820
+ }
13821
+ function lumaBase(ctx) {
13822
+ return ctx.endpoints?.luma ?? DEFAULT_BASE_URL;
13823
+ }
13824
+ function lumaKey(ctx) {
13825
+ const key = ctx.keys.luma;
13826
+ if (!key) {
13827
+ throw new ModelKitError({
13828
+ code: "auth",
13829
+ message: "Missing API key for luma",
13830
+ provider: "luma",
13831
+ model: ctx.model.id,
13832
+ retryable: false
13833
+ });
13834
+ }
13835
+ return key;
13836
+ }
13837
+ function checkPrompt(req, ctx) {
13838
+ const prompt = req.prompt.trim();
13839
+ if (prompt.length === 0) {
13840
+ throw new ModelKitError({
13841
+ code: "missing_input",
13842
+ message: `${ctx.model.id} needs a prompt`,
13843
+ provider: "luma",
13844
+ model: ctx.model.id,
13845
+ retryable: false
13846
+ });
13847
+ }
13848
+ if (req.prompt.length > PROMPT_MAX_CHARS) {
13849
+ throw new ModelKitError({
13850
+ code: "unsupported_option",
13851
+ message: `${ctx.model.id} takes a prompt of at most ${PROMPT_MAX_CHARS} characters`,
13852
+ provider: "luma",
13853
+ model: ctx.model.id,
13854
+ retryable: false
13855
+ });
13856
+ }
13857
+ return req.prompt;
13858
+ }
13859
+ function imageRef(ref) {
13860
+ if ("url" in ref) return { url: ref.url };
13861
+ return { data: mediaRefToBase64(ref.bytes), media_type: ref.mime };
13862
+ }
13863
+ function videoRef(ref) {
13864
+ if ("url" in ref) return { url: ref.url, media_type: "video/mp4" };
13865
+ const mediaType = ref.mime.startsWith("video/") ? ref.mime : "video/mp4";
13866
+ return { data: mediaRefToBase64(ref.bytes), media_type: mediaType };
13867
+ }
13868
+ function lumaOutputFormat(req, ctx) {
13869
+ const format = req.outputFormat;
13870
+ if (format === void 0 || format === "png" || format === "jpeg") return format;
13871
+ if ((ctx.call.onUnsupported ?? "error") === "error") {
13872
+ throw new ModelKitError({
13873
+ code: "unsupported_option",
13874
+ message: `${ctx.model.id} has no outputFormat "${format}". Supported: png, jpeg.`,
13875
+ provider: "luma",
13876
+ model: ctx.model.id,
13877
+ retryable: false
13878
+ });
13879
+ }
13880
+ ctx.warnings.push({
13881
+ code: "option_ignored",
13882
+ field: "outputFormat",
13883
+ from: format,
13884
+ to: "png",
13885
+ message: `${ctx.model.id} has no outputFormat "${format}"; using png`
13886
+ });
13887
+ return "png";
13888
+ }
13889
+ function refuseOrDrop(ctx, field, message) {
13890
+ if ((ctx.call.onUnsupported ?? "error") === "error") {
13891
+ throw new ModelKitError({
13892
+ code: "unsupported_option",
13893
+ message,
13894
+ provider: "luma",
13895
+ model: ctx.model.id,
13896
+ retryable: false
13897
+ });
13898
+ }
13899
+ ctx.warnings.push({ code: "option_ignored", field, message });
13900
+ }
13901
+ function lumaVideoMode(ctx) {
13902
+ const mode = LUMA_VIDEO_MODES.get(ctx.model.id);
13903
+ if (mode === void 0) {
13904
+ throw new ModelKitError({
13905
+ code: "unsupported_option",
13906
+ message: `luma routes ray-3.2 rows by catalog id; "${ctx.model.id}" is not a luma video row`,
13907
+ provider: "luma",
13908
+ model: ctx.model.id,
13909
+ retryable: false
13910
+ });
13911
+ }
13912
+ return mode;
13913
+ }
13914
+ function reframeTooTall(req) {
13915
+ const vertical = req.aspectRatio === "9:16" || req.aspectRatio === "3:4";
13916
+ return vertical && req.resolution === "1080p";
13917
+ }
13918
+ function sentResolution(req, mode) {
13919
+ return mode === "reframe" && reframeTooTall(req) ? "720p" : req.resolution;
13920
+ }
13921
+ function lumaReframeResolution(req, ctx) {
13922
+ const requested = req.resolution;
13923
+ if (requested === void 0 || !reframeTooTall(req)) return requested;
13924
+ const message = `${ctx.model.id} renders 9:16 and 3:4 reframes at up to 720p`;
13925
+ if ((ctx.call.onUnsupported ?? "error") === "error") {
13926
+ throw new ModelKitError({
13927
+ code: "unsupported_option",
13928
+ message,
13929
+ provider: "luma",
13930
+ model: ctx.model.id,
13931
+ retryable: false
13932
+ });
13933
+ }
13934
+ ctx.warnings.push({
13935
+ code: "resolution_coerced",
13936
+ field: "resolution",
13937
+ from: requested,
13938
+ to: "720p",
13939
+ message
13940
+ });
13941
+ return "720p";
13942
+ }
13943
+ var lumaGenerationSchema = import_zod25.z.looseObject({
13944
+ id: import_zod25.z.string(),
13945
+ state: import_zod25.z.string().optional(),
13946
+ output: import_zod25.z.array(
13947
+ import_zod25.z.looseObject({
13948
+ type: import_zod25.z.string().optional(),
13949
+ url: import_zod25.z.string().optional()
13950
+ })
13951
+ ).optional(),
13952
+ failure_reason: import_zod25.z.string().optional().nullable(),
13953
+ failure_code: import_zod25.z.string().optional().nullable()
13954
+ });
13955
+ function lumaFailure(ctx, code, reason) {
13956
+ const detail = reason ?? code ?? "unknown_error";
13957
+ const message = `Luma generation failed: ${detail}`;
13958
+ switch (code) {
13959
+ case "content_moderated":
13960
+ return new ModelKitError({
13961
+ code: "content_blocked",
13962
+ message,
13963
+ provider: "luma",
13964
+ model: ctx.model.id,
13965
+ retryable: false
13966
+ });
13967
+ case "rate_limited":
13968
+ return new ModelKitError({
13969
+ code: "rate_limited",
13970
+ message,
13971
+ provider: "luma",
13972
+ model: ctx.model.id
13973
+ });
13974
+ case "generation_failed":
13975
+ case "output_not_found":
13976
+ return new ModelKitError({
13977
+ code: "provider_rejected",
13978
+ message,
13979
+ provider: "luma",
13980
+ model: ctx.model.id,
13981
+ retryable: true
13982
+ });
13983
+ case "budget_exhausted":
13984
+ return new ModelKitError({
13985
+ code: "provider_billing",
13986
+ message,
13987
+ provider: "luma",
13988
+ model: ctx.model.id,
13989
+ retryable: false
13990
+ });
13991
+ case "image_too_large":
13992
+ case "unsupported_format":
13993
+ return new ModelKitError({
13994
+ code: "unsupported_option",
13995
+ message,
13996
+ provider: "luma",
13997
+ model: ctx.model.id,
13998
+ retryable: false
13999
+ });
14000
+ default:
14001
+ return new ModelKitError({
14002
+ code: "provider_rejected",
14003
+ message,
14004
+ provider: "luma",
14005
+ model: ctx.model.id,
14006
+ retryable: false
14007
+ });
14008
+ }
14009
+ }
14010
+ function requestInit2(ctx, key, body) {
14011
+ const init = {
14012
+ method: body !== void 0 ? "POST" : "GET",
14013
+ headers: { Authorization: `Bearer ${key}`, "Content-Type": "application/json" }
14014
+ };
14015
+ if (body !== void 0) init.body = body;
14016
+ if (ctx.signal !== void 0) init.signal = ctx.signal;
14017
+ return init;
14018
+ }
14019
+ async function lumaRequest(ctx, url, body) {
14020
+ const key = lumaKey(ctx);
14021
+ let res;
14022
+ try {
14023
+ res = await ctx.fetch(url, requestInit2(ctx, key, body));
14024
+ } catch (cause) {
14025
+ throw classifyThrown("luma", cause, ctx.model.id);
14026
+ }
14027
+ if (!res.ok) {
14028
+ throw classifyHttpError(res.status, await readBody(res), "luma", ctx.model.id);
14029
+ }
14030
+ return res;
14031
+ }
14032
+ async function submitGeneration(ctx, body) {
14033
+ const retryOptions = mediaRetryOptions(ctx.call, ctx.signal);
14034
+ const res = await withRetry(
14035
+ () => lumaRequest(ctx, `${lumaBase(ctx)}/v1/generations`, JSON.stringify(body)),
14036
+ retryOptions
14037
+ );
14038
+ const parsed = lumaGenerationSchema.safeParse(await res.json());
14039
+ if (!parsed.success) {
14040
+ throw new ModelKitError({
14041
+ code: "provider_rejected",
14042
+ message: "Luma submit returned no generation id",
14043
+ provider: "luma",
14044
+ model: ctx.model.id,
14045
+ retryable: false
14046
+ });
14047
+ }
14048
+ return parsed.data;
14049
+ }
14050
+ async function readGeneration(ctx, id) {
14051
+ const res = await lumaRequest(ctx, `${lumaBase(ctx)}/v1/generations/${id}`);
14052
+ const payload = JsonValueSchema.parse(await res.json());
14053
+ const parsed = lumaGenerationSchema.safeParse(payload);
14054
+ if (!parsed.success) {
14055
+ throw new ModelKitError({
14056
+ code: "provider_rejected",
14057
+ message: "Luma returned an unreadable generation",
14058
+ provider: "luma",
14059
+ model: ctx.model.id,
14060
+ retryable: false
14061
+ });
14062
+ }
14063
+ return parsed.data;
14064
+ }
14065
+ function outputUrl(generation, type) {
14066
+ const outputs = generation.output ?? [];
14067
+ const match = outputs.find((item) => item.type === type && item.url);
14068
+ if (match?.url) return match.url;
14069
+ const fallback = outputs.find((item) => item.url);
14070
+ return fallback?.url;
14071
+ }
14072
+ async function readGenerationOnce(ctx, id) {
14073
+ try {
14074
+ return await readGeneration(ctx, id);
14075
+ } catch (cause) {
14076
+ if (ctx.call.maxRetries === 0) throw cause;
14077
+ if (isModelKitError(cause) && (cause.code === "network" || cause.status === 429 || (cause.status ?? 0) >= 500)) {
14078
+ return void 0;
14079
+ }
14080
+ throw cause;
14081
+ }
14082
+ }
14083
+ async function checkGeneration(ctx, id, type) {
14084
+ const generation = await readGenerationOnce(ctx, id);
14085
+ if (generation === void 0) return void 0;
14086
+ if (generation.state === "completed") {
14087
+ const url = outputUrl(generation, type);
14088
+ if (!url) {
14089
+ throw new ModelKitError({
14090
+ code: "provider_rejected",
14091
+ message: "Luma completed without an output URL",
14092
+ provider: "luma",
14093
+ model: ctx.model.id,
14094
+ retryable: false
14095
+ });
14096
+ }
14097
+ return url;
14098
+ }
14099
+ if (generation.state === "failed") {
14100
+ throw lumaFailure(
14101
+ ctx,
14102
+ generation.failure_code ?? void 0,
14103
+ generation.failure_reason ?? void 0
14104
+ );
14105
+ }
14106
+ return void 0;
14107
+ }
14108
+ async function downloadBytes(ctx, url, fallback) {
14109
+ let bytes;
14110
+ let header;
14111
+ try {
14112
+ const out = await mediaRefToBytes({ url }, ctx.fetch, {
14113
+ ...ctx.signal !== void 0 && { signal: ctx.signal },
14114
+ provider: "luma",
14115
+ model: ctx.model.id
14116
+ });
14117
+ bytes = out.bytes;
14118
+ header = out.mime;
14119
+ } catch (cause) {
14120
+ throw classifyThrown("luma", cause, ctx.model.id);
14121
+ }
14122
+ const kind = fallback.split("/")[0];
14123
+ const usable = header !== void 0 && header.startsWith(`${kind}/`);
14124
+ return { bytes, mime: usable ? header : fallback };
14125
+ }
14126
+ function imageUsage2(ctx) {
14127
+ return {
14128
+ model: ctx.model.id,
14129
+ provider: "luma",
14130
+ modality: "IMAGE",
14131
+ unit: "images",
14132
+ quantity: 1,
14133
+ imageSize: "2K"
14134
+ };
14135
+ }
14136
+ var GENERATE_SECONDS = 5;
14137
+ var MAX_PLAUSIBLE_SECONDS = 60;
14138
+ function billedSeconds(req, mode, bytes) {
14139
+ if (mode === "generate") return GENERATE_SECONDS;
14140
+ const measured = mp4DurationSeconds(bytes);
14141
+ const source2 = req.sourceVideo;
14142
+ const length = measured !== void 0 && measured > 0 && measured <= MAX_PLAUSIBLE_SECONDS ? measured : source2 !== void 0 && "url" in source2 ? source2.durationSeconds : void 0;
14143
+ return Math.max(length ?? 0, GENERATE_SECONDS);
14144
+ }
14145
+ function videoUsage4(ctx, req, mode, bytes) {
14146
+ const seconds3 = billedSeconds(req, mode, bytes);
14147
+ const event = {
14148
+ model: ctx.model.id,
14149
+ provider: "luma",
14150
+ modality: "VIDEO",
14151
+ unit: "seconds",
14152
+ quantity: seconds3,
14153
+ durationSeconds: seconds3
14154
+ };
14155
+ const resolution = sentResolution(req, mode);
14156
+ if (resolution !== void 0) event.resolution = resolution;
14157
+ return event;
14158
+ }
14159
+ function imageBody(req, ctx, outputFormat) {
14160
+ const prompt = checkPrompt(req, ctx);
14161
+ const model = ctx.model.providerModelId;
14162
+ const ratio = req.aspectRatio !== "adaptive" ? req.aspectRatio : void 0;
14163
+ const [source2, ...rest] = req.references ?? [];
14164
+ if (source2 === void 0) {
14165
+ const body2 = { model, type: "image", prompt };
14166
+ if (ratio !== void 0) body2["aspect_ratio"] = ratio;
14167
+ if (outputFormat !== void 0) body2["output_format"] = outputFormat;
14168
+ return body2;
14169
+ }
14170
+ if (ratio !== void 0) {
14171
+ ctx.warnings.push({
14172
+ code: "option_ignored",
14173
+ field: "aspectRatio",
14174
+ message: `${ctx.model.id} keeps the source image's aspect ratio on an edit`
14175
+ });
14176
+ }
14177
+ const body = {
14178
+ model,
14179
+ type: "image_edit",
14180
+ prompt,
14181
+ source: imageRef(source2)
14182
+ };
14183
+ if (rest.length > 0) body["image_ref"] = rest.map(imageRef);
14184
+ if (outputFormat !== void 0) body["output_format"] = outputFormat;
14185
+ return body;
14186
+ }
14187
+ function videoBody(req, ctx, mode) {
14188
+ const prompt = checkPrompt(req, ctx);
14189
+ const model = ctx.model.providerModelId;
14190
+ const hasReferences = (req.referenceImages ?? []).length + (req.referenceVideos ?? []).length + (req.referenceAudios ?? []).length > 0;
14191
+ if (hasReferences) {
14192
+ refuseOrDrop(
14193
+ ctx,
14194
+ "referenceImages",
14195
+ `${ctx.model.id} takes no reference images, videos or audio`
14196
+ );
14197
+ }
14198
+ if (mode === "generate") {
14199
+ const body = {
14200
+ model,
14201
+ type: "video",
14202
+ prompt,
14203
+ video: {
14204
+ ...req.resolution !== void 0 && { resolution: req.resolution },
14205
+ duration: "5s",
14206
+ ...req.firstFrame !== void 0 && { start_frame: imageRef(req.firstFrame) },
14207
+ ...req.lastFrame !== void 0 && { end_frame: imageRef(req.lastFrame) }
14208
+ }
14209
+ };
14210
+ if (req.aspectRatio !== void 0 && req.aspectRatio !== "adaptive") {
14211
+ body["aspect_ratio"] = req.aspectRatio;
14212
+ }
14213
+ return body;
14214
+ }
14215
+ const sourceVideo = req.sourceVideo;
14216
+ if (!sourceVideo) {
14217
+ throw new ModelKitError({
14218
+ code: "missing_input",
14219
+ message: `${ctx.model.id} requires sourceVideo`,
14220
+ provider: "luma",
14221
+ model: ctx.model.id,
14222
+ retryable: false
14223
+ });
14224
+ }
14225
+ if (mode === "edit") {
14226
+ if (req.lastFrame !== void 0) {
14227
+ refuseOrDrop(ctx, "lastFrame", `${ctx.model.id} takes no end frame`);
14228
+ }
14229
+ const body = {
14230
+ model,
14231
+ type: "video_edit",
14232
+ prompt,
14233
+ source: videoRef(sourceVideo),
14234
+ video: {
14235
+ ...req.resolution !== void 0 && { resolution: req.resolution },
14236
+ ...req.firstFrame !== void 0 && { start_frame: imageRef(req.firstFrame) },
14237
+ edit: { auto_controls: true }
14238
+ }
14239
+ };
14240
+ return body;
14241
+ }
14242
+ if (req.firstFrame !== void 0) {
14243
+ refuseOrDrop(ctx, "firstFrame", `${ctx.model.id} takes no start frame`);
14244
+ }
14245
+ if (req.lastFrame !== void 0) {
14246
+ refuseOrDrop(ctx, "lastFrame", `${ctx.model.id} takes no end frame`);
14247
+ }
14248
+ const aspectRatio = req.aspectRatio;
14249
+ if (aspectRatio === void 0 || aspectRatio === "adaptive") {
14250
+ throw new ModelKitError({
14251
+ code: "missing_input",
14252
+ message: `${ctx.model.id} needs an aspectRatio target`,
14253
+ provider: "luma",
14254
+ model: ctx.model.id,
14255
+ retryable: false
14256
+ });
14257
+ }
14258
+ const resolution = lumaReframeResolution(req, ctx);
14259
+ return {
14260
+ model,
14261
+ type: "video_reframe",
14262
+ prompt,
14263
+ aspect_ratio: aspectRatio,
14264
+ source: videoRef(sourceVideo),
14265
+ video: { ...resolution !== void 0 && { resolution } }
14266
+ };
14267
+ }
14268
+ async function generateLumaImage(req, ctx) {
14269
+ throwIfAborted(ctx.signal);
14270
+ lumaKey(ctx);
14271
+ if (!LUMA_IMAGE_MODEL_IDS.has(ctx.model.providerModelId.toLowerCase())) {
14272
+ throw new ModelKitError({
14273
+ code: "unsupported_option",
14274
+ message: `luma has no image mapping for "${ctx.model.providerModelId}"`,
14275
+ provider: "luma",
14276
+ model: ctx.model.id,
14277
+ retryable: false
14278
+ });
14279
+ }
14280
+ const format = lumaOutputFormat(req, ctx);
14281
+ const created = await submitGeneration(ctx, imageBody(req, ctx, format));
14282
+ const url = await pollUntil(checkGeneration.bind(null, ctx, created.id, "image"), {
14283
+ intervalMs: IMAGE_POLL_INTERVAL_MS,
14284
+ timeoutMs: IMAGE_TIMEOUT_MS,
14285
+ ...ctx.signal !== void 0 && { signal: ctx.signal }
14286
+ });
14287
+ const image = await downloadBytes(ctx, url, format === "jpeg" ? "image/jpeg" : "image/png");
14288
+ return {
14289
+ images: [image],
14290
+ usage: imageUsage2(ctx),
14291
+ raw: created
14292
+ };
14293
+ }
14294
+ function lumaVideoJob(ctx, req, mode, id) {
14295
+ return {
14296
+ jobId: id,
14297
+ async poll() {
14298
+ const generation = await readGenerationOnce(ctx, id);
14299
+ if (generation?.state === "failed") return { status: "failed" };
14300
+ if (generation?.state === "completed") return { status: "succeeded" };
14301
+ return { status: "pending" };
14302
+ },
14303
+ async result(opts) {
14304
+ const signal = opts?.signal ?? ctx.signal;
14305
+ const url = await pollUntil(checkGeneration.bind(null, ctx, id, "video"), {
14306
+ intervalMs: opts?.pollIntervalMs ?? VIDEO_POLL_INTERVAL_MS,
14307
+ timeoutMs: opts?.timeoutMs ?? VIDEO_TIMEOUT_MS,
14308
+ ...signal !== void 0 && { signal }
14309
+ });
14310
+ const video = await downloadBytes(ctx, url, "video/mp4");
14311
+ const result = {
14312
+ video,
14313
+ usage: videoUsage4(ctx, req, mode, video.bytes),
14314
+ raw: { id }
14315
+ };
14316
+ return result;
14317
+ }
14318
+ };
14319
+ }
14320
+ async function generateLumaVideo(req, ctx) {
14321
+ throwIfAborted(ctx.signal);
14322
+ lumaKey(ctx);
14323
+ if (ctx.model.providerModelId.toLowerCase() !== LUMA_VIDEO_MODEL_ID) {
14324
+ throw new ModelKitError({
14325
+ code: "unsupported_option",
14326
+ message: `luma has no video mapping for "${ctx.model.providerModelId}"`,
14327
+ provider: "luma",
14328
+ model: ctx.model.id,
14329
+ retryable: false
14330
+ });
14331
+ }
14332
+ const mode = lumaVideoMode(ctx);
14333
+ const created = await submitGeneration(ctx, videoBody(req, ctx, mode));
14334
+ return lumaVideoJob(ctx, req, mode, created.id);
14335
+ }
14336
+ var lumaAdapter = {
14337
+ id: "luma",
14338
+ generateImage: generateLumaImage,
14339
+ generateVideo: generateLumaVideo,
14340
+ resumeVideo: (req, jobId, ctx) => {
14341
+ throwIfAborted(ctx.signal);
14342
+ lumaKey(ctx);
14343
+ return lumaVideoJob(ctx, req, lumaVideoMode(ctx), jobId);
14344
+ }
14345
+ };
14346
+
13554
14347
  // src/adapters/openai-image.ts
13555
14348
  var import_openai2 = __toESM(require("openai"), 1);
13556
- var import_zod25 = require("zod");
14349
+ var import_zod26 = require("zod");
13557
14350
  var MAX_EDIT_REFERENCES = 16;
13558
14351
  function aspectRatioToSize(ratio, tier) {
13559
14352
  const TIER_LONG_EDGE = {
@@ -13636,9 +14429,9 @@ function toProviderError2(cause, ctx) {
13636
14429
  ctx.model.id
13637
14430
  );
13638
14431
  }
13639
- var openaiUsageSchema = import_zod25.z.object({
13640
- input_tokens: import_zod25.z.number().optional(),
13641
- output_tokens: import_zod25.z.number().optional()
14432
+ var openaiUsageSchema = import_zod26.z.object({
14433
+ input_tokens: import_zod26.z.number().optional(),
14434
+ output_tokens: import_zod26.z.number().optional()
13642
14435
  });
13643
14436
  var openaiImageAdapter = {
13644
14437
  id: "openai-image",
@@ -14050,13 +14843,13 @@ var prodiaAdapter = {
14050
14843
  var import_replicate = __toESM(require("replicate"), 1);
14051
14844
 
14052
14845
  // src/adapters/replicate-http.ts
14053
- var import_zod26 = require("zod");
14054
- var predictionSchema = import_zod26.z.object({
14055
- id: import_zod26.z.string(),
14056
- status: import_zod26.z.string(),
14846
+ var import_zod27 = require("zod");
14847
+ var predictionSchema = import_zod27.z.object({
14848
+ id: import_zod27.z.string(),
14849
+ status: import_zod27.z.string(),
14057
14850
  output: JsonValueSchema.optional(),
14058
14851
  error: JsonValueSchema.optional(),
14059
- metrics: import_zod26.z.object({ predict_time: import_zod26.z.number().optional() }).optional()
14852
+ metrics: import_zod27.z.object({ predict_time: import_zod27.z.number().optional() }).optional()
14060
14853
  });
14061
14854
  function createReplicateHttpClient(opts) {
14062
14855
  async function request(path, body, signal) {
@@ -14107,7 +14900,7 @@ function createReplicateHttpClient(opts) {
14107
14900
  }
14108
14901
 
14109
14902
  // src/adapters/replicate.ts
14110
- var import_zod27 = require("zod");
14903
+ var import_zod28 = require("zod");
14111
14904
  var POLL_INTERVAL_MS = 1e3;
14112
14905
  var POLL_TIMEOUT_MS = 15 * 6e4;
14113
14906
  var defaultReplicateDeps = {
@@ -14147,8 +14940,8 @@ function route(providerModelId) {
14147
14940
  if (!providerModelId.includes("/")) return { version: providerModelId };
14148
14941
  return { model: providerModelId };
14149
14942
  }
14150
- var replicateResponseStatusSchema = import_zod27.z.object({
14151
- response: import_zod27.z.object({ status: import_zod27.z.number() }).partial()
14943
+ var replicateResponseStatusSchema = import_zod28.z.object({
14944
+ response: import_zod28.z.object({ status: import_zod28.z.number() }).partial()
14152
14945
  });
14153
14946
  function replicateError(cause, ctx) {
14154
14947
  if (isModelKitError(cause)) return cause;
@@ -14157,10 +14950,6 @@ function replicateError(cause, ctx) {
14157
14950
  const status = info.status ?? (nested.success ? nested.data.response?.status : void 0);
14158
14951
  return classifyThrown("replicate", cause, ctx.model.id, { status, body: info.body });
14159
14952
  }
14160
- function baseModelId(providerModelId) {
14161
- const colon = providerModelId.indexOf(":");
14162
- return colon === -1 ? providerModelId : providerModelId.slice(0, colon);
14163
- }
14164
14953
  function predictionErrorBody(cause) {
14165
14954
  const parsed = JsonValueSchema.safeParse(cause);
14166
14955
  if (parsed.success) return parsed.data;
@@ -14175,7 +14964,7 @@ function isUrlFn(value) {
14175
14964
  function extractOutputUrl(output) {
14176
14965
  const root = Array.isArray(output) ? output[0] : output;
14177
14966
  if (root === void 0) throw noOutput();
14178
- const asString = import_zod27.z.string().safeParse(root);
14967
+ const asString = import_zod28.z.string().safeParse(root);
14179
14968
  if (asString.success && asString.data) return asString.data;
14180
14969
  if (root instanceof URL) return root.href;
14181
14970
  if (isObjectOutput(root)) {
@@ -14184,15 +14973,15 @@ function extractOutputUrl(output) {
14184
14973
  if (urlValue !== void 0 && isUrlFn(urlValue)) {
14185
14974
  const called = urlValue();
14186
14975
  if (called instanceof URL) return called.href;
14187
- const calledString = import_zod27.z.string().safeParse(called);
14976
+ const calledString = import_zod28.z.string().safeParse(called);
14188
14977
  if (calledString.success && calledString.data) return calledString.data;
14189
14978
  } else {
14190
- const direct = import_zod27.z.string().safeParse(urlValue);
14979
+ const direct = import_zod28.z.string().safeParse(urlValue);
14191
14980
  if (direct.success && direct.data) return direct.data;
14192
14981
  }
14193
14982
  }
14194
14983
  if ("default" in root) {
14195
- const fallback = import_zod27.z.string().safeParse(root.default);
14984
+ const fallback = import_zod28.z.string().safeParse(root.default);
14196
14985
  if (fallback.success && fallback.data) return fallback.data;
14197
14986
  }
14198
14987
  }
@@ -14253,7 +15042,7 @@ async function runPrediction(ctx, input, deps) {
14253
15042
  }
14254
15043
  return void 0;
14255
15044
  }, pollOpts);
14256
- const predictTime = import_zod27.z.number().safeParse(done.metrics?.predict_time);
15045
+ const predictTime = import_zod28.z.number().safeParse(done.metrics?.predict_time);
14257
15046
  if (done.output === void 0) throw noOutput();
14258
15047
  return {
14259
15048
  url: extractOutputUrl(done.output),
@@ -14261,7 +15050,7 @@ async function runPrediction(ctx, input, deps) {
14261
15050
  raw: done
14262
15051
  };
14263
15052
  }
14264
- function imageUsage2(ctx, req) {
15053
+ function imageUsage3(ctx, req) {
14265
15054
  const event = {
14266
15055
  model: ctx.model.id,
14267
15056
  provider: "replicate",
@@ -14273,7 +15062,7 @@ function imageUsage2(ctx, req) {
14273
15062
  if (req.quality !== void 0) event.imageQuality = req.quality;
14274
15063
  return event;
14275
15064
  }
14276
- function videoUsage4(ctx, req) {
15065
+ function videoUsage5(ctx, req) {
14277
15066
  const event = {
14278
15067
  model: ctx.model.id,
14279
15068
  provider: "replicate",
@@ -14314,17 +15103,7 @@ var REPLICATE_IMAGE_KINDS = {
14314
15103
  "qwen-edit-apps/qwen-image-edit-plus-lora-skin": "qwen-edit"
14315
15104
  };
14316
15105
  function imageKind(providerModelId) {
14317
- return REPLICATE_IMAGE_KINDS[baseModelId(providerModelId)] ?? "generic";
14318
- }
14319
- var REPLICATE_VIDEO_KINDS = {
14320
- "kling-video/v2.5-turbo/pro/image-to-video": "kling",
14321
- "kling-video/v3.0/pro/image-to-video": "kling",
14322
- "kwaivgi/kling-v3-video": "kling",
14323
- "kwaivgi/kling-v3-omni-video": "kling-omni",
14324
- "minimax/hailuo-02": "hailuo"
14325
- };
14326
- function videoKind(providerModelId) {
14327
- return REPLICATE_VIDEO_KINDS[baseModelId(providerModelId)];
15106
+ return REPLICATE_IMAGE_KINDS[replicateBaseModelId(providerModelId)] ?? "generic";
14328
15107
  }
14329
15108
  function isReplicateVideoKnown(providerModelId) {
14330
15109
  return videoKind(providerModelId) !== void 0;
@@ -14335,7 +15114,7 @@ var REPLICATE_AUDIO_KINDS = {
14335
15114
  "google/lyria-2": "lyria"
14336
15115
  };
14337
15116
  function audioKind(providerModelId) {
14338
- return REPLICATE_AUDIO_KINDS[baseModelId(providerModelId)];
15117
+ return REPLICATE_AUDIO_KINDS[replicateBaseModelId(providerModelId)];
14339
15118
  }
14340
15119
  function isReplicateAudioKnown(providerModelId) {
14341
15120
  return audioKind(providerModelId) !== void 0;
@@ -14509,7 +15288,7 @@ async function generateReplicateImage(req, ctx, deps) {
14509
15288
  }
14510
15289
  const { url, predictTime, raw } = await runPrediction(ctx, input, deps);
14511
15290
  const file = await download(ctx, url);
14512
- const usage3 = imageUsage2(ctx, req);
15291
+ const usage3 = imageUsage3(ctx, req);
14513
15292
  usage3.quantity = predictTime;
14514
15293
  return { images: [{ bytes: file.bytes, mime: file.mime }], usage: usage3, raw, outputUrl: url };
14515
15294
  }
@@ -14539,14 +15318,6 @@ function replicateVideoResolution(providerModelId, req) {
14539
15318
  if (kind === "hailuo") return hailuoResolution(req);
14540
15319
  return req.resolution;
14541
15320
  }
14542
- var KLING_TIERS = [
14543
- { mode: "standard", resolution: "720p" },
14544
- { mode: "pro", resolution: "1080p" },
14545
- { mode: "4k", resolution: "4k" }
14546
- ];
14547
- function klingTier(req) {
14548
- return KLING_TIERS.find((tier) => tier.resolution === req.resolution) ?? KLING_TIERS.find((tier) => tier.mode === req.mode) ?? KLING_TIERS[0];
14549
- }
14550
15321
  function klingOmniInput(req) {
14551
15322
  const input = {
14552
15323
  prompt: req.prompt,
@@ -14624,7 +15395,7 @@ async function generateReplicateVideo(req, ctx, deps) {
14624
15395
  throw replicateError(cause, ctx);
14625
15396
  }
14626
15397
  if (current.status === "succeeded") {
14627
- const predictTime = import_zod27.z.number().safeParse(current.metrics?.predict_time);
15398
+ const predictTime = import_zod28.z.number().safeParse(current.metrics?.predict_time);
14628
15399
  if (current.output === void 0) throw noOutput();
14629
15400
  return {
14630
15401
  url: extractOutputUrl(current.output),
@@ -14643,7 +15414,7 @@ async function generateReplicateVideo(req, ctx, deps) {
14643
15414
  return void 0;
14644
15415
  };
14645
15416
  const toResult = (done) => {
14646
- const usage3 = videoUsage4(ctx, req);
15417
+ const usage3 = videoUsage5(ctx, req);
14647
15418
  usage3.quantity = done.predictTime;
14648
15419
  return { video: { url: done.url }, usage: usage3, raw: done.raw };
14649
15420
  };
@@ -14766,7 +15537,7 @@ async function generateReplicateAudio(req, ctx, deps) {
14766
15537
  // src/adapters/xai-video.ts
14767
15538
  var import_ai3 = require("ai");
14768
15539
  var import_xai2 = require("@ai-sdk/xai");
14769
- var import_zod28 = require("zod");
15540
+ var import_zod29 = require("zod");
14770
15541
  var POLL_INTERVAL_MS2 = 1e4;
14771
15542
  var POLL_TIMEOUT_MS2 = 15 * 6e4;
14772
15543
  var defaultXaiDeps = {
@@ -14779,8 +15550,8 @@ function createXaiVideoAdapter(deps = defaultXaiDeps) {
14779
15550
  };
14780
15551
  }
14781
15552
  var xaiVideoAdapter = createXaiVideoAdapter();
14782
- var xaiResponseStatusSchema = import_zod28.z.object({
14783
- response: import_zod28.z.object({ status: import_zod28.z.number() }).partial()
15553
+ var xaiResponseStatusSchema = import_zod29.z.object({
15554
+ response: import_zod29.z.object({ status: import_zod29.z.number() }).partial()
14784
15555
  });
14785
15556
  function xaiError(cause, ctx) {
14786
15557
  const info = describeThrown(cause);
@@ -14788,12 +15559,12 @@ function xaiError(cause, ctx) {
14788
15559
  const status = info.status ?? (nested.success ? nested.data.response?.status : void 0);
14789
15560
  return classifyThrown("xai-video", cause, ctx.model.id, { status, body: info.body });
14790
15561
  }
14791
- var xaiMetaSchema = import_zod28.z.object({
15562
+ var xaiMetaSchema = import_zod29.z.object({
14792
15563
  videoUrl: JsonValueSchema.optional(),
14793
15564
  costInUsdTicks: JsonValueSchema.optional(),
14794
15565
  duration: JsonValueSchema.optional()
14795
15566
  });
14796
- var xaiProviderMetadataSchema = import_zod28.z.object({ xai: xaiMetaSchema });
15567
+ var xaiProviderMetadataSchema = import_zod29.z.object({ xai: xaiMetaSchema });
14797
15568
  function metaOf(result) {
14798
15569
  const parsed = xaiProviderMetadataSchema.partial().safeParse(result.providerMetadata);
14799
15570
  if (!parsed.success) return {};
@@ -14909,7 +15680,7 @@ async function mapResult(ctx, req, result) {
14909
15680
  };
14910
15681
  }
14911
15682
  const meta = metaOf(result);
14912
- const videoUrl = import_zod28.z.string().safeParse(meta.videoUrl);
15683
+ const videoUrl = import_zod29.z.string().safeParse(meta.videoUrl);
14913
15684
  if (videoUrl.success && videoUrl.data) {
14914
15685
  const res = await ctx.fetch(videoUrl.data);
14915
15686
  if (!res.ok) {
@@ -14932,7 +15703,7 @@ async function mapResult(ctx, req, result) {
14932
15703
  });
14933
15704
  }
14934
15705
  function usage2(ctx, req, meta) {
14935
- const costTicks = import_zod28.z.number().safeParse(meta.costInUsdTicks);
15706
+ const costTicks = import_zod29.z.number().safeParse(meta.costInUsdTicks);
14936
15707
  const cost = costTicks.success ? costTicks.data / 1e10 : void 0;
14937
15708
  const event = {
14938
15709
  model: ctx.model.id,
@@ -14980,7 +15751,8 @@ var DEFAULT_ADAPTERS = {
14980
15751
  replicate: replicateAdapter,
14981
15752
  prodia: prodiaAdapter,
14982
15753
  higgsfield: higgsfieldAdapter,
14983
- "xai-video": xaiVideoAdapter
15754
+ "xai-video": xaiVideoAdapter,
15755
+ luma: lumaAdapter
14984
15756
  };
14985
15757
 
14986
15758
  // src/catalog/data-only.ts
@@ -15096,6 +15868,10 @@ function dataOnlyCapable(row2) {
15096
15868
  reason: "higgsfield providerModelId must be a platform path, not a URL"
15097
15869
  };
15098
15870
  }
15871
+ case "luma": {
15872
+ const reason = lumaRowRefusal(row2);
15873
+ return reason === void 0 ? { ok: true } : { ok: false, reason };
15874
+ }
15099
15875
  default: {
15100
15876
  return assertNever(row2.adapter);
15101
15877
  }
@@ -15115,22 +15891,22 @@ var liveState = store[KEY] ??= {
15115
15891
  var liveCatalog = liveState.catalog;
15116
15892
 
15117
15893
  // src/catalog/load.ts
15118
- var import_zod29 = require("zod");
15894
+ var import_zod30 = require("zod");
15119
15895
  function rowCount() {
15120
15896
  return liveCatalog.list({ lifecycle: ["active", "deprecated", "retired"] }).length;
15121
15897
  }
15122
15898
  function authHeaders(auth) {
15123
15899
  return "bearer" in auth ? { Authorization: `Bearer ${auth.bearer}` } : { "x-api-key": auth.apiKey };
15124
15900
  }
15125
- var EnvelopeSchema = import_zod29.z.object({
15126
- rows: import_zod29.z.array(JsonValueSchema),
15127
- revision: import_zod29.z.string().optional()
15901
+ var EnvelopeSchema = import_zod30.z.object({
15902
+ rows: import_zod30.z.array(JsonValueSchema),
15903
+ revision: import_zod30.z.string().optional()
15128
15904
  });
15129
15905
  function dropReason(path, message) {
15130
15906
  return path.length === 0 ? message : `${path.map((part) => String(part)).join(".")} ${message}`;
15131
15907
  }
15132
15908
  function rowId(raw) {
15133
- const parsed = import_zod29.z.object({ id: import_zod29.z.string().min(1) }).loose().safeParse(raw);
15909
+ const parsed = import_zod30.z.object({ id: import_zod30.z.string().min(1) }).loose().safeParse(raw);
15134
15910
  if (!parsed.success) return "?";
15135
15911
  return parsed.data.id;
15136
15912
  }
@@ -15774,8 +16550,10 @@ function resolveVideoOptions(model, request, onUnsupported = "error") {
15774
16550
  warnings
15775
16551
  );
15776
16552
  if (aspectRatio !== void 0) merged.aspectRatio = aspectRatio;
16553
+ fillReplicateKlingResolution(model, merged);
15777
16554
  const resolution = resolveVideoResolution(merged.resolution, model, onUnsupported, warnings);
15778
16555
  if (resolution !== void 0) merged.resolution = resolution;
16556
+ if (isReplicateKling(model)) delete merged.mode;
15779
16557
  const duration = resolveDuration(
15780
16558
  merged.durationSeconds,
15781
16559
  c.durations,
@@ -16177,7 +16955,8 @@ var ADAPTER_KEYS = {
16177
16955
  replicate: ["replicate"],
16178
16956
  prodia: ["prodia"],
16179
16957
  higgsfield: ["higgsfield"],
16180
- "xai-video": ["xai"]
16958
+ "xai-video": ["xai"],
16959
+ luma: ["luma"]
16181
16960
  };
16182
16961
  function hasAll(keys, names) {
16183
16962
  if (!keys) return false;
@@ -16235,6 +17014,7 @@ function keysFromEnv(env, prefix = "") {
16235
17014
  set("prodia", pick2("PRODIA_API_KEY", "PRODIA_TOKEN"));
16236
17015
  set("higgsfield", pick2("HIGGSFIELD_API_KEY"));
16237
17016
  set("higgsfieldSecret", pick2("HIGGSFIELD_API_SECRET"));
17017
+ set("luma", pick2("LUMA_API_KEY", "LUMA_AGENTS_API_KEY"));
16238
17018
  set("cloudflare", pick2("CLOUDFLARE_AI_GATEWAY_TOKEN", "CF_AIG_TOKEN"));
16239
17019
  const bearer = pick2("AWS_BEARER_TOKEN_BEDROCK");
16240
17020
  const accessKeyId = pick2("AWS_ACCESS_KEY_ID");
@@ -16257,7 +17037,7 @@ function keysFromEnv(env, prefix = "") {
16257
17037
 
16258
17038
  // src/text.ts
16259
17039
  var import_ai4 = require("ai");
16260
- var import_zod30 = require("zod");
17040
+ var import_zod31 = require("zod");
16261
17041
  function mergeTextProviderOptions(catalog, caller) {
16262
17042
  const merged = {};
16263
17043
  const catalogOptions = catalog.providerOptions ?? {};
@@ -16296,8 +17076,8 @@ function passthroughTextModel(model) {
16296
17076
  pricingKind: "token"
16297
17077
  };
16298
17078
  }
16299
- var perplexityCostSchema = import_zod30.z.object({
16300
- perplexity: import_zod30.z.object({ cost: import_zod30.z.object({ totalCost: import_zod30.z.number().finite().nonnegative() }) })
17079
+ var perplexityCostSchema = import_zod31.z.object({
17080
+ perplexity: import_zod31.z.object({ cost: import_zod31.z.object({ totalCost: import_zod31.z.number().finite().nonnegative() }) })
16301
17081
  });
16302
17082
  function reportedCostUsd(metadata) {
16303
17083
  const parsed = perplexityCostSchema.safeParse(metadata);
@@ -16370,7 +17150,7 @@ function createTextSteps(initial, resolve, callerProviderOptions) {
16370
17150
 
16371
17151
  // src/client.ts
16372
17152
  var import_ai5 = require("ai");
16373
- var import_zod31 = require("zod");
17153
+ var import_zod32 = require("zod");
16374
17154
 
16375
17155
  // src/generated-image.ts
16376
17156
  function toGeneratedImage(image) {
@@ -16719,10 +17499,10 @@ function textAbortOptions(sdk, context) {
16719
17499
  if (abortSignal) return { abortSignal };
16720
17500
  return {};
16721
17501
  }
16722
- var catalogId = import_zod31.z.string();
16723
- var exactSize = import_zod31.z.templateLiteral([import_zod31.z.number(), "x", import_zod31.z.number()]);
16724
- var sdkRatio = import_zod31.z.templateLiteral([import_zod31.z.number(), ":", import_zod31.z.number()]);
16725
- var sizeTier = import_zod31.z.enum(["512", "1K", "2K", "4K"]);
17502
+ var catalogId = import_zod32.z.string();
17503
+ var exactSize = import_zod32.z.templateLiteral([import_zod32.z.number(), "x", import_zod32.z.number()]);
17504
+ var sdkRatio = import_zod32.z.templateLiteral([import_zod32.z.number(), ":", import_zod32.z.number()]);
17505
+ var sizeTier = import_zod32.z.enum(["512", "1K", "2K", "4K"]);
16726
17506
  function isCatalogId(model) {
16727
17507
  return catalogId.safeParse(model).success;
16728
17508
  }
@@ -17467,6 +18247,7 @@ var describe = (modelId) => createDefaultModelKit().describe(modelId);
17467
18247
  languageModel,
17468
18248
  liveCatalog,
17469
18249
  liveState,
18250
+ lumaAdapter,
17470
18251
  mergeTextProviderOptions,
17471
18252
  missingTierKeys,
17472
18253
  model3dCall,