@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.d.cts CHANGED
@@ -298,6 +298,8 @@ interface CatalogCapabilities {
298
298
  acceptsSourceVideo?: boolean;
299
299
  /** Video rows: first/last frames and reference images are mutually exclusive inputs. */
300
300
  framesExcludeReferences?: boolean;
301
+ /** Video rows: a last frame is accepted only together with a first frame. */
302
+ lastFrameRequiresFirstFrame?: boolean;
301
303
  /** Text rows. `reasoning`: the model streams reasoning the UI should surface. */
302
304
  reasoning?: boolean;
303
305
  vision?: boolean;
@@ -875,6 +877,7 @@ declare const CatalogModelSchemaBase: z.ZodObject<{
875
877
  capabilities: z.ZodOptional<z.ZodObject<{
876
878
  acceptsSourceVideo: z.ZodOptional<z.ZodBoolean>;
877
879
  framesExcludeReferences: z.ZodOptional<z.ZodBoolean>;
880
+ lastFrameRequiresFirstFrame: z.ZodOptional<z.ZodBoolean>;
878
881
  reasoning: z.ZodOptional<z.ZodBoolean>;
879
882
  vision: z.ZodOptional<z.ZodBoolean>;
880
883
  contextWindow: z.ZodOptional<z.ZodNumber>;
package/dist/index.d.ts CHANGED
@@ -298,6 +298,8 @@ interface CatalogCapabilities {
298
298
  acceptsSourceVideo?: boolean;
299
299
  /** Video rows: first/last frames and reference images are mutually exclusive inputs. */
300
300
  framesExcludeReferences?: boolean;
301
+ /** Video rows: a last frame is accepted only together with a first frame. */
302
+ lastFrameRequiresFirstFrame?: boolean;
301
303
  /** Text rows. `reasoning`: the model streams reasoning the UI should surface. */
302
304
  reasoning?: boolean;
303
305
  vision?: boolean;
@@ -875,6 +877,7 @@ declare const CatalogModelSchemaBase: z.ZodObject<{
875
877
  capabilities: z.ZodOptional<z.ZodObject<{
876
878
  acceptsSourceVideo: z.ZodOptional<z.ZodBoolean>;
877
879
  framesExcludeReferences: z.ZodOptional<z.ZodBoolean>;
880
+ lastFrameRequiresFirstFrame: z.ZodOptional<z.ZodBoolean>;
878
881
  reasoning: z.ZodOptional<z.ZodBoolean>;
879
882
  vision: z.ZodOptional<z.ZodBoolean>;
880
883
  contextWindow: z.ZodOptional<z.ZodNumber>;
package/dist/index.js CHANGED
@@ -169,13 +169,16 @@ var GEMINI_RATIOS = [
169
169
  "5:4",
170
170
  "adaptive"
171
171
  ];
172
- function geminiImage(id, label, aliases, sizes, display, exposure, references) {
172
+ var GEMINI_PROMPT_MAX_CHARS = 5e4;
173
+ var GPT_IMAGE_PROMPT = { referenceSyntax: "numbered", maxChars: 32e3 };
174
+ function geminiImage(id, label, aliases, sizes, display, exposure, references, promptMaxChars) {
175
+ const prompt = { referenceSyntax: "caption" };
176
+ if (promptMaxChars !== void 0) prompt.maxChars = promptMaxChars;
173
177
  const constraints = {
174
178
  aspectRatios: [...GEMINI_RATIOS],
175
179
  sizes,
176
180
  supports: ["safetySettings", "temperature"],
177
- // google-image sends MediaRef.label as a text part beside each reference.
178
- prompt: { referenceSyntax: "caption" }
181
+ prompt
179
182
  };
180
183
  if (references !== void 0) constraints.references = references;
181
184
  const model = {
@@ -207,7 +210,8 @@ var IMAGE_MODELS = [
207
210
  logo: "https://d31vvfpk2ppdyq.cloudfront.net/assets/image/192485d2-a76a-4657-9c06-930b2c70badd_compressed.png"
208
211
  },
209
212
  ["cowork", "space_builder"],
210
- { images: 14 }
213
+ { images: 14 },
214
+ GEMINI_PROMPT_MAX_CHARS
211
215
  ),
212
216
  geminiImage(
213
217
  "gemini-3.1-flash-image-preview",
@@ -221,7 +225,8 @@ var IMAGE_MODELS = [
221
225
  logo: "https://d31vvfpk2ppdyq.cloudfront.net/assets/image/192485d2-a76a-4657-9c06-930b2c70badd_compressed.png"
222
226
  },
223
227
  ["cowork", "space_builder"],
224
- { images: 14 }
228
+ { images: 14 },
229
+ GEMINI_PROMPT_MAX_CHARS
225
230
  ),
226
231
  geminiImage(
227
232
  "gemini-3.1-flash-lite-image",
@@ -234,7 +239,8 @@ var IMAGE_MODELS = [
234
239
  logo: "https://d31vvfpk2ppdyq.cloudfront.net/assets/image/192485d2-a76a-4657-9c06-930b2c70badd_compressed.png"
235
240
  },
236
241
  ["cowork", "space_builder"],
237
- { images: 14 }
242
+ { images: 14 },
243
+ GEMINI_PROMPT_MAX_CHARS
238
244
  ),
239
245
  // trigger-workflows validates 1K only; pricing seeds 2K/4K rows so allow all three.
240
246
  geminiImage(
@@ -272,7 +278,8 @@ var IMAGE_MODELS = [
272
278
  sizes: ["1K", "2K", "4K"],
273
279
  qualityOptions: ["auto", "low", "medium", "high"],
274
280
  references: { images: 16 },
275
- supports: ["quality", "count", "outputFormat"]
281
+ supports: ["quality", "count", "outputFormat"],
282
+ prompt: GPT_IMAGE_PROMPT
276
283
  },
277
284
  defaults: { size: "1K", aspectRatio: "adaptive", quality: "auto" },
278
285
  usage: { ...IMAGE_USAGE, slug: "gpt" },
@@ -307,7 +314,8 @@ var IMAGE_MODELS = [
307
314
  sizes: ["1K", "2K", "4K"],
308
315
  qualityOptions: ["auto", "low", "medium", "high", "xhigh", "max"],
309
316
  references: { images: 16 },
310
- supports: ["quality", "count", "outputFormat"]
317
+ supports: ["quality", "count", "outputFormat"],
318
+ prompt: GPT_IMAGE_PROMPT
311
319
  },
312
320
  defaults: { size: "1K", aspectRatio: "adaptive", quality: "auto" },
313
321
  usage: { ...IMAGE_USAGE },
@@ -341,7 +349,8 @@ var IMAGE_MODELS = [
341
349
  sizes: ["1K", "2K", "4K"],
342
350
  qualityOptions: ["auto", "low", "medium", "high", "xhigh", "max"],
343
351
  references: { images: 16 },
344
- supports: ["quality", "count", "outputFormat"]
352
+ supports: ["quality", "count", "outputFormat"],
353
+ prompt: GPT_IMAGE_PROMPT
345
354
  },
346
355
  defaults: { size: "1K", aspectRatio: "adaptive", quality: "auto" },
347
356
  usage: { ...IMAGE_USAGE },
@@ -464,7 +473,9 @@ var IMAGE_MODELS = [
464
473
  sizes: ["1K"],
465
474
  references: { images: 8 },
466
475
  // flux-2-pro's job type always returns image/png; only the seed is forwarded.
467
- supports: ["seed"]
476
+ supports: ["seed"],
477
+ // Prodia's flux-2-pro schema gives the prompt a maxLength of 32,768.
478
+ prompt: { referenceSyntax: "numbered", maxChars: 32768 }
468
479
  },
469
480
  defaults: { size: "1K", aspectRatio: "1:1" },
470
481
  usage: { ...IMAGE_USAGE },
@@ -1258,12 +1269,17 @@ var TEXT_MODELS = [
1258
1269
  var VIDEO_USAGE = { unit: "seconds" };
1259
1270
  var SEEDANCE_RATIOS = ["21:9", "16:9", "4:3", "1:1", "3:4", "9:16", "adaptive"];
1260
1271
  var KLING_RATIOS = ["16:9", "9:16", "1:1", "adaptive"];
1272
+ var KLING_OMNI_RATIOS = ["16:9", "9:16", "1:1"];
1261
1273
  var OMNI_RATIOS = ["16:9", "9:16"];
1262
1274
  var GROK_RATIOS = ["1:1", "16:9", "9:16", "4:3", "3:4", "3:2", "2:3"];
1263
1275
  var H3_RATIOS = ["21:9", "16:9", "4:3", "1:1", "3:4", "9:16"];
1264
1276
  var H3_REFERENCE_RATIOS = ["adaptive", ...H3_RATIOS];
1265
1277
  var NEGATIVE_PROMPT = "blur, distort, and low quality";
1266
1278
  var FRAMES_EXCLUDE_REFERENCES = { framesExcludeReferences: true };
1279
+ var KLING_PROMPT = { referenceSyntax: "numbered", maxChars: 2500 };
1280
+ var SEEDANCE_PROMPT = { referenceSyntax: "numbered", maxChars: 3e4 };
1281
+ var H3_PROMPT = { referenceSyntax: "numbered", maxChars: 5e4 };
1282
+ var OMNI_PROMPT = { referenceSyntax: "caption", maxChars: 5e4 };
1267
1283
  var BYTEDANCE_LOGO = "https://d31vvfpk2ppdyq.cloudfront.net/assets/image/3bba5d81-4e57-4e71-911b-cdfa05acca2c_compressed.png";
1268
1284
  var GOOGLE_LOGO2 = "https://d31vvfpk2ppdyq.cloudfront.net/assets/image/192485d2-a76a-4657-9c06-930b2c70badd_compressed.png";
1269
1285
  var VIDEO_MODELS = [
@@ -1284,9 +1300,13 @@ var VIDEO_MODELS = [
1284
1300
  constraints: {
1285
1301
  aspectRatios: [...OMNI_RATIOS],
1286
1302
  resolutions: ["720p", "1080p", "4k"],
1303
+ // Veo also takes 4 and 6, but must be 8 at 1080p/4k or with reference images;
1304
+ // [8] until the kit has a rule that forces it there (CHANGELOG 0.2.21).
1287
1305
  durations: [8],
1288
1306
  references: { images: 3 },
1289
1307
  supports: ["lastFrame", "seed"],
1308
+ // 1,024 text tokens; 3,000 characters assumes ~2.9 characters per token.
1309
+ prompt: { referenceSyntax: "numbered", maxChars: 3e3 },
1290
1310
  audio: "always"
1291
1311
  },
1292
1312
  defaults: { durationSeconds: 8, resolution: "720p", aspectRatio: "16:9" },
@@ -1297,7 +1317,8 @@ var VIDEO_MODELS = [
1297
1317
  description: "Google's highest-quality video generation model. Supports up to 4K resolution and 8-second clips with reference image conditioning.",
1298
1318
  logo: GOOGLE_LOGO2
1299
1319
  },
1300
- capabilities: { ...FRAMES_EXCLUDE_REFERENCES }
1320
+ // Veo's lastFrame "must be used in combination with the image parameter".
1321
+ capabilities: { ...FRAMES_EXCLUDE_REFERENCES, lastFrameRequiresFirstFrame: true }
1301
1322
  },
1302
1323
  {
1303
1324
  id: "veo-3.1-fast",
@@ -1382,8 +1403,7 @@ var VIDEO_MODELS = [
1382
1403
  references: { images: 6, videos: 1, videoSeconds: { max: 10 } },
1383
1404
  sourceVideoSeconds: { max: 10 },
1384
1405
  supports: [],
1385
- // generateOmniVideo sends MediaRef.label as a text part beside each image.
1386
- prompt: { referenceSyntax: "caption" },
1406
+ prompt: OMNI_PROMPT,
1387
1407
  audio: "always"
1388
1408
  },
1389
1409
  defaults: {},
@@ -1417,10 +1437,10 @@ var VIDEO_MODELS = [
1417
1437
  // extension can upload a <= 10 s source with task "extend"; later ones
1418
1438
  // chain by `previousInteractionId` (the API rejects the id together
1419
1439
  // with a video task, so those calls carry the prompt alone).
1420
- supports: ["videoExtension"],
1440
+ // First-to-last-frame interpolation: `<LAST_FRAME>` goes with `<FIRST_FRAME>`.
1441
+ supports: ["videoExtension", "lastFrame"],
1421
1442
  extension: { segmentSeconds: 10, maxTotalSeconds: 40 },
1422
- // generateOmniVideo sends MediaRef.label as a text part beside each image.
1423
- prompt: { referenceSyntax: "caption" },
1443
+ prompt: OMNI_PROMPT,
1424
1444
  audio: "always"
1425
1445
  },
1426
1446
  defaults: {},
@@ -1431,7 +1451,7 @@ var VIDEO_MODELS = [
1431
1451
  logo: GOOGLE_LOGO2,
1432
1452
  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."
1433
1453
  },
1434
- capabilities: { acceptsSourceVideo: true }
1454
+ capabilities: { acceptsSourceVideo: true, lastFrameRequiresFirstFrame: true }
1435
1455
  },
1436
1456
  {
1437
1457
  // vibe-triggers and Space Builder drop the ratio when a frame is wired; Cowork always sends it.
@@ -1457,7 +1477,9 @@ var VIDEO_MODELS = [
1457
1477
  },
1458
1478
  sourceVideoSeconds: { min: 2, max: 15, inReferenceTotal: true },
1459
1479
  ratioIgnoredWith: ["firstFrame", "sourceVideo"],
1460
- supports: ["generateAudio", "seed", "lastFrame", "watermark"],
1480
+ // No seed: 2.x echoes it but does not honour it (two seeded runs differed, 2026-10-01).
1481
+ supports: ["generateAudio", "lastFrame", "watermark"],
1482
+ prompt: SEEDANCE_PROMPT,
1461
1483
  audio: "optional"
1462
1484
  },
1463
1485
  defaults: {
@@ -1497,7 +1519,9 @@ var VIDEO_MODELS = [
1497
1519
  },
1498
1520
  sourceVideoSeconds: { min: 2, max: 15, inReferenceTotal: true },
1499
1521
  ratioIgnoredWith: ["firstFrame", "sourceVideo"],
1500
- supports: ["generateAudio", "seed", "lastFrame", "watermark"],
1522
+ // No seed: 2.x echoes it but does not honour it (two seeded runs differed, 2026-10-01).
1523
+ supports: ["generateAudio", "lastFrame", "watermark"],
1524
+ prompt: SEEDANCE_PROMPT,
1501
1525
  audio: "optional"
1502
1526
  },
1503
1527
  defaults: {
@@ -1516,7 +1540,6 @@ var VIDEO_MODELS = [
1516
1540
  capabilities: { acceptsSourceVideo: true, ...FRAMES_EXCLUDE_REFERENCES }
1517
1541
  },
1518
1542
  {
1519
- // generateAudio defaults to false here (trigger-workflows); Space Builder defaults it to true.
1520
1543
  // omni_reference_task_type edit/extend: adaptive ratio, edit sends duration -1.
1521
1544
  id: "seedance-2.5",
1522
1545
  aliases: ["seedance-2-5", "dreamina-seedance-2-5-260628"],
@@ -1538,14 +1561,16 @@ var VIDEO_MODELS = [
1538
1561
  },
1539
1562
  sourceVideoSeconds: { min: 2, max: 30, editMin: 4, inReferenceTotal: true },
1540
1563
  ratioIgnoredWith: ["firstFrame", "sourceVideo"],
1541
- supports: ["generateAudio", "seed", "lastFrame", "watermark", "taskType"],
1564
+ // No seed: 2.x echoes it but does not honour it (two seeded runs differed, 2026-10-01).
1565
+ supports: ["generateAudio", "lastFrame", "watermark", "taskType"],
1566
+ prompt: SEEDANCE_PROMPT,
1542
1567
  audio: "optional"
1543
1568
  },
1544
1569
  defaults: {
1545
1570
  durationSeconds: 5,
1546
1571
  resolution: "720p",
1547
1572
  aspectRatio: "16:9",
1548
- generateAudio: false
1573
+ generateAudio: true
1549
1574
  },
1550
1575
  usage: { ...VIDEO_USAGE },
1551
1576
  pricingKind: "video_per_second",
@@ -1557,7 +1582,6 @@ var VIDEO_MODELS = [
1557
1582
  capabilities: { acceptsSourceVideo: true, ...FRAMES_EXCLUDE_REFERENCES }
1558
1583
  },
1559
1584
  {
1560
- // Cowork allows 7 reference images and durations 4/6/8/10; Space Builder allows 5 and 1-15s.
1561
1585
  id: "grok-imagine",
1562
1586
  aliases: ["grok-imagine-video", "grokImagine"],
1563
1587
  label: "Grok Imagine",
@@ -1571,7 +1595,8 @@ var VIDEO_MODELS = [
1571
1595
  resolutions: ["480p", "720p"],
1572
1596
  durations: { min: 1, max: 15 },
1573
1597
  durationPresets: [4, 6, 8, 10],
1574
- references: { images: 5 },
1598
+ // xAI: "A maximum of 7 reference images can be provided per request."
1599
+ references: { images: 7 },
1575
1600
  supports: [],
1576
1601
  // xAI can switch sound off (generate_audio); xai-video.ts never sends it.
1577
1602
  audio: "always"
@@ -1596,16 +1621,16 @@ var VIDEO_MODELS = [
1596
1621
  providerModelId: "kling-video/v2.5-turbo/pro/image-to-video",
1597
1622
  lifecycle: "active",
1598
1623
  exposure: ["cowork", "space_builder"],
1624
+ // The endpoint has no aspect_ratio or last_image_url: the start image sets the framing.
1599
1625
  constraints: {
1600
- aspectRatios: [...KLING_RATIOS],
1601
1626
  durations: [5, 10],
1602
1627
  requires: ["firstFrame"],
1603
- supports: ["cfgScale", "negativePrompt", "lastFrame"],
1628
+ supports: ["cfgScale", "negativePrompt"],
1629
+ prompt: KLING_PROMPT,
1604
1630
  audio: "never"
1605
1631
  },
1606
1632
  defaults: {
1607
1633
  durationSeconds: 5,
1608
- aspectRatio: "16:9",
1609
1634
  cfgScale: 0.5,
1610
1635
  negativePrompt: NEGATIVE_PROMPT
1611
1636
  },
@@ -1614,7 +1639,7 @@ var VIDEO_MODELS = [
1614
1639
  display: {
1615
1640
  provider: "kuaishou",
1616
1641
  logo: "https://cdn.jsdelivr.net/npm/@lobehub/icons-static-png@1.97.0/light/kling-color.png",
1617
- 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."
1642
+ description: "Kuaishou Kling 2.5 Turbo Pro image-to-video; animates a single input image from a text prompt, 5- or 10-second clips."
1618
1643
  },
1619
1644
  capabilities: { ...FRAMES_EXCLUDE_REFERENCES }
1620
1645
  },
@@ -1628,19 +1653,18 @@ var VIDEO_MODELS = [
1628
1653
  providerModelId: "kling-video/v3.0/pro/image-to-video",
1629
1654
  lifecycle: "active",
1630
1655
  exposure: ["cowork", "space_builder"],
1656
+ // The endpoint has no aspect_ratio or negative_prompt; sound is "on"/"off" (default on).
1631
1657
  constraints: {
1632
- aspectRatios: [...KLING_RATIOS],
1633
1658
  durations: { min: 3, max: 15 },
1634
1659
  requires: ["firstFrame"],
1635
- supports: ["cfgScale", "negativePrompt", "lastFrame"],
1636
- // Higgsfield has a `sound` switch (default on); KlingCreate doesn't send it.
1637
- audio: "always"
1660
+ supports: ["cfgScale", "lastFrame", "generateAudio"],
1661
+ prompt: KLING_PROMPT,
1662
+ audio: "optional"
1638
1663
  },
1639
1664
  defaults: {
1640
1665
  durationSeconds: 5,
1641
- aspectRatio: "16:9",
1642
1666
  cfgScale: 0.5,
1643
- negativePrompt: NEGATIVE_PROMPT
1667
+ generateAudio: true
1644
1668
  },
1645
1669
  usage: { ...VIDEO_USAGE },
1646
1670
  pricingKind: "video_per_second",
@@ -1652,7 +1676,7 @@ var VIDEO_MODELS = [
1652
1676
  capabilities: { ...FRAMES_EXCLUDE_REFERENCES }
1653
1677
  },
1654
1678
  {
1655
- // mode standard/pro selects 720p/1080p; the endpoint takes no separate resolution.
1679
+ // mode standard/pro/4k selects 720p/1080p/4K; the endpoint takes no separate resolution.
1656
1680
  // Replicate: "Max 7 [images] without video, 4 with video"; 4K does not support
1657
1681
  // reference_video. No `videos` limit yet: the adapter sends no reference video,
1658
1682
  // so `imagesWithVideo` stays dormant until one is wired through.
@@ -1665,22 +1689,25 @@ var VIDEO_MODELS = [
1665
1689
  lifecycle: "active",
1666
1690
  exposure: ["cowork", "space_builder"],
1667
1691
  constraints: {
1668
- aspectRatios: [...KLING_RATIOS],
1669
- resolutions: ["720p", "1080p"],
1692
+ aspectRatios: [...KLING_OMNI_RATIOS],
1693
+ resolutions: ["720p", "1080p", "4k"],
1670
1694
  durations: { min: 3, max: 15 },
1671
1695
  references: { images: 7, imagesWithVideo: 4 },
1672
1696
  supports: ["generateAudio", "lastFrame"],
1697
+ prompt: KLING_PROMPT,
1673
1698
  audio: "optional"
1674
1699
  },
1675
- defaults: { durationSeconds: 5, aspectRatio: "16:9", generateAudio: false, mode: "pro" },
1700
+ defaults: { durationSeconds: 5, aspectRatio: "16:9", generateAudio: true, mode: "pro" },
1676
1701
  usage: { unit: "compute_seconds" },
1677
1702
  pricingKind: "video_per_second",
1678
1703
  display: {
1679
1704
  provider: "kuaishou",
1680
1705
  logo: "https://cdn.jsdelivr.net/npm/@lobehub/icons-static-png@1.97.0/light/kling-color.png",
1681
- 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."
1682
- },
1683
- capabilities: {}
1706
+ 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."
1707
+ }
1708
+ // end_image "Requires start_image", but no lastFrameRequiresFirstFrame: SpaceLab resolves
1709
+ // replicate rows in error mode, so the flag would refuse its end-frame-only nodes, which
1710
+ // the adapter sends as start_image.
1684
1711
  },
1685
1712
  {
1686
1713
  id: "minimax-h3",
@@ -1695,7 +1722,10 @@ var VIDEO_MODELS = [
1695
1722
  aspectRatios: [...H3_RATIOS],
1696
1723
  resolutions: ["480p", "768p", "2k", "4k"],
1697
1724
  durations: { min: 5, max: 15 },
1725
+ // fal's image-to-video endpoint has no aspect_ratio; the frame sets it.
1726
+ ratioIgnoredWith: ["firstFrame"],
1698
1727
  supports: ["seed", "promptExpansion", "lastFrame", "safetyChecker"],
1728
+ prompt: H3_PROMPT,
1699
1729
  audio: "always"
1700
1730
  },
1701
1731
  defaults: {
@@ -1724,9 +1754,12 @@ var VIDEO_MODELS = [
1724
1754
  exposure: ["cowork", "space_builder"],
1725
1755
  constraints: {
1726
1756
  aspectRatios: [...H3_RATIOS],
1727
- resolutions: ["480p", "768p"],
1757
+ resolutions: ["480p", "768p", "1080p"],
1728
1758
  durations: { min: 5, max: 15 },
1759
+ // fal's image-to-video endpoint has no aspect_ratio; the frame sets it.
1760
+ ratioIgnoredWith: ["firstFrame"],
1729
1761
  supports: ["seed", "promptExpansion", "lastFrame", "safetyChecker"],
1762
+ prompt: H3_PROMPT,
1730
1763
  audio: "always"
1731
1764
  },
1732
1765
  defaults: {
@@ -1740,7 +1773,7 @@ var VIDEO_MODELS = [
1740
1773
  display: {
1741
1774
  provider: "minimax",
1742
1775
  logo: "https://d31vvfpk2ppdyq.cloudfront.net/assets/image/f4c2e906-28b7-4a4a-b491-df8fef7808ed.png",
1743
- description: "High-speed MiniMax H3 Max variant for text-to-video and image-to-video (first/last frame) only, up to 768p."
1776
+ description: "High-speed MiniMax H3 Max variant for text-to-video and image-to-video (first/last frame) only, up to 1080p."
1744
1777
  },
1745
1778
  capabilities: { ...FRAMES_EXCLUDE_REFERENCES }
1746
1779
  },
@@ -1757,6 +1790,8 @@ var VIDEO_MODELS = [
1757
1790
  aspectRatios: [...H3_REFERENCE_RATIOS],
1758
1791
  resolutions: ["480p", "768p", "2k", "4k"],
1759
1792
  durations: { min: 5, max: 15 },
1793
+ // A start frame routes to image-to-video, which has no aspect_ratio.
1794
+ ratioIgnoredWith: ["firstFrame"],
1760
1795
  references: {
1761
1796
  images: 9,
1762
1797
  videos: 3,
@@ -1766,6 +1801,7 @@ var VIDEO_MODELS = [
1766
1801
  videoSeconds: { min: 2, max: 15, total: 15 }
1767
1802
  },
1768
1803
  supports: ["seed", "promptExpansion", "safetyChecker"],
1804
+ prompt: H3_PROMPT,
1769
1805
  audio: "always"
1770
1806
  },
1771
1807
  defaults: {
@@ -1796,8 +1832,10 @@ var VIDEO_MODELS = [
1796
1832
  exposure: ["cowork", "space_builder"],
1797
1833
  constraints: {
1798
1834
  aspectRatios: [...H3_REFERENCE_RATIOS],
1799
- resolutions: ["480p", "768p"],
1835
+ resolutions: ["480p", "768p", "1080p"],
1800
1836
  durations: { min: 5, max: 15 },
1837
+ // A start frame routes to image-to-video, which has no aspect_ratio.
1838
+ ratioIgnoredWith: ["firstFrame"],
1801
1839
  references: {
1802
1840
  images: 9,
1803
1841
  videos: 3,
@@ -1807,6 +1845,7 @@ var VIDEO_MODELS = [
1807
1845
  videoSeconds: { min: 2, max: 15, total: 15 }
1808
1846
  },
1809
1847
  supports: ["seed", "promptExpansion", "safetyChecker"],
1848
+ prompt: H3_PROMPT,
1810
1849
  audio: "always"
1811
1850
  },
1812
1851
  defaults: {
@@ -2354,6 +2393,7 @@ var CatalogDisplaySchema = z3.looseObject({
2354
2393
  var CatalogCapabilitiesSchema = z3.looseObject({
2355
2394
  acceptsSourceVideo: z3.boolean().optional(),
2356
2395
  framesExcludeReferences: z3.boolean().optional(),
2396
+ lastFrameRequiresFirstFrame: z3.boolean().optional(),
2357
2397
  reasoning: z3.boolean().optional(),
2358
2398
  vision: z3.boolean().optional(),
2359
2399
  contextWindow: z3.number().optional()
@@ -4578,7 +4618,12 @@ async function generateOmniVideo(req, ctx, createGenAIClient) {
4578
4618
  const extend = req.taskType === "extend";
4579
4619
  const previousId = extend ? req.previousInteractionId : void 0;
4580
4620
  const chained = previousId !== void 0;
4581
- for (const field of ["referenceImages", "referenceVideos", "firstFrame"]) {
4621
+ for (const field of [
4622
+ "referenceImages",
4623
+ "referenceVideos",
4624
+ "firstFrame",
4625
+ "lastFrame"
4626
+ ]) {
4582
4627
  const value = req[field];
4583
4628
  if (!chained || value === void 0 || Array.isArray(value) && value.length === 0)
4584
4629
  continue;
@@ -4592,6 +4637,14 @@ async function generateOmniVideo(req, ctx, createGenAIClient) {
4592
4637
  const images = chained ? [] : req.referenceImages ?? [];
4593
4638
  const videos = chained ? [] : req.referenceVideos ?? [];
4594
4639
  const firstFrame = chained ? void 0 : req.firstFrame;
4640
+ const lastFrame = firstFrame ? req.lastFrame : void 0;
4641
+ if (!chained && req.lastFrame !== void 0 && lastFrame === void 0) {
4642
+ ctx.warnings.push({
4643
+ code: "option_ignored",
4644
+ field: "lastFrame",
4645
+ message: `${modelId} takes an end frame only with a start frame; lastFrame ignored`
4646
+ });
4647
+ }
4595
4648
  if (extend && !chained && req.sourceVideo === void 0) {
4596
4649
  throw new ModelKitError({
4597
4650
  code: "missing_input",
@@ -4676,27 +4729,7 @@ async function generateOmniVideo(req, ctx, createGenAIClient) {
4676
4729
  mime_type: media.mime
4677
4730
  });
4678
4731
  }
4679
- if (firstFrame) {
4680
- if (firstFrame.label !== void 0)
4681
- parts.push({ type: "text", text: firstFrame.label });
4682
- const media = await resolveMedia(
4683
- firstFrame,
4684
- ctx.fetch,
4685
- ctx.signal,
4686
- "image/png",
4687
- modelId
4688
- );
4689
- if (media.bytes.length > OMNI_INLINE_LIMIT_BYTES) {
4690
- parts.push({
4691
- type: "image",
4692
- uri: await uploadOne("image", media.bytes, media.mime),
4693
- mime_type: media.mime
4694
- });
4695
- } else {
4696
- parts.push({ type: "image", data: toBase64(media.bytes), mime_type: media.mime });
4697
- }
4698
- }
4699
- for (const ref of images) {
4732
+ const pushImage = async (ref) => {
4700
4733
  if (ref.label !== void 0) parts.push({ type: "text", text: ref.label });
4701
4734
  const media = await resolveMedia(ref, ctx.fetch, ctx.signal, "image/png", modelId);
4702
4735
  if (media.bytes.length > OMNI_INLINE_LIMIT_BYTES) {
@@ -4708,8 +4741,12 @@ async function generateOmniVideo(req, ctx, createGenAIClient) {
4708
4741
  } else {
4709
4742
  parts.push({ type: "image", data: toBase64(media.bytes), mime_type: media.mime });
4710
4743
  }
4711
- }
4712
- parts.push({ type: "text", text: req.prompt });
4744
+ };
4745
+ if (firstFrame) await pushImage(firstFrame);
4746
+ if (lastFrame) await pushImage(lastFrame);
4747
+ for (const ref of images) await pushImage(ref);
4748
+ const prompt = lastFrame ? `<FIRST_FRAME> <LAST_FRAME> ${req.prompt}` : req.prompt;
4749
+ parts.push({ type: "text", text: prompt });
4713
4750
  try {
4714
4751
  interaction = await withRetry(async () => {
4715
4752
  try {
@@ -5239,6 +5276,26 @@ function videoUsage3(ctx, req, duration) {
5239
5276
  if (req.durationSeconds !== void 0) event.durationSeconds = req.durationSeconds;
5240
5277
  return event;
5241
5278
  }
5279
+ var KLING_ENDPOINT_FIELDS = /* @__PURE__ */ new Map([
5280
+ [
5281
+ "kling-video/v2.5-turbo/pro/image-to-video",
5282
+ { aspectRatio: false, negativePrompt: true, lastFrame: false, sound: false }
5283
+ ],
5284
+ [
5285
+ "kling-video/v3.0/pro/image-to-video",
5286
+ { aspectRatio: false, negativePrompt: false, lastFrame: true, sound: true }
5287
+ ]
5288
+ ]);
5289
+ var UNLISTED_KLING_FIELDS = {
5290
+ aspectRatio: true,
5291
+ negativePrompt: true,
5292
+ lastFrame: true,
5293
+ sound: false
5294
+ };
5295
+ function klingEndpointFields(providerModelId) {
5296
+ const path = (providerModelId.startsWith("/") ? providerModelId.slice(1) : providerModelId).toLowerCase();
5297
+ return KLING_ENDPOINT_FIELDS.get(path) ?? UNLISTED_KLING_FIELDS;
5298
+ }
5242
5299
  var klingCreateSchema = z13.object({ status_url: z13.string() });
5243
5300
  var klingStatusSchema = z13.object({
5244
5301
  status: z13.string().optional(),
@@ -5273,6 +5330,13 @@ async function createRequest(ctx, url, init) {
5273
5330
  }
5274
5331
  return res;
5275
5332
  }
5333
+ function ignoreKlingField(ctx, field) {
5334
+ ctx.warnings.push({
5335
+ code: "option_ignored",
5336
+ field,
5337
+ message: `${ctx.model.id}: the Higgsfield endpoint has no ${field} input; ignored`
5338
+ });
5339
+ }
5276
5340
  async function generateKlingVideo(req, ctx) {
5277
5341
  const firstFrame = req.firstFrame ? refToUrl(req.firstFrame) : void 0;
5278
5342
  if (!firstFrame) {
@@ -5284,16 +5348,29 @@ async function generateKlingVideo(req, ctx) {
5284
5348
  retryable: false
5285
5349
  });
5286
5350
  }
5351
+ const fields = klingEndpointFields(ctx.model.providerModelId);
5287
5352
  const body = {
5288
5353
  prompt: req.prompt.trim(),
5289
5354
  image_url: firstFrame,
5290
- cfg_scale: req.cfgScale ?? 0.5,
5291
- negative_prompt: req.negativePrompt ?? DEFAULT_NEGATIVE_PROMPT
5355
+ cfg_scale: req.cfgScale ?? 0.5
5292
5356
  };
5357
+ if (fields.negativePrompt) {
5358
+ body.negative_prompt = req.negativePrompt ?? DEFAULT_NEGATIVE_PROMPT;
5359
+ } else if (req.negativePrompt !== void 0) {
5360
+ ignoreKlingField(ctx, "negativePrompt");
5361
+ }
5293
5362
  if (req.durationSeconds !== void 0) body.duration = req.durationSeconds;
5294
- if (req.aspectRatio !== void 0 && req.aspectRatio !== "adaptive")
5295
- body.aspect_ratio = req.aspectRatio;
5296
- if (req.lastFrame) body.last_image_url = refToUrl(req.lastFrame);
5363
+ if (req.aspectRatio !== void 0 && req.aspectRatio !== "adaptive") {
5364
+ if (fields.aspectRatio) body.aspect_ratio = req.aspectRatio;
5365
+ else ignoreKlingField(ctx, "aspectRatio");
5366
+ }
5367
+ if (req.lastFrame) {
5368
+ if (fields.lastFrame) body.last_image_url = refToUrl(req.lastFrame);
5369
+ else ignoreKlingField(ctx, "lastFrame");
5370
+ }
5371
+ if (fields.sound && req.generateAudio !== void 0) {
5372
+ body.sound = req.generateAudio ? "on" : "off";
5373
+ }
5297
5374
  const endpoint = klingEndpoint(ctx.model.providerModelId, ctx.model.id);
5298
5375
  const retryOptions = mediaRetryOptions(ctx.call, ctx.signal);
5299
5376
  const createRes = await withRetry(
@@ -6595,24 +6672,22 @@ function klingInput(req) {
6595
6672
  return input;
6596
6673
  }
6597
6674
  function klingOmniMode(req) {
6675
+ if (req.resolution === "4k") return "4k";
6598
6676
  if (req.resolution === "1080p") return "pro";
6599
6677
  if (req.resolution === "720p") return "standard";
6600
6678
  return req.mode ?? "standard";
6601
6679
  }
6602
6680
  function klingOmniInput(req) {
6603
- const images = (req.firstFrame ? [req.firstFrame] : []).map(refToUrl);
6604
- if (req.lastFrame) images.push(refToUrl(req.lastFrame));
6605
6681
  const input = {
6606
6682
  prompt: req.prompt,
6607
6683
  duration: req.durationSeconds ?? 10,
6608
6684
  mode: klingOmniMode(req),
6609
6685
  generate_audio: req.generateAudio ?? false
6610
6686
  };
6611
- const startImage = images[0];
6612
- if (startImage !== void 0) {
6613
- input.start_image = startImage;
6614
- const endImage = images[1];
6615
- if (endImage !== void 0) input.end_image = endImage;
6687
+ const start = req.firstFrame ?? req.lastFrame;
6688
+ if (start) {
6689
+ input.start_image = refToUrl(start);
6690
+ if (req.firstFrame && req.lastFrame) input.end_image = refToUrl(req.lastFrame);
6616
6691
  } else if (req.aspectRatio !== "adaptive") {
6617
6692
  input.aspect_ratio = req.aspectRatio ?? "16:9";
6618
6693
  }
@@ -6860,7 +6935,7 @@ async function generateXaiVideo(req, ctx, deps) {
6860
6935
  retryable: false
6861
6936
  });
6862
6937
  }
6863
- const refUrls = (req.referenceImages ?? []).map(refToUrl).slice(0, 5);
6938
+ const refUrls = (req.referenceImages ?? []).map(refToUrl).slice(0, 7);
6864
6939
  const xaiOptions = {
6865
6940
  pollIntervalMs: POLL_INTERVAL_MS2,
6866
6941
  pollTimeoutMs: POLL_TIMEOUT_MS2
@@ -7574,6 +7649,24 @@ function resolveVideoOptions(model, request, onUnsupported = "error") {
7574
7649
  });
7575
7650
  delete merged.sourceVideo;
7576
7651
  }
7652
+ if (merged.lastFrame !== void 0 && merged.firstFrame === void 0 && model.capabilities?.lastFrameRequiresFirstFrame) {
7653
+ if (onUnsupported === "error") {
7654
+ throw new ModelKitError({
7655
+ code: "missing_input",
7656
+ message: `${model.id} takes an end frame only together with a start frame`,
7657
+ model: model.id,
7658
+ retryable: false
7659
+ });
7660
+ }
7661
+ warnings.push({
7662
+ code: "option_ignored",
7663
+ field: "lastFrame",
7664
+ to: "firstFrame",
7665
+ message: `end frame used as start frame: ${model.id} needs a start frame with an end frame`
7666
+ });
7667
+ merged.firstFrame = merged.lastFrame;
7668
+ delete merged.lastFrame;
7669
+ }
7577
7670
  const hasFrames = merged.firstFrame !== void 0 || merged.lastFrame !== void 0;
7578
7671
  const hasRefs = compact(merged.referenceImages).length + compact(merged.referenceVideos).length + compact(merged.referenceAudios).length > 0;
7579
7672
  if (hasFrames && hasRefs && model.capabilities?.framesExcludeReferences) {