@shop-os/modelkit 0.2.20 → 0.2.22

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
@@ -279,13 +279,16 @@ var GEMINI_RATIOS = [
279
279
  "5:4",
280
280
  "adaptive"
281
281
  ];
282
- function geminiImage(id, label, aliases, sizes, display, exposure, references) {
282
+ var GEMINI_PROMPT_MAX_CHARS = 5e4;
283
+ var GPT_IMAGE_PROMPT = { referenceSyntax: "numbered", maxChars: 32e3 };
284
+ function geminiImage(id, label, aliases, sizes, display, exposure, references, promptMaxChars) {
285
+ const prompt = { referenceSyntax: "caption" };
286
+ if (promptMaxChars !== void 0) prompt.maxChars = promptMaxChars;
283
287
  const constraints = {
284
288
  aspectRatios: [...GEMINI_RATIOS],
285
289
  sizes,
286
290
  supports: ["safetySettings", "temperature"],
287
- // google-image sends MediaRef.label as a text part beside each reference.
288
- prompt: { referenceSyntax: "caption" }
291
+ prompt
289
292
  };
290
293
  if (references !== void 0) constraints.references = references;
291
294
  const model = {
@@ -317,7 +320,8 @@ var IMAGE_MODELS = [
317
320
  logo: "https://d31vvfpk2ppdyq.cloudfront.net/assets/image/192485d2-a76a-4657-9c06-930b2c70badd_compressed.png"
318
321
  },
319
322
  ["cowork", "space_builder"],
320
- { images: 14 }
323
+ { images: 14 },
324
+ GEMINI_PROMPT_MAX_CHARS
321
325
  ),
322
326
  geminiImage(
323
327
  "gemini-3.1-flash-image-preview",
@@ -331,7 +335,8 @@ var IMAGE_MODELS = [
331
335
  logo: "https://d31vvfpk2ppdyq.cloudfront.net/assets/image/192485d2-a76a-4657-9c06-930b2c70badd_compressed.png"
332
336
  },
333
337
  ["cowork", "space_builder"],
334
- { images: 14 }
338
+ { images: 14 },
339
+ GEMINI_PROMPT_MAX_CHARS
335
340
  ),
336
341
  geminiImage(
337
342
  "gemini-3.1-flash-lite-image",
@@ -344,7 +349,8 @@ var IMAGE_MODELS = [
344
349
  logo: "https://d31vvfpk2ppdyq.cloudfront.net/assets/image/192485d2-a76a-4657-9c06-930b2c70badd_compressed.png"
345
350
  },
346
351
  ["cowork", "space_builder"],
347
- { images: 14 }
352
+ { images: 14 },
353
+ GEMINI_PROMPT_MAX_CHARS
348
354
  ),
349
355
  // trigger-workflows validates 1K only; pricing seeds 2K/4K rows so allow all three.
350
356
  geminiImage(
@@ -382,7 +388,8 @@ var IMAGE_MODELS = [
382
388
  sizes: ["1K", "2K", "4K"],
383
389
  qualityOptions: ["auto", "low", "medium", "high"],
384
390
  references: { images: 16 },
385
- supports: ["quality", "count", "outputFormat"]
391
+ supports: ["quality", "count", "outputFormat"],
392
+ prompt: GPT_IMAGE_PROMPT
386
393
  },
387
394
  defaults: { size: "1K", aspectRatio: "adaptive", quality: "auto" },
388
395
  usage: { ...IMAGE_USAGE, slug: "gpt" },
@@ -417,7 +424,8 @@ var IMAGE_MODELS = [
417
424
  sizes: ["1K", "2K", "4K"],
418
425
  qualityOptions: ["auto", "low", "medium", "high", "xhigh", "max"],
419
426
  references: { images: 16 },
420
- supports: ["quality", "count", "outputFormat"]
427
+ supports: ["quality", "count", "outputFormat"],
428
+ prompt: GPT_IMAGE_PROMPT
421
429
  },
422
430
  defaults: { size: "1K", aspectRatio: "adaptive", quality: "auto" },
423
431
  usage: { ...IMAGE_USAGE },
@@ -451,7 +459,8 @@ var IMAGE_MODELS = [
451
459
  sizes: ["1K", "2K", "4K"],
452
460
  qualityOptions: ["auto", "low", "medium", "high", "xhigh", "max"],
453
461
  references: { images: 16 },
454
- supports: ["quality", "count", "outputFormat"]
462
+ supports: ["quality", "count", "outputFormat"],
463
+ prompt: GPT_IMAGE_PROMPT
455
464
  },
456
465
  defaults: { size: "1K", aspectRatio: "adaptive", quality: "auto" },
457
466
  usage: { ...IMAGE_USAGE },
@@ -574,7 +583,9 @@ var IMAGE_MODELS = [
574
583
  sizes: ["1K"],
575
584
  references: { images: 8 },
576
585
  // flux-2-pro's job type always returns image/png; only the seed is forwarded.
577
- supports: ["seed"]
586
+ supports: ["seed"],
587
+ // Prodia's flux-2-pro schema gives the prompt a maxLength of 32,768.
588
+ prompt: { referenceSyntax: "numbered", maxChars: 32768 }
578
589
  },
579
590
  defaults: { size: "1K", aspectRatio: "1:1" },
580
591
  usage: { ...IMAGE_USAGE },
@@ -1368,12 +1379,17 @@ var TEXT_MODELS = [
1368
1379
  var VIDEO_USAGE = { unit: "seconds" };
1369
1380
  var SEEDANCE_RATIOS = ["21:9", "16:9", "4:3", "1:1", "3:4", "9:16", "adaptive"];
1370
1381
  var KLING_RATIOS = ["16:9", "9:16", "1:1", "adaptive"];
1382
+ var KLING_OMNI_RATIOS = ["16:9", "9:16", "1:1"];
1371
1383
  var OMNI_RATIOS = ["16:9", "9:16"];
1372
1384
  var GROK_RATIOS = ["1:1", "16:9", "9:16", "4:3", "3:4", "3:2", "2:3"];
1373
1385
  var H3_RATIOS = ["21:9", "16:9", "4:3", "1:1", "3:4", "9:16"];
1374
1386
  var H3_REFERENCE_RATIOS = ["adaptive", ...H3_RATIOS];
1375
1387
  var NEGATIVE_PROMPT = "blur, distort, and low quality";
1376
1388
  var FRAMES_EXCLUDE_REFERENCES = { framesExcludeReferences: true };
1389
+ var KLING_PROMPT = { referenceSyntax: "numbered", maxChars: 2500 };
1390
+ var SEEDANCE_PROMPT = { referenceSyntax: "numbered", maxChars: 3e4 };
1391
+ var H3_PROMPT = { referenceSyntax: "numbered", maxChars: 5e4 };
1392
+ var OMNI_PROMPT = { referenceSyntax: "caption", maxChars: 5e4 };
1377
1393
  var BYTEDANCE_LOGO = "https://d31vvfpk2ppdyq.cloudfront.net/assets/image/3bba5d81-4e57-4e71-911b-cdfa05acca2c_compressed.png";
1378
1394
  var GOOGLE_LOGO2 = "https://d31vvfpk2ppdyq.cloudfront.net/assets/image/192485d2-a76a-4657-9c06-930b2c70badd_compressed.png";
1379
1395
  var VIDEO_MODELS = [
@@ -1394,9 +1410,13 @@ var VIDEO_MODELS = [
1394
1410
  constraints: {
1395
1411
  aspectRatios: [...OMNI_RATIOS],
1396
1412
  resolutions: ["720p", "1080p", "4k"],
1413
+ // Veo also takes 4 and 6, but must be 8 at 1080p/4k or with reference images;
1414
+ // [8] until the kit has a rule that forces it there (CHANGELOG 0.2.21).
1397
1415
  durations: [8],
1398
1416
  references: { images: 3 },
1399
1417
  supports: ["lastFrame", "seed"],
1418
+ // 1,024 text tokens; 3,000 characters assumes ~2.9 characters per token.
1419
+ prompt: { referenceSyntax: "numbered", maxChars: 3e3 },
1400
1420
  audio: "always"
1401
1421
  },
1402
1422
  defaults: { durationSeconds: 8, resolution: "720p", aspectRatio: "16:9" },
@@ -1407,7 +1427,8 @@ var VIDEO_MODELS = [
1407
1427
  description: "Google's highest-quality video generation model. Supports up to 4K resolution and 8-second clips with reference image conditioning.",
1408
1428
  logo: GOOGLE_LOGO2
1409
1429
  },
1410
- capabilities: { ...FRAMES_EXCLUDE_REFERENCES }
1430
+ // Veo's lastFrame "must be used in combination with the image parameter".
1431
+ capabilities: { ...FRAMES_EXCLUDE_REFERENCES, lastFrameRequiresFirstFrame: true }
1411
1432
  },
1412
1433
  {
1413
1434
  id: "veo-3.1-fast",
@@ -1492,8 +1513,7 @@ var VIDEO_MODELS = [
1492
1513
  references: { images: 6, videos: 1, videoSeconds: { max: 10 } },
1493
1514
  sourceVideoSeconds: { max: 10 },
1494
1515
  supports: [],
1495
- // generateOmniVideo sends MediaRef.label as a text part beside each image.
1496
- prompt: { referenceSyntax: "caption" },
1516
+ prompt: OMNI_PROMPT,
1497
1517
  audio: "always"
1498
1518
  },
1499
1519
  defaults: {},
@@ -1527,10 +1547,10 @@ var VIDEO_MODELS = [
1527
1547
  // extension can upload a <= 10 s source with task "extend"; later ones
1528
1548
  // chain by `previousInteractionId` (the API rejects the id together
1529
1549
  // with a video task, so those calls carry the prompt alone).
1530
- supports: ["videoExtension"],
1550
+ // First-to-last-frame interpolation: `<LAST_FRAME>` goes with `<FIRST_FRAME>`.
1551
+ supports: ["videoExtension", "lastFrame"],
1531
1552
  extension: { segmentSeconds: 10, maxTotalSeconds: 40 },
1532
- // generateOmniVideo sends MediaRef.label as a text part beside each image.
1533
- prompt: { referenceSyntax: "caption" },
1553
+ prompt: OMNI_PROMPT,
1534
1554
  audio: "always"
1535
1555
  },
1536
1556
  defaults: {},
@@ -1541,7 +1561,7 @@ var VIDEO_MODELS = [
1541
1561
  logo: GOOGLE_LOGO2,
1542
1562
  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
1563
  },
1544
- capabilities: { acceptsSourceVideo: true }
1564
+ capabilities: { acceptsSourceVideo: true, lastFrameRequiresFirstFrame: true }
1545
1565
  },
1546
1566
  {
1547
1567
  // vibe-triggers and Space Builder drop the ratio when a frame is wired; Cowork always sends it.
@@ -1567,7 +1587,9 @@ var VIDEO_MODELS = [
1567
1587
  },
1568
1588
  sourceVideoSeconds: { min: 2, max: 15, inReferenceTotal: true },
1569
1589
  ratioIgnoredWith: ["firstFrame", "sourceVideo"],
1570
- supports: ["generateAudio", "seed", "lastFrame", "watermark"],
1590
+ // No seed: 2.x echoes it but does not honour it (two seeded runs differed, 2026-10-01).
1591
+ supports: ["generateAudio", "lastFrame", "watermark"],
1592
+ prompt: SEEDANCE_PROMPT,
1571
1593
  audio: "optional"
1572
1594
  },
1573
1595
  defaults: {
@@ -1607,7 +1629,9 @@ var VIDEO_MODELS = [
1607
1629
  },
1608
1630
  sourceVideoSeconds: { min: 2, max: 15, inReferenceTotal: true },
1609
1631
  ratioIgnoredWith: ["firstFrame", "sourceVideo"],
1610
- supports: ["generateAudio", "seed", "lastFrame", "watermark"],
1632
+ // No seed: 2.x echoes it but does not honour it (two seeded runs differed, 2026-10-01).
1633
+ supports: ["generateAudio", "lastFrame", "watermark"],
1634
+ prompt: SEEDANCE_PROMPT,
1611
1635
  audio: "optional"
1612
1636
  },
1613
1637
  defaults: {
@@ -1626,7 +1650,6 @@ var VIDEO_MODELS = [
1626
1650
  capabilities: { acceptsSourceVideo: true, ...FRAMES_EXCLUDE_REFERENCES }
1627
1651
  },
1628
1652
  {
1629
- // generateAudio defaults to false here (trigger-workflows); Space Builder defaults it to true.
1630
1653
  // omni_reference_task_type edit/extend: adaptive ratio, edit sends duration -1.
1631
1654
  id: "seedance-2.5",
1632
1655
  aliases: ["seedance-2-5", "dreamina-seedance-2-5-260628"],
@@ -1648,14 +1671,16 @@ var VIDEO_MODELS = [
1648
1671
  },
1649
1672
  sourceVideoSeconds: { min: 2, max: 30, editMin: 4, inReferenceTotal: true },
1650
1673
  ratioIgnoredWith: ["firstFrame", "sourceVideo"],
1651
- supports: ["generateAudio", "seed", "lastFrame", "watermark", "taskType"],
1674
+ // No seed: 2.x echoes it but does not honour it (two seeded runs differed, 2026-10-01).
1675
+ supports: ["generateAudio", "lastFrame", "watermark", "taskType"],
1676
+ prompt: SEEDANCE_PROMPT,
1652
1677
  audio: "optional"
1653
1678
  },
1654
1679
  defaults: {
1655
1680
  durationSeconds: 5,
1656
1681
  resolution: "720p",
1657
1682
  aspectRatio: "16:9",
1658
- generateAudio: false
1683
+ generateAudio: true
1659
1684
  },
1660
1685
  usage: { ...VIDEO_USAGE },
1661
1686
  pricingKind: "video_per_second",
@@ -1667,7 +1692,6 @@ var VIDEO_MODELS = [
1667
1692
  capabilities: { acceptsSourceVideo: true, ...FRAMES_EXCLUDE_REFERENCES }
1668
1693
  },
1669
1694
  {
1670
- // Cowork allows 7 reference images and durations 4/6/8/10; Space Builder allows 5 and 1-15s.
1671
1695
  id: "grok-imagine",
1672
1696
  aliases: ["grok-imagine-video", "grokImagine"],
1673
1697
  label: "Grok Imagine",
@@ -1681,7 +1705,8 @@ var VIDEO_MODELS = [
1681
1705
  resolutions: ["480p", "720p"],
1682
1706
  durations: { min: 1, max: 15 },
1683
1707
  durationPresets: [4, 6, 8, 10],
1684
- references: { images: 5 },
1708
+ // xAI: "A maximum of 7 reference images can be provided per request."
1709
+ references: { images: 7 },
1685
1710
  supports: [],
1686
1711
  // xAI can switch sound off (generate_audio); xai-video.ts never sends it.
1687
1712
  audio: "always"
@@ -1706,16 +1731,16 @@ var VIDEO_MODELS = [
1706
1731
  providerModelId: "kling-video/v2.5-turbo/pro/image-to-video",
1707
1732
  lifecycle: "active",
1708
1733
  exposure: ["cowork", "space_builder"],
1734
+ // The endpoint has no aspect_ratio or last_image_url: the start image sets the framing.
1709
1735
  constraints: {
1710
- aspectRatios: [...KLING_RATIOS],
1711
1736
  durations: [5, 10],
1712
1737
  requires: ["firstFrame"],
1713
- supports: ["cfgScale", "negativePrompt", "lastFrame"],
1738
+ supports: ["cfgScale", "negativePrompt"],
1739
+ prompt: KLING_PROMPT,
1714
1740
  audio: "never"
1715
1741
  },
1716
1742
  defaults: {
1717
1743
  durationSeconds: 5,
1718
- aspectRatio: "16:9",
1719
1744
  cfgScale: 0.5,
1720
1745
  negativePrompt: NEGATIVE_PROMPT
1721
1746
  },
@@ -1724,7 +1749,7 @@ var VIDEO_MODELS = [
1724
1749
  display: {
1725
1750
  provider: "kuaishou",
1726
1751
  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."
1752
+ 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
1753
  },
1729
1754
  capabilities: { ...FRAMES_EXCLUDE_REFERENCES }
1730
1755
  },
@@ -1738,19 +1763,18 @@ var VIDEO_MODELS = [
1738
1763
  providerModelId: "kling-video/v3.0/pro/image-to-video",
1739
1764
  lifecycle: "active",
1740
1765
  exposure: ["cowork", "space_builder"],
1766
+ // The endpoint has no aspect_ratio or negative_prompt; sound is "on"/"off" (default on).
1741
1767
  constraints: {
1742
- aspectRatios: [...KLING_RATIOS],
1743
1768
  durations: { min: 3, max: 15 },
1744
1769
  requires: ["firstFrame"],
1745
- supports: ["cfgScale", "negativePrompt", "lastFrame"],
1746
- // Higgsfield has a `sound` switch (default on); KlingCreate doesn't send it.
1747
- audio: "always"
1770
+ supports: ["cfgScale", "lastFrame", "generateAudio"],
1771
+ prompt: KLING_PROMPT,
1772
+ audio: "optional"
1748
1773
  },
1749
1774
  defaults: {
1750
1775
  durationSeconds: 5,
1751
- aspectRatio: "16:9",
1752
1776
  cfgScale: 0.5,
1753
- negativePrompt: NEGATIVE_PROMPT
1777
+ generateAudio: true
1754
1778
  },
1755
1779
  usage: { ...VIDEO_USAGE },
1756
1780
  pricingKind: "video_per_second",
@@ -1762,7 +1786,7 @@ var VIDEO_MODELS = [
1762
1786
  capabilities: { ...FRAMES_EXCLUDE_REFERENCES }
1763
1787
  },
1764
1788
  {
1765
- // mode standard/pro selects 720p/1080p; the endpoint takes no separate resolution.
1789
+ // mode standard/pro/4k selects 720p/1080p/4K; the endpoint takes no separate resolution.
1766
1790
  // Replicate: "Max 7 [images] without video, 4 with video"; 4K does not support
1767
1791
  // reference_video. No `videos` limit yet: the adapter sends no reference video,
1768
1792
  // so `imagesWithVideo` stays dormant until one is wired through.
@@ -1775,22 +1799,25 @@ var VIDEO_MODELS = [
1775
1799
  lifecycle: "active",
1776
1800
  exposure: ["cowork", "space_builder"],
1777
1801
  constraints: {
1778
- aspectRatios: [...KLING_RATIOS],
1779
- resolutions: ["720p", "1080p"],
1802
+ aspectRatios: [...KLING_OMNI_RATIOS],
1803
+ resolutions: ["720p", "1080p", "4k"],
1780
1804
  durations: { min: 3, max: 15 },
1781
1805
  references: { images: 7, imagesWithVideo: 4 },
1782
1806
  supports: ["generateAudio", "lastFrame"],
1807
+ prompt: KLING_PROMPT,
1783
1808
  audio: "optional"
1784
1809
  },
1785
- defaults: { durationSeconds: 5, aspectRatio: "16:9", generateAudio: false, mode: "pro" },
1810
+ defaults: { durationSeconds: 5, aspectRatio: "16:9", generateAudio: true, mode: "pro" },
1786
1811
  usage: { unit: "compute_seconds" },
1787
1812
  pricingKind: "video_per_second",
1788
1813
  display: {
1789
1814
  provider: "kuaishou",
1790
1815
  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: {}
1816
+ 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."
1817
+ }
1818
+ // end_image "Requires start_image", but no lastFrameRequiresFirstFrame: SpaceLab resolves
1819
+ // replicate rows in error mode, so the flag would refuse its end-frame-only nodes, which
1820
+ // the adapter sends as start_image.
1794
1821
  },
1795
1822
  {
1796
1823
  id: "minimax-h3",
@@ -1805,7 +1832,10 @@ var VIDEO_MODELS = [
1805
1832
  aspectRatios: [...H3_RATIOS],
1806
1833
  resolutions: ["480p", "768p", "2k", "4k"],
1807
1834
  durations: { min: 5, max: 15 },
1835
+ // fal's image-to-video endpoint has no aspect_ratio; the frame sets it.
1836
+ ratioIgnoredWith: ["firstFrame"],
1808
1837
  supports: ["seed", "promptExpansion", "lastFrame", "safetyChecker"],
1838
+ prompt: H3_PROMPT,
1809
1839
  audio: "always"
1810
1840
  },
1811
1841
  defaults: {
@@ -1834,9 +1864,12 @@ var VIDEO_MODELS = [
1834
1864
  exposure: ["cowork", "space_builder"],
1835
1865
  constraints: {
1836
1866
  aspectRatios: [...H3_RATIOS],
1837
- resolutions: ["480p", "768p"],
1867
+ resolutions: ["480p", "768p", "1080p"],
1838
1868
  durations: { min: 5, max: 15 },
1869
+ // fal's image-to-video endpoint has no aspect_ratio; the frame sets it.
1870
+ ratioIgnoredWith: ["firstFrame"],
1839
1871
  supports: ["seed", "promptExpansion", "lastFrame", "safetyChecker"],
1872
+ prompt: H3_PROMPT,
1840
1873
  audio: "always"
1841
1874
  },
1842
1875
  defaults: {
@@ -1850,7 +1883,7 @@ var VIDEO_MODELS = [
1850
1883
  display: {
1851
1884
  provider: "minimax",
1852
1885
  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."
1886
+ description: "High-speed MiniMax H3 Max variant for text-to-video and image-to-video (first/last frame) only, up to 1080p."
1854
1887
  },
1855
1888
  capabilities: { ...FRAMES_EXCLUDE_REFERENCES }
1856
1889
  },
@@ -1867,6 +1900,8 @@ var VIDEO_MODELS = [
1867
1900
  aspectRatios: [...H3_REFERENCE_RATIOS],
1868
1901
  resolutions: ["480p", "768p", "2k", "4k"],
1869
1902
  durations: { min: 5, max: 15 },
1903
+ // A start frame routes to image-to-video, which has no aspect_ratio.
1904
+ ratioIgnoredWith: ["firstFrame"],
1870
1905
  references: {
1871
1906
  images: 9,
1872
1907
  videos: 3,
@@ -1876,6 +1911,7 @@ var VIDEO_MODELS = [
1876
1911
  videoSeconds: { min: 2, max: 15, total: 15 }
1877
1912
  },
1878
1913
  supports: ["seed", "promptExpansion", "safetyChecker"],
1914
+ prompt: H3_PROMPT,
1879
1915
  audio: "always"
1880
1916
  },
1881
1917
  defaults: {
@@ -1906,8 +1942,10 @@ var VIDEO_MODELS = [
1906
1942
  exposure: ["cowork", "space_builder"],
1907
1943
  constraints: {
1908
1944
  aspectRatios: [...H3_REFERENCE_RATIOS],
1909
- resolutions: ["480p", "768p"],
1945
+ resolutions: ["480p", "768p", "1080p"],
1910
1946
  durations: { min: 5, max: 15 },
1947
+ // A start frame routes to image-to-video, which has no aspect_ratio.
1948
+ ratioIgnoredWith: ["firstFrame"],
1911
1949
  references: {
1912
1950
  images: 9,
1913
1951
  videos: 3,
@@ -1917,6 +1955,7 @@ var VIDEO_MODELS = [
1917
1955
  videoSeconds: { min: 2, max: 15, total: 15 }
1918
1956
  },
1919
1957
  supports: ["seed", "promptExpansion", "safetyChecker"],
1958
+ prompt: H3_PROMPT,
1920
1959
  audio: "always"
1921
1960
  },
1922
1961
  defaults: {
@@ -2464,6 +2503,7 @@ var CatalogDisplaySchema = import_zod3.z.looseObject({
2464
2503
  var CatalogCapabilitiesSchema = import_zod3.z.looseObject({
2465
2504
  acceptsSourceVideo: import_zod3.z.boolean().optional(),
2466
2505
  framesExcludeReferences: import_zod3.z.boolean().optional(),
2506
+ lastFrameRequiresFirstFrame: import_zod3.z.boolean().optional(),
2467
2507
  reasoning: import_zod3.z.boolean().optional(),
2468
2508
  vision: import_zod3.z.boolean().optional(),
2469
2509
  contextWindow: import_zod3.z.number().optional()
@@ -4686,7 +4726,12 @@ async function generateOmniVideo(req, ctx, createGenAIClient) {
4686
4726
  const extend = req.taskType === "extend";
4687
4727
  const previousId = extend ? req.previousInteractionId : void 0;
4688
4728
  const chained = previousId !== void 0;
4689
- for (const field of ["referenceImages", "referenceVideos", "firstFrame"]) {
4729
+ for (const field of [
4730
+ "referenceImages",
4731
+ "referenceVideos",
4732
+ "firstFrame",
4733
+ "lastFrame"
4734
+ ]) {
4690
4735
  const value = req[field];
4691
4736
  if (!chained || value === void 0 || Array.isArray(value) && value.length === 0)
4692
4737
  continue;
@@ -4700,6 +4745,14 @@ async function generateOmniVideo(req, ctx, createGenAIClient) {
4700
4745
  const images = chained ? [] : req.referenceImages ?? [];
4701
4746
  const videos = chained ? [] : req.referenceVideos ?? [];
4702
4747
  const firstFrame = chained ? void 0 : req.firstFrame;
4748
+ const lastFrame = firstFrame ? req.lastFrame : void 0;
4749
+ if (!chained && req.lastFrame !== void 0 && lastFrame === void 0) {
4750
+ ctx.warnings.push({
4751
+ code: "option_ignored",
4752
+ field: "lastFrame",
4753
+ message: `${modelId} takes an end frame only with a start frame; lastFrame ignored`
4754
+ });
4755
+ }
4703
4756
  if (extend && !chained && req.sourceVideo === void 0) {
4704
4757
  throw new ModelKitError({
4705
4758
  code: "missing_input",
@@ -4784,27 +4837,7 @@ async function generateOmniVideo(req, ctx, createGenAIClient) {
4784
4837
  mime_type: media.mime
4785
4838
  });
4786
4839
  }
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) {
4840
+ const pushImage = async (ref) => {
4808
4841
  if (ref.label !== void 0) parts.push({ type: "text", text: ref.label });
4809
4842
  const media = await resolveMedia(ref, ctx.fetch, ctx.signal, "image/png", modelId);
4810
4843
  if (media.bytes.length > OMNI_INLINE_LIMIT_BYTES) {
@@ -4816,8 +4849,12 @@ async function generateOmniVideo(req, ctx, createGenAIClient) {
4816
4849
  } else {
4817
4850
  parts.push({ type: "image", data: toBase64(media.bytes), mime_type: media.mime });
4818
4851
  }
4819
- }
4820
- parts.push({ type: "text", text: req.prompt });
4852
+ };
4853
+ if (firstFrame) await pushImage(firstFrame);
4854
+ if (lastFrame) await pushImage(lastFrame);
4855
+ for (const ref of images) await pushImage(ref);
4856
+ const prompt = lastFrame ? `<FIRST_FRAME> <LAST_FRAME> ${req.prompt}` : req.prompt;
4857
+ parts.push({ type: "text", text: prompt });
4821
4858
  try {
4822
4859
  interaction = await withRetry(async () => {
4823
4860
  try {
@@ -5347,6 +5384,26 @@ function videoUsage3(ctx, req, duration) {
5347
5384
  if (req.durationSeconds !== void 0) event.durationSeconds = req.durationSeconds;
5348
5385
  return event;
5349
5386
  }
5387
+ var KLING_ENDPOINT_FIELDS = /* @__PURE__ */ new Map([
5388
+ [
5389
+ "kling-video/v2.5-turbo/pro/image-to-video",
5390
+ { aspectRatio: false, negativePrompt: true, lastFrame: false, sound: false }
5391
+ ],
5392
+ [
5393
+ "kling-video/v3.0/pro/image-to-video",
5394
+ { aspectRatio: false, negativePrompt: false, lastFrame: true, sound: true }
5395
+ ]
5396
+ ]);
5397
+ var UNLISTED_KLING_FIELDS = {
5398
+ aspectRatio: true,
5399
+ negativePrompt: true,
5400
+ lastFrame: true,
5401
+ sound: false
5402
+ };
5403
+ function klingEndpointFields(providerModelId) {
5404
+ const path = (providerModelId.startsWith("/") ? providerModelId.slice(1) : providerModelId).toLowerCase();
5405
+ return KLING_ENDPOINT_FIELDS.get(path) ?? UNLISTED_KLING_FIELDS;
5406
+ }
5350
5407
  var klingCreateSchema = import_zod13.z.object({ status_url: import_zod13.z.string() });
5351
5408
  var klingStatusSchema = import_zod13.z.object({
5352
5409
  status: import_zod13.z.string().optional(),
@@ -5381,6 +5438,13 @@ async function createRequest(ctx, url, init) {
5381
5438
  }
5382
5439
  return res;
5383
5440
  }
5441
+ function ignoreKlingField(ctx, field) {
5442
+ ctx.warnings.push({
5443
+ code: "option_ignored",
5444
+ field,
5445
+ message: `${ctx.model.id}: the Higgsfield endpoint has no ${field} input; ignored`
5446
+ });
5447
+ }
5384
5448
  async function generateKlingVideo(req, ctx) {
5385
5449
  const firstFrame = req.firstFrame ? refToUrl(req.firstFrame) : void 0;
5386
5450
  if (!firstFrame) {
@@ -5392,16 +5456,29 @@ async function generateKlingVideo(req, ctx) {
5392
5456
  retryable: false
5393
5457
  });
5394
5458
  }
5459
+ const fields = klingEndpointFields(ctx.model.providerModelId);
5395
5460
  const body = {
5396
5461
  prompt: req.prompt.trim(),
5397
5462
  image_url: firstFrame,
5398
- cfg_scale: req.cfgScale ?? 0.5,
5399
- negative_prompt: req.negativePrompt ?? DEFAULT_NEGATIVE_PROMPT
5463
+ cfg_scale: req.cfgScale ?? 0.5
5400
5464
  };
5465
+ if (fields.negativePrompt) {
5466
+ body.negative_prompt = req.negativePrompt ?? DEFAULT_NEGATIVE_PROMPT;
5467
+ } else if (req.negativePrompt !== void 0) {
5468
+ ignoreKlingField(ctx, "negativePrompt");
5469
+ }
5401
5470
  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);
5471
+ if (req.aspectRatio !== void 0 && req.aspectRatio !== "adaptive") {
5472
+ if (fields.aspectRatio) body.aspect_ratio = req.aspectRatio;
5473
+ else ignoreKlingField(ctx, "aspectRatio");
5474
+ }
5475
+ if (req.lastFrame) {
5476
+ if (fields.lastFrame) body.last_image_url = refToUrl(req.lastFrame);
5477
+ else ignoreKlingField(ctx, "lastFrame");
5478
+ }
5479
+ if (fields.sound && req.generateAudio !== void 0) {
5480
+ body.sound = req.generateAudio ? "on" : "off";
5481
+ }
5405
5482
  const endpoint = klingEndpoint(ctx.model.providerModelId, ctx.model.id);
5406
5483
  const retryOptions = mediaRetryOptions(ctx.call, ctx.signal);
5407
5484
  const createRes = await withRetry(
@@ -6703,24 +6780,22 @@ function klingInput(req) {
6703
6780
  return input;
6704
6781
  }
6705
6782
  function klingOmniMode(req) {
6783
+ if (req.resolution === "4k") return "4k";
6706
6784
  if (req.resolution === "1080p") return "pro";
6707
6785
  if (req.resolution === "720p") return "standard";
6708
6786
  return req.mode ?? "standard";
6709
6787
  }
6710
6788
  function klingOmniInput(req) {
6711
- const images = (req.firstFrame ? [req.firstFrame] : []).map(refToUrl);
6712
- if (req.lastFrame) images.push(refToUrl(req.lastFrame));
6713
6789
  const input = {
6714
6790
  prompt: req.prompt,
6715
6791
  duration: req.durationSeconds ?? 10,
6716
6792
  mode: klingOmniMode(req),
6717
6793
  generate_audio: req.generateAudio ?? false
6718
6794
  };
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;
6795
+ const start = req.firstFrame ?? req.lastFrame;
6796
+ if (start) {
6797
+ input.start_image = refToUrl(start);
6798
+ if (req.firstFrame && req.lastFrame) input.end_image = refToUrl(req.lastFrame);
6724
6799
  } else if (req.aspectRatio !== "adaptive") {
6725
6800
  input.aspect_ratio = req.aspectRatio ?? "16:9";
6726
6801
  }
@@ -6968,7 +7043,7 @@ async function generateXaiVideo(req, ctx, deps) {
6968
7043
  retryable: false
6969
7044
  });
6970
7045
  }
6971
- const refUrls = (req.referenceImages ?? []).map(refToUrl).slice(0, 5);
7046
+ const refUrls = (req.referenceImages ?? []).map(refToUrl).slice(0, 7);
6972
7047
  const xaiOptions = {
6973
7048
  pollIntervalMs: POLL_INTERVAL_MS2,
6974
7049
  pollTimeoutMs: POLL_TIMEOUT_MS2
@@ -7682,6 +7757,24 @@ function resolveVideoOptions(model, request, onUnsupported = "error") {
7682
7757
  });
7683
7758
  delete merged.sourceVideo;
7684
7759
  }
7760
+ if (merged.lastFrame !== void 0 && merged.firstFrame === void 0 && model.capabilities?.lastFrameRequiresFirstFrame) {
7761
+ if (onUnsupported === "error") {
7762
+ throw new ModelKitError({
7763
+ code: "missing_input",
7764
+ message: `${model.id} takes an end frame only together with a start frame`,
7765
+ model: model.id,
7766
+ retryable: false
7767
+ });
7768
+ }
7769
+ warnings.push({
7770
+ code: "option_ignored",
7771
+ field: "lastFrame",
7772
+ to: "firstFrame",
7773
+ message: `end frame used as start frame: ${model.id} needs a start frame with an end frame`
7774
+ });
7775
+ merged.firstFrame = merged.lastFrame;
7776
+ delete merged.lastFrame;
7777
+ }
7685
7778
  const hasFrames = merged.firstFrame !== void 0 || merged.lastFrame !== void 0;
7686
7779
  const hasRefs = compact(merged.referenceImages).length + compact(merged.referenceVideos).length + compact(merged.referenceAudios).length > 0;
7687
7780
  if (hasFrames && hasRefs && model.capabilities?.framesExcludeReferences) {