@shop-os/modelkit 0.7.8 → 0.7.11

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
@@ -4844,6 +4844,8 @@ function range(min, max) {
4844
4844
  }
4845
4845
  var KLING_PROMPT = { referenceSyntax: "numbered", maxChars: 2500 };
4846
4846
  var H3_PROMPT = { referenceSyntax: "numbered", maxChars: 5e4 };
4847
+ var KLING_V4_PROMPT = { referenceSyntax: "numbered", maxChars: 2500 };
4848
+ var KLING_V4_AUDIO = "always";
4847
4849
  var NO_REFERENCES = { images: 0, videos: 0, audios: 0 };
4848
4850
  function klingMotionControl(tier) {
4849
4851
  const label = tier === "pro" ? "Pro" : "Standard";
@@ -4944,6 +4946,79 @@ var VIDEO_FAMILY_MODELS = [
4944
4946
  },
4945
4947
  capabilities: { acceptsSourceVideo: true, videoEdit: true }
4946
4948
  },
4949
+ {
4950
+ // https://fal.ai/models/kling/v4/reference-to-video/api (+ /text-to-video,
4951
+ // /image-to-video): one row; references (images or videos) go to reference-to-video,
4952
+ // else a start frame to image-to-video, else text-to-video. Reference images: 10, the
4953
+ // start frame included (fal: "counts toward the image reference limit"), 300 px min.
4954
+ // Reference videos: 5, 3-30.05 s each and 30 s combined, 700 px min, 23.9-60.05 fps.
4955
+ // The prompt names references as @Image1, @Video1 (no space; @Element1 for elements,
4956
+ // which the kit does not send). No sound switch on any endpoint.
4957
+ id: "kling-4.0",
4958
+ aliases: ["kling-4", "kling-v4"],
4959
+ label: "Kling 4.0",
4960
+ type: "video",
4961
+ adapter: "fal",
4962
+ providerModelId: "kling/v4",
4963
+ lifecycle: "active",
4964
+ exposure: ["cowork", "space_builder"],
4965
+ constraints: {
4966
+ aspectRatios: ["adaptive", "16:9", "9:16", "1:1", "21:9"],
4967
+ resolutions: ["720p", "1080p"],
4968
+ durations: { min: 3, max: 30 },
4969
+ durationPresets: [5, 10, 15, 30],
4970
+ references: {
4971
+ ...NO_REFERENCES,
4972
+ images: 10,
4973
+ videos: 5,
4974
+ videoSeconds: { min: 3, max: 30.05, total: 30 }
4975
+ },
4976
+ supports: ["lastFrame"],
4977
+ prompt: KLING_V4_PROMPT,
4978
+ audio: KLING_V4_AUDIO
4979
+ },
4980
+ defaults: { durationSeconds: 5, resolution: "1080p", aspectRatio: "16:9" },
4981
+ usage: { ...VIDEO_USAGE },
4982
+ pricingKind: "video_per_second",
4983
+ display: {
4984
+ provider: "kuaishou",
4985
+ logo: KLING_LOGO,
4986
+ description: "Kuaishou Kling 4.0: text-to-video, start/end frames, or reference-to-video from up to 10 images and 5 videos (30 seconds combined), 3-30 seconds at 720p or 1080p. Prompts name references @Image1, @Video1."
4987
+ },
4988
+ capabilities: { lastFrameRequiresFirstFrame: true }
4989
+ },
4990
+ {
4991
+ // https://fal.ai/models/kling/v4/video-to-video/edit/api: source 3-30.05 s (700 px
4992
+ // min, 23.9-60.05 fps), up to 10 reference images. duration 3-30, or left out for the
4993
+ // source's length. The prompt names the source @Video1 and images @Image1, @Image2.
4994
+ id: "kling-4.0-edit",
4995
+ aliases: ["kling-4-edit", "kling-v4-edit"],
4996
+ label: "Kling 4.0 Edit",
4997
+ type: "video",
4998
+ adapter: "fal",
4999
+ providerModelId: "kling/v4/video-to-video/edit",
5000
+ lifecycle: "active",
5001
+ exposure: ["cowork", "space_builder"],
5002
+ constraints: {
5003
+ resolutions: ["720p", "1080p"],
5004
+ durations: { min: 3, max: 30 },
5005
+ references: { ...NO_REFERENCES, images: 10 },
5006
+ requires: ["sourceVideo"],
5007
+ sourceVideoSeconds: { min: 3, max: 30.05 },
5008
+ supports: [],
5009
+ prompt: KLING_V4_PROMPT,
5010
+ audio: KLING_V4_AUDIO
5011
+ },
5012
+ defaults: { resolution: "1080p" },
5013
+ usage: { ...VIDEO_USAGE },
5014
+ pricingKind: "video_per_second",
5015
+ display: {
5016
+ provider: "kuaishou",
5017
+ logo: KLING_LOGO,
5018
+ description: "Kuaishou Kling 4.0 video edit: change a 3-30 second clip with a prompt and up to 10 reference images, at 720p or 1080p, as long as the source or a set 3-30 seconds. Prompts name the source @Video1 and images @Image1."
5019
+ },
5020
+ capabilities: { acceptsSourceVideo: true, videoEdit: true }
5021
+ },
4947
5022
  {
4948
5023
  // https://fal.ai/models/minimax/h3-max-turbo/text-to-video/api (+ /image-to-video).
4949
5024
  // image-to-video takes a start frame, an end frame, or both, and no aspect_ratio.
@@ -5802,38 +5877,38 @@ var VIDEO_MODELS = [
5802
5877
  capabilities: { ...FRAMES_EXCLUDE_REFERENCES }
5803
5878
  },
5804
5879
  {
5805
- // mode standard/pro/4k selects 720p/1080p/4K; the endpoint takes no separate resolution.
5806
- // Replicate: "Max 7 [images] without video, 4 with video"; 4K does not support
5807
- // reference_video. No `videos` limit yet: the adapter sends no reference video,
5808
- // so `imagesWithVideo` stays dormant until one is wired through.
5880
+ // fal Kling O3 (adapters/fal-kling-omni.ts): mode standard/pro/4k selects 720p/1080p/4K
5881
+ // in the endpoint path. Moved off Replicate kwaivgi/kling-v3-omni-video, which fal sells
5882
+ // at half the price; the id and inputs stay. The Replicate mapper stays for deployed
5883
+ // model_catalog rows not yet switched. Frames and reference images share fal's cap of 7.
5884
+ // Reference video, multi-shot and keep-sound were never wired, so stay off.
5809
5885
  id: "kling-v3-omni-video",
5810
5886
  aliases: ["kwaivgi/kling-v3-omni-video", "kling-v3-omni"],
5811
5887
  label: "Kling V3 Omni",
5812
5888
  type: "video",
5813
- adapter: "replicate",
5814
- providerModelId: "kwaivgi/kling-v3-omni-video",
5889
+ adapter: "fal",
5890
+ providerModelId: "fal-ai/kling-video/o3",
5815
5891
  lifecycle: "active",
5816
5892
  exposure: ["cowork", "space_builder"],
5817
5893
  constraints: {
5818
5894
  aspectRatios: [...KLING_OMNI_RATIOS],
5819
5895
  resolutions: ["720p", "1080p", "4k"],
5820
5896
  durations: { min: 3, max: 15 },
5821
- references: { images: 7, imagesWithVideo: 4 },
5897
+ references: { images: 7, videos: 0, audios: 0 },
5822
5898
  supports: ["generateAudio", "lastFrame"],
5823
5899
  prompt: KLING_PROMPT2,
5824
5900
  audio: "optional"
5825
5901
  },
5826
5902
  defaults: { durationSeconds: 5, aspectRatio: "16:9", generateAudio: true, mode: "pro" },
5827
- usage: { unit: "compute_seconds" },
5903
+ usage: { ...VIDEO_USAGE3 },
5828
5904
  pricingKind: "video_per_second",
5829
5905
  display: {
5830
5906
  provider: "kuaishou",
5831
5907
  logo: "https://cdn.jsdelivr.net/npm/@lobehub/icons-static-png@1.97.0/light/kling-color.png",
5832
- description: "Kuaishou Kling Video 3.0 Omni unified generation and editing from text, start/end images, up to 7 reference images, and reference video; up to 15 seconds at up to 4K."
5908
+ description: "Kuaishou Kling Video 3.0 Omni generation from text, start/end images and up to 7 reference images, with optional native audio; up to 15 seconds at up to 4K."
5833
5909
  }
5834
- // end_image "Requires start_image", but no lastFrameRequiresFirstFrame: SpaceLab resolves
5835
- // replicate rows in error mode, so the flag would refuse its end-frame-only nodes, which
5836
- // the adapter sends as start_image.
5910
+ // No lastFrameRequiresFirstFrame: fal image-to-video needs a start frame, so the adapter
5911
+ // sends a lone end frame as the start frame, as the Replicate model took it.
5837
5912
  },
5838
5913
  {
5839
5914
  id: "minimax-h3",
@@ -9448,11 +9523,11 @@ function toolInput(ctx, req, output) {
9448
9523
  }
9449
9524
  var EXR_DEFAULT_PROMPT = "The same video in high dynamic range, with unchanged content, motion, framing and timing.";
9450
9525
  async function submitFalVideoTool(req, ctx, opts) {
9451
- const plan3 = planVideoTool(ctx.model, req);
9452
- if (!plan3.ok) {
9526
+ const plan4 = planVideoTool(ctx.model, req);
9527
+ if (!plan4.ok) {
9453
9528
  throw new ModelKitError({
9454
9529
  code: "unsupported_option",
9455
- message: plan3.error,
9530
+ message: plan4.error,
9456
9531
  provider: "fal",
9457
9532
  model: ctx.model.id,
9458
9533
  retryable: false
@@ -9460,7 +9535,7 @@ async function submitFalVideoTool(req, ctx, opts) {
9460
9535
  }
9461
9536
  const target = new URL(`${FAL_QUEUE_ORIGIN}/${ctx.model.providerModelId}`);
9462
9537
  if (opts.webhookUrl !== void 0) target.searchParams.set("fal_webhook", opts.webhookUrl);
9463
- const res = await queueFetch(ctx, target.toString(), "POST", toolInput(ctx, req, plan3.output));
9538
+ const res = await queueFetch(ctx, target.toString(), "POST", toolInput(ctx, req, plan4.output));
9464
9539
  const parsed = submitResponseSchema.safeParse(await readJson(ctx, res));
9465
9540
  if (!parsed.success) {
9466
9541
  throw new ModelKitError({
@@ -9478,7 +9553,7 @@ async function submitFalVideoTool(req, ctx, opts) {
9478
9553
  statusUrl: parsed.data.status_url,
9479
9554
  responseUrl: parsed.data.response_url,
9480
9555
  cancelUrl: parsed.data.cancel_url,
9481
- output: plan3.output
9556
+ output: plan4.output
9482
9557
  };
9483
9558
  }
9484
9559
  async function falVideoToolStatus(sub, ctx) {
@@ -10167,14 +10242,14 @@ function planFalImage(req, providerModelId, model) {
10167
10242
  const exact = falExactSize(spec, req);
10168
10243
  const call = { label: spec.label, req, refs, edit: edit2, ...exact !== void 0 && { exact } };
10169
10244
  const built = family.build(call, model);
10170
- const plan3 = {
10245
+ const plan4 = {
10171
10246
  endpoint: edit2 && family.editEndpoint !== void 0 ? family.editEndpoint : id,
10172
10247
  input: built.input,
10173
10248
  output: family.output ?? "images"
10174
10249
  };
10175
- if (built.imageQuality !== void 0) plan3.imageQuality = built.imageQuality;
10176
- if (built.imageSize !== void 0) plan3.imageSize = built.imageSize;
10177
- return plan3;
10250
+ if (built.imageQuality !== void 0) plan4.imageQuality = built.imageQuality;
10251
+ if (built.imageSize !== void 0) plan4.imageSize = built.imageSize;
10252
+ return plan4;
10178
10253
  }
10179
10254
  var FalFileSchema = import_zod14.z.object({ url: import_zod14.z.string(), content_type: import_zod14.z.string().nullish() });
10180
10255
  var ImagesPayloadSchema = import_zod14.z.object({ images: import_zod14.z.array(FalFileSchema) });
@@ -10196,18 +10271,18 @@ async function downloadFalFile(ctx, file) {
10196
10271
  const mime = res.headers.get("content-type") ?? file.content_type ?? "";
10197
10272
  return { bytes: new Uint8Array(await res.arrayBuffer()), mime };
10198
10273
  }
10199
- function filesOf(payload, plan3) {
10200
- if (plan3.output === "image") {
10274
+ function filesOf(payload, plan4) {
10275
+ if (plan4.output === "image") {
10201
10276
  const parsed2 = ImagePayloadSchema.safeParse(payload);
10202
10277
  return parsed2.success ? [parsed2.data.image] : [];
10203
10278
  }
10204
10279
  const parsed = ImagesPayloadSchema.safeParse(payload);
10205
10280
  return parsed.success ? parsed.data.images : [];
10206
10281
  }
10207
- async function collectFalPlanImages(payload, plan3, ctx, req) {
10208
- const files = filesOf(payload, plan3);
10282
+ async function collectFalPlanImages(payload, plan4, ctx, req) {
10283
+ const files = filesOf(payload, plan4);
10209
10284
  if (files.length === 0) {
10210
- throw fail2(ctx.model.id, `fal ${plan3.endpoint} returned no image`, "provider_rejected");
10285
+ throw fail2(ctx.model.id, `fal ${plan4.endpoint} returned no image`, "provider_rejected");
10211
10286
  }
10212
10287
  const images = await Promise.all(
10213
10288
  files.map(async (file) => {
@@ -10222,18 +10297,18 @@ async function collectFalPlanImages(payload, plan3, ctx, req) {
10222
10297
  unit: "images",
10223
10298
  quantity: images.length
10224
10299
  };
10225
- const billedSize = plan3.imageSize ?? req.size;
10300
+ const billedSize = plan4.imageSize ?? req.size;
10226
10301
  if (billedSize !== void 0 && ctx.model.type === "image") usage3.imageSize = billedSize;
10227
- if (plan3.imageQuality !== void 0) usage3.imageQuality = plan3.imageQuality;
10302
+ if (plan4.imageQuality !== void 0) usage3.imageQuality = plan4.imageQuality;
10228
10303
  const result = { images, usage: usage3, raw: payload };
10229
10304
  const remote = files.find((f) => !f.url.startsWith("data:"));
10230
10305
  if (remote !== void 0) result.outputUrl = remote.url;
10231
10306
  return result;
10232
10307
  }
10233
10308
  async function generateFalFamilyImage(req, ctx, run) {
10234
- const plan3 = planFalImage(req, ctx.model.providerModelId, ctx.model.id);
10235
- const payload = await run(plan3.endpoint, plan3.input);
10236
- return collectFalPlanImages(payload, plan3, ctx, req);
10309
+ const plan4 = planFalImage(req, ctx.model.providerModelId, ctx.model.id);
10310
+ const payload = await run(plan4.endpoint, plan4.input);
10311
+ return collectFalPlanImages(payload, plan4, ctx, req);
10237
10312
  }
10238
10313
 
10239
10314
  // src/adapters/fal-mask.ts
@@ -11251,6 +11326,8 @@ var MOTION_PRO = "fal-ai/kling-video/v3/pro/motion-control";
11251
11326
  var MOTION_STANDARD = "fal-ai/kling-video/v3/standard/motion-control";
11252
11327
  var O3_REFERENCE = "fal-ai/kling-video/o3/pro/reference-to-video";
11253
11328
  var O3_EDIT = "fal-ai/kling-video/o3/pro/video-to-video/edit";
11329
+ var V4_FAMILY = "kling/v4";
11330
+ var V4_EDIT = "kling/v4/video-to-video/edit";
11254
11331
  function motionControl(endpoint, req, ctx) {
11255
11332
  const [character] = req.referenceImages ?? [];
11256
11333
  const [motion] = req.referenceVideos ?? [];
@@ -11310,6 +11387,61 @@ var klingV3Family = {
11310
11387
  return motionControl(id, req, ctx);
11311
11388
  }
11312
11389
  };
11390
+ function v4Generate(req, ctx) {
11391
+ const images = urls(req.referenceImages);
11392
+ const videos = urls(req.referenceVideos);
11393
+ const duration = wholeSeconds(req, ctx);
11394
+ const resolution = resolutionOf(req, ctx);
11395
+ const input = { prompt: req.prompt.trim(), duration, resolution };
11396
+ let endpoint;
11397
+ if (images.length + videos.length > 0) {
11398
+ endpoint = `${V4_FAMILY}/reference-to-video`;
11399
+ if (images.length > 0) input["image_urls"] = images;
11400
+ if (videos.length > 0) input["video_urls"] = videos;
11401
+ if (req.firstFrame) input["start_image_url"] = refToUrl(req.firstFrame);
11402
+ if (req.aspectRatio !== void 0) {
11403
+ input["aspect_ratio"] = req.aspectRatio === "adaptive" ? "auto" : req.aspectRatio;
11404
+ }
11405
+ } else if (req.firstFrame) {
11406
+ endpoint = `${V4_FAMILY}/image-to-video`;
11407
+ input["start_image_url"] = refToUrl(req.firstFrame);
11408
+ if (req.lastFrame) input["end_image_url"] = refToUrl(req.lastFrame);
11409
+ } else {
11410
+ endpoint = `${V4_FAMILY}/text-to-video`;
11411
+ const ratio = concreteRatio(req);
11412
+ if (ratio !== void 0) input["aspect_ratio"] = ratio;
11413
+ }
11414
+ return { endpoint, input, resolution, seconds: requestedSeconds(duration) };
11415
+ }
11416
+ function v4Edit(req, ctx) {
11417
+ if (req.sourceVideo === void 0) throw refusal(ctx, "needs a source video", "missing_input");
11418
+ const resolution = resolutionOf(req, ctx);
11419
+ const input = {
11420
+ prompt: req.prompt.trim(),
11421
+ video_url: refToUrl(req.sourceVideo),
11422
+ resolution
11423
+ };
11424
+ const images = urls(req.referenceImages);
11425
+ if (images.length > 0) input["image_urls"] = images;
11426
+ if (req.durationSeconds === void 0) {
11427
+ return {
11428
+ endpoint: V4_EDIT,
11429
+ input,
11430
+ resolution,
11431
+ seconds: inputLengthSeconds(req, req.sourceVideo)
11432
+ };
11433
+ }
11434
+ const duration = wholeSeconds(req, ctx);
11435
+ input["duration"] = duration;
11436
+ return { endpoint: V4_EDIT, input, resolution, seconds: requestedSeconds(duration) };
11437
+ }
11438
+ var KLING_V4_VIDEO_IDS = /* @__PURE__ */ new Set([V4_FAMILY, V4_EDIT]);
11439
+ var klingV4Family = {
11440
+ ids: KLING_V4_VIDEO_IDS,
11441
+ plan(req, ctx) {
11442
+ return ctx.model.providerModelId.toLowerCase() === V4_EDIT ? v4Edit(req, ctx) : v4Generate(req, ctx);
11443
+ }
11444
+ };
11313
11445
 
11314
11446
  // src/replicate-video-kinds.ts
11315
11447
  function replicateBaseModelId(providerModelId) {
@@ -11331,15 +11463,57 @@ var KLING_TIERS = [
11331
11463
  { mode: "pro", resolution: "1080p" },
11332
11464
  { mode: "4k", resolution: "4k" }
11333
11465
  ];
11466
+ var KLING_O3_OMNI = "fal-ai/kling-video/o3";
11334
11467
  function klingTier(req) {
11335
11468
  return KLING_TIERS.find((tier) => tier.resolution === req.resolution) ?? KLING_TIERS.find((tier) => tier.mode === req.mode) ?? KLING_TIERS[0];
11336
11469
  }
11337
11470
 
11471
+ // src/adapters/fal-kling-omni.ts
11472
+ function plan(req, ctx) {
11473
+ const tier = klingTier(req);
11474
+ const duration = wholeSeconds(req, ctx);
11475
+ const input = {
11476
+ prompt: req.prompt.trim(),
11477
+ duration: String(duration),
11478
+ generate_audio: req.generateAudio ?? true
11479
+ };
11480
+ const start = req.firstFrame ?? req.lastFrame;
11481
+ const end = req.firstFrame ? req.lastFrame : void 0;
11482
+ const images = urls(req.referenceImages);
11483
+ let task;
11484
+ if (images.length > 0) {
11485
+ task = "reference-to-video";
11486
+ input["image_urls"] = images;
11487
+ if (start) input["start_image_url"] = refToUrl(start);
11488
+ if (end) input["end_image_url"] = refToUrl(end);
11489
+ } else if (start) {
11490
+ task = "image-to-video";
11491
+ input["image_url"] = refToUrl(start);
11492
+ if (end) input["end_image_url"] = refToUrl(end);
11493
+ } else {
11494
+ task = "text-to-video";
11495
+ }
11496
+ const ratio = concreteRatio(req);
11497
+ if (ratio !== void 0 && start === void 0) input["aspect_ratio"] = ratio;
11498
+ return {
11499
+ endpoint: `${KLING_O3_OMNI}/${tier.mode}/${task}`,
11500
+ input,
11501
+ resolution: tier.resolution,
11502
+ seconds: requestedSeconds(duration)
11503
+ };
11504
+ }
11505
+ var klingOmniFamily = {
11506
+ ids: /* @__PURE__ */ new Set([KLING_O3_OMNI]),
11507
+ plan
11508
+ };
11509
+
11338
11510
  // src/resolve-video-rules.ts
11339
11511
  var MOTION_PRO2 = "fal-ai/kling-video/v3/pro/motion-control";
11340
11512
  var MOTION_STANDARD2 = "fal-ai/kling-video/v3/standard/motion-control";
11341
11513
  var O3_REFERENCE2 = "fal-ai/kling-video/o3/pro/reference-to-video";
11342
11514
  var O3_EDIT2 = "fal-ai/kling-video/o3/pro/video-to-video/edit";
11515
+ var KLING_V4 = "kling/v4";
11516
+ var KLING_V4_EDIT = "kling/v4/video-to-video/edit";
11343
11517
  var H3_TURBO = "minimax/h3-max-turbo";
11344
11518
  var H3_RECAST = "minimax/h3-max/recast";
11345
11519
  var H3_RELIGHT = "minimax/h3-max/relight";
@@ -11348,6 +11522,7 @@ var HAPPY_HORSE_EDIT = "alibaba/happy-horse/video-edit";
11348
11522
  var VIDU_Q4 = "fal-ai/vidu/q4";
11349
11523
  var OMNIHUMAN_V2 = "bytedance/omnihuman/v2";
11350
11524
  var KLING_O3_IMAGE_CAP = 7;
11525
+ var KLING_V4_IMAGE_CAP = 10;
11351
11526
  var TURBO_AUDIO_MIN_SECONDS = 2;
11352
11527
  var OMNIHUMAN_AUDIO_SECONDS = { min: 4, max: 300 };
11353
11528
  function missing(message) {
@@ -11403,6 +11578,23 @@ function o3Reference2(req) {
11403
11578
  }
11404
11579
  return needsPrompt(req);
11405
11580
  }
11581
+ function hasReferences(req) {
11582
+ return count(req.referenceImages) + count(req.referenceVideos) > 0;
11583
+ }
11584
+ function klingV4(req) {
11585
+ if (hasReferences(req)) {
11586
+ if (req.lastFrame !== void 0) {
11587
+ return unsupported2("takes an end frame only without reference images or videos");
11588
+ }
11589
+ const images = count(req.referenceImages) + (req.firstFrame === void 0 ? 0 : 1);
11590
+ if (images > KLING_V4_IMAGE_CAP) {
11591
+ return unsupported2(
11592
+ `takes up to ${KLING_V4_IMAGE_CAP} images in all, the start frame included; this request has ${images}`
11593
+ );
11594
+ }
11595
+ }
11596
+ return needsPrompt(req);
11597
+ }
11406
11598
  function h3Turbo(req) {
11407
11599
  if (count(req.referenceImages) + count(req.referenceVideos) > 0) {
11408
11600
  return unsupported2("takes no reference images or videos");
@@ -11479,7 +11671,10 @@ var RULES = /* @__PURE__ */ new Map([
11479
11671
  [MOTION_PRO2, motionControl2],
11480
11672
  [MOTION_STANDARD2, motionControl2],
11481
11673
  [O3_REFERENCE2, o3Reference2],
11674
+ [KLING_O3_OMNI, o3Reference2],
11482
11675
  [O3_EDIT2, (req) => needsSource(req) ?? needsPrompt(req)],
11676
+ [KLING_V4, klingV4],
11677
+ [KLING_V4_EDIT, (req) => needsSource(req) ?? needsPrompt(req)],
11483
11678
  [H3_TURBO, h3Turbo],
11484
11679
  [MINIMAX_H3_INSERT, h3Insert],
11485
11680
  [H3_RECAST, h3Recast],
@@ -11508,13 +11703,14 @@ function h3EndFrameRatioWarning(model, ratio) {
11508
11703
  message: `${model.id} follows the end frame's aspect ratio`
11509
11704
  };
11510
11705
  }
11511
- function isReplicateKling(model) {
11706
+ function takesKlingMode(model) {
11707
+ if (model.adapter === "fal") return model.providerModelId.toLowerCase() === KLING_O3_OMNI;
11512
11708
  if (model.adapter !== "replicate") return false;
11513
11709
  const kind = videoKind(model.providerModelId);
11514
11710
  return kind === "kling" || kind === "kling-omni";
11515
11711
  }
11516
- function fillReplicateKlingResolution(model, req) {
11517
- if (isReplicateKling(model) && req.resolution === void 0) {
11712
+ function fillKlingModeResolution(model, req) {
11713
+ if (takesKlingMode(model) && req.resolution === void 0) {
11518
11714
  req.resolution = klingTier(req).resolution;
11519
11715
  }
11520
11716
  }
@@ -11528,6 +11724,19 @@ function applyFalVideoRules(model, req, callerRatio, warnings) {
11528
11724
  warnings.push(viduSilentWarning(model));
11529
11725
  req.generateAudio = false;
11530
11726
  }
11727
+ if (id === KLING_V4 && req.firstFrame !== void 0 && !hasReferences(req)) {
11728
+ if (req.aspectRatio !== void 0) {
11729
+ if (callerRatio !== void 0) {
11730
+ warnings.push({
11731
+ code: "ratio_dropped",
11732
+ field: "aspectRatio",
11733
+ from: req.aspectRatio,
11734
+ message: `${model.id} follows the start frame's aspect ratio`
11735
+ });
11736
+ }
11737
+ delete req.aspectRatio;
11738
+ }
11739
+ }
11531
11740
  const endFrameOnly = req.lastFrame !== void 0 && req.firstFrame === void 0;
11532
11741
  if (id === H3_TURBO && endFrameOnly && req.aspectRatio !== void 0) {
11533
11742
  if (callerRatio !== void 0 && callerRatio !== "adaptive") {
@@ -11653,7 +11862,7 @@ var minimaxH3MaxFamily = {
11653
11862
 
11654
11863
  // src/adapters/fal-omnihuman.ts
11655
11864
  var OMNIHUMAN_V22 = "bytedance/omnihuman/v2";
11656
- function plan(req, ctx) {
11865
+ function plan2(req, ctx) {
11657
11866
  const [image] = req.referenceImages ?? [];
11658
11867
  const [audio] = req.referenceAudios ?? [];
11659
11868
  if (image === void 0 || audio === void 0) {
@@ -11664,14 +11873,14 @@ function plan(req, ctx) {
11664
11873
  return { endpoint: OMNIHUMAN_V22, input, seconds: inputLengthSeconds(req, audio) };
11665
11874
  }
11666
11875
  var OMNIHUMAN_MODEL_IDS = /* @__PURE__ */ new Set([OMNIHUMAN_V22]);
11667
- var omnihumanFamily = { ids: OMNIHUMAN_MODEL_IDS, plan };
11876
+ var omnihumanFamily = { ids: OMNIHUMAN_MODEL_IDS, plan: plan2 };
11668
11877
 
11669
11878
  // src/adapters/fal-vidu.ts
11670
11879
  var FAMILY2 = "fal-ai/vidu/q4";
11671
11880
  function viduTier(resolution) {
11672
11881
  return resolution === "2k" || resolution === "4k" ? upperTier(resolution) : resolution;
11673
11882
  }
11674
- function plan2(req, ctx) {
11883
+ function plan3(req, ctx) {
11675
11884
  const images = urls(req.referenceImages);
11676
11885
  const audios = urls(req.referenceAudios);
11677
11886
  const duration = wholeSeconds(req, ctx);
@@ -11698,11 +11907,13 @@ function plan2(req, ctx) {
11698
11907
  return { endpoint, input, resolution, seconds: requestedSeconds(duration) };
11699
11908
  }
11700
11909
  var VIDU_MODEL_IDS = /* @__PURE__ */ new Set([FAMILY2]);
11701
- var viduFamily = { ids: VIDU_MODEL_IDS, plan: plan2 };
11910
+ var viduFamily = { ids: VIDU_MODEL_IDS, plan: plan3 };
11702
11911
 
11703
11912
  // src/adapters/fal-video-families.ts
11704
11913
  var FAMILIES2 = [
11705
11914
  klingV3Family,
11915
+ klingOmniFamily,
11916
+ klingV4Family,
11706
11917
  minimaxH3MaxFamily,
11707
11918
  happyHorseFamily,
11708
11919
  viduFamily,
@@ -11989,8 +12200,8 @@ async function collectFalImages(payload, ctx, req, label) {
11989
12200
  };
11990
12201
  }
11991
12202
  async function generateSpecImage(req, ctx, deps, spec) {
11992
- const hasReferences = (req.references ?? []).length > 0;
11993
- const endpoint = hasReferences && spec.spec.editEndpoint !== void 0 ? spec.spec.editEndpoint : ctx.model.providerModelId;
12203
+ const hasReferences2 = (req.references ?? []).length > 0;
12204
+ const endpoint = hasReferences2 && spec.spec.editEndpoint !== void 0 ? spec.spec.editEndpoint : ctx.model.providerModelId;
11994
12205
  const payload = await runJob(ctx, deps, endpoint, spec.input);
11995
12206
  return collectFalImages(payload, ctx, req, `fal ${endpoint}`);
11996
12207
  }
@@ -12179,7 +12390,7 @@ function extractVideoUrl(result, endpoint, model) {
12179
12390
  }
12180
12391
  async function generateFalVideo(req, ctx, deps) {
12181
12392
  const providerModelId = ctx.model.providerModelId;
12182
- const plan3 = planFalFamilyVideo(req, ctx);
12393
+ const plan4 = planFalFamilyVideo(req, ctx);
12183
12394
  const start = (endpoint2, input, usageOf2) => startFalVideoJob(
12184
12395
  ctx,
12185
12396
  () => falClientOf(ctx, deps),
@@ -12187,7 +12398,7 @@ async function generateFalVideo(req, ctx, deps) {
12187
12398
  input,
12188
12399
  videoFileDecoder(ctx, req, endpoint2, usageOf2)
12189
12400
  );
12190
- if (plan3 !== void 0) return start(plan3.endpoint, plan3.input, planUsage(ctx, plan3));
12401
+ if (plan4 !== void 0) return start(plan4.endpoint, plan4.input, planUsage(ctx, plan4));
12191
12402
  if (isKling(providerModelId)) return start(KLING_O1_ENDPOINT, klingO1Input(req));
12192
12403
  if (isWan(providerModelId)) {
12193
12404
  const { endpoint: endpoint2, input } = wanVideo(req, providerModelId.toLowerCase());
@@ -12237,9 +12448,9 @@ function videoFileDecoder(ctx, req, endpoint, usageOf2) {
12237
12448
  raw: payload
12238
12449
  });
12239
12450
  }
12240
- function planUsage(ctx, plan3) {
12451
+ function planUsage(ctx, plan4) {
12241
12452
  return (payload) => {
12242
- const seconds3 = plan3.seconds(payload);
12453
+ const seconds3 = plan4.seconds(payload);
12243
12454
  return {
12244
12455
  model: ctx.model.id,
12245
12456
  provider: "fal",
@@ -12247,7 +12458,7 @@ function planUsage(ctx, plan3) {
12247
12458
  unit: "seconds",
12248
12459
  quantity: seconds3,
12249
12460
  durationSeconds: seconds3,
12250
- ...plan3.resolution !== void 0 && { resolution: plan3.resolution }
12461
+ ...plan4.resolution !== void 0 && { resolution: plan4.resolution }
12251
12462
  };
12252
12463
  };
12253
12464
  }
@@ -14335,8 +14546,8 @@ function imageBody(req, ctx, outputFormat) {
14335
14546
  function videoBody(req, ctx, mode) {
14336
14547
  const prompt = checkPrompt(req, ctx);
14337
14548
  const model = lumaModel(ctx);
14338
- const hasReferences = (req.referenceImages ?? []).length + (req.referenceVideos ?? []).length + (req.referenceAudios ?? []).length > 0;
14339
- if (hasReferences) {
14549
+ const hasReferences2 = (req.referenceImages ?? []).length + (req.referenceVideos ?? []).length + (req.referenceAudios ?? []).length > 0;
14550
+ if (hasReferences2) {
14340
14551
  refuseOrDrop(
14341
14552
  ctx,
14342
14553
  "referenceImages",
@@ -16699,10 +16910,10 @@ function resolveVideoOptions(model, request, onUnsupported = "error") {
16699
16910
  warnings
16700
16911
  );
16701
16912
  if (aspectRatio !== void 0) merged.aspectRatio = aspectRatio;
16702
- fillReplicateKlingResolution(model, merged);
16913
+ fillKlingModeResolution(model, merged);
16703
16914
  const resolution = resolveVideoResolution(merged.resolution, model, onUnsupported, warnings);
16704
16915
  if (resolution !== void 0) merged.resolution = resolution;
16705
- if (isReplicateKling(model)) delete merged.mode;
16916
+ if (takesKlingMode(model)) delete merged.mode;
16706
16917
  const duration = resolveDuration(
16707
16918
  merged.durationSeconds,
16708
16919
  c.durations,