@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/CHANGELOG.md CHANGED
@@ -9,6 +9,23 @@ header in the release PR.
9
9
 
10
10
  ## [Unreleased]
11
11
 
12
+ ## [0.7.0] - Unreleased
13
+
14
+ ENG-3272: Luma (Agents API, direct) image and video models.
15
+
16
+ ### Added
17
+ - `luma` adapter for the Luma Agents API (`POST /v1/generations`, then poll). Key `LUMA_API_KEY` (alias `LUMA_AGENTS_API_KEY`); Sloosh reads `SLOOSH_LUMA_API_KEY`. `AdapterEndpoints.luma` overrides the base URL (default `https://agents.lumalabs.ai`). Video jobs reconnect by the Luma generation id (`resumeVideo`).
18
+ - Image rows `uni-1` and `uni-1-max` (2K output). With no input image they generate from the prompt; with input images the first is the source to edit (`image_edit`) and up to 8 more are references.
19
+ - Video rows `ray-3.2` (5 s text- or image-to-video with optional first and last frames), `ray-3.2-edit` (changes a 5-18 s source clip) and `ray-3.2-reframe` (changes a 5-10 s source clip to a new aspect ratio). Both outputs keep the source length, so neither row has a duration choice. Luma bills them its 5 s price for any source while the rows are priced per source second, so sources under 5 s are refused. Resolutions 360p, 720p and 1080p; 1080p is refused for 9:16 and 3:4 reframes (`onUnsupported: "coerce"` sends 720p). `ray-3.2` offers 5 s only: Luma bills a 10 s clip at 3x a 5 s clip, which a per-second price cannot express.
20
+ - Video usage: `ray-3.2` reports its fixed 5 s block. Edit and reframe report the output length read from the MP4 (`moov/mvhd`, since Luma returns no duration), else the caller's measured `sourceVideo.durationSeconds`, and never under 5 s (Luma's minimum charge). The event carries the resolution Luma rendered (720p for a coerced vertical reframe). `VideoJob.poll()` reports a failed Luma generation as `failed` whatever its failure code.
21
+ - `ray-3.2-edit` sends a first frame as the edit's guide frame (`video.start_frame`). A Uni edit keeps the source's shape and warns (`option_ignored`) when a ratio was asked for.
22
+ - `dataOnlyCapable` refuses a luma video row whose id is not `ray-3.2`, `ray-3.2-edit` or `ray-3.2-reframe` (the adapter routes by catalog id) and a luma row whose `providerModelId` does not fit its type. `DataOnlyRow` now includes `id`.
23
+
24
+ ## [0.6.1] - Unreleased
25
+
26
+ ### Fixed
27
+ - Every Replicate Kling row (kind `kling` or `kling-omni`, today `kling-v3-omni-video`): the resolved request keeps only the `resolution` that renders and drops `mode`. A 720p request no longer carries the row default `mode: "pro"` next to it. A request without a resolution takes its `mode`'s (`standard` 720p, `pro` 1080p; neither: 720p) before the resolution check, so a row that does not list that resolution refuses it (`error`) or coerces it with a `resolution_coerced` warning (`coerce`). What the provider receives for an accepted request is unchanged.
28
+
12
29
  ## [0.6.0] - Unreleased
13
30
 
14
31
  ### Added
package/README.md CHANGED
@@ -61,7 +61,7 @@ await clip.cleanup?.();
61
61
 
62
62
  A video job that outlives its caller can be rejoined with `resumeVideo`: pass the same
63
63
  request and the `jobId` from `generateVideo` and await `job.result()` as usual. Only the
64
- ark adapter (Seedance) supports it today; every other adapter throws `unsupported_option`.
64
+ ark (Seedance) and luma (Ray 3.2) adapters support it today; every other adapter throws `unsupported_option`.
65
65
 
66
66
  ### Video tools
67
67
 
@@ -108,7 +108,7 @@ before submit), and `model3dPricingSku(row, options, inputs)` names the flat pri
108
108
  bills under; `model3dSkus(row)` lists them all, and pro-backend keeps a `credit_pricing_flat`
109
109
  row for each. All three are in the browser-safe catalog entry.
110
110
 
111
- Keys come from `process.env` by default: bare names for pro (`OPENAI_API_KEY`, `FAL_KEY`, `ARK_API_KEY` ...), the `ENTERPRISE_` prefix for enterprise and the `SLOOSH_` prefix for sloosh. Pass `keys: { pro, enterprise, sloosh }` to override. `tier: "sloosh"` uses the sloosh keys only: a missing key is a `ModelKitError` with code `auth`, never a fallback to pro.
111
+ Keys come from `process.env` by default: bare names for pro (`OPENAI_API_KEY`, `FAL_KEY`, `ARK_API_KEY`, `LUMA_API_KEY` ...), the `ENTERPRISE_` prefix for enterprise and the `SLOOSH_` prefix for sloosh (sloosh reads `SLOOSH_LUMA_API_KEY` for Luma). Pass `keys: { pro, enterprise, sloosh }` to override. `tier: "sloosh"` uses the sloosh keys only: a missing key is a `ModelKitError` with code `auth`, never a fallback to pro.
112
112
 
