@shop-os/modelkit 0.2.20 → 0.2.21

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
@@ -1368,12 +1368,14 @@ var TEXT_MODELS = [
1368
1368
  var VIDEO_USAGE = { unit: "seconds" };
1369
1369
  var SEEDANCE_RATIOS = ["21:9", "16:9", "4:3", "1:1", "3:4", "9:16", "adaptive"];
1370
1370
  var KLING_RATIOS = ["16:9", "9:16", "1:1", "adaptive"];
1371
+ var KLING_OMNI_RATIOS = ["16:9", "9:16", "1:1"];
1371
1372
  var OMNI_RATIOS = ["16:9", "9:16"];
1372
1373
  var GROK_RATIOS = ["1:1", "16:9", "9:16", "4:3", "3:4", "3:2", "2:3"];
1373
1374
  var H3_RATIOS = ["21:9", "16:9", "4:3", "1:1", "3:4", "9:16"];
1374
1375
  var H3_REFERENCE_RATIOS = ["adaptive", ...H3_RATIOS];
1375
1376
  var NEGATIVE_PROMPT = "blur, distort, and low quality";
1376
1377
  var FRAMES_EXCLUDE_REFERENCES = { framesExcludeReferences: true };
1378
+ var KLING_PROMPT = { referenceSyntax: "numbered", maxChars: 2500 };
1377
1379
  var BYTEDANCE_LOGO = "https://d31vvfpk2ppdyq.cloudfront.net/assets/image/3bba5d81-4e57-4e71-911b-cdfa05acca2c_compressed.png";
1378
1380
  var GOOGLE_LOGO2 = "https://d31vvfpk2ppdyq.cloudfront.net/assets/image/192485d2-a76a-4657-9c06-930b2c70badd_compressed.png";
1379
1381
  var VIDEO_MODELS = [
@@ -1394,9 +1396,13 @@ var VIDEO_MODELS = [
1394
1396
  constraints: {
1395
1397
  aspectRatios: [...OMNI_RATIOS],
1396
1398
  resolutions: ["720p", "1080p", "4k"],
1399
+ // Veo also takes 4 and 6, but must be 8 at 1080p/4k or with reference images;
1400
+ // [8] until the kit has a rule that forces it there (CHANGELOG 0.2.21).
1397
1401
  durations: [8],
1398
1402
  references: { images: 3 },
1399
1403
  supports: ["lastFrame", "seed"],
1404
+ // 1,024 text tokens; 3,000 characters assumes ~2.9 characters per token.
1405
+ prompt: { referenceSyntax: "numbered", maxChars: 3e3 },
1400
1406
  audio: "always"
1401
1407
  },
1402
1408
  defaults: { durationSeconds: 8, resolution: "720p", aspectRatio: "16:9" },
@@ -1407,7 +1413,8 @@ var VIDEO_MODELS = [
1407
1413
  description: "Google's highest-quality video generation model. Supports up to 4K resolution and 8-second clips with reference image conditioning.",
1408
1414
  logo: GOOGLE_LOGO2
1409
1415
  },
1410
- capabilities: { ...FRAMES_EXCLUDE_REFERENCES }
1416
+ // Veo's lastFrame "must be used in combination with the image parameter".
1417
+ capabilities: { ...FRAMES_EXCLUDE_REFERENCES, lastFrameRequiresFirstFrame: true }
1411
1418
  },
1412
1419
  {
1413
1420
  id: "veo-3.1-fast",
@@ -1527,7 +1534,8 @@ var VIDEO_MODELS = [
1527
1534
  // extension can upload a <= 10 s source with task "extend"; later ones
1528
1535
  // chain by `previousInteractionId` (the API rejects the id together
1529
1536
  // with a video task, so those calls carry the prompt alone).
1530
- supports: ["videoExtension"],
1537
+ // First-to-last-frame interpolation: `<LAST_FRAME>` goes with `<FIRST_FRAME>`.
1538
+ supports: ["videoExtension", "lastFrame"],
1531
1539
  extension: { segmentSeconds: 10, maxTotalSeconds: 40 },
1532
1540
  // generateOmniVideo sends MediaRef.label as a text part beside each image.
1533
1541
  prompt: { referenceSyntax: "caption" },
@@ -1541,7 +1549,7 @@ var VIDEO_MODELS = [
1541
1549
  logo: GOOGLE_LOGO2,
1542
1550
  description: "Google Gemini Omni Flash 1.1 conversational video generation, editing and extension; text, image, and up to 10-second video input, 3-10 second output at up to 4K and 24 FPS, extendable to 40 seconds."
1543
1551
  },
1544
- capabilities: { acceptsSourceVideo: true }
1552
+ capabilities: { acceptsSourceVideo: true, lastFrameRequiresFirstFrame: true }
1545
1553
  },
1546
1554
  {
1547
1555
  // vibe-triggers and Space Builder drop the ratio when a frame is wired; Cowork always sends it.
@@ -1567,7 +1575,8 @@ var VIDEO_MODELS = [
1567
1575
  },
1568
1576
  sourceVideoSeconds: { min: 2, max: 15, inReferenceTotal: true },
1569
1577
  ratioIgnoredWith: ["firstFrame", "sourceVideo"],
1570
- supports: ["generateAudio", "seed", "lastFrame", "watermark"],
1578
+ // No seed: 2.x echoes it but does not honour it (two seeded runs differed, 2026-10-01).
1579
+ supports: ["generateAudio", "lastFrame", "watermark"],
1571
1580
  audio: "optional"
1572
1581
  },
1573
1582
  defaults: {
@@ -1607,7 +1616,8 @@ var VIDEO_MODELS = [
1607
1616
  },
1608
1617
  sourceVideoSeconds: { min: 2, max: 15, inReferenceTotal: true },
1609
1618
  ratioIgnoredWith: ["firstFrame", "sourceVideo"],
1610
- supports: ["generateAudio", "seed", "lastFrame", "watermark"],
1619
+ // No seed: 2.x echoes it but does not honour it (two seeded runs differed, 2026-10-01).
1620
+ supports: ["generateAudio", "lastFrame", "watermark"],
1611
1621
  audio: "optional"
1612
1622
  },
1613
1623
  defaults: {
@@ -1626,7 +1636,6 @@ var VIDEO_MODELS = [
1626
1636
  capabilities: { acceptsSourceVideo: true, ...FRAMES_EXCLUDE_REFERENCES }
1627
1637
  },
1628
1638
  {
1629
- // generateAudio defaults to false here (trigger-workflows); Space Builder defaults it to true.
1630
1639
  // omni_reference_task_type edit/extend: adaptive ratio, edit sends duration -1.
1631
1640
  id: "seedance-2.5",
1632
1641
  aliases: ["seedance-2-5", "dreamina-seedance-2-5-260628"],
@@ -1648,14 +1657,15 @@ var VIDEO_MODELS = [
1648
1657
  },
1649
1658
  sourceVideoSeconds: { min: 2, max: 30, editMin: 4, inReferenceTotal: true },
1650
1659
  ratioIgnoredWith: ["firstFrame", "sourceVideo"],
1651
- supports: ["generateAudio", "seed", "lastFrame", "watermark", "taskType"],
1660
+ // No seed: 2.x echoes it but does not honour it (two seeded runs differed, 2026-10-01).
1661
+ supports: ["generateAudio", "lastFrame", "watermark", "taskType"],
1652
1662
  audio: "optional"
1653
1663
  },
1654
1664
  defaults: {
1655
1665
  durationSeconds: 5,
1656
1666
  resolution: "720p",
1657
1667
  aspectRatio: "16:9",
1658
- generateAudio: false
1668
+ generateAudio: true
1659
1669
  },
1660
1670
  usage: { ...VIDEO_USAGE },
1661
1671
  pricingKind: "video_per_second",
@@ -1667,7 +1677,6 @@ var VIDEO_MODELS = [
1667
1677
  capabilities: { acceptsSourceVideo: true, ...FRAMES_EXCLUDE_REFERENCES }
1668
1678
  },
1669
1679
  {
1670
- // Cowork allows 7 reference images and durations 4/6/8/10; Space Builder allows 5 and 1-15s.
1671
1680
  id: "grok-imagine",
1672
1681
  aliases: ["grok-imagine-video", "grokImagine"],
1673
1682
  label: "Grok Imagine",
@@ -1681,7 +1690,8 @@ var VIDEO_MODELS = [
1681
1690
  resolutions: ["480p", "720p"],
1682
1691
  durations: { min: 1, max: 15 },
1683
1692
  durationPresets: [4, 6, 8, 10],
1684
- references: { images: 5 },
1693
+ // xAI: "A maximum of 7 reference images can be provided per request."
1694
+ references: { images: 7 },
1685
1695
  supports: [],
1686
1696
  // xAI can switch sound off (generate_audio); xai-video.ts never sends it.
1687
1697
  audio: "always"
@@ -1706,16 +1716,16 @@ var VIDEO_MODELS = [
1706
1716
  providerModelId: "kling-video/v2.5-turbo/pro/image-to-video",
1707
1717
  lifecycle: "active",
1708
1718
  exposure: ["cowork", "space_builder"],
1719
+ // The endpoint has no aspect_ratio or last_image_url: the start image sets the framing.
1709
1720
  constraints: {
1710
- aspectRatios: [...KLING_RATIOS],
1711
1721
  durations: [5, 10],
1712
1722
  requires: ["firstFrame"],
1713
- supports: ["cfgScale", "negativePrompt", "lastFrame"],
1723
+ supports: ["cfgScale", "negativePrompt"],
1724
+ prompt: KLING_PROMPT,
1714
1725
  audio: "never"
1715
1726
  },
1716
1727
  defaults: {
1717
1728
  durationSeconds: 5,
1718
- aspectRatio: "16:9",
1719
1729
  cfgScale: 0.5,
1720
1730
  negativePrompt: NEGATIVE_PROMPT
1721
1731
  },
@@ -1724,7 +1734,7 @@ var VIDEO_MODELS = [
1724
1734
  display: {
1725
1735
  provider: "kuaishou",
1726
1736
  logo: "https://cdn.jsdelivr.net/npm/@lobehub/icons-static-png@1.97.0/light/kling-color.png",
1727
- description: "Kuaishou Kling 2.5 Turbo Pro image-to-video; animates a single input image from a text prompt with an optional end image, 5- or 10-second clips."
1737
+ description: "Kuaishou Kling 2.5 Turbo Pro image-to-video; animates a single input image from a text prompt, 5- or 10-second clips."
1728
1738
  },
1729
1739
  capabilities: { ...FRAMES_EXCLUDE_REFERENCES }
1730
1740
  },
@@ -1738,19 +1748,18 @@ var VIDEO_MODELS = [
1738
1748
  providerModelId: "kling-video/v3.0/pro/image-to-video",
1739
1749
  lifecycle: "active",
1740
1750
  exposure: ["cowork", "space_builder"],
1751
+ // The endpoint has no aspect_ratio or negative_prompt; sound is "on"/"off" (default on).
1741
1752
  constraints: {
1742
- aspectRatios: [...KLING_RATIOS],
1743
1753
  durations: { min: 3, max: 15 },
1744
1754
  requires: ["firstFrame"],
1745
- supports: ["cfgScale", "negativePrompt", "lastFrame"],
1746
- // Higgsfield has a `sound` switch (default on); KlingCreate doesn't send it.
1747
- audio: "always"
1755
+ supports: ["cfgScale", "lastFrame", "generateAudio"],
1756
+ prompt: KLING_PROMPT,
1757
+ audio: "optional"
1748
1758
  },
1749
1759
  defaults: {
1750
1760
  durationSeconds: 5,
1751
- aspectRatio: "16:9",
1752
1761
  cfgScale: 0.5,
1753
- negativePrompt: NEGATIVE_PROMPT
1762
+ generateAudio: true
1754
1763
  },
1755
1764
  usage: { ...VIDEO_USAGE },
1756
1765
  pricingKind: "video_per_second",
@@ -1762,7 +1771,7 @@ var VIDEO_MODELS = [
1762
1771
  capabilities: { ...FRAMES_EXCLUDE_REFERENCES }
1763
1772
  },
1764
1773
  {
1765
- // mode standard/pro selects 720p/1080p; the endpoint takes no separate resolution.
1774
+ // mode standard/pro/4k selects 720p/1080p/4K; the endpoint takes no separate resolution.
1766
1775
  // Replicate: "Max 7 [images] without video, 4 with video"; 4K does not support
1767
1776
  // reference_video. No `videos` limit yet: the adapter sends no reference video,
1768
1777
  // so `imagesWithVideo` stays dormant until one is wired through.
@@ -1775,22 +1784,25 @@ var VIDEO_MODELS = [
1775
1784
  lifecycle: "active",
1776
1785
  exposure: ["cowork", "space_builder"],
1777
1786
  constraints: {
1778
- aspectRatios: [...KLING_RATIOS],
1779
- resolutions: ["720p", "1080p"],
1787
+ aspectRatios: [...KLING_OMNI_RATIOS],
1788
+ resolutions: ["720p", "1080p", "4k"],
1780
1789
  durations: { min: 3, max: 15 },
1781
1790
  references: { images: 7, imagesWithVideo: 4 },
1782
1791
  supports: ["generateAudio", "lastFrame"],
1792
+ prompt: KLING_PROMPT,
1783
1793
  audio: "optional"
1784
1794
  },
1785
- defaults: { durationSeconds: 5, aspectRatio: "16:9", generateAudio: false, mode: "pro" },
1795
+ defaults: { durationSeconds: 5, aspectRatio: "16:9", generateAudio: true, mode: "pro" },
1786
1796
  usage: { unit: "compute_seconds" },
1787
1797
  pricingKind: "video_per_second",
1788
1798
  display: {
1789
1799
  provider: "kuaishou",
1790
1800
  logo: "https://cdn.jsdelivr.net/npm/@lobehub/icons-static-png@1.97.0/light/kling-color.png",
1791
- 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 1080p."
1792
- },
1793
- capabilities: {}
1801
+ 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."
1802
+ }
1803
+ // end_image "Requires start_image", but no lastFrameRequiresFirstFrame: SpaceLab resolves
1804
+ // replicate rows in error mode, so the flag would refuse its end-frame-only nodes, which
1805
+ // the adapter sends as start_image.
1794
1806
  },
1795
1807
  {
1796
1808
  id: "minimax-h3",
@@ -1805,6 +1817,8 @@ var VIDEO_MODELS = [
1805
1817
  aspectRatios: [...H3_RATIOS],
1806
1818
  resolutions: ["480p", "768p", "2k", "4k"],
1807
1819
  durations: { min: 5, max: 15 },
1820
+ // fal's image-to-video endpoint has no aspect_ratio; the frame sets it.
1821
+ ratioIgnoredWith: ["firstFrame"],
1808
1822
  supports: ["seed", "promptExpansion", "lastFrame", "safetyChecker"],
1809
1823
  audio: "always"
1810
1824
  },
@@ -1834,8 +1848,10 @@ var VIDEO_MODELS = [
1834
1848
  exposure: ["cowork", "space_builder"],
1835
1849
  constraints: {
1836
1850
  aspectRatios: [...H3_RATIOS],
1837
- resolutions: ["480p", "768p"],
1851
+ resolutions: ["480p", "768p", "1080p"],
1838
1852
  durations: { min: 5, max: 15 },
1853
+ // fal's image-to-video endpoint has no aspect_ratio; the frame sets it.
1854
+ ratioIgnoredWith: ["firstFrame"],
1839
1855
  supports: ["seed", "promptExpansion", "lastFrame", "safetyChecker"],
1840
1856
  audio: "always"
1841
1857
  },
@@ -1850,7 +1866,7 @@ var VIDEO_MODELS = [
1850
1866
  display: {
1851
1867
  provider: "minimax",
1852
1868
  logo: "https://d31vvfpk2ppdyq.cloudfront.net/assets/image/f4c2e906-28b7-4a4a-b491-df8fef7808ed.png",
1853
- description: "High-speed MiniMax H3 Max variant for text-to-video and image-to-video (first/last frame) only, up to 768p."
1869
+ description: "High-speed MiniMax H3 Max variant for text-to-video and image-to-video (first/last frame) only, up to 1080p."
1854
1870
  },
1855
1871
  capabilities: { ...FRAMES_EXCLUDE_REFERENCES }
1856
1872
  },
@@ -1867,6 +1883,8 @@ var VIDEO_MODELS = [
1867
1883
  aspectRatios: [...H3_REFERENCE_RATIOS],
1868
1884
  resolutions: ["480p", "768p", "2k", "4k"],
1869
1885
  durations: { min: 5, max: 15 },
1886
+ // A start frame routes to image-to-video, which has no aspect_ratio.
1887
+ ratioIgnoredWith: ["firstFrame"],
1870
1888
  references: {
1871
1889
  images: 9,
1872
1890
  videos: 3,
@@ -1906,8 +1924,10 @@ var VIDEO_MODELS = [
1906
1924
  exposure: ["cowork", "space_builder"],
1907
1925
  constraints: {
1908
1926
  aspectRatios: [...H3_REFERENCE_RATIOS],
1909
- resolutions: ["480p", "768p"],
1927
+ resolutions: ["480p", "768p", "1080p"],
1910
1928
  durations: { min: 5, max: 15 },
1929
+ // A start frame routes to image-to-video, which has no aspect_ratio.
1930
+ ratioIgnoredWith: ["firstFrame"],
1911
1931
  references: {
1912
1932
  images: 9,
1913
1933
  videos: 3,
@@ -2464,6 +2484,7 @@ var CatalogDisplaySchema = import_zod3.z.looseObject({
2464
2484
  var CatalogCapabilitiesSchema = import_zod3.z.looseObject({
2465
2485
  acceptsSourceVideo: import_zod3.z.boolean().optional(),
2466
2486
  framesExcludeReferences: import_zod3.z.boolean().optional(),
2487
+ lastFrameRequiresFirstFrame: import_zod3.z.boolean().optional(),
2467
2488
  reasoning: import_zod3.z.boolean().optional(),
2468
2489
  vision: import_zod3.z.boolean().optional(),
2469
2490
  contextWindow: import_zod3.z.number().optional()
@@ -4686,7 +4707,12 @@ async function generateOmniVideo(req, ctx, createGenAIClient) {
4686
4707
  const extend = req.taskType === "extend";
4687
4708
  const previousId = extend ? req.previousInteractionId : void 0;
4688
4709
  const chained = previousId !== void 0;
4689
- for (const field of ["referenceImages", "referenceVideos", "firstFrame"]) {
4710
+ for (const field of [
4711
+ "referenceImages",
4712
+ "referenceVideos",
4713
+ "firstFrame",
4714
+ "lastFrame"
4715
+ ]) {
4690
4716
  const value = req[field];
4691
4717
  if (!chained || value === void 0 || Array.isArray(value) && value.length === 0)
4692
4718
  continue;
@@ -4700,6 +4726,14 @@ async function generateOmniVideo(req, ctx, createGenAIClient) {
4700
4726
  const images = chained ? [] : req.referenceImages ?? [];
4701
4727
  const videos = chained ? [] : req.referenceVideos ?? [];
4702
4728
  const firstFrame = chained ? void 0 : req.firstFrame;
4729
+ const lastFrame = firstFrame ? req.lastFrame : void 0;
4730
+ if (!chained && req.lastFrame !== void 0 && lastFrame === void 0) {
4731
+ ctx.warnings.push({
4732
+ code: "option_ignored",
4733
+ field: "lastFrame",
4734
+ message: `${modelId} takes an end frame only with a start frame; lastFrame ignored`
4735
+ });
4736
+ }
4703
4737
  if (extend && !chained && req.sourceVideo === void 0) {
4704
4738
  throw new ModelKitError({
4705
4739
  code: "missing_input",
@@ -4784,27 +4818,7 @@ async function generateOmniVideo(req, ctx, createGenAIClient) {
4784
4818
  mime_type: media.mime
4785
4819
  });
4786
4820
  }
4787
- if (firstFrame) {
4788
- if (firstFrame.label !== void 0)
4789
- parts.push({ type: "text", text: firstFrame.label });
4790
- const media = await resolveMedia(
4791
- firstFrame,
4792
- ctx.fetch,
4793
- ctx.signal,
4794
- "image/png",
4795
- modelId
4796
- );
4797
- if (media.bytes.length > OMNI_INLINE_LIMIT_BYTES) {
4798
- parts.push({
4799
- type: "image",
4800
- uri: await uploadOne("image", media.bytes, media.mime),
4801
- mime_type: media.mime
4802
- });
4803
- } else {
4804
- parts.push({ type: "image", data: toBase64(media.bytes), mime_type: media.mime });
4805
- }
4806
- }
4807
- for (const ref of images) {
4821
+ const pushImage = async (ref) => {
4808
4822
  if (ref.label !== void 0) parts.push({ type: "text", text: ref.label });
4809
4823
  const media = await resolveMedia(ref, ctx.fetch, ctx.signal, "image/png", modelId);
4810
4824
  if (media.bytes.length > OMNI_INLINE_LIMIT_BYTES) {
@@ -4816,8 +4830,12 @@ async function generateOmniVideo(req, ctx, createGenAIClient) {
4816
4830
  } else {
4817
4831
  parts.push({ type: "image", data: toBase64(media.bytes), mime_type: media.mime });
4818
4832
  }
4819
- }
4820
- parts.push({ type: "text", text: req.prompt });
4833
+ };
4834
+ if (firstFrame) await pushImage(firstFrame);
4835
+ if (lastFrame) await pushImage(lastFrame);
4836
+ for (const ref of images) await pushImage(ref);
4837
+ const prompt = lastFrame ? `<FIRST_FRAME> <LAST_FRAME> ${req.prompt}` : req.prompt;
4838
+ parts.push({ type: "text", text: prompt });
4821
4839
  try {
4822
4840
  interaction = await withRetry(async () => {
4823
4841
  try {
@@ -5347,6 +5365,26 @@ function videoUsage3(ctx, req, duration) {
5347
5365
  if (req.durationSeconds !== void 0) event.durationSeconds = req.durationSeconds;
5348
5366
  return event;
5349
5367
  }
5368
+ var KLING_ENDPOINT_FIELDS = /* @__PURE__ */ new Map([
5369
+ [
5370
+ "kling-video/v2.5-turbo/pro/image-to-video",
5371
+ { aspectRatio: false, negativePrompt: true, lastFrame: false, sound: false }
5372
+ ],
5373
+ [
5374
+ "kling-video/v3.0/pro/image-to-video",
5375
+ { aspectRatio: false, negativePrompt: false, lastFrame: true, sound: true }
5376
+ ]
5377
+ ]);
5378
+ var UNLISTED_KLING_FIELDS = {
5379
+ aspectRatio: true,
5380
+ negativePrompt: true,
5381
+ lastFrame: true,
5382
+ sound: false
5383
+ };
5384
+ function klingEndpointFields(providerModelId) {
5385
+ const path = (providerModelId.startsWith("/") ? providerModelId.slice(1) : providerModelId).toLowerCase();
5386
+ return KLING_ENDPOINT_FIELDS.get(path) ?? UNLISTED_KLING_FIELDS;
5387
+ }
5350
5388
  var klingCreateSchema = import_zod13.z.object({ status_url: import_zod13.z.string() });
5351
5389
  var klingStatusSchema = import_zod13.z.object({
5352
5390
  status: import_zod13.z.string().optional(),
@@ -5381,6 +5419,13 @@ async function createRequest(ctx, url, init) {
5381
5419
  }
5382
5420
  return res;
5383
5421
  }
5422
+ function ignoreKlingField(ctx, field) {
5423
+ ctx.warnings.push({
5424
+ code: "option_ignored",
5425
+ field,
5426
+ message: `${ctx.model.id}: the Higgsfield endpoint has no ${field} input; ignored`
5427
+ });
5428
+ }
5384
5429
  async function generateKlingVideo(req, ctx) {
5385
5430
  const firstFrame = req.firstFrame ? refToUrl(req.firstFrame) : void 0;
5386
5431
  if (!firstFrame) {
@@ -5392,16 +5437,29 @@ async function generateKlingVideo(req, ctx) {
5392
5437
  retryable: false
5393
5438
  });
5394
5439
  }
5440
+ const fields = klingEndpointFields(ctx.model.providerModelId);
5395
5441
  const body = {
5396
5442
  prompt: req.prompt.trim(),
5397
5443
  image_url: firstFrame,
5398
- cfg_scale: req.cfgScale ?? 0.5,
5399
- negative_prompt: req.negativePrompt ?? DEFAULT_NEGATIVE_PROMPT
5444
+ cfg_scale: req.cfgScale ?? 0.5
5400
5445
  };
5446
+ if (fields.negativePrompt) {
5447
+ body.negative_prompt = req.negativePrompt ?? DEFAULT_NEGATIVE_PROMPT;
5448
+ } else if (req.negativePrompt !== void 0) {
5449
+ ignoreKlingField(ctx, "negativePrompt");
5450
+ }
5401
5451
  if (req.durationSeconds !== void 0) body.duration = req.durationSeconds;
5402
- if (req.aspectRatio !== void 0 && req.aspectRatio !== "adaptive")
5403
- body.aspect_ratio = req.aspectRatio;
5404
- if (req.lastFrame) body.last_image_url = refToUrl(req.lastFrame);
5452
+ if (req.aspectRatio !== void 0 && req.aspectRatio !== "adaptive") {
5453
+ if (fields.aspectRatio) body.aspect_ratio = req.aspectRatio;
5454
+ else ignoreKlingField(ctx, "aspectRatio");
5455
+ }
5456
+ if (req.lastFrame) {
5457
+ if (fields.lastFrame) body.last_image_url = refToUrl(req.lastFrame);
5458
+ else ignoreKlingField(ctx, "lastFrame");
5459
+ }
5460
+ if (fields.sound && req.generateAudio !== void 0) {
5461
+ body.sound = req.generateAudio ? "on" : "off";
5462
+ }
5405
5463
  const endpoint = klingEndpoint(ctx.model.providerModelId, ctx.model.id);
5406
5464
  const retryOptions = mediaRetryOptions(ctx.call, ctx.signal);
5407
5465
  const createRes = await withRetry(
@@ -6703,24 +6761,22 @@ function klingInput(req) {
6703
6761
  return input;
6704
6762
  }
6705
6763
  function klingOmniMode(req) {
6764
+ if (req.resolution === "4k") return "4k";
6706
6765
  if (req.resolution === "1080p") return "pro";
6707
6766
  if (req.resolution === "720p") return "standard";
6708
6767
  return req.mode ?? "standard";
6709
6768
  }
6710
6769
  function klingOmniInput(req) {
6711
- const images = (req.firstFrame ? [req.firstFrame] : []).map(refToUrl);
6712
- if (req.lastFrame) images.push(refToUrl(req.lastFrame));
6713
6770
  const input = {
6714
6771
  prompt: req.prompt,
6715
6772
  duration: req.durationSeconds ?? 10,
6716
6773
  mode: klingOmniMode(req),
6717
6774
  generate_audio: req.generateAudio ?? false
6718
6775
  };
6719
- const startImage = images[0];
6720
- if (startImage !== void 0) {
6721
- input.start_image = startImage;
6722
- const endImage = images[1];
6723
- if (endImage !== void 0) input.end_image = endImage;
6776
+ const start = req.firstFrame ?? req.lastFrame;
6777
+ if (start) {
6778
+ input.start_image = refToUrl(start);
6779
+ if (req.firstFrame && req.lastFrame) input.end_image = refToUrl(req.lastFrame);
6724
6780
  } else if (req.aspectRatio !== "adaptive") {
6725
6781
  input.aspect_ratio = req.aspectRatio ?? "16:9";
6726
6782
  }
@@ -6968,7 +7024,7 @@ async function generateXaiVideo(req, ctx, deps) {
6968
7024
  retryable: false
6969
7025
  });
6970
7026
  }
6971
- const refUrls = (req.referenceImages ?? []).map(refToUrl).slice(0, 5);
7027
+ const refUrls = (req.referenceImages ?? []).map(refToUrl).slice(0, 7);
6972
7028
  const xaiOptions = {
6973
7029
  pollIntervalMs: POLL_INTERVAL_MS2,
6974
7030
  pollTimeoutMs: POLL_TIMEOUT_MS2
@@ -7682,6 +7738,24 @@ function resolveVideoOptions(model, request, onUnsupported = "error") {
7682
7738
  });
7683
7739
  delete merged.sourceVideo;
7684
7740
  }
7741
+ if (merged.lastFrame !== void 0 && merged.firstFrame === void 0 && model.capabilities?.lastFrameRequiresFirstFrame) {
7742
+ if (onUnsupported === "error") {
7743
+ throw new ModelKitError({
7744
+ code: "missing_input",
7745
+ message: `${model.id} takes an end frame only together with a start frame`,
7746
+ model: model.id,
7747
+ retryable: false
7748
+ });
7749
+ }
7750
+ warnings.push({
7751
+ code: "option_ignored",
7752
+ field: "lastFrame",
7753
+ to: "firstFrame",
7754
+ message: `end frame used as start frame: ${model.id} needs a start frame with an end frame`
7755
+ });
7756
+ merged.firstFrame = merged.lastFrame;
7757
+ delete merged.lastFrame;
7758
+ }
7685
7759
  const hasFrames = merged.firstFrame !== void 0 || merged.lastFrame !== void 0;
7686
7760
  const hasRefs = compact(merged.referenceImages).length + compact(merged.referenceVideos).length + compact(merged.referenceAudios).length > 0;
7687
7761
  if (hasFrames && hasRefs && model.capabilities?.framesExcludeReferences) {