@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.js CHANGED
@@ -4706,6 +4706,8 @@ function range(min, max) {
4706
4706
  }
4707
4707
  var KLING_PROMPT = { referenceSyntax: "numbered", maxChars: 2500 };
4708
4708
  var H3_PROMPT = { referenceSyntax: "numbered", maxChars: 5e4 };
4709
+ var KLING_V4_PROMPT = { referenceSyntax: "numbered", maxChars: 2500 };
4710
+ var KLING_V4_AUDIO = "always";
4709
4711
  var NO_REFERENCES = { images: 0, videos: 0, audios: 0 };
4710
4712
  function klingMotionControl(tier) {
4711
4713
  const label = tier === "pro" ? "Pro" : "Standard";
@@ -4806,6 +4808,79 @@ var VIDEO_FAMILY_MODELS = [
4806
4808
  },
4807
4809
  capabilities: { acceptsSourceVideo: true, videoEdit: true }
4808
4810
  },
4811
+ {
4812
+ // https://fal.ai/models/kling/v4/reference-to-video/api (+ /text-to-video,
4813
+ // /image-to-video): one row; references (images or videos) go to reference-to-video,
4814
+ // else a start frame to image-to-video, else text-to-video. Reference images: 10, the
4815
+ // start frame included (fal: "counts toward the image reference limit"), 300 px min.
4816
+ // Reference videos: 5, 3-30.05 s each and 30 s combined, 700 px min, 23.9-60.05 fps.
4817
+ // The prompt names references as @Image1, @Video1 (no space; @Element1 for elements,
4818
+ // which the kit does not send). No sound switch on any endpoint.
4819
+ id: "kling-4.0",
4820
+ aliases: ["kling-4", "kling-v4"],
4821
+ label: "Kling 4.0",
4822
+ type: "video",
4823
+ adapter: "fal",
4824
+ providerModelId: "kling/v4",
4825
+ lifecycle: "active",
4826
+ exposure: ["cowork", "space_builder"],
4827
+ constraints: {
4828
+ aspectRatios: ["adaptive", "16:9", "9:16", "1:1", "21:9"],
4829
+ resolutions: ["720p", "1080p"],
4830
+ durations: { min: 3, max: 30 },
4831
+ durationPresets: [5, 10, 15, 30],
4832
+ references: {
4833
+ ...NO_REFERENCES,
4834
+ images: 10,
4835
+ videos: 5,
4836
+ videoSeconds: { min: 3, max: 30.05, total: 30 }
4837
+ },
4838
+ supports: ["lastFrame"],
4839
+ prompt: KLING_V4_PROMPT,
4840
+ audio: KLING_V4_AUDIO
4841
+ },
4842
+ defaults: { durationSeconds: 5, resolution: "1080p", aspectRatio: "16:9" },
4843
+ usage: { ...VIDEO_USAGE },
4844
+ pricingKind: "video_per_second",
4845
+ display: {
4846
+ provider: "kuaishou",
4847
+ logo: KLING_LOGO,
4848
+ 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."
4849
+ },
4850
+ capabilities: { lastFrameRequiresFirstFrame: true }
4851
+ },
4852
+ {
4853
+ // https://fal.ai/models/kling/v4/video-to-video/edit/api: source 3-30.05 s (700 px
4854
+ // min, 23.9-60.05 fps), up to 10 reference images. duration 3-30, or left out for the
4855
+ // source's length. The prompt names the source @Video1 and images @Image1, @Image2.
4856
+ id: "kling-4.0-edit",
4857
+ aliases: ["kling-4-edit", "kling-v4-edit"],
4858
+ label: "Kling 4.0 Edit",
4859
+ type: "video",
4860
+ adapter: "fal",
4861
+ providerModelId: "kling/v4/video-to-video/edit",
4862
+ lifecycle: "active",
4863
+ exposure: ["cowork", "space_builder"],
4864
+ constraints: {
4865
+ resolutions: ["720p", "1080p"],
4866
+ durations: { min: 3, max: 30 },
4867
+ references: { ...NO_REFERENCES, images: 10 },
4868
+ requires: ["sourceVideo"],
4869
+ sourceVideoSeconds: { min: 3, max: 30.05 },
4870
+ supports: [],
4871
+ prompt: KLING_V4_PROMPT,
4872
+ audio: KLING_V4_AUDIO
4873
+ },
4874
+ defaults: { resolution: "1080p" },
4875
+ usage: { ...VIDEO_USAGE },
4876
+ pricingKind: "video_per_second",
4877
+ display: {
4878
+ provider: "kuaishou",
4879
+ logo: KLING_LOGO,
4880
+ 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."
4881
+ },
4882
+ capabilities: { acceptsSourceVideo: true, videoEdit: true }
4883
+ },
4809
4884
  {
4810
4885
  // https://fal.ai/models/minimax/h3-max-turbo/text-to-video/api (+ /image-to-video).
4811
4886
  // image-to-video takes a start frame, an end frame, or both, and no aspect_ratio.
@@ -5664,38 +5739,38 @@ var VIDEO_MODELS = [
5664
5739
  capabilities: { ...FRAMES_EXCLUDE_REFERENCES }
5665
5740
  },
5666
5741
  {
5667
- // mode standard/pro/4k selects 720p/1080p/4K; the endpoint takes no separate resolution.
5668
- // Replicate: "Max 7 [images] without video, 4 with video"; 4K does not support
5669
- // reference_video. No `videos` limit yet: the adapter sends no reference video,
5670
- // so `imagesWithVideo` stays dormant until one is wired through.
5742
+ // fal Kling O3 (adapters/fal-kling-omni.ts): mode standard/pro/4k selects 720p/1080p/4K
5743
+ // in the endpoint path. Moved off Replicate kwaivgi/kling-v3-omni-video, which fal sells
5744
+ // at half the price; the id and inputs stay. The Replicate mapper stays for deployed
5745
+ // model_catalog rows not yet switched. Frames and reference images share fal's cap of 7.
5746
+ // Reference video, multi-shot and keep-sound were never wired, so stay off.
5671
5747
  id: "kling-v3-omni-video",
5672
5748
  aliases: ["kwaivgi/kling-v3-omni-video", "kling-v3-omni"],
5673
5749
  label: "Kling V3 Omni",
5674
5750
  type: "video",
5675
- adapter: "replicate",
5676
- providerModelId: "kwaivgi/kling-v3-omni-video",
5751
+ adapter: "fal",
5752
+ providerModelId: "fal-ai/kling-video/o3",
5677
5753
  lifecycle: "active",
5678
5754
  exposure: ["cowork", "space_builder"],
5679
5755
  constraints: {
5680
5756
  aspectRatios: [...KLING_OMNI_RATIOS],
5681
5757
  resolutions: ["720p", "1080p", "4k"],
5682
5758
  durations: { min: 3, max: 15 },
5683
- references: { images: 7, imagesWithVideo: 4 },
5759
+ references: { images: 7, videos: 0, audios: 0 },
5684
5760
  supports: ["generateAudio", "lastFrame"],
5685
5761
  prompt: KLING_PROMPT2,
5686
5762
  audio: "optional"
5687
5763
  },
5688
5764
  defaults: { durationSeconds: 5, aspectRatio: "16:9", generateAudio: true, mode: "pro" },
5689
- usage: { unit: "compute_seconds" },
5765
+ usage: { ...VIDEO_USAGE3 },
5690
5766
  pricingKind: "video_per_second",
5691
5767
  display: {
5692
5768
  provider: "kuaishou",
5693
5769
  logo: "https://cdn.jsdelivr.net/npm/@lobehub/icons-static-png@1.97.0/light/kling-color.png",
5694
- 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."
5770
+ 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."
5695
5771
  }
5696
- // end_image "Requires start_image", but no lastFrameRequiresFirstFrame: SpaceLab resolves
5697
- // replicate rows in error mode, so the flag would refuse its end-frame-only nodes, which
5698
- // the adapter sends as start_image.
5772
+ // No lastFrameRequiresFirstFrame: fal image-to-video needs a start frame, so the adapter
5773
+ // sends a lone end frame as the start frame, as the Replicate model took it.
5699
5774
  },
5700
5775
  {
5701
5776
  id: "minimax-h3",
@@ -9310,11 +9385,11 @@ function toolInput(ctx, req, output) {
9310
9385
  }
9311
9386
  var EXR_DEFAULT_PROMPT = "The same video in high dynamic range, with unchanged content, motion, framing and timing.";
9312
9387
  async function submitFalVideoTool(req, ctx, opts) {
9313
- const plan3 = planVideoTool(ctx.model, req);
9314
- if (!plan3.ok) {
9388
+ const plan4 = planVideoTool(ctx.model, req);
9389
+ if (!plan4.ok) {
9315
9390
  throw new ModelKitError({
9316
9391
  code: "unsupported_option",
9317
- message: plan3.error,
9392
+ message: plan4.error,
9318
9393
  provider: "fal",
9319
9394
  model: ctx.model.id,
9320
9395
  retryable: false
@@ -9322,7 +9397,7 @@ async function submitFalVideoTool(req, ctx, opts) {
9322
9397
  }
9323
9398
  const target = new URL(`${FAL_QUEUE_ORIGIN}/${ctx.model.providerModelId}`);
9324
9399
  if (opts.webhookUrl !== void 0) target.searchParams.set("fal_webhook", opts.webhookUrl);
9325
- const res = await queueFetch(ctx, target.toString(), "POST", toolInput(ctx, req, plan3.output));
9400
+ const res = await queueFetch(ctx, target.toString(), "POST", toolInput(ctx, req, plan4.output));
9326
9401
  const parsed = submitResponseSchema.safeParse(await readJson(ctx, res));
9327
9402
  if (!parsed.success) {
9328
9403
  throw new ModelKitError({
@@ -9340,7 +9415,7 @@ async function submitFalVideoTool(req, ctx, opts) {
9340
9415
  statusUrl: parsed.data.status_url,
9341
9416
  responseUrl: parsed.data.response_url,
9342
9417
  cancelUrl: parsed.data.cancel_url,
9343
- output: plan3.output
9418
+ output: plan4.output
9344
9419
  };
9345
9420
  }
9346
9421
  async function falVideoToolStatus(sub, ctx) {
@@ -10029,14 +10104,14 @@ function planFalImage(req, providerModelId, model) {
10029
10104
  const exact = falExactSize(spec, req);
10030
10105
  const call = { label: spec.label, req, refs, edit: edit2, ...exact !== void 0 && { exact } };
10031
10106
  const built = family.build(call, model);
10032
- const plan3 = {
10107
+ const plan4 = {
10033
10108
  endpoint: edit2 && family.editEndpoint !== void 0 ? family.editEndpoint : id,
10034
10109
  input: built.input,
10035
10110
  output: family.output ?? "images"
10036
10111
  };
10037
- if (built.imageQuality !== void 0) plan3.imageQuality = built.imageQuality;
10038
- if (built.imageSize !== void 0) plan3.imageSize = built.imageSize;
10039
- return plan3;
10112
+ if (built.imageQuality !== void 0) plan4.imageQuality = built.imageQuality;
10113
+ if (built.imageSize !== void 0) plan4.imageSize = built.imageSize;
10114
+ return plan4;
10040
10115
  }
10041
10116
  var FalFileSchema = z14.object({ url: z14.string(), content_type: z14.string().nullish() });
10042
10117
  var ImagesPayloadSchema = z14.object({ images: z14.array(FalFileSchema) });
@@ -10058,18 +10133,18 @@ async function downloadFalFile(ctx, file) {
10058
10133
  const mime = res.headers.get("content-type") ?? file.content_type ?? "";
10059
10134
  return { bytes: new Uint8Array(await res.arrayBuffer()), mime };
10060
10135
  }
10061
- function filesOf(payload, plan3) {
10062
- if (plan3.output === "image") {
10136
+ function filesOf(payload, plan4) {
10137
+ if (plan4.output === "image") {
10063
10138
  const parsed2 = ImagePayloadSchema.safeParse(payload);
10064
10139
  return parsed2.success ? [parsed2.data.image] : [];
10065
10140
  }
10066
10141
  const parsed = ImagesPayloadSchema.safeParse(payload);
10067
10142
  return parsed.success ? parsed.data.images : [];
10068
10143
  }
10069
- async function collectFalPlanImages(payload, plan3, ctx, req) {
10070
- const files = filesOf(payload, plan3);
10144
+ async function collectFalPlanImages(payload, plan4, ctx, req) {
10145
+ const files = filesOf(payload, plan4);
10071
10146
  if (files.length === 0) {
10072
- throw fail2(ctx.model.id, `fal ${plan3.endpoint} returned no image`, "provider_rejected");
10147
+ throw fail2(ctx.model.id, `fal ${plan4.endpoint} returned no image`, "provider_rejected");
10073
10148
  }
10074
10149
  const images = await Promise.all(
10075
10150
  files.map(async (file) => {
@@ -10084,18 +10159,18 @@ async function collectFalPlanImages(payload, plan3, ctx, req) {
10084
10159
  unit: "images",
10085
10160
  quantity: images.length
10086
10161
  };
10087
- const billedSize = plan3.imageSize ?? req.size;
10162
+ const billedSize = plan4.imageSize ?? req.size;
10088
10163
  if (billedSize !== void 0 && ctx.model.type === "image") usage3.imageSize = billedSize;
10089
- if (plan3.imageQuality !== void 0) usage3.imageQuality = plan3.imageQuality;
10164
+ if (plan4.imageQuality !== void 0) usage3.imageQuality = plan4.imageQuality;
10090
10165
  const result = { images, usage: usage3, raw: payload };
10091
10166
  const remote = files.find((f) => !f.url.startsWith("data:"));
10092
10167
  if (remote !== void 0) result.outputUrl = remote.url;
10093
10168
  return result;
10094
10169
  }
10095
10170
  async function generateFalFamilyImage(req, ctx, run) {
10096
- const plan3 = planFalImage(req, ctx.model.providerModelId, ctx.model.id);
10097
- const payload = await run(plan3.endpoint, plan3.input);
10098
- return collectFalPlanImages(payload, plan3, ctx, req);
10171
+ const plan4 = planFalImage(req, ctx.model.providerModelId, ctx.model.id);
10172
+ const payload = await run(plan4.endpoint, plan4.input);
10173
+ return collectFalPlanImages(payload, plan4, ctx, req);
10099
10174
  }
10100
10175
 
10101
10176
  // src/adapters/fal-mask.ts
@@ -11113,6 +11188,8 @@ var MOTION_PRO = "fal-ai/kling-video/v3/pro/motion-control";
11113
11188
  var MOTION_STANDARD = "fal-ai/kling-video/v3/standard/motion-control";
11114
11189
  var O3_REFERENCE = "fal-ai/kling-video/o3/pro/reference-to-video";
11115
11190
  var O3_EDIT = "fal-ai/kling-video/o3/pro/video-to-video/edit";
11191
+ var V4_FAMILY = "kling/v4";
11192
+ var V4_EDIT = "kling/v4/video-to-video/edit";
11116
11193
  function motionControl(endpoint, req, ctx) {
11117
11194
  const [character] = req.referenceImages ?? [];
11118
11195
  const [motion] = req.referenceVideos ?? [];
@@ -11172,6 +11249,61 @@ var klingV3Family = {
11172
11249
  return motionControl(id, req, ctx);
11173
11250
  }
11174
11251
  };
11252
+ function v4Generate(req, ctx) {
11253
+ const images = urls(req.referenceImages);
11254
+ const videos = urls(req.referenceVideos);
11255
+ const duration = wholeSeconds(req, ctx);
11256
+ const resolution = resolutionOf(req, ctx);
11257
+ const input = { prompt: req.prompt.trim(), duration, resolution };
11258
+ let endpoint;
11259
+ if (images.length + videos.length > 0) {
11260
+ endpoint = `${V4_FAMILY}/reference-to-video`;
11261
+ if (images.length > 0) input["image_urls"] = images;
11262
+ if (videos.length > 0) input["video_urls"] = videos;
11263
+ if (req.firstFrame) input["start_image_url"] = refToUrl(req.firstFrame);
11264
+ if (req.aspectRatio !== void 0) {
11265
+ input["aspect_ratio"] = req.aspectRatio === "adaptive" ? "auto" : req.aspectRatio;
11266
+ }
11267
+ } else if (req.firstFrame) {
11268
+ endpoint = `${V4_FAMILY}/image-to-video`;
11269
+ input["start_image_url"] = refToUrl(req.firstFrame);
11270
+ if (req.lastFrame) input["end_image_url"] = refToUrl(req.lastFrame);
11271
+ } else {
11272
+ endpoint = `${V4_FAMILY}/text-to-video`;
11273
+ const ratio = concreteRatio(req);
11274
+ if (ratio !== void 0) input["aspect_ratio"] = ratio;
11275
+ }
11276
+ return { endpoint, input, resolution, seconds: requestedSeconds(duration) };
11277
+ }
11278
+ function v4Edit(req, ctx) {
11279
+ if (req.sourceVideo === void 0) throw refusal(ctx, "needs a source video", "missing_input");
11280
+ const resolution = resolutionOf(req, ctx);
11281
+ const input = {
11282
+ prompt: req.prompt.trim(),
11283
+ video_url: refToUrl(req.sourceVideo),
11284
+ resolution
11285
+ };
11286
+ const images = urls(req.referenceImages);
11287
+ if (images.length > 0) input["image_urls"] = images;
11288
+ if (req.durationSeconds === void 0) {
11289
+ return {
11290
+ endpoint: V4_EDIT,
11291
+ input,
11292
+ resolution,
11293
+ seconds: inputLengthSeconds(req, req.sourceVideo)
11294
+ };
11295
+ }
11296
+ const duration = wholeSeconds(req, ctx);
11297
+ input["duration"] = duration;
11298
+ return { endpoint: V4_EDIT, input, resolution, seconds: requestedSeconds(duration) };
11299
+ }
11300
+ var KLING_V4_VIDEO_IDS = /* @__PURE__ */ new Set([V4_FAMILY, V4_EDIT]);
11301
+ var klingV4Family = {
11302
+ ids: KLING_V4_VIDEO_IDS,
11303
+ plan(req, ctx) {
11304
+ return ctx.model.providerModelId.toLowerCase() === V4_EDIT ? v4Edit(req, ctx) : v4Generate(req, ctx);
11305
+ }
11306
+ };
11175
11307
 
11176
11308
  // src/replicate-video-kinds.ts
11177
11309
  function replicateBaseModelId(providerModelId) {
@@ -11193,15 +11325,57 @@ var KLING_TIERS = [
11193
11325
  { mode: "pro", resolution: "1080p" },
11194
11326
  { mode: "4k", resolution: "4k" }
11195
11327
  ];
11328
+ var KLING_O3_OMNI = "fal-ai/kling-video/o3";
11196
11329
  function klingTier(req) {
11197
11330
  return KLING_TIERS.find((tier) => tier.resolution === req.resolution) ?? KLING_TIERS.find((tier) => tier.mode === req.mode) ?? KLING_TIERS[0];
11198
11331
  }
11199
11332
 
11333
+ // src/adapters/fal-kling-omni.ts
11334
+ function plan(req, ctx) {
11335
+ const tier = klingTier(req);
11336
+ const duration = wholeSeconds(req, ctx);
11337
+ const input = {
11338
+ prompt: req.prompt.trim(),
11339
+ duration: String(duration),
11340
+ generate_audio: req.generateAudio ?? true
11341
+ };
11342
+ const start = req.firstFrame ?? req.lastFrame;
11343
+ const end = req.firstFrame ? req.lastFrame : void 0;
11344
+ const images = urls(req.referenceImages);
11345
+ let task;
11346
+ if (images.length > 0) {
11347
+ task = "reference-to-video";
11348
+ input["image_urls"] = images;
11349
+ if (start) input["start_image_url"] = refToUrl(start);
11350
+ if (end) input["end_image_url"] = refToUrl(end);
11351
+ } else if (start) {
11352
+ task = "image-to-video";
11353
+ input["image_url"] = refToUrl(start);
11354
+ if (end) input["end_image_url"] = refToUrl(end);
11355
+ } else {
11356
+ task = "text-to-video";
11357
+ }
11358
+ const ratio = concreteRatio(req);
11359
+ if (ratio !== void 0 && start === void 0) input["aspect_ratio"] = ratio;
11360
+ return {
11361
+ endpoint: `${KLING_O3_OMNI}/${tier.mode}/${task}`,
11362
+ input,
11363
+ resolution: tier.resolution,
11364
+ seconds: requestedSeconds(duration)
11365
+ };
11366
+ }
11367
+ var klingOmniFamily = {
11368
+ ids: /* @__PURE__ */ new Set([KLING_O3_OMNI]),
11369
+ plan
11370
+ };
11371
+
11200
11372
  // src/resolve-video-rules.ts
11201
11373
  var MOTION_PRO2 = "fal-ai/kling-video/v3/pro/motion-control";
11202
11374
  var MOTION_STANDARD2 = "fal-ai/kling-video/v3/standard/motion-control";
11203
11375
  var O3_REFERENCE2 = "fal-ai/kling-video/o3/pro/reference-to-video";
11204
11376
  var O3_EDIT2 = "fal-ai/kling-video/o3/pro/video-to-video/edit";
11377
+ var KLING_V4 = "kling/v4";
11378
+ var KLING_V4_EDIT = "kling/v4/video-to-video/edit";
11205
11379
  var H3_TURBO = "minimax/h3-max-turbo";
11206
11380
  var H3_RECAST = "minimax/h3-max/recast";
11207
11381
  var H3_RELIGHT = "minimax/h3-max/relight";
@@ -11210,6 +11384,7 @@ var HAPPY_HORSE_EDIT = "alibaba/happy-horse/video-edit";
11210
11384
  var VIDU_Q4 = "fal-ai/vidu/q4";
11211
11385
  var OMNIHUMAN_V2 = "bytedance/omnihuman/v2";
11212
11386
  var KLING_O3_IMAGE_CAP = 7;
11387
+ var KLING_V4_IMAGE_CAP = 10;
11213
11388
  var TURBO_AUDIO_MIN_SECONDS = 2;
11214
11389
  var OMNIHUMAN_AUDIO_SECONDS = { min: 4, max: 300 };
11215
11390
  function missing(message) {
@@ -11265,6 +11440,23 @@ function o3Reference2(req) {
11265
11440
  }
11266
11441
  return needsPrompt(req);
11267
11442
  }
11443
+ function hasReferences(req) {
11444
+ return count(req.referenceImages) + count(req.referenceVideos) > 0;
11445
+ }
11446
+ function klingV4(req) {
11447
+ if (hasReferences(req)) {
11448
+ if (req.lastFrame !== void 0) {
11449
+ return unsupported2("takes an end frame only without reference images or videos");
11450
+ }
11451
+ const images = count(req.referenceImages) + (req.firstFrame === void 0 ? 0 : 1);
11452
+ if (images > KLING_V4_IMAGE_CAP) {
11453
+ return unsupported2(
11454
+ `takes up to ${KLING_V4_IMAGE_CAP} images in all, the start frame included; this request has ${images}`
11455
+ );
11456
+ }
11457
+ }
11458
+ return needsPrompt(req);
11459
+ }
11268
11460
  function h3Turbo(req) {
11269
11461
  if (count(req.referenceImages) + count(req.referenceVideos) > 0) {
11270
11462
  return unsupported2("takes no reference images or videos");
@@ -11341,7 +11533,10 @@ var RULES = /* @__PURE__ */ new Map([
11341
11533
  [MOTION_PRO2, motionControl2],
11342
11534
  [MOTION_STANDARD2, motionControl2],
11343
11535
  [O3_REFERENCE2, o3Reference2],
11536
+ [KLING_O3_OMNI, o3Reference2],
11344
11537
  [O3_EDIT2, (req) => needsSource(req) ?? needsPrompt(req)],
11538
+ [KLING_V4, klingV4],
11539
+ [KLING_V4_EDIT, (req) => needsSource(req) ?? needsPrompt(req)],
11345
11540
  [H3_TURBO, h3Turbo],
11346
11541
  [MINIMAX_H3_INSERT, h3Insert],
11347
11542
  [H3_RECAST, h3Recast],
@@ -11370,13 +11565,14 @@ function h3EndFrameRatioWarning(model, ratio) {
11370
11565
  message: `${model.id} follows the end frame's aspect ratio`
11371
11566
  };
11372
11567
  }
11373
- function isReplicateKling(model) {
11568
+ function takesKlingMode(model) {
11569
+ if (model.adapter === "fal") return model.providerModelId.toLowerCase() === KLING_O3_OMNI;
11374
11570
  if (model.adapter !== "replicate") return false;
11375
11571
  const kind = videoKind(model.providerModelId);
11376
11572
  return kind === "kling" || kind === "kling-omni";
11377
11573
  }
11378
- function fillReplicateKlingResolution(model, req) {
11379
- if (isReplicateKling(model) && req.resolution === void 0) {
11574
+ function fillKlingModeResolution(model, req) {
11575
+ if (takesKlingMode(model) && req.resolution === void 0) {
11380
11576
  req.resolution = klingTier(req).resolution;
11381
11577
  }
11382
11578
  }
@@ -11390,6 +11586,19 @@ function applyFalVideoRules(model, req, callerRatio, warnings) {
11390
11586
  warnings.push(viduSilentWarning(model));
11391
11587
  req.generateAudio = false;
11392
11588
  }
11589
+ if (id === KLING_V4 && req.firstFrame !== void 0 && !hasReferences(req)) {
11590
+ if (req.aspectRatio !== void 0) {
11591
+ if (callerRatio !== void 0) {
11592
+ warnings.push({
11593
+ code: "ratio_dropped",
11594
+ field: "aspectRatio",
11595
+ from: req.aspectRatio,
11596
+ message: `${model.id} follows the start frame's aspect ratio`
11597
+ });
11598
+ }
11599
+ delete req.aspectRatio;
11600
+ }
11601
+ }
11393
11602
  const endFrameOnly = req.lastFrame !== void 0 && req.firstFrame === void 0;
11394
11603
  if (id === H3_TURBO && endFrameOnly && req.aspectRatio !== void 0) {
11395
11604
  if (callerRatio !== void 0 && callerRatio !== "adaptive") {
@@ -11515,7 +11724,7 @@ var minimaxH3MaxFamily = {
11515
11724
 
11516
11725
  // src/adapters/fal-omnihuman.ts
11517
11726
  var OMNIHUMAN_V22 = "bytedance/omnihuman/v2";
11518
- function plan(req, ctx) {
11727
+ function plan2(req, ctx) {
11519
11728
  const [image] = req.referenceImages ?? [];
11520
11729
  const [audio] = req.referenceAudios ?? [];
11521
11730
  if (image === void 0 || audio === void 0) {
@@ -11526,14 +11735,14 @@ function plan(req, ctx) {
11526
11735
  return { endpoint: OMNIHUMAN_V22, input, seconds: inputLengthSeconds(req, audio) };
11527
11736
  }
11528
11737
  var OMNIHUMAN_MODEL_IDS = /* @__PURE__ */ new Set([OMNIHUMAN_V22]);
11529
- var omnihumanFamily = { ids: OMNIHUMAN_MODEL_IDS, plan };
11738
+ var omnihumanFamily = { ids: OMNIHUMAN_MODEL_IDS, plan: plan2 };
11530
11739
 
11531
11740
  // src/adapters/fal-vidu.ts
11532
11741
  var FAMILY2 = "fal-ai/vidu/q4";
11533
11742
  function viduTier(resolution) {
11534
11743
  return resolution === "2k" || resolution === "4k" ? upperTier(resolution) : resolution;
11535
11744
  }
11536
- function plan2(req, ctx) {
11745
+ function plan3(req, ctx) {
11537
11746
  const images = urls(req.referenceImages);
11538
11747
  const audios = urls(req.referenceAudios);
11539
11748
  const duration = wholeSeconds(req, ctx);
@@ -11560,11 +11769,13 @@ function plan2(req, ctx) {
11560
11769
  return { endpoint, input, resolution, seconds: requestedSeconds(duration) };
11561
11770
  }
11562
11771
  var VIDU_MODEL_IDS = /* @__PURE__ */ new Set([FAMILY2]);
11563
- var viduFamily = { ids: VIDU_MODEL_IDS, plan: plan2 };
11772
+ var viduFamily = { ids: VIDU_MODEL_IDS, plan: plan3 };
11564
11773
 
11565
11774
  // src/adapters/fal-video-families.ts
11566
11775
  var FAMILIES2 = [
11567
11776
  klingV3Family,
11777
+ klingOmniFamily,
11778
+ klingV4Family,
11568
11779
  minimaxH3MaxFamily,
11569
11780
  happyHorseFamily,
11570
11781
  viduFamily,
@@ -11851,8 +12062,8 @@ async function collectFalImages(payload, ctx, req, label) {
11851
12062
  };
11852
12063
  }
11853
12064
  async function generateSpecImage(req, ctx, deps, spec) {
11854
- const hasReferences = (req.references ?? []).length > 0;
11855
- const endpoint = hasReferences && spec.spec.editEndpoint !== void 0 ? spec.spec.editEndpoint : ctx.model.providerModelId;
12065
+ const hasReferences2 = (req.references ?? []).length > 0;
12066
+ const endpoint = hasReferences2 && spec.spec.editEndpoint !== void 0 ? spec.spec.editEndpoint : ctx.model.providerModelId;
11856
12067
  const payload = await runJob(ctx, deps, endpoint, spec.input);
11857
12068
  return collectFalImages(payload, ctx, req, `fal ${endpoint}`);
11858
12069
  }
@@ -12041,7 +12252,7 @@ function extractVideoUrl(result, endpoint, model) {
12041
12252
  }
12042
12253
  async function generateFalVideo(req, ctx, deps) {
12043
12254
  const providerModelId = ctx.model.providerModelId;
12044
- const plan3 = planFalFamilyVideo(req, ctx);
12255
+ const plan4 = planFalFamilyVideo(req, ctx);
12045
12256
  const start = (endpoint2, input, usageOf2) => startFalVideoJob(
12046
12257
  ctx,
12047
12258
  () => falClientOf(ctx, deps),
@@ -12049,7 +12260,7 @@ async function generateFalVideo(req, ctx, deps) {
12049
12260
  input,
12050
12261
  videoFileDecoder(ctx, req, endpoint2, usageOf2)
12051
12262
  );
12052
- if (plan3 !== void 0) return start(plan3.endpoint, plan3.input, planUsage(ctx, plan3));
12263
+ if (plan4 !== void 0) return start(plan4.endpoint, plan4.input, planUsage(ctx, plan4));
12053
12264
  if (isKling(providerModelId)) return start(KLING_O1_ENDPOINT, klingO1Input(req));
12054
12265
  if (isWan(providerModelId)) {
12055
12266
  const { endpoint: endpoint2, input } = wanVideo(req, providerModelId.toLowerCase());
@@ -12099,9 +12310,9 @@ function videoFileDecoder(ctx, req, endpoint, usageOf2) {
12099
12310
  raw: payload
12100
12311
  });
12101
12312
  }
12102
- function planUsage(ctx, plan3) {
12313
+ function planUsage(ctx, plan4) {
12103
12314
  return (payload) => {
12104
- const seconds3 = plan3.seconds(payload);
12315
+ const seconds3 = plan4.seconds(payload);
12105
12316
  return {
12106
12317
  model: ctx.model.id,
12107
12318
  provider: "fal",
@@ -12109,7 +12320,7 @@ function planUsage(ctx, plan3) {
12109
12320
  unit: "seconds",
12110
12321
  quantity: seconds3,
12111
12322
  durationSeconds: seconds3,
12112
- ...plan3.resolution !== void 0 && { resolution: plan3.resolution }
12323
+ ...plan4.resolution !== void 0 && { resolution: plan4.resolution }
12113
12324
  };
12114
12325
  };
12115
12326
  }
@@ -14199,8 +14410,8 @@ function imageBody(req, ctx, outputFormat) {
14199
14410
  function videoBody(req, ctx, mode) {
14200
14411
  const prompt = checkPrompt(req, ctx);
14201
14412
  const model = lumaModel(ctx);
14202
- const hasReferences = (req.referenceImages ?? []).length + (req.referenceVideos ?? []).length + (req.referenceAudios ?? []).length > 0;
14203
- if (hasReferences) {
14413
+ const hasReferences2 = (req.referenceImages ?? []).length + (req.referenceVideos ?? []).length + (req.referenceAudios ?? []).length > 0;
14414
+ if (hasReferences2) {
14204
14415
  refuseOrDrop(
14205
14416
  ctx,
14206
14417
  "referenceImages",
@@ -16563,10 +16774,10 @@ function resolveVideoOptions(model, request, onUnsupported = "error") {
16563
16774
  warnings
16564
16775
  );
16565
16776
  if (aspectRatio !== void 0) merged.aspectRatio = aspectRatio;
16566
- fillReplicateKlingResolution(model, merged);
16777
+ fillKlingModeResolution(model, merged);
16567
16778
  const resolution = resolveVideoResolution(merged.resolution, model, onUnsupported, warnings);
16568
16779
  if (resolution !== void 0) merged.resolution = resolution;
16569
- if (isReplicateKling(model)) delete merged.mode;
16780
+ if (takesKlingMode(model)) delete merged.mode;
16570
16781
  const duration = resolveDuration(
16571
16782
  merged.durationSeconds,
16572
16783
  c.durations,