113
113
  ## Browser-safe catalog
114
114
 
@@ -344,7 +344,7 @@ the app's secrets, not a Cloudflare gateway token.
344
344
 
345
345
  ## Adapters
346
346
 
347
- `ai-sdk-text`, `google-image`, `openai-image`, `google-genai` (Veo, Omni with `delivery: "uri"`, Lyria 3, Files API media parts), `ark` (Seedream, Seedance), `fal`, `replicate` (images, video, Lyria 2, MMAudio), `prodia`, `higgsfield`, `xai-video`. See `src/adapters/`.
347
+ `ai-sdk-text`, `google-image`, `openai-image`, `google-genai` (Veo, Omni with `delivery: "uri"`, Lyria 3, Files API media parts), `ark` (Seedream, Seedance), `fal`, `replicate` (images, video, Lyria 2, MMAudio), `prodia`, `higgsfield`, `xai-video`, `luma` (Uni images, Ray 3.2 video via the Agents API; `LUMA_API_KEY`, base-url override `endpoints.luma`). See `src/adapters/`.
348
348
 
349
349
  ## Not in the package
350
350
 
package/dist/catalog.cjs CHANGED
@@ -445,8 +445,59 @@ var FAL_IMAGE_MODELS = [
445
445
  }
446
446
  ];
447
447
 
448
- // src/catalog/image.ts
448
+ // src/catalog/image-luma.ts
449
449
  var IMAGE_USAGE2 = { unit: "images" };
