@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.
@@ -295,6 +295,8 @@ interface CatalogCapabilities {
295
295
  acceptsSourceVideo?: boolean;
296
296
  /** Video rows: first/last frames and reference images are mutually exclusive inputs. */
297
297
  framesExcludeReferences?: boolean;
298
+ /** Video rows: a last frame is accepted only together with a first frame. */
299
+ lastFrameRequiresFirstFrame?: boolean;
298
300
  /** Text rows. `reasoning`: the model streams reasoning the UI should surface. */
299
301
  reasoning?: boolean;
300
302
  vision?: boolean;
@@ -848,6 +850,7 @@ declare const CatalogModelSchemaBase: z.ZodObject<{
848
850
  capabilities: z.ZodOptional<z.ZodObject<{
849
851
  acceptsSourceVideo: z.ZodOptional<z.ZodBoolean>;
850
852
  framesExcludeReferences: z.ZodOptional<z.ZodBoolean>;
853
+ lastFrameRequiresFirstFrame: z.ZodOptional<z.ZodBoolean>;
851
854
  reasoning: z.ZodOptional<z.ZodBoolean>;
852
855
  vision: z.ZodOptional<z.ZodBoolean>;
853
856
  contextWindow: z.ZodOptional<z.ZodNumber>;
package/dist/catalog.d.ts CHANGED
@@ -295,6 +295,8 @@ interface CatalogCapabilities {
295
295
  acceptsSourceVideo?: boolean;
296
296
  /** Video rows: first/last frames and reference images are mutually exclusive inputs. */
297
297
  framesExcludeReferences?: boolean;
298
+ /** Video rows: a last frame is accepted only together with a first frame. */
299
+ lastFrameRequiresFirstFrame?: boolean;
298
300
  /** Text rows. `reasoning`: the model streams reasoning the UI should surface. */
299
301
  reasoning?: boolean;
300
302
  vision?: boolean;
@@ -848,6 +850,7 @@ declare const CatalogModelSchemaBase: z.ZodObject<{
848
850
  capabilities: z.ZodOptional<z.ZodObject<{
849
851
  acceptsSourceVideo: z.ZodOptional<z.ZodBoolean>;
850
852
  framesExcludeReferences: z.ZodOptional<z.ZodBoolean>;
853
+ lastFrameRequiresFirstFrame: z.ZodOptional<z.ZodBoolean>;
851
854
  reasoning: z.ZodOptional<z.ZodBoolean>;
852
855
  vision: z.ZodOptional<z.ZodBoolean>;
853
856
  contextWindow: z.ZodOptional<z.ZodNumber>;
package/dist/catalog.js CHANGED
@@ -1247,12 +1247,14 @@ var TEXT_MODELS = [
1247
1247
  var VIDEO_USAGE = { unit: "seconds" };
1248
1248
  var SEEDANCE_RATIOS = ["21:9", "16:9", "4:3", "1:1", "3:4", "9:16", "adaptive"];
1249
1249
  var KLING_RATIOS = ["16:9", "9:16", "1:1", "adaptive"];
1250
+ var KLING_OMNI_RATIOS = ["16:9", "9:16", "1:1"];
1250
1251
  var OMNI_RATIOS = ["16:9", "9:16"];
1251
1252
  var GROK_RATIOS = ["1:1", "16:9", "9:16", "4:3", "3:4", "3:2", "2:3"];
1252
1253
  var H3_RATIOS = ["21:9", "16:9", "4:3", "1:1", "3:4", "9:16"];
1253
1254
  var H3_REFERENCE_RATIOS = ["adaptive", ...H3_RATIOS];
1254
1255
  var NEGATIVE_PROMPT = "blur, distort, and low quality";
1255
1256
  var FRAMES_EXCLUDE_REFERENCES = { framesExcludeReferences: true };
1257
+ var KLING_PROMPT = { referenceSyntax: "numbered", maxChars: 2500 };
1256
1258
  var BYTEDANCE_LOGO = "https://d31vvfpk2ppdyq.cloudfront.net/assets/image/3bba5d81-4e57-4e71-911b-cdfa05acca2c_compressed.png";
1257
1259
  var GOOGLE_LOGO2 = "https://d31vvfpk2ppdyq.cloudfront.net/assets/image/192485d2-a76a-4657-9c06-930b2c70badd_compressed.png";
1258
1260
  var VIDEO_MODELS = [
@@ -1273,9 +1275,13 @@ var VIDEO_MODELS = [
1273
1275
  constraints: {
1274
1276
  aspectRatios: [...OMNI_RATIOS],
1275
1277
  resolutions: ["720p", "1080p", "4k"],
1278
+ // Veo also takes 4 and 6, but must be 8 at 1080p/4k or with reference images;
1279
+ // [8] until the kit has a rule that forces it there (CHANGELOG 0.2.21).
1276
1280
  durations: [8],
1277
1281
  references: { images: 3 },
1278
1282
  supports: ["lastFrame", "seed"],
1283
+ // 1,024 text tokens; 3,000 characters assumes ~2.9 characters per token.
1284
+ prompt: { referenceSyntax: "numbered", maxChars: 3e3 },
1279
1285
  audio: "always"
1280
1286
  },
1281
1287
  defaults: { durationSeconds: 8, resolution: "720p", aspectRatio: "16:9" },
@@ -1286,7 +1292,8 @@ var VIDEO_MODELS = [
1286
1292
  description: "Google's highest-quality video generation model. Supports up to 4K resolution and 8-second clips with reference image conditioning.",
1287
1293
  logo: GOOGLE_LOGO2
1288
1294
  },
1289
- capabilities: { ...FRAMES_EXCLUDE_REFERENCES }
1295
+ // Veo's lastFrame "must be used in combination with the image parameter".
1296
+ capabilities: { ...FRAMES_EXCLUDE_REFERENCES, lastFrameRequiresFirstFrame: true }
1290
1297
  },
1291
1298
  {
1292
1299
  id: "veo-3.1-fast",
@@ -1406,7 +1413,8 @@ var VIDEO_MODELS = [
1406
1413
  // extension can upload a <= 10 s source with task "extend"; later ones
1407
1414
  // chain by `previousInteractionId` (the API rejects the id together
1408
1415
  // with a video task, so those calls carry the prompt alone).
1409
- supports: ["videoExtension"],
1416
+ // First-to-last-frame interpolation: `<LAST_FRAME>` goes with `<FIRST_FRAME>`.
1417
+ supports: ["videoExtension", "lastFrame"],
1410
1418
  extension: { segmentSeconds: 10, maxTotalSeconds: 40 },
1411
1419
  // generateOmniVideo sends MediaRef.label as a text part beside each image.
1412
1420
  prompt: { referenceSyntax: "caption" },
@@ -1420,7 +1428,7 @@ var VIDEO_MODELS = [
1420
1428
  logo: GOOGLE_LOGO2,
1421
1429
  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."
1422
1430
  },
1423
- capabilities: { acceptsSourceVideo: true }
1431
+ capabilities: { acceptsSourceVideo: true, lastFrameRequiresFirstFrame: true }
1424
1432
  },
1425
1433
  {
1426
1434
  // vibe-triggers and Space Builder drop the ratio when a frame is wired; Cowork always sends it.
@@ -1446,7 +1454,8 @@ var VIDEO_MODELS = [
1446
1454
  },
1447
1455
  sourceVideoSeconds: { min: 2, max: 15, inReferenceTotal: true },
1448
1456
  ratioIgnoredWith: ["firstFrame", "sourceVideo"],
1449
- supports: ["generateAudio", "seed", "lastFrame", "watermark"],
1457
+ // No seed: 2.x echoes it but does not honour it (two seeded runs differed, 2026-10-01).
1458
+ supports: ["generateAudio", "lastFrame", "watermark"],
1450
1459
  audio: "optional"
1451
1460
  },
1452
1461
  defaults: {
@@ -1486,7 +1495,8 @@ var VIDEO_MODELS = [
1486
1495
  },
1487
1496
  sourceVideoSeconds: { min: 2, max: 15, inReferenceTotal: true },
1488
1497
  ratioIgnoredWith: ["firstFrame", "sourceVideo"],
1489
- supports: ["generateAudio", "seed", "lastFrame", "watermark"],
1498
+ // No seed: 2.x echoes it but does not honour it (two seeded runs differed, 2026-10-01).
1499
+ supports: ["generateAudio", "lastFrame", "watermark"],
1490
1500
  audio: "optional"
1491
1501
  },
1492
1502
  defaults: {
@@ -1505,7 +1515,6 @@ var VIDEO_MODELS = [
1505
1515
  capabilities: { acceptsSourceVideo: true, ...FRAMES_EXCLUDE_REFERENCES }
1506
1516
  },
1507
1517
  {
1508
- // generateAudio defaults to false here (trigger-workflows); Space Builder defaults it to true.
1509
1518
  // omni_reference_task_type edit/extend: adaptive ratio, edit sends duration -1.
1510
1519
  id: "seedance-2.5",
1511
1520
  aliases: ["seedance-2-5", "dreamina-seedance-2-5-260628"],
@@ -1527,14 +1536,15 @@ var VIDEO_MODELS = [
1527
1536
  },
1528
1537
  sourceVideoSeconds: { min: 2, max: 30, editMin: 4, inReferenceTotal: true },
1529
1538
  ratioIgnoredWith: ["firstFrame", "sourceVideo"],
1530
- supports: ["generateAudio", "seed", "lastFrame", "watermark", "taskType"],
1539
+ // No seed: 2.x echoes it but does not honour it (two seeded runs differed, 2026-10-01).
1540
+ supports: ["generateAudio", "lastFrame", "watermark", "taskType"],
1531
1541
  audio: "optional"
1532
1542
  },
1533
1543
  defaults: {
1534
1544
  durationSeconds: 5,
1535
1545
  resolution: "720p",
1536
1546
  aspectRatio: "16:9",
1537
- generateAudio: false
1547
+ generateAudio: true
1538
1548
  },
1539
1549
  usage: { ...VIDEO_USAGE },
1540
1550
  pricingKind: "video_per_second",
@@ -1546,7 +1556,6 @@ var VIDEO_MODELS = [
1546
1556
  capabilities: { acceptsSourceVideo: true, ...FRAMES_EXCLUDE_REFERENCES }
1547
1557
  },
1548
1558
  {
1549
- // Cowork allows 7 reference images and durations 4/6/8/10; Space Builder allows 5 and 1-15s.
1550
1559
  id: "grok-imagine",
1551
1560
  aliases: ["grok-imagine-video", "grokImagine"],
1552
1561
  label: "Grok Imagine",
@@ -1560,7 +1569,8 @@ var VIDEO_MODELS = [
1560
1569
  resolutions: ["480p", "720p"],
1561
1570
  durations: { min: 1, max: 15 },
1562
1571
  durationPresets: [4, 6, 8, 10],
1563
- references: { images: 5 },
1572
+ // xAI: "A maximum of 7 reference images can be provided per request."
1573
+ references: { images: 7 },
1564
1574
  supports: [],
1565
1575
  // xAI can switch sound off (generate_audio); xai-video.ts never sends it.
1566
1576
  audio: "always"
@@ -1585,16 +1595,16 @@ var VIDEO_MODELS = [
1585
1595
  providerModelId: "kling-video/v2.5-turbo/pro/image-to-video",
1586
1596
  lifecycle: "active",
1587
1597
  exposure: ["cowork", "space_builder"],
1598
+ // The endpoint has no aspect_ratio or last_image_url: the start image sets the framing.
1588
1599
  constraints: {
1589
- aspectRatios: [...KLING_RATIOS],
1590
1600
  durations: [5, 10],
1591
1601
  requires: ["firstFrame"],
1592
- supports: ["cfgScale", "negativePrompt", "lastFrame"],
1602
+ supports: ["cfgScale", "negativePrompt"],
1603
+ prompt: KLING_PROMPT,
1593
1604
  audio: "never"
1594
1605
  },
1595
1606
  defaults: {
1596
1607
  durationSeconds: 5,
1597
- aspectRatio: "16:9",
1598
1608
  cfgScale: 0.5,
1599
1609
  negativePrompt: NEGATIVE_PROMPT
1600
1610
  },
@@ -1603,7 +1613,7 @@ var VIDEO_MODELS = [
1603
1613
  display: {
1604
1614
  provider: "kuaishou",
1605
1615
  logo: "https://cdn.jsdelivr.net/npm/@lobehub/icons-static-png@1.97.0/light/kling-color.png",
1606
- 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."
1616
+ description: "Kuaishou Kling 2.5 Turbo Pro image-to-video; animates a single input image from a text prompt, 5- or 10-second clips."
1607
1617
  },
1608
1618
  capabilities: { ...FRAMES_EXCLUDE_REFERENCES }
1609
1619
  },
@@ -1617,19 +1627,18 @@ var VIDEO_MODELS = [
1617
1627
  providerModelId: "kling-video/v3.0/pro/image-to-video",
1618
1628
  lifecycle: "active",
1619
1629
  exposure: ["cowork", "space_builder"],
1630
+ // The endpoint has no aspect_ratio or negative_prompt; sound is "on"/"off" (default on).
1620
1631
  constraints: {
1621
- aspectRatios: [...KLING_RATIOS],
1622
1632
  durations: { min: 3, max: 15 },
1623
1633
  requires: ["firstFrame"],
1624
- supports: ["cfgScale", "negativePrompt", "lastFrame"],
1625
- // Higgsfield has a `sound` switch (default on); KlingCreate doesn't send it.
1626
- audio: "always"
1634
+ supports: ["cfgScale", "lastFrame", "generateAudio"],
1635
+ prompt: KLING_PROMPT,
1636
+ audio: "optional"
1627
1637
  },
1628
1638
  defaults: {
1629
1639
  durationSeconds: 5,
1630
- aspectRatio: "16:9",
1631
1640
  cfgScale: 0.5,
1632
- negativePrompt: NEGATIVE_PROMPT
1641
+ generateAudio: true
1633
1642
  },
1634
1643
  usage: { ...VIDEO_USAGE },
1635
1644
  pricingKind: "video_per_second",
@@ -1641,7 +1650,7 @@ var VIDEO_MODELS = [
1641
1650
  capabilities: { ...FRAMES_EXCLUDE_REFERENCES }
1642
1651
  },
1643
1652
  {
1644
- // mode standard/pro selects 720p/1080p; the endpoint takes no separate resolution.
1653
+ // mode standard/pro/4k selects 720p/1080p/4K; the endpoint takes no separate resolution.
1645
1654
  // Replicate: "Max 7 [images] without video, 4 with video"; 4K does not support
1646
1655
  // reference_video. No `videos` limit yet: the adapter sends no reference video,
1647
1656
  // so `imagesWithVideo` stays dormant until one is wired through.
@@ -1654,22 +1663,25 @@ var VIDEO_MODELS = [
1654
1663
  lifecycle: "active",
1655
1664
  exposure: ["cowork", "space_builder"],
1656
1665
  constraints: {
1657
- aspectRatios: [...KLING_RATIOS],
1658
- resolutions: ["720p", "1080p"],
1666
+ aspectRatios: [...KLING_OMNI_RATIOS],
1667
+ resolutions: ["720p", "1080p", "4k"],
1659
1668
  durations: { min: 3, max: 15 },
1660
1669
  references: { images: 7, imagesWithVideo: 4 },
1661
1670
  supports: ["generateAudio", "lastFrame"],
1671
+ prompt: KLING_PROMPT,
1662
1672
  audio: "optional"
1663
1673
  },
1664
- defaults: { durationSeconds: 5, aspectRatio: "16:9", generateAudio: false, mode: "pro" },
1674
+ defaults: { durationSeconds: 5, aspectRatio: "16:9", generateAudio: true, mode: "pro" },
1665
1675
  usage: { unit: "compute_seconds" },
1666
1676
  pricingKind: "video_per_second",
1667
1677
  display: {
1668
1678
  provider: "kuaishou",
1669
1679
  logo: "https://cdn.jsdelivr.net/npm/@lobehub/icons-static-png@1.97.0/light/kling-color.png",
1670
- 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."
1671
- },
1672
- capabilities: {}
1680
+ 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."
1681
+ }
1682
+ // end_image "Requires start_image", but no lastFrameRequiresFirstFrame: SpaceLab resolves
1683
+ // replicate rows in error mode, so the flag would refuse its end-frame-only nodes, which
1684
+ // the adapter sends as start_image.
1673
1685
  },
1674
1686
  {
1675
1687
  id: "minimax-h3",
@@ -1684,6 +1696,8 @@ var VIDEO_MODELS = [
1684
1696
  aspectRatios: [...H3_RATIOS],
1685
1697
  resolutions: ["480p", "768p", "2k", "4k"],
1686
1698
  durations: { min: 5, max: 15 },
1699
+ // fal's image-to-video endpoint has no aspect_ratio; the frame sets it.
1700
+ ratioIgnoredWith: ["firstFrame"],
1687
1701
  supports: ["seed", "promptExpansion", "lastFrame", "safetyChecker"],
1688
1702
  audio: "always"
1689
1703
  },
@@ -1713,8 +1727,10 @@ var VIDEO_MODELS = [
1713
1727
  exposure: ["cowork", "space_builder"],
1714
1728
  constraints: {
1715
1729
  aspectRatios: [...H3_RATIOS],
1716
- resolutions: ["480p", "768p"],
1730
+ resolutions: ["480p", "768p", "1080p"],
1717
1731
  durations: { min: 5, max: 15 },
1732
+ // fal's image-to-video endpoint has no aspect_ratio; the frame sets it.
1733
+ ratioIgnoredWith: ["firstFrame"],
1718
1734
  supports: ["seed", "promptExpansion", "lastFrame", "safetyChecker"],
1719
1735
  audio: "always"
1720
1736
  },
@@ -1729,7 +1745,7 @@ var VIDEO_MODELS = [
1729
1745
  display: {
1730
1746
  provider: "minimax",
1731
1747
  logo: "https://d31vvfpk2ppdyq.cloudfront.net/assets/image/f4c2e906-28b7-4a4a-b491-df8fef7808ed.png",
1732
- description: "High-speed MiniMax H3 Max variant for text-to-video and image-to-video (first/last frame) only, up to 768p."
1748
+ description: "High-speed MiniMax H3 Max variant for text-to-video and image-to-video (first/last frame) only, up to 1080p."
1733
1749
  },
1734
1750
  capabilities: { ...FRAMES_EXCLUDE_REFERENCES }
1735
1751
  },
@@ -1746,6 +1762,8 @@ var VIDEO_MODELS = [
1746
1762
  aspectRatios: [...H3_REFERENCE_RATIOS],
1747
1763
  resolutions: ["480p", "768p", "2k", "4k"],
1748
1764
  durations: { min: 5, max: 15 },
1765
+ // A start frame routes to image-to-video, which has no aspect_ratio.
1766
+ ratioIgnoredWith: ["firstFrame"],
1749
1767
  references: {
1750
1768
  images: 9,
1751
1769
  videos: 3,
@@ -1785,8 +1803,10 @@ var VIDEO_MODELS = [
1785
1803
  exposure: ["cowork", "space_builder"],
1786
1804
  constraints: {
1787
1805
  aspectRatios: [...H3_REFERENCE_RATIOS],
1788
- resolutions: ["480p", "768p"],
1806
+ resolutions: ["480p", "768p", "1080p"],
1789
1807
  durations: { min: 5, max: 15 },
1808
+ // A start frame routes to image-to-video, which has no aspect_ratio.
1809
+ ratioIgnoredWith: ["firstFrame"],
1790
1810
  references: {
1791
1811
  images: 9,
1792
1812
  videos: 3,
@@ -2344,6 +2364,7 @@ var CatalogDisplaySchema = z3.looseObject({
2344
2364
  var CatalogCapabilitiesSchema = z3.looseObject({
2345
2365
  acceptsSourceVideo: z3.boolean().optional(),
2346
2366
  framesExcludeReferences: z3.boolean().optional(),
2367
+ lastFrameRequiresFirstFrame: z3.boolean().optional(),
2347
2368
  reasoning: z3.boolean().optional(),
2348
2369
  vision: z3.boolean().optional(),
2349
2370
  contextWindow: z3.number().optional()
@@ -2925,6 +2946,24 @@ function resolveVideoOptions(model, request, onUnsupported = "error") {
2925
2946
  });
2926
2947
  delete merged.sourceVideo;
2927
2948
  }
2949
+ if (merged.lastFrame !== void 0 && merged.firstFrame === void 0 && model.capabilities?.lastFrameRequiresFirstFrame) {
2950
+ if (onUnsupported === "error") {
2951
+ throw new ModelKitError({
2952
+ code: "missing_input",
2953
+ message: `${model.id} takes an end frame only together with a start frame`,
2954
+ model: model.id,
2955
+ retryable: false
2956
+ });
2957
+ }
2958
+ warnings.push({
2959
+ code: "option_ignored",
2960
+ field: "lastFrame",
2961
+ to: "firstFrame",
2962
+ message: `end frame used as start frame: ${model.id} needs a start frame with an end frame`
2963
+ });
2964
+ merged.firstFrame = merged.lastFrame;
2965
+ delete merged.lastFrame;
2966
+ }
2928
2967
  const hasFrames = merged.firstFrame !== void 0 || merged.lastFrame !== void 0;
2929
2968
  const hasRefs = compact(merged.referenceImages).length + compact(merged.referenceVideos).length + compact(merged.referenceAudios).length > 0;
2930
2969
  if (hasFrames && hasRefs && model.capabilities?.framesExcludeReferences) {