@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/CHANGELOG.md CHANGED
@@ -9,6 +9,53 @@ header in the release PR.
9
9
 
10
10
  ## [Unreleased]
11
11
 
12
+ ## [0.2.21] - Unreleased
13
+
14
+ ### Added
15
+ - `capabilities.lastFrameRequiresFirstFrame` (boolean): a video row that takes an end frame only together with a start frame. On such a row, `resolveVideoOptions` in `error` mode refuses an end frame without a start frame with a non-retryable `missing_input`; in `coerce` mode the lone end frame becomes the start frame, with an `option_ignored` warning ("end frame used as start frame: <model> needs a start frame with an end frame"). Set on `veo-3.1` ("must be used in combination with the image parameter") and `gemini-omni-1.1-flash` (`<LAST_FRAME>` "must be used with `<FIRST_FRAME>`"). Not set on `kling-v3-omni-video` (`end_image` "Requires start_image"): error-mode callers get `missing_input`, and SpaceLab's vibe-triggers resolves replicate and google-genai rows in error mode, so the flag would refuse its end-frame-only Kling Omni nodes. Without the flag the Replicate adapter keeps sending a lone end frame as `start_image`.
16
+ - `gemini-omni-1.1-flash` takes an end frame (`supports: ["videoExtension", "lastFrame"]`). The adapter sends the start image, then the end image, then any references, and prefixes the prompt with the docs' role tags `<FIRST_FRAME> <LAST_FRAME>`. A chained extension drops the end frame with a warning, as it drops the other conditioning media.
17
+ - Higgsfield `kling-3.0` sound switch: `generateAudio` maps to the endpoint's `sound: "on" | "off"`. The row is now `audio: "optional"`, `supports` `generateAudio`, default `generateAudio: true` (the provider default is on).
18
+ - `kling-v3-omni-video` offers `4k`: the Replicate adapter maps `resolution: "4k"` to `mode: "4k"` (4K does not take a reference video, which the adapter never sends).
19
+ - `constraints.prompt.maxChars`: 2500 on `kling-2.5`, `kling-3.0` and `kling-v3-omni-video` (Higgsfield 3.0 truncates past 2,500; Replicate's Omni says "Max 2500 characters"; Higgsfield 2.5 documents none, the same model on fal and Kling's API caps it at 2,500), 3000 on `veo-3.1` (1,024 text tokens at about 2.9 characters per token; a CJK prompt can hit the token limit sooner). Each keeps its `numbered` reference syntax.
20
+
21
+ ### Changed
22
+ - Higgsfield Kling adapter sends each endpoint only the fields its schema has. `kling-video/v2.5-turbo/pro/image-to-video` gets no `aspect_ratio` and no `last_image_url`; `kling-video/v3.0/pro/image-to-video` gets no `aspect_ratio` and no `negative_prompt`. A dropped caller value becomes an `option_ignored` warning. An unlisted Higgsfield Kling endpoint keeps the fields it was sent before.
23
+ - `kling-2.5`: no `aspectRatios`, no ratio default, `supports` loses `lastFrame`.
24
+ - `kling-3.0`: no `aspectRatios`, no ratio default, `supports` loses `negativePrompt` (and the default negative prompt).
25
+ - `kling-v3-omni-video`: `aspectRatios` loses `adaptive` (not in Replicate's enum; "Auto" meant the provider's 16:9); `resolutions` gains `4k`; default `generateAudio: true`.
26
+ - `seedance-2.0`, `seedance-2.0-fast`, `seedance-2.5`: `supports` loses `seed`. BytePlus accepts and echoes `seed` on 2.x, but two identical seeded tasks gave different videos (SSIM 0.83, 2026-10-01). The ark adapter still sends `seed` for rows that support it (Seedream and others).
27
+ - `seedance-2.5`: default `generateAudio: true` (sound on by default where it is optional).
28
+ - `minimax-h3-max`, `minimax-h3-max-reference`: `resolutions` gain `1080p` (sent as fal's `1080P`).
29
+ - `minimax-h3`, `minimax-h3-max`, `minimax-h3-reference`, `minimax-h3-max-reference`: `ratioIgnoredWith: ["firstFrame"]`. A start frame routes to fal's image-to-video endpoint, which has no `aspect_ratio`.
30
+ - `grok-imagine`: `references.images` 7 (was 5); the xAI adapter caps at 7 ("A maximum of 7 reference images can be provided per request"). The description still says 5 because it is pinned to the Cowork catalog fixture.
31
+
32
+ ### Deferred
33
+ - `veo-3.1` stays at `durations: [8]`. Veo also takes 4 s and 6 s, but must be 8 s at 1080p or 4k and with reference images, and the kit has no rule that forces a duration from other options. Offering `[4, 6, 8]` now would snap SpaceLab's stored Veo nodes (default 5 s) to 4 s, which Veo rejects at 1080p/4k and which halves 720p clips. Ship it with a kit rule that forces 8 s in those cases (`veo-3.1-fast` and `veo-3.1-lite` already list 4/6/8 and have this gap).
34
+
35
+ ### Live rows to edit after publish
36
+ Apps read the pro-backend `model_catalog` rows, not this file. Before the live edit, add `credit_pricing_video` rows for `kling-v3-omni-video` at `4k` and for `minimax-h3-max` / `minimax-h3-max-reference` at `1080p`: without them those options fail to quote. At the version bump in shopos-ui, update vibe-api's pinned pricing producer contract (`contracts/backend-pricing-contract.json`, checked by `producer-contract.test.ts` against the bundled rows), which gains the same `kling-v3-omni-video`/`4k` and `minimax-h3-max`/`1080p` keys.
37
+
38
+ Preconditions:
39
+ - Remove the `kling-2.5` and `kling-3.0` aspect ratios only after cowork with Shop-OS/shopos-backend-ts#1197 (including its ratio-less projection fix) is deployed. Before that, #1197's `?frames=1` projection 500s on a ratio-less row.
40
+ - Set `capabilities.lastFrameRequiresFirstFrame` through pro-backend's admin catalog API. Don't save the `veo-3.1` or `gemini-omni-1.1-flash` rows in pro-admin until its row form keeps unknown capability keys and has the checkbox (fix in Shop-OS/shopos-ui#2711); today it drops the whole `capabilities` object.
41
+
42
+ Behaviour after the edit:
43
+ - `seedance-2.5` and `kling-v3-omni-video` default to sound on, and chat and Creative Director send the row default, so their videos gain sound too. Credits are unchanged until audio pricing lands. Kling V3 Omni with audio costs $0.224/s at standard (720p) and $0.28/s at pro (1080p) on Replicate, against $0.168/s and $0.224/s without: check the `credit_pricing_video` margin.
44
+ - A SpaceLab Kling 3.0 node with a stored `generate_audio: false` goes silent after the `kling-3.0` edit, where it had sound before.
45
+
46
+ After pro-backend is on 0.2.21, in each environment:
47
+ - `kling-2.5`: remove `constraints.aspectRatios`; remove `defaults.aspectRatio`; `constraints.supports` → `["cfgScale", "negativePrompt"]`; `constraints.prompt` → `{ "referenceSyntax": "numbered", "maxChars": 2500 }`; `display.description` → the new text (no end image).
48
+ - `kling-3.0`: remove `constraints.aspectRatios`; remove `defaults.aspectRatio` and `defaults.negativePrompt`; `constraints.supports` → `["cfgScale", "lastFrame", "generateAudio"]`; `constraints.audio` → `"optional"`; `defaults.generateAudio` → `true`; `constraints.prompt` → `{ "referenceSyntax": "numbered", "maxChars": 2500 }`. Higgsfield lists this endpoint at $0.112-$0.168/s without labelling the sound split; check whether `credit_pricing_video` should price sound on and off separately.
49
+ - `kling-v3-omni-video`: `constraints.aspectRatios` → `["16:9", "9:16", "1:1"]`; `constraints.resolutions` → `["720p", "1080p", "4k"]`; `defaults.generateAudio` → `true`; `constraints.prompt` → `{ "referenceSyntax": "numbered", "maxChars": 2500 }`; `display.description` → "... up to 4K". Needs a `credit_pricing_video` row for `kling-v3-omni-video` at `4k` first (Replicate: $0.42/s, with or without audio).
50
+ - `veo-3.1`: `constraints.prompt` → `{ "referenceSyntax": "numbered", "maxChars": 3000 }`; `capabilities.lastFrameRequiresFirstFrame` → `true`.
51
+ - `gemini-omni-1.1-flash`: `constraints.supports` → `["videoExtension", "lastFrame"]`; `capabilities.lastFrameRequiresFirstFrame` → `true`.
52
+ - `seedance-2.0`, `seedance-2.0-fast`: `constraints.supports` → `["generateAudio", "lastFrame", "watermark"]`.
53
+ - `seedance-2.5`: `constraints.supports` → `["generateAudio", "lastFrame", "watermark", "taskType"]`; `defaults.generateAudio` → `true`.
54
+ - `minimax-h3`, `minimax-h3-reference`: `constraints.ratioIgnoredWith` → `["firstFrame"]`.
55
+ - `minimax-h3-max`, `minimax-h3-max-reference`: `constraints.resolutions` → `["480p", "768p", "1080p"]`; `constraints.ratioIgnoredWith` → `["firstFrame"]`; `display.description` of `minimax-h3-max` → "... up to 1080p". Needs `credit_pricing_video` rows for both at `1080p`.
56
+ - `grok-imagine`: `constraints.references.images` → `7`.
57
+ - `p-video-2-pro` (pro-admin-only, not in this file): `constraints.aspectRatios` without `adaptive` (Replicate's enum has none); `constraints.audio` → `"always"` (still outstanding from 0.2.20).
58
+
12
59
  ## [0.2.20] - Unreleased
13
60
 
14
61
  ### Added
package/dist/catalog.cjs CHANGED
@@ -1306,12 +1306,14 @@ var TEXT_MODELS = [
1306
1306
  var VIDEO_USAGE = { unit: "seconds" };
1307
1307
  var SEEDANCE_RATIOS = ["21:9", "16:9", "4:3", "1:1", "3:4", "9:16", "adaptive"];
1308
1308
  var KLING_RATIOS = ["16:9", "9:16", "1:1", "adaptive"];
1309
+ var KLING_OMNI_RATIOS = ["16:9", "9:16", "1:1"];
1309
1310
  var OMNI_RATIOS = ["16:9", "9:16"];
1310
1311
  var GROK_RATIOS = ["1:1", "16:9", "9:16", "4:3", "3:4", "3:2", "2:3"];
1311
1312
  var H3_RATIOS = ["21:9", "16:9", "4:3", "1:1", "3:4", "9:16"];
1312
1313
  var H3_REFERENCE_RATIOS = ["adaptive", ...H3_RATIOS];
1313
1314
  var NEGATIVE_PROMPT = "blur, distort, and low quality";
1314
1315
  var FRAMES_EXCLUDE_REFERENCES = { framesExcludeReferences: true };
1316
+ var KLING_PROMPT = { referenceSyntax: "numbered", maxChars: 2500 };
1315
1317
  var BYTEDANCE_LOGO = "https://d31vvfpk2ppdyq.cloudfront.net/assets/image/3bba5d81-4e57-4e71-911b-cdfa05acca2c_compressed.png";
1316
1318
  var GOOGLE_LOGO2 = "https://d31vvfpk2ppdyq.cloudfront.net/assets/image/192485d2-a76a-4657-9c06-930b2c70badd_compressed.png";
1317
1319
  var VIDEO_MODELS = [
@@ -1332,9 +1334,13 @@ var VIDEO_MODELS = [
1332
1334
  constraints: {
1333
1335
  aspectRatios: [...OMNI_RATIOS],
1334
1336
  resolutions: ["720p", "1080p", "4k"],
1337
+ // Veo also takes 4 and 6, but must be 8 at 1080p/4k or with reference images;
1338
+ // [8] until the kit has a rule that forces it there (CHANGELOG 0.2.21).
1335
1339
  durations: [8],
1336
1340
  references: { images: 3 },
1337
1341
  supports: ["lastFrame", "seed"],
1342
+ // 1,024 text tokens; 3,000 characters assumes ~2.9 characters per token.
1343
+ prompt: { referenceSyntax: "numbered", maxChars: 3e3 },
1338
1344
  audio: "always"
1339
1345
  },
1340
1346
  defaults: { durationSeconds: 8, resolution: "720p", aspectRatio: "16:9" },
@@ -1345,7 +1351,8 @@ var VIDEO_MODELS = [
1345
1351
  description: "Google's highest-quality video generation model. Supports up to 4K resolution and 8-second clips with reference image conditioning.",
1346
1352
  logo: GOOGLE_LOGO2
1347
1353
  },
1348
- capabilities: { ...FRAMES_EXCLUDE_REFERENCES }
1354
+ // Veo's lastFrame "must be used in combination with the image parameter".
1355
+ capabilities: { ...FRAMES_EXCLUDE_REFERENCES, lastFrameRequiresFirstFrame: true }
1349
1356
  },
1350
1357
  {
1351
1358
  id: "veo-3.1-fast",
@@ -1465,7 +1472,8 @@ var VIDEO_MODELS = [
1465
1472
  // extension can upload a <= 10 s source with task "extend"; later ones
1466
1473
  // chain by `previousInteractionId` (the API rejects the id together
1467
1474
  // with a video task, so those calls carry the prompt alone).
1468
- supports: ["videoExtension"],
1475
+ // First-to-last-frame interpolation: `<LAST_FRAME>` goes with `<FIRST_FRAME>`.
1476
+ supports: ["videoExtension", "lastFrame"],
1469
1477
  extension: { segmentSeconds: 10, maxTotalSeconds: 40 },
1470
1478
  // generateOmniVideo sends MediaRef.label as a text part beside each image.
1471
1479
  prompt: { referenceSyntax: "caption" },
@@ -1479,7 +1487,7 @@ var VIDEO_MODELS = [
1479
1487
  logo: GOOGLE_LOGO2,
1480
1488
  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."
1481
1489
  },
1482
- capabilities: { acceptsSourceVideo: true }
1490
+ capabilities: { acceptsSourceVideo: true, lastFrameRequiresFirstFrame: true }
1483
1491
  },
1484
1492
  {
1485
1493
  // vibe-triggers and Space Builder drop the ratio when a frame is wired; Cowork always sends it.
@@ -1505,7 +1513,8 @@ var VIDEO_MODELS = [
1505
1513
  },
1506
1514
  sourceVideoSeconds: { min: 2, max: 15, inReferenceTotal: true },
1507
1515
  ratioIgnoredWith: ["firstFrame", "sourceVideo"],
1508
- supports: ["generateAudio", "seed", "lastFrame", "watermark"],
1516
+ // No seed: 2.x echoes it but does not honour it (two seeded runs differed, 2026-10-01).
1517
+ supports: ["generateAudio", "lastFrame", "watermark"],
1509
1518
  audio: "optional"
1510
1519
  },
1511
1520
  defaults: {
@@ -1545,7 +1554,8 @@ var VIDEO_MODELS = [
1545
1554
  },
1546
1555
  sourceVideoSeconds: { min: 2, max: 15, inReferenceTotal: true },
1547
1556
  ratioIgnoredWith: ["firstFrame", "sourceVideo"],
1548
- supports: ["generateAudio", "seed", "lastFrame", "watermark"],
1557
+ // No seed: 2.x echoes it but does not honour it (two seeded runs differed, 2026-10-01).
1558
+ supports: ["generateAudio", "lastFrame", "watermark"],
1549
1559
  audio: "optional"
1550
1560
  },
1551
1561
  defaults: {
@@ -1564,7 +1574,6 @@ var VIDEO_MODELS = [
1564
1574
  capabilities: { acceptsSourceVideo: true, ...FRAMES_EXCLUDE_REFERENCES }
1565
1575
  },
1566
1576
  {
1567
- // generateAudio defaults to false here (trigger-workflows); Space Builder defaults it to true.
1568
1577
  // omni_reference_task_type edit/extend: adaptive ratio, edit sends duration -1.
1569
1578
  id: "seedance-2.5",
1570
1579
  aliases: ["seedance-2-5", "dreamina-seedance-2-5-260628"],
@@ -1586,14 +1595,15 @@ var VIDEO_MODELS = [
1586
1595
  },
1587
1596
  sourceVideoSeconds: { min: 2, max: 30, editMin: 4, inReferenceTotal: true },
1588
1597
  ratioIgnoredWith: ["firstFrame", "sourceVideo"],
1589
- supports: ["generateAudio", "seed", "lastFrame", "watermark", "taskType"],
1598
+ // No seed: 2.x echoes it but does not honour it (two seeded runs differed, 2026-10-01).
1599
+ supports: ["generateAudio", "lastFrame", "watermark", "taskType"],
1590
1600
  audio: "optional"
1591
1601
  },
1592
1602
  defaults: {
1593
1603
  durationSeconds: 5,
1594
1604
  resolution: "720p",
1595
1605
  aspectRatio: "16:9",
1596
- generateAudio: false
1606
+ generateAudio: true
1597
1607
  },
1598
1608
  usage: { ...VIDEO_USAGE },
1599
1609
  pricingKind: "video_per_second",
@@ -1605,7 +1615,6 @@ var VIDEO_MODELS = [
1605
1615
  capabilities: { acceptsSourceVideo: true, ...FRAMES_EXCLUDE_REFERENCES }
1606
1616
  },
1607
1617
  {
1608
- // Cowork allows 7 reference images and durations 4/6/8/10; Space Builder allows 5 and 1-15s.
1609
1618
  id: "grok-imagine",
1610
1619
  aliases: ["grok-imagine-video", "grokImagine"],
1611
1620
  label: "Grok Imagine",
@@ -1619,7 +1628,8 @@ var VIDEO_MODELS = [
1619
1628
  resolutions: ["480p", "720p"],
1620
1629
  durations: { min: 1, max: 15 },
1621
1630
  durationPresets: [4, 6, 8, 10],
1622
- references: { images: 5 },
1631
+ // xAI: "A maximum of 7 reference images can be provided per request."
1632
+ references: { images: 7 },
1623
1633
  supports: [],
1624
1634
  // xAI can switch sound off (generate_audio); xai-video.ts never sends it.
1625
1635
  audio: "always"
@@ -1644,16 +1654,16 @@ var VIDEO_MODELS = [
1644
1654
  providerModelId: "kling-video/v2.5-turbo/pro/image-to-video",
1645
1655
  lifecycle: "active",
1646
1656
  exposure: ["cowork", "space_builder"],
1657
+ // The endpoint has no aspect_ratio or last_image_url: the start image sets the framing.
1647
1658
  constraints: {
1648
- aspectRatios: [...KLING_RATIOS],
1649
1659
  durations: [5, 10],
1650
1660
  requires: ["firstFrame"],
1651
- supports: ["cfgScale", "negativePrompt", "lastFrame"],
1661
+ supports: ["cfgScale", "negativePrompt"],
1662
+ prompt: KLING_PROMPT,
1652
1663
  audio: "never"
1653
1664
  },
1654
1665
  defaults: {
1655
1666
  durationSeconds: 5,
1656
- aspectRatio: "16:9",
1657
1667
  cfgScale: 0.5,
1658
1668
  negativePrompt: NEGATIVE_PROMPT
1659
1669
  },
@@ -1662,7 +1672,7 @@ var VIDEO_MODELS = [
1662
1672
  display: {
1663
1673
  provider: "kuaishou",
1664
1674
  logo: "https://cdn.jsdelivr.net/npm/@lobehub/icons-static-png@1.97.0/light/kling-color.png",
1665
- 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."
1675
+ description: "Kuaishou Kling 2.5 Turbo Pro image-to-video; animates a single input image from a text prompt, 5- or 10-second clips."
1666
1676
  },
1667
1677
  capabilities: { ...FRAMES_EXCLUDE_REFERENCES }
1668
1678
  },
@@ -1676,19 +1686,18 @@ var VIDEO_MODELS = [
1676
1686
  providerModelId: "kling-video/v3.0/pro/image-to-video",
1677
1687
  lifecycle: "active",
1678
1688
  exposure: ["cowork", "space_builder"],
1689
+ // The endpoint has no aspect_ratio or negative_prompt; sound is "on"/"off" (default on).
1679
1690
  constraints: {
1680
- aspectRatios: [...KLING_RATIOS],
1681
1691
  durations: { min: 3, max: 15 },
1682
1692
  requires: ["firstFrame"],
1683
- supports: ["cfgScale", "negativePrompt", "lastFrame"],
1684
- // Higgsfield has a `sound` switch (default on); KlingCreate doesn't send it.
1685
- audio: "always"
1693
+ supports: ["cfgScale", "lastFrame", "generateAudio"],
1694
+ prompt: KLING_PROMPT,
1695
+ audio: "optional"
1686
1696
  },
1687
1697
  defaults: {
1688
1698
  durationSeconds: 5,
1689
- aspectRatio: "16:9",
1690
1699
  cfgScale: 0.5,
1691
- negativePrompt: NEGATIVE_PROMPT
1700
+ generateAudio: true
1692
1701
  },
1693
1702
  usage: { ...VIDEO_USAGE },
1694
1703
  pricingKind: "video_per_second",
@@ -1700,7 +1709,7 @@ var VIDEO_MODELS = [
1700
1709
  capabilities: { ...FRAMES_EXCLUDE_REFERENCES }
1701
1710
  },
1702
1711
  {
1703
- // mode standard/pro selects 720p/1080p; the endpoint takes no separate resolution.
1712
+ // mode standard/pro/4k selects 720p/1080p/4K; the endpoint takes no separate resolution.
1704
1713
  // Replicate: "Max 7 [images] without video, 4 with video"; 4K does not support
1705
1714
  // reference_video. No `videos` limit yet: the adapter sends no reference video,
1706
1715
  // so `imagesWithVideo` stays dormant until one is wired through.
@@ -1713,22 +1722,25 @@ var VIDEO_MODELS = [
1713
1722
  lifecycle: "active",
1714
1723
  exposure: ["cowork", "space_builder"],
1715
1724
  constraints: {
1716
- aspectRatios: [...KLING_RATIOS],
1717
- resolutions: ["720p", "1080p"],
1725
+ aspectRatios: [...KLING_OMNI_RATIOS],
1726
+ resolutions: ["720p", "1080p", "4k"],
1718
1727
  durations: { min: 3, max: 15 },
1719
1728
  references: { images: 7, imagesWithVideo: 4 },
1720
1729
  supports: ["generateAudio", "lastFrame"],
1730
+ prompt: KLING_PROMPT,
1721
1731
  audio: "optional"
1722
1732
  },
1723
- defaults: { durationSeconds: 5, aspectRatio: "16:9", generateAudio: false, mode: "pro" },
1733
+ defaults: { durationSeconds: 5, aspectRatio: "16:9", generateAudio: true, mode: "pro" },
1724
1734
  usage: { unit: "compute_seconds" },
1725
1735
  pricingKind: "video_per_second",
1726
1736
  display: {
1727
1737
  provider: "kuaishou",
1728
1738
  logo: "https://cdn.jsdelivr.net/npm/@lobehub/icons-static-png@1.97.0/light/kling-color.png",
1729
- 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."
1730
- },
1731
- capabilities: {}
1739
+ 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."
1740
+ }
1741
+ // end_image "Requires start_image", but no lastFrameRequiresFirstFrame: SpaceLab resolves
1742
+ // replicate rows in error mode, so the flag would refuse its end-frame-only nodes, which
1743
+ // the adapter sends as start_image.
1732
1744
  },
1733
1745
  {
1734
1746
  id: "minimax-h3",
@@ -1743,6 +1755,8 @@ var VIDEO_MODELS = [
1743
1755
  aspectRatios: [...H3_RATIOS],
1744
1756
  resolutions: ["480p", "768p", "2k", "4k"],
1745
1757
  durations: { min: 5, max: 15 },
1758
+ // fal's image-to-video endpoint has no aspect_ratio; the frame sets it.
1759
+ ratioIgnoredWith: ["firstFrame"],
1746
1760
  supports: ["seed", "promptExpansion", "lastFrame", "safetyChecker"],
1747
1761
  audio: "always"
1748
1762
  },
@@ -1772,8 +1786,10 @@ var VIDEO_MODELS = [
1772
1786
  exposure: ["cowork", "space_builder"],
1773
1787
  constraints: {
1774
1788
  aspectRatios: [...H3_RATIOS],
1775
- resolutions: ["480p", "768p"],
1789
+ resolutions: ["480p", "768p", "1080p"],
1776
1790
  durations: { min: 5, max: 15 },
1791
+ // fal's image-to-video endpoint has no aspect_ratio; the frame sets it.
1792
+ ratioIgnoredWith: ["firstFrame"],
1777
1793
  supports: ["seed", "promptExpansion", "lastFrame", "safetyChecker"],
1778
1794
  audio: "always"
1779
1795
  },
@@ -1788,7 +1804,7 @@ var VIDEO_MODELS = [
1788
1804
  display: {
1789
1805
  provider: "minimax",
1790
1806
  logo: "https://d31vvfpk2ppdyq.cloudfront.net/assets/image/f4c2e906-28b7-4a4a-b491-df8fef7808ed.png",
1791
- description: "High-speed MiniMax H3 Max variant for text-to-video and image-to-video (first/last frame) only, up to 768p."
1807
+ description: "High-speed MiniMax H3 Max variant for text-to-video and image-to-video (first/last frame) only, up to 1080p."
1792
1808
  },
1793
1809
  capabilities: { ...FRAMES_EXCLUDE_REFERENCES }
1794
1810
  },
@@ -1805,6 +1821,8 @@ var VIDEO_MODELS = [
1805
1821
  aspectRatios: [...H3_REFERENCE_RATIOS],
1806
1822
  resolutions: ["480p", "768p", "2k", "4k"],
1807
1823
  durations: { min: 5, max: 15 },
1824
+ // A start frame routes to image-to-video, which has no aspect_ratio.
1825
+ ratioIgnoredWith: ["firstFrame"],
1808
1826
  references: {
1809
1827
  images: 9,
1810
1828
  videos: 3,
@@ -1844,8 +1862,10 @@ var VIDEO_MODELS = [
1844
1862
  exposure: ["cowork", "space_builder"],
1845
1863
  constraints: {
1846
1864
  aspectRatios: [...H3_REFERENCE_RATIOS],
1847
- resolutions: ["480p", "768p"],
1865
+ resolutions: ["480p", "768p", "1080p"],
1848
1866
  durations: { min: 5, max: 15 },
1867
+ // A start frame routes to image-to-video, which has no aspect_ratio.
1868
+ ratioIgnoredWith: ["firstFrame"],
1849
1869
  references: {
1850
1870
  images: 9,
1851
1871
  videos: 3,
@@ -2403,6 +2423,7 @@ var CatalogDisplaySchema = import_zod3.z.looseObject({
2403
2423
  var CatalogCapabilitiesSchema = import_zod3.z.looseObject({
2404
2424
  acceptsSourceVideo: import_zod3.z.boolean().optional(),
2405
2425
  framesExcludeReferences: import_zod3.z.boolean().optional(),
2426
+ lastFrameRequiresFirstFrame: import_zod3.z.boolean().optional(),
2406
2427
  reasoning: import_zod3.z.boolean().optional(),
2407
2428
  vision: import_zod3.z.boolean().optional(),
2408
2429
  contextWindow: import_zod3.z.number().optional()
@@ -2984,6 +3005,24 @@ function resolveVideoOptions(model, request, onUnsupported = "error") {
2984
3005
  });
2985
3006
  delete merged.sourceVideo;
2986
3007
  }
3008
+ if (merged.lastFrame !== void 0 && merged.firstFrame === void 0 && model.capabilities?.lastFrameRequiresFirstFrame) {
3009
+ if (onUnsupported === "error") {
3010
+ throw new ModelKitError({
3011
+ code: "missing_input",
3012
+ message: `${model.id} takes an end frame only together with a start frame`,
3013
+ model: model.id,
3014
+ retryable: false
3015
+ });
3016
+ }
3017
+ warnings.push({
3018
+ code: "option_ignored",
3019
+ field: "lastFrame",
3020
+ to: "firstFrame",
3021
+ message: `end frame used as start frame: ${model.id} needs a start frame with an end frame`
3022
+ });
3023
+ merged.firstFrame = merged.lastFrame;
3024
+ delete merged.lastFrame;
3025
+ }
2987
3026
  const hasFrames = merged.firstFrame !== void 0 || merged.lastFrame !== void 0;
2988
3027
  const hasRefs = compact(merged.referenceImages).length + compact(merged.referenceVideos).length + compact(merged.referenceAudios).length > 0;
2989
3028
  if (hasFrames && hasRefs && model.capabilities?.framesExcludeReferences) {