450
+ var EXPOSURE2 = ["cowork", "space_builder"];
451
+ var LOBE2 = "https://cdn.jsdelivr.net/npm/@lobehub/icons-static-png@1.97.0/light";
452
+ var LUMA_LOGO = `${LOBE2}/luma-color.png`;
453
+ var UNI_RATIOS = ["adaptive", "16:9", "3:2", "1:1", "2:3", "9:16"];
454
+ function uniImage(id, label, providerModelId, description) {
455
+ return {
456
+ // https://docs.agents.lumalabs.ai: type "image" without references, else
457
+ // "image_edit" with the first reference as source and up to 8 more as
458
+ // image_ref. The output is always 2048 px on the long side.
459
+ id,
460
+ aliases: [],
461
+ label,
462
+ type: "image",
463
+ adapter: "luma",
464
+ providerModelId,
465
+ lifecycle: "active",
466
+ exposure: EXPOSURE2,
467
+ constraints: {
468
+ aspectRatios: [...UNI_RATIOS],
469
+ sizes: ["2K"],
470
+ references: { images: 9 },
471
+ supports: ["outputFormat"],
472
+ prompt: { referenceSyntax: "numbered", maxChars: 6e3 }
473
+ },
474
+ defaults: { size: "2K", aspectRatio: "adaptive" },
475
+ usage: { ...IMAGE_USAGE2 },
476
+ pricingKind: "image_size",
477
+ display: {
478
+ provider: "luma",
479
+ description,
480
+ logo: LUMA_LOGO
481
+ }
482
+ };
483
+ }
484
+ var LUMA_IMAGE_MODELS = [
485
+ uniImage(
486
+ "uni-1",
487
+ "Uni-1",
488
+ "uni-1",
489
+ "Luma Uni-1 fast image generation and editing; takes a prompt and up to 9 reference images and outputs 2K."
490
+ ),
491
+ uniImage(
492
+ "uni-1-max",
493
+ "Uni-1 Max",
494
+ "uni-1-max",
495
+ "Luma Uni-1 Max higher-quality image generation and editing; takes a prompt and up to 9 reference images and outputs 2K."
496
+ )
497
+ ];
498
+
499
+ // src/catalog/image.ts
500
+ var IMAGE_USAGE3 = { unit: "images" };
450
501
  var GEMINI_RATIOS = [
451
502
  "1:1",
452
503
  "2:3",
@@ -491,7 +542,7 @@ function geminiImage(id, label, aliases, sizes, display, exposure, references, p
491
542
  exposure,
492
543
  constraints,
493
544
  defaults: { size: "1K", aspectRatio: "1:1" },
494
- usage: { ...IMAGE_USAGE2, slug: "gemini" },
545
+ usage: { ...IMAGE_USAGE3, slug: "gemini" },
495
546
  pricingKind: "image_size",
496
547
  display
497
548
  };
@@ -604,7 +655,7 @@ var IMAGE_MODELS = [
604
655
  prompt: GPT_IMAGE_PROMPT
605
656
  },
606
657
  defaults: { size: "1K", aspectRatio: "adaptive", quality: "auto" },
607
- usage: { ...IMAGE_USAGE2, slug: "gpt" },
658
+ usage: { ...IMAGE_USAGE3, slug: "gpt" },
608
659
  pricingKind: "image_size",
609
660
  display: {
610
661
  provider: "openai",
@@ -640,7 +691,7 @@ var IMAGE_MODELS = [
640
691
  prompt: GPT_IMAGE_PROMPT
641
692
  },
642
693
  defaults: { size: "1K", aspectRatio: "adaptive", quality: "auto" },
643
- usage: { ...IMAGE_USAGE2 },
694
+ usage: { ...IMAGE_USAGE3 },
644
695
  pricingKind: "image_size",
645
696
  display: {
646
697
  provider: "openai",
@@ -675,7 +726,7 @@ var IMAGE_MODELS = [
675
726
  prompt: GPT_IMAGE_PROMPT
676
727
  },
677
728
  defaults: { size: "1K", aspectRatio: "adaptive", quality: "auto" },
678
- usage: { ...IMAGE_USAGE2 },
729
+ usage: { ...IMAGE_USAGE3 },
679
730
  pricingKind: "image_size",
680
731
  display: {
681
732
  provider: "openai",
@@ -710,7 +761,7 @@ var IMAGE_MODELS = [
710
761
  supports: ["count", "outputFormat"]
711
762
  },
712
763
  defaults: { size: "1K", aspectRatio: "1:1", outputFormat: "png", count: 1 },
713
- usage: { ...IMAGE_USAGE2, slug: "muse" },
764
+ usage: { ...IMAGE_USAGE3, slug: "muse" },
714
765
  pricingKind: "image_size",
715
766
  display: {
716
767
  provider: "meta",
@@ -737,7 +788,7 @@ var IMAGE_MODELS = [
737
788
  supports: ["quality", "count", "seed"]
738
789
  },
739
790
  defaults: { size: "2K", aspectRatio: "1:1", quality: "medium", count: 1 },
740
- usage: { ...IMAGE_USAGE2, slug: "ideogram" },
791
+ usage: { ...IMAGE_USAGE3, slug: "ideogram" },
741
792
  pricingKind: "image_size",
742
793
  display: {
743
794
  provider: "ideogram",
@@ -762,7 +813,7 @@ var IMAGE_MODELS = [
762
813
  supports: ["seed", "outputFormat"]
763
814
  },
764
815
  defaults: { size: "1K", aspectRatio: "1:1" },
765
- usage: { ...IMAGE_USAGE2 },
816
+ usage: { ...IMAGE_USAGE3 },
766
817
  pricingKind: "image_size",
767
818
  display: {
768
819
  provider: "bytedance",
@@ -800,7 +851,7 @@ var IMAGE_MODELS = [
800
851
  supports: ["seed"]
801
852
  },
802
853
  defaults: { size: "1K", aspectRatio: "1:1" },
803
- usage: { ...IMAGE_USAGE2 },
854
+ usage: { ...IMAGE_USAGE3 },
804
855
  pricingKind: "image_size",
805
856
  display: {
806
857
  provider: "bytedance",
@@ -826,7 +877,7 @@ var IMAGE_MODELS = [
826
877
  prompt: { referenceSyntax: "numbered", maxChars: 32768 }
827
878
  },
828
879
  defaults: { size: "1K", aspectRatio: "1:1" },
829
- usage: { ...IMAGE_USAGE2 },
880
+ usage: { ...IMAGE_USAGE3 },
830
881
  pricingKind: "image_size",
831
882
  display: {
832
883
  provider: "black-forest-labs",
@@ -853,7 +904,7 @@ var IMAGE_MODELS = [
853
904
  supports: []
854
905
  },
855
906
  defaults: { size: "1K", aspectRatio: "1:1" },
856
- usage: { ...IMAGE_USAGE2 },
907
+ usage: { ...IMAGE_USAGE3 },
857
908
  pricingKind: "image_size",
858
909
  display: {
859
910
  provider: "google",
@@ -878,7 +929,7 @@ var IMAGE_MODELS = [
878
929
  supports: []
879
930
  },
880
931
  defaults: { size: "1K", aspectRatio: "1:1" },
881
- usage: { ...IMAGE_USAGE2 },
932
+ usage: { ...IMAGE_USAGE3 },
882
933
  pricingKind: "image_size",
883
934
  display: {
884
935
  provider: "google",
@@ -964,7 +1015,8 @@ var IMAGE_MODELS = [
964
1015
  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."
965
1016
  }
966
1017
  },
967
- ...FAL_IMAGE_MODELS
1018
+ ...FAL_IMAGE_MODELS,
1019
+ ...LUMA_IMAGE_MODELS
968
1020
  ];
969
1021
 
970
1022
  // src/catalog/audio-formats.ts
@@ -1160,7 +1212,7 @@ var VEED_SUBTITLE_LANGUAGES = [
1160
1212
  ];
1161
1213
 
1162
1214
  // src/catalog/canvas-tools.ts
1163
- var EXPOSURE2 = ["cowork", "space_builder"];
1215
+ var EXPOSURE3 = ["cowork", "space_builder"];
1164
1216
  var META_LOGO = "https://d31vvfpk2ppdyq.cloudfront.net/assets/characters/1787909065794-k9a7zz.png";
1165
1217
  var VEED_SUBTITLE_PRESETS = [
1166
1218
  "simple",
@@ -1202,7 +1254,7 @@ var SAM_3_IMAGE = {
1202
1254
  adapter: "fal",
1203
1255
  providerModelId: "fal-ai/sam-3/image",
1204
1256
  lifecycle: "active",
1205
- exposure: EXPOSURE2,
1257
+ exposure: EXPOSURE3,
1206
1258
  constraints: {
1207
1259
  references: { images: 1 },
1208
1260
  requires: ["references"],
@@ -1225,7 +1277,7 @@ var VEED_SUBTITLES = {
1225
1277
  adapter: "fal",
1226
1278
  providerModelId: "veed/subtitles",
1227
1279
  lifecycle: "active",
1228
- exposure: EXPOSURE2,
1280
+ exposure: EXPOSURE3,
1229
1281
  constraints: {
1230
1282
  supports: [],
1231
1283
  // fal: up to 2 hours at 1080p or below, 1 hour above.
@@ -3330,7 +3382,7 @@ var GOOGLE_LOGO = "https://d31vvfpk2ppdyq.cloudfront.net/assets/image/192485d2-a
3330
3382
  var MINIMAX_LOGO = "https://d31vvfpk2ppdyq.cloudfront.net/assets/image/f4c2e906-28b7-4a4a-b491-df8fef7808ed.png";
3331
3383
  var SYNC_LOGO = "https://cdn.jsdelivr.net/npm/@lobehub/icons-static-png@1.97.0/light/sync.png";
3332
3384
  var PER_CALL = { unit: "images" };
3333
- var EXPOSURE3 = ["cowork", "space_builder"];
3385
+ var EXPOSURE4 = ["cowork", "space_builder"];
3334
3386
  var LIPSYNC_AUDIO = { audioSeconds: { max: 30 } };
3335
3387
  function elevenLabsTts(tts) {
3336
3388
  return {
@@ -3341,7 +3393,7 @@ function elevenLabsTts(tts) {
3341
3393
  adapter: "fal",
3342
3394
  providerModelId: tts.providerModelId,
3343
3395
  lifecycle: "active",
3344
- exposure: EXPOSURE3,
3396
+ exposure: EXPOSURE4,
3345
3397
  constraints: {
3346
3398
  voices: ELEVENLABS_VOICES,
3347
3399
  languages: tts.languages,
@@ -3366,7 +3418,7 @@ function geminiTts(id, label, description) {
3366
3418
  adapter: "fal",
3367
3419
  providerModelId,
3368
3420
  lifecycle: "active",
3369
- exposure: EXPOSURE3,
3421
+ exposure: EXPOSURE4,
3370
3422
  constraints: {
3371
3423
  voices: GEMINI_TTS_VOICES,
3372
3424
  supports: ["style"],
@@ -3389,7 +3441,7 @@ function syncLipsync(id, label, providerModelId, syncModel, description) {
3389
3441
  adapter: "fal",
3390
3442
  providerModelId,
3391
3443
  lifecycle: "active",
3392
- exposure: EXPOSURE3,
3444
+ exposure: EXPOSURE4,
3393
3445
  constraints: {
3394
3446
  requires: ["sourceVideo", "sourceAudio"],
3395
3447
  references: LIPSYNC_AUDIO,
@@ -3450,7 +3502,7 @@ var MEDIA_NODE_MODELS = [
3450
3502
  adapter: "fal",
3451
3503
  providerModelId: "fal-ai/minimax/speech-2.8-hd",
3452
3504
  lifecycle: "active",
3453
- exposure: EXPOSURE3,
3505
+ exposure: EXPOSURE4,
3454
3506
  constraints: {
3455
3507
  voices: MINIMAX_VOICES,
3456
3508
  languages: MINIMAX_SPEECH_LANGUAGES,
@@ -3475,7 +3527,7 @@ var MEDIA_NODE_MODELS = [
3475
3527
  adapter: "fal",
3476
3528
  providerModelId: "fal-ai/elevenlabs/voice-changer",
3477
3529
  lifecycle: "active",
3478
- exposure: EXPOSURE3,
3530
+ exposure: EXPOSURE4,
3479
3531
  constraints: {
3480
3532
  requires: ["sourceAudio"],
3481
3533
  // The flat price assumes at most a minute of speech.
@@ -3502,7 +3554,7 @@ var MEDIA_NODE_MODELS = [
3502
3554
  adapter: "fal",
3503
3555
  providerModelId: "fal-ai/elevenlabs/dubbing",
3504
3556
  lifecycle: "active",
3505
- exposure: EXPOSURE3,
3557
+ exposure: EXPOSURE4,
3506
3558
  constraints: {
3507
3559
  requiresOneOf: ["sourceVideo", "sourceAudio"],
3508
3560
  // The flat price covers 60 s of source.
@@ -3550,7 +3602,7 @@ var MEDIA_NODE_MODELS = [
3550
3602
  adapter: "fal",
3551
3603
  providerModelId: "veed/lipsync/v2",
3552
3604
  lifecycle: "active",
3553
- exposure: EXPOSURE3,
3605
+ exposure: EXPOSURE4,
3554
3606
  constraints: {
3555
3607
  requires: ["sourceVideo", "sourceAudio"],
3556
3608
  references: LIPSYNC_AUDIO,
@@ -3576,7 +3628,7 @@ var MEDIA_NODE_MODELS = [
3576
3628
  adapter: "fal",
3577
3629
  providerModelId: "minimax/h3-max/lip-sync/image-to-video",
3578
3630
  lifecycle: "active",
3579
- exposure: EXPOSURE3,
3631
+ exposure: EXPOSURE4,
3580
3632
  constraints: {
3581
3633
  requires: ["firstFrame", "sourceAudio"],
3582
3634
  resolutions: ["480p", "768p", "1080p", "2k"],
@@ -3602,7 +3654,7 @@ var MEDIA_NODE_MODELS = [
3602
3654
  adapter: "fal",
3603
3655
  providerModelId: "fal-ai/elevenlabs/speech-to-text/scribe-v2",
3604
3656
  lifecycle: "active",
3605
- exposure: EXPOSURE3,
3657
+ exposure: EXPOSURE4,
3606
3658
  constraints: {
3607
3659
  requires: ["sourceAudio"],
3608
3660
  // The flat price covers 10 minutes of audio.
@@ -5138,8 +5190,113 @@ var VIDEO_FAMILY_MODELS = [
5138
5190
  }
5139
5191
  ];
5140
5192
 
5141
- // src/catalog/video.ts
5193
+ // src/catalog/video-luma.ts
5142
5194
  var VIDEO_USAGE2 = { unit: "seconds" };
5195
+ var EXPOSURE5 = ["cowork", "space_builder"];
5196
+ var LUMA_LOGO2 = "https://cdn.jsdelivr.net/npm/@lobehub/icons-static-png@1.97.0/light/luma-color.png";
5197
+ var RAY_RATIOS = ["9:16", "3:4", "1:1", "4:3", "16:9", "21:9"];
5198
+ var RAY_RESOLUTIONS = ["360p", "720p", "1080p"];
5199
+ var RAY_PROMPT = { referenceSyntax: "numbered", maxChars: 6e3 };
5200
+ var LUMA_VIDEO_MODELS = [
5201
+ {
5202
+ // https://docs.agents.lumalabs.ai: type "video" with a fixed 5 s duration;
5203
+ // firstFrame and lastFrame ride as start_frame and end_frame.
5204
+ id: "ray-3.2",
5205
+ aliases: [],
5206
+ label: "Ray 3.2",
5207
+ type: "video",
5208
+ adapter: "luma",
5209
+ providerModelId: "ray-3.2",
5210
+ lifecycle: "active",
5211
+ exposure: EXPOSURE5,
5212
+ constraints: {
5213
+ aspectRatios: ["adaptive", ...RAY_RATIOS],
5214
+ resolutions: [...RAY_RESOLUTIONS],
5215
+ durations: [5],
5216
+ supports: ["lastFrame"],
5217
+ prompt: { ...RAY_PROMPT },
5218
+ audio: "never"
5219
+ },
5220
+ defaults: { durationSeconds: 5, resolution: "720p", aspectRatio: "adaptive" },
5221
+ usage: { ...VIDEO_USAGE2 },
5222
+ pricingKind: "video_per_second",
5223
+ display: {
5224
+ provider: "luma",
5225
+ description: "Luma Ray 3.2 text- and image-to-video with optional first and last frames, 5-second clips at up to 1080p.",
5226
+ logo: LUMA_LOGO2
5227
+ },
5228
+ capabilities: {}
5229
+ },
5230
+ {
5231
+ // https://docs.agents.lumalabs.ai: type "video_edit" with a source video of
5232
+ // up to 18 s; the output keeps the source length, so there is no durations
5233
+ // knob, no aspect_ratio and no end frame. Luma bills its 5 s price for any
5234
+ // source (live, 2026-10-21) and the row is priced per source second, so a
5235
+ // source under 5 s is refused: it would be billed below Luma's charge.
5236
+ id: "ray-3.2-edit",
5237
+ aliases: [],
5238
+ label: "Ray 3.2 Edit",
5239
+ type: "video",
5240
+ adapter: "luma",
5241
+ providerModelId: "ray-3.2",
5242
+ lifecycle: "active",
5243
+ exposure: EXPOSURE5,
5244
+ constraints: {
5245
+ resolutions: [...RAY_RESOLUTIONS],
5246
+ requires: ["sourceVideo"],
5247
+ sourceVideoSeconds: { min: 5, max: 18 },
5248
+ supports: [],
5249
+ prompt: { ...RAY_PROMPT },
5250
+ audio: "never"
5251
+ },
5252
+ defaults: { resolution: "720p" },
5253
+ usage: { ...VIDEO_USAGE2 },
5254
+ pricingKind: "video_per_second",
5255
+ display: {
5256
+ provider: "luma",
5257
+ description: "Luma Ray 3.2 video edit: changes a 5-18 second clip from a prompt, keeping its length.",
5258
+ logo: LUMA_LOGO2
5259
+ },
5260
+ capabilities: { acceptsSourceVideo: true, videoEdit: true }
5261
+ },
5262
+ {
5263
+ // https://docs.agents.lumalabs.ai: type "video_reframe" with a required
5264
+ // target aspect_ratio; the source is up to 10 s and the output keeps its
5265
+ // length whatever video.duration says, while Luma bills that duration
5266
+ // (live, 2026-10-21). So there is no durations knob, and a source under
5267
+ // 5 s is refused, as on ray-3.2-edit. 1080p is not offered for the
5268
+ // vertical targets 9:16 and 3:4.
5269
+ id: "ray-3.2-reframe",
5270
+ aliases: [],
5271
+ label: "Ray 3.2 Reframe",
5272
+ type: "video",
5273
+ adapter: "luma",
5274
+ providerModelId: "ray-3.2",
5275
+ lifecycle: "active",
5276
+ exposure: EXPOSURE5,
5277
+ constraints: {
5278
+ aspectRatios: [...RAY_RATIOS],
5279
+ resolutions: [...RAY_RESOLUTIONS],
5280
+ requires: ["sourceVideo"],
5281
+ sourceVideoSeconds: { min: 5, max: 10 },
5282
+ supports: [],
5283
+ prompt: { ...RAY_PROMPT },
5284
+ audio: "never"
5285
+ },
5286
+ defaults: { resolution: "720p", aspectRatio: "16:9" },
5287
+ usage: { ...VIDEO_USAGE2 },
5288
+ pricingKind: "video_per_second",
5289
+ display: {
5290
+ provider: "luma",
5291
+ 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.",
5292
+ logo: LUMA_LOGO2
5293
+ },
5294
+ capabilities: { acceptsSourceVideo: true, videoEdit: true }
5295
+ }
5296
+ ];
5297
+
5298
+ // src/catalog/video.ts
5299
+ var VIDEO_USAGE3 = { unit: "seconds" };
5143
5300
  var SEEDANCE_RATIOS = ["21:9", "16:9", "4:3", "1:1", "3:4", "9:16", "adaptive"];
5144
5301
  var KLING_RATIOS2 = ["16:9", "9:16", "1:1", "adaptive"];
5145
5302
  var KLING_OMNI_RATIOS = ["16:9", "9:16", "1:1"];
@@ -5192,7 +5349,7 @@ var VIDEO_MODELS = [
5192
5349
  audio: "always"
5193
5350
  },
5194
5351
  defaults: { durationSeconds: 8, resolution: "720p", aspectRatio: "16:9" },
5195
- usage: { ...VIDEO_USAGE2, slug: "veo" },
5352
+ usage: { ...VIDEO_USAGE3, slug: "veo" },
5196
5353
  pricingKind: "video_per_second",
5197
5354
  display: {
5198
5355
  provider: "google",
@@ -5225,7 +5382,7 @@ var VIDEO_MODELS = [
5225
5382
  audio: "always"
5226
5383
  },
5227
5384
  defaults: { durationSeconds: 8, resolution: "720p", aspectRatio: "16:9" },
5228
- usage: { ...VIDEO_USAGE2 },
5385
+ usage: { ...VIDEO_USAGE3 },
5229
5386
  pricingKind: "video_per_second",
5230
5387
  display: {
5231
5388
  provider: "google",
@@ -5255,7 +5412,7 @@ var VIDEO_MODELS = [
5255
5412
  audio: "always"
5256
5413
  },
5257
5414
  defaults: { durationSeconds: 8, resolution: "720p", aspectRatio: "16:9" },
5258
- usage: { ...VIDEO_USAGE2 },
5415
+ usage: { ...VIDEO_USAGE3 },
5259
5416
  pricingKind: "video_per_second",
5260
5417
  display: {
5261
5418
  provider: "google",
@@ -5292,7 +5449,7 @@ var VIDEO_MODELS = [
5292
5449
  audio: "always"
5293
5450
  },
5294
5451
  defaults: {},
5295
- usage: { ...VIDEO_USAGE2, slug: "gemini_omni" },
5452
+ usage: { ...VIDEO_USAGE3, slug: "gemini_omni" },
5296
5453
  pricingKind: "video_per_second",
5297
5454
  display: {
5298
5455
  provider: "google",
@@ -5329,7 +5486,7 @@ var VIDEO_MODELS = [
5329
5486
  audio: "always"
5330
5487
  },
5331
5488
  defaults: {},
5332
- usage: { ...VIDEO_USAGE2 },
5489
+ usage: { ...VIDEO_USAGE3 },
5333
5490
  pricingKind: "video_per_second",
5334
5491
  display: {
5335
5492
  provider: "google",
@@ -5378,7 +5535,7 @@ var VIDEO_MODELS = [
5378
5535
  aspectRatio: "16:9",
5379
5536
  generateAudio: true
5380
5537
  },
5381
- usage: { ...VIDEO_USAGE2, slug: "seedance2", providerLabel: "byteplus" },
5538
+ usage: { ...VIDEO_USAGE3, slug: "seedance2", providerLabel: "byteplus" },
5382
5539
  pricingKind: "video_per_second",
5383
5540
  display: {
5384
5541
  provider: "bytedance",
@@ -5421,7 +5578,7 @@ var VIDEO_MODELS = [
5421
5578
  aspectRatio: "16:9",
5422
5579
  generateAudio: true
5423
5580
  },
5424
- usage: { ...VIDEO_USAGE2 },
5581
+ usage: { ...VIDEO_USAGE3 },
5425
5582
  pricingKind: "video_per_second",
5426
5583
  display: {
5427
5584
  provider: "bytedance",
@@ -5471,7 +5628,7 @@ var VIDEO_MODELS = [
5471
5628
  aspectRatio: "16:9",
5472
5629
  generateAudio: true
5473
5630
  },
5474
- usage: { ...VIDEO_USAGE2 },
5631
+ usage: { ...VIDEO_USAGE3 },
5475
5632
  pricingKind: "video_per_second",
5476
5633
  display: {
5477
5634
  provider: "bytedance",
@@ -5533,7 +5690,7 @@ var VIDEO_MODELS = [
5533
5690
  cfgScale: 0.5,
5534
5691
  negativePrompt: NEGATIVE_PROMPT
5535
5692
  },
5536
- usage: { ...VIDEO_USAGE2 },
5693
+ usage: { ...VIDEO_USAGE3 },
5537
5694
  pricingKind: "video_per_second",
5538
5695
  display: {
5539
5696
  provider: "kuaishou",
@@ -5565,7 +5722,7 @@ var VIDEO_MODELS = [
5565
5722
  cfgScale: 0.5,
5566
5723
  generateAudio: true
5567
5724
  },
5568
- usage: { ...VIDEO_USAGE2 },
5725
+ usage: { ...VIDEO_USAGE3 },
5569
5726
  pricingKind: "video_per_second",
5570
5727
  display: {
5571
5728
  provider: "kuaishou",
@@ -5633,7 +5790,7 @@ var VIDEO_MODELS = [
5633
5790
  aspectRatio: "16:9",
5634
5791
  promptExpansion: "balanced"
5635
5792
  },
5636
- usage: { ...VIDEO_USAGE2 },
5793
+ usage: { ...VIDEO_USAGE3 },
5637
5794
  pricingKind: "video_per_second",
5638
5795
  display: {
5639
5796
  provider: "minimax",
@@ -5667,7 +5824,7 @@ var VIDEO_MODELS = [
5667
5824
  aspectRatio: "16:9",
5668
5825
  promptExpansion: "balanced"
5669
5826
  },
5670
- usage: { ...VIDEO_USAGE2 },
5827
+ usage: { ...VIDEO_USAGE3 },
5671
5828
  pricingKind: "video_per_second",
5672
5829
  display: {
5673
5830
  provider: "minimax",
@@ -5709,7 +5866,7 @@ var VIDEO_MODELS = [
5709
5866
  aspectRatio: "adaptive",
5710
5867
  promptExpansion: "balanced"
5711
5868
  },
5712
- usage: { ...VIDEO_USAGE2 },
5869
+ usage: { ...VIDEO_USAGE3 },
5713
5870
  pricingKind: "video_per_second",
5714
5871
  display: {
5715
5872
  provider: "minimax",
@@ -5753,7 +5910,7 @@ var VIDEO_MODELS = [
5753
5910
  aspectRatio: "adaptive",
5754
5911
  promptExpansion: "balanced"
5755
5912
  },
5756
- usage: { ...VIDEO_USAGE2 },
5913
+ usage: { ...VIDEO_USAGE3 },
5757
5914
  pricingKind: "video_per_second",
5758
5915
  display: {
5759
5916
  provider: "minimax",
@@ -5798,7 +5955,7 @@ var VIDEO_MODELS = [
5798
5955
  aspectRatio: "adaptive",
5799
5956
  generateAudio: true
5800
5957
  },
5801
- usage: { ...VIDEO_USAGE2 },
5958
+ usage: { ...VIDEO_USAGE3 },
5802
5959
  pricingKind: "video_per_second",
5803
5960
  display: {
5804
5961
  provider: "alibaba",
@@ -5825,7 +5982,7 @@ var VIDEO_MODELS = [
5825
5982
  audio: "never"
5826
5983
  },
5827
5984
  defaults: { durationSeconds: 5, aspectRatio: "16:9" },
5828
- usage: { ...VIDEO_USAGE2 },
5985
+ usage: { ...VIDEO_USAGE3 },
5829
5986
  pricingKind: "video_per_second",
5830
5987
  display: {
5831
5988
  provider: "kuaishou",
@@ -5874,7 +6031,7 @@ var VIDEO_MODELS = [
5874
6031
  audio: "never"
5875
6032
  },
5876
6033
  defaults: {},
5877
- usage: { ...VIDEO_USAGE2 },
6034
+ usage: { ...VIDEO_USAGE3 },
5878
6035
  // Higgsfield bills DoP per generation: a fixed-length clip with no duration knob.
5879
6036
  pricingKind: "flat",
5880
6037
  display: {
@@ -5883,7 +6040,8 @@ var VIDEO_MODELS = [
5883
6040
  },
5884
6041
  capabilities: {}
5885
6042
  },
5886
- ...VIDEO_FAMILY_MODELS
6043
+ ...VIDEO_FAMILY_MODELS,
6044
+ ...LUMA_VIDEO_MODELS
5887
6045
  ];
5888
6046
 
5889
6047
  // src/catalog/video-tools.ts
@@ -6863,7 +7021,8 @@ var ADAPTER_IDS = [
6863
7021
  "replicate",
6864
7022
  "prodia",
6865
7023
  "higgsfield",
6866
- "xai-video"
7024
+ "xai-video",
7025
+ "luma"
6867
7026
  ];
6868
7027
  var ModelTypeSchema = import_zod3.z.enum([
6869
7028
  "text",
@@ -8275,6 +8434,30 @@ function applyFalImageRules(model, req, callerSize, warnings) {
8275
8434
  req.size = exact.tier;
8276
8435
  }
8277
8436
 
8437
+ // src/replicate-video-kinds.ts
8438
+ function replicateBaseModelId(providerModelId) {
8439
+ const colon = providerModelId.indexOf(":");
8440
+ return colon === -1 ? providerModelId : providerModelId.slice(0, colon);
8441
+ }
8442
+ var REPLICATE_VIDEO_KINDS = /* @__PURE__ */ new Map([
8443
+ ["kling-video/v2.5-turbo/pro/image-to-video", "kling"],
8444
+ ["kling-video/v3.0/pro/image-to-video", "kling"],
8445
+ ["kwaivgi/kling-v3-video", "kling"],
8446
+ ["kwaivgi/kling-v3-omni-video", "kling-omni"],
8447
+ ["minimax/hailuo-02", "hailuo"]
8448
+ ]);
8449
+ function videoKind(providerModelId) {
8450
+ return REPLICATE_VIDEO_KINDS.get(replicateBaseModelId(providerModelId));
8451
+ }
8452
+ var KLING_TIERS = [
8453
+ { mode: "standard", resolution: "720p" },
8454
+ { mode: "pro", resolution: "1080p" },
8455
+ { mode: "4k", resolution: "4k" }
8456
+ ];
8457
+ function klingTier(req) {
8458
+ return KLING_TIERS.find((tier) => tier.resolution === req.resolution) ?? KLING_TIERS.find((tier) => tier.mode === req.mode) ?? KLING_TIERS[0];
8459
+ }
8460
+
8278
8461
  // src/resolve-video-rules.ts
8279
8462
  var MOTION_PRO = "fal-ai/kling-video/v3/pro/motion-control";
8280
8463
  var MOTION_STANDARD = "fal-ai/kling-video/v3/standard/motion-control";
@@ -8448,6 +8631,16 @@ function h3EndFrameRatioWarning(model, ratio) {
8448
8631
  message: `${model.id} follows the end frame's aspect ratio`
8449
8632
  };
8450
8633
  }
8634
+ function isReplicateKling(model) {
8635
+ if (model.adapter !== "replicate") return false;
8636
+ const kind = videoKind(model.providerModelId);
8637
+ return kind === "kling" || kind === "kling-omni";
8638
+ }
8639
+ function fillReplicateKlingResolution(model, req) {
8640
+ if (isReplicateKling(model) && req.resolution === void 0) {
8641
+ req.resolution = klingTier(req).resolution;
8642
+ }
8643
+ }
8451
8644
  function applyFalVideoRules(model, req, callerRatio, warnings) {
8452
8645
  if (model.adapter !== "fal") return;
8453
8646
  const problem = falVideoProblem(model.providerModelId, withPromptForResolve(req));
@@ -8837,8 +9030,10 @@ function resolveVideoOptions(model, request, onUnsupported = "error") {
8837
9030
  warnings
8838
9031
  );
8839
9032
  if (aspectRatio !== void 0) merged.aspectRatio = aspectRatio;
9033
+ fillReplicateKlingResolution(model, merged);
8840
9034
  const resolution = resolveVideoResolution(merged.resolution, model, onUnsupported, warnings);
8841
9035
  if (resolution !== void 0) merged.resolution = resolution;
9036
+ if (isReplicateKling(model)) delete merged.mode;
8842
9037
  const duration = resolveDuration(
8843
9038
  merged.durationSeconds,
8844
9039
  c.durations,