@shop-os/modelkit 0.2.20 → 0.2.21

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.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
@@ -1258,12 +1258,14 @@ var TEXT_MODELS = [
1258
1258
  var VIDEO_USAGE = { unit: "seconds" };
1259
1259
  var SEEDANCE_RATIOS = ["21:9", "16:9", "4:3", "1:1", "3:4", "9:16", "adaptive"];
1260
1260
  var KLING_RATIOS = ["16:9", "9:16", "1:1", "adaptive"];
1261
+ var KLING_OMNI_RATIOS = ["16:9", "9:16", "1:1"];
1261
1262
  var OMNI_RATIOS = ["16:9", "9:16"];
1262
1263
  var GROK_RATIOS = ["1:1", "16:9", "9:16", "4:3", "3:4", "3:2", "2:3"];
1263
1264
  var H3_RATIOS = ["21:9", "16:9", "4:3", "1:1", "3:4", "9:16"];
1264
1265
  var H3_REFERENCE_RATIOS = ["adaptive", ...H3_RATIOS];
1265
1266
  var NEGATIVE_PROMPT = "blur, distort, and low quality";
1266
1267
  var FRAMES_EXCLUDE_REFERENCES = { framesExcludeReferences: true };
1268
+ var KLING_PROMPT = { referenceSyntax: "numbered", maxChars: 2500 };
1267
1269
  var BYTEDANCE_LOGO = "https://d31vvfpk2ppdyq.cloudfront.net/assets/image/3bba5d81-4e57-4e71-911b-cdfa05acca2c_compressed.png";
1268
1270
  var GOOGLE_LOGO2 = "https://d31vvfpk2ppdyq.cloudfront.net/assets/image/192485d2-a76a-4657-9c06-930b2c70badd_compressed.png";
1269
1271
  var VIDEO_MODELS = [
@@ -1284,9 +1286,13 @@ var VIDEO_MODELS = [
1284
1286
  constraints: {
1285
1287
  aspectRatios: [...OMNI_RATIOS],
1286
1288
  resolutions: ["720p", "1080p", "4k"],
1289
+ // Veo also takes 4 and 6, but must be 8 at 1080p/4k or with reference images;
1290
+ // [8] until the kit has a rule that forces it there (CHANGELOG 0.2.21).
1287
1291
  durations: [8],
1288
1292
  references: { images: 3 },
1289
1293
  supports: ["lastFrame", "seed"],
1294
+ // 1,024 text tokens; 3,000 characters assumes ~2.9 characters per token.
1295
+ prompt: { referenceSyntax: "numbered", maxChars: 3e3 },
1290
1296
  audio: "always"
1291
1297
  },
1292
1298
  defaults: { durationSeconds: 8, resolution: "720p", aspectRatio: "16:9" },
@@ -1297,7 +1303,8 @@ var VIDEO_MODELS = [
1297
1303
  description: "Google's highest-quality video generation model. Supports up to 4K resolution and 8-second clips with reference image conditioning.",
1298
1304
  logo: GOOGLE_LOGO2
1299
1305
  },
1300
- capabilities: { ...FRAMES_EXCLUDE_REFERENCES }
1306
+ // Veo's lastFrame "must be used in combination with the image parameter".
1307
+ capabilities: { ...FRAMES_EXCLUDE_REFERENCES, lastFrameRequiresFirstFrame: true }
1301
1308
  },
1302
1309
  {
1303
1310
  id: "veo-3.1-fast",
@@ -1417,7 +1424,8 @@ var VIDEO_MODELS = [
1417
1424
  // extension can upload a <= 10 s source with task "extend"; later ones
1418
1425
  // chain by `previousInteractionId` (the API rejects the id together
1419
1426
  // with a video task, so those calls carry the prompt alone).
1420
- supports: ["videoExtension"],
1427
+ // First-to-last-frame interpolation: `<LAST_FRAME>` goes with `<FIRST_FRAME>`.
1428
+ supports: ["videoExtension", "lastFrame"],
1421
1429
  extension: { segmentSeconds: 10, maxTotalSeconds: 40 },
1422
1430
  // generateOmniVideo sends MediaRef.label as a text part beside each image.
1423
1431
  prompt: { referenceSyntax: "caption" },
@@ -1431,7 +1439,7 @@ var VIDEO_MODELS = [
1431
1439
  logo: GOOGLE_LOGO2,
1432
1440
  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
1441
  },
1434
- capabilities: { acceptsSourceVideo: true }
1442
+ capabilities: { acceptsSourceVideo: true, lastFrameRequiresFirstFrame: true }
1435
1443
  },
1436
1444
  {
1437
1445
  // vibe-triggers and Space Builder drop the ratio when a frame is wired; Cowork always sends it.
@@ -1457,7 +1465,8 @@ var VIDEO_MODELS = [
1457
1465
  },
1458
1466
  sourceVideoSeconds: { min: 2, max: 15, inReferenceTotal: true },
1459
1467
  ratioIgnoredWith: ["firstFrame", "sourceVideo"],
1460
- supports: ["generateAudio", "seed", "lastFrame", "watermark"],
1468
+ // No seed: 2.x echoes it but does not honour it (two seeded runs differed, 2026-10-01).
1469
+ supports: ["generateAudio", "lastFrame", "watermark"],
1461
1470
  audio: "optional"
1462
1471
  },
1463
1472
  defaults: {
@@ -1497,7 +1506,8 @@ var VIDEO_MODELS = [
1497
1506
  },
1498
1507
  sourceVideoSeconds: { min: 2, max: 15, inReferenceTotal: true },
1499
1508
  ratioIgnoredWith: ["firstFrame", "sourceVideo"],
1500
- supports: ["generateAudio", "seed", "lastFrame", "watermark"],
1509
+ // No seed: 2.x echoes it but does not honour it (two seeded runs differed, 2026-10-01).
1510
+ supports: ["generateAudio", "lastFrame", "watermark"],
1501
1511
  audio: "optional"
1502
1512
  },
1503
1513
  defaults: {
@@ -1516,7 +1526,6 @@ var VIDEO_MODELS = [
1516
1526
  capabilities: { acceptsSourceVideo: true, ...FRAMES_EXCLUDE_REFERENCES }
1517
1527
  },
1518
1528
  {
1519
- // generateAudio defaults to false here (trigger-workflows); Space Builder defaults it to true.
1520
1529
  // omni_reference_task_type edit/extend: adaptive ratio, edit sends duration -1.
1521
1530
  id: "seedance-2.5",
1522
1531
  aliases: ["seedance-2-5", "dreamina-seedance-2-5-260628"],
@@ -1538,14 +1547,15 @@ var VIDEO_MODELS = [
1538
1547
  },
1539
1548
  sourceVideoSeconds: { min: 2, max: 30, editMin: 4, inReferenceTotal: true },
1540
1549
  ratioIgnoredWith: ["firstFrame", "sourceVideo"],
1541
- supports: ["generateAudio", "seed", "lastFrame", "watermark", "taskType"],
1550
+ // No seed: 2.x echoes it but does not honour it (two seeded runs differed, 2026-10-01).
1551
+ supports: ["generateAudio", "lastFrame", "watermark", "taskType"],
1542
1552
  audio: "optional"
1543
1553
  },
1544
1554
  defaults: {
1545
1555
  durationSeconds: 5,
1546
1556
  resolution: "720p",
1547
1557
  aspectRatio: "16:9",
1548
- generateAudio: false
1558
+ generateAudio: true
1549
1559
  },
1550
1560
  usage: { ...VIDEO_USAGE },
1551
1561
  pricingKind: "video_per_second",
@@ -1557,7 +1567,6 @@ var VIDEO_MODELS = [
1557
1567
  capabilities: { acceptsSourceVideo: true, ...FRAMES_EXCLUDE_REFERENCES }
1558
1568
  },
1559
1569
  {
1560
- // Cowork allows 7 reference images and durations 4/6/8/10; Space Builder allows 5 and 1-15s.
1561
1570
  id: "grok-imagine",
1562
1571
  aliases: ["grok-imagine-video", "grokImagine"],
1563
1572
  label: "Grok Imagine",
@@ -1571,7 +1580,8 @@ var VIDEO_MODELS = [
1571
1580
  resolutions: ["480p", "720p"],
1572
1581
  durations: { min: 1, max: 15 },
1573
1582
  durationPresets: [4, 6, 8, 10],
1574
- references: { images: 5 },
1583
+ // xAI: "A maximum of 7 reference images can be provided per request."
1584
+ references: { images: 7 },
1575
1585
  supports: [],
1576
1586
  // xAI can switch sound off (generate_audio); xai-video.ts never sends it.
1577
1587
  audio: "always"
@@ -1596,16 +1606,16 @@ var VIDEO_MODELS = [
1596
1606
  providerModelId: "kling-video/v2.5-turbo/pro/image-to-video",
1597
1607
  lifecycle: "active",
1598
1608
  exposure: ["cowork", "space_builder"],
1609
+ // The endpoint has no aspect_ratio or last_image_url: the start image sets the framing.
1599
1610
  constraints: {
1600
- aspectRatios: [...KLING_RATIOS],
1601
1611
  durations: [5, 10],
1602
1612
  requires: ["firstFrame"],
1603
- supports: ["cfgScale", "negativePrompt", "lastFrame"],
1613
+ supports: ["cfgScale", "negativePrompt"],
1614
+ prompt: KLING_PROMPT,
1604
1615
  audio: "never"
1605
1616
  },
1606
1617
  defaults: {
1607
1618
  durationSeconds: 5,
1608
- aspectRatio: "16:9",
1609
1619
  cfgScale: 0.5,
1610
1620
  negativePrompt: NEGATIVE_PROMPT
1611
1621
  },
@@ -1614,7 +1624,7 @@ var VIDEO_MODELS = [
1614
1624
  display: {
1615
1625
  provider: "kuaishou",
1616
1626
  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."
1627
+ 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
1628
  },
1619
1629
  capabilities: { ...FRAMES_EXCLUDE_REFERENCES }
1620
1630
  },
@@ -1628,19 +1638,18 @@ var VIDEO_MODELS = [
1628
1638
  providerModelId: "kling-video/v3.0/pro/image-to-video",
1629
1639
  lifecycle: "active",
1630
1640
  exposure: ["cowork", "space_builder"],
1641
+ // The endpoint has no aspect_ratio or negative_prompt; sound is "on"/"off" (default on).
1631
1642
  constraints: {
1632
- aspectRatios: [...KLING_RATIOS],
1633
1643
  durations: { min: 3, max: 15 },
1634
1644
  requires: ["firstFrame"],
1635
- supports: ["cfgScale", "negativePrompt", "lastFrame"],
1636
- // Higgsfield has a `sound` switch (default on); KlingCreate doesn't send it.
1637
- audio: "always"
1645
+ supports: ["cfgScale", "lastFrame", "generateAudio"],
1646
+ prompt: KLING_PROMPT,
1647
+ audio: "optional"
1638
1648
  },
1639
1649
  defaults: {
1640
1650
  durationSeconds: 5,
1641
- aspectRatio: "16:9",
1642
1651
  cfgScale: 0.5,
1643
- negativePrompt: NEGATIVE_PROMPT
1652
+ generateAudio: true
1644
1653
  },
1645
1654
  usage: { ...VIDEO_USAGE },
1646
1655
  pricingKind: "video_per_second",
@@ -1652,7 +1661,7 @@ var VIDEO_MODELS = [
1652
1661
  capabilities: { ...FRAMES_EXCLUDE_REFERENCES }
1653
1662
  },
1654
1663
  {
1655
- // mode standard/pro selects 720p/1080p; the endpoint takes no separate resolution.
1664
+ // mode standard/pro/4k selects 720p/1080p/4K; the endpoint takes no separate resolution.
1656
1665
  // Replicate: "Max 7 [images] without video, 4 with video"; 4K does not support
1657
1666
  // reference_video. No `videos` limit yet: the adapter sends no reference video,
1658
1667
  // so `imagesWithVideo` stays dormant until one is wired through.
@@ -1665,22 +1674,25 @@ var VIDEO_MODELS = [
1665
1674
  lifecycle: "active",
1666
1675
  exposure: ["cowork", "space_builder"],
1667
1676
  constraints: {
1668
- aspectRatios: [...KLING_RATIOS],
1669
- resolutions: ["720p", "1080p"],
1677
+ aspectRatios: [...KLING_OMNI_RATIOS],
1678
+ resolutions: ["720p", "1080p", "4k"],
1670
1679
  durations: { min: 3, max: 15 },
1671
1680
  references: { images: 7, imagesWithVideo: 4 },
1672
1681
  supports: ["generateAudio", "lastFrame"],
1682
+ prompt: KLING_PROMPT,
1673
1683
  audio: "optional"
1674
1684
  },
1675
- defaults: { durationSeconds: 5, aspectRatio: "16:9", generateAudio: false, mode: "pro" },
1685
+ defaults: { durationSeconds: 5, aspectRatio: "16:9", generateAudio: true, mode: "pro" },
1676
1686
  usage: { unit: "compute_seconds" },
1677
1687
  pricingKind: "video_per_second",
1678
1688
  display: {
1679
1689
  provider: "kuaishou",
1680
1690
  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: {}
1691
+ 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."
1692
+ }
1693
+ // end_image "Requires start_image", but no lastFrameRequiresFirstFrame: SpaceLab resolves
1694
+ // replicate rows in error mode, so the flag would refuse its end-frame-only nodes, which
1695
+ // the adapter sends as start_image.
1684
1696
  },
1685
1697
  {
1686
1698
  id: "minimax-h3",
@@ -1695,6 +1707,8 @@ var VIDEO_MODELS = [
1695
1707
  aspectRatios: [...H3_RATIOS],
1696
1708
  resolutions: ["480p", "768p", "2k", "4k"],
1697
1709
  durations: { min: 5, max: 15 },
1710
+ // fal's image-to-video endpoint has no aspect_ratio; the frame sets it.
1711
+ ratioIgnoredWith: ["firstFrame"],
1698
1712
  supports: ["seed", "promptExpansion", "lastFrame", "safetyChecker"],
1699
1713
  audio: "always"
1700
1714
  },
@@ -1724,8 +1738,10 @@ var VIDEO_MODELS = [
1724
1738
  exposure: ["cowork", "space_builder"],
1725
1739
  constraints: {
1726
1740
  aspectRatios: [...H3_RATIOS],
1727
- resolutions: ["480p", "768p"],
1741
+ resolutions: ["480p", "768p", "1080p"],
1728
1742
  durations: { min: 5, max: 15 },
1743
+ // fal's image-to-video endpoint has no aspect_ratio; the frame sets it.
1744
+ ratioIgnoredWith: ["firstFrame"],
1729
1745
  supports: ["seed", "promptExpansion", "lastFrame", "safetyChecker"],
1730
1746
  audio: "always"
1731
1747
  },
@@ -1740,7 +1756,7 @@ var VIDEO_MODELS = [
1740
1756
  display: {
1741
1757
  provider: "minimax",
1742
1758
  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."
1759
+ description: "High-speed MiniMax H3 Max variant for text-to-video and image-to-video (first/last frame) only, up to 1080p."
1744
1760
  },
1745
1761
  capabilities: { ...FRAMES_EXCLUDE_REFERENCES }
1746
1762
  },
@@ -1757,6 +1773,8 @@ var VIDEO_MODELS = [
1757
1773
  aspectRatios: [...H3_REFERENCE_RATIOS],
1758
1774
  resolutions: ["480p", "768p", "2k", "4k"],
1759
1775
  durations: { min: 5, max: 15 },
1776
+ // A start frame routes to image-to-video, which has no aspect_ratio.
1777
+ ratioIgnoredWith: ["firstFrame"],
1760
1778
  references: {
1761
1779
  images: 9,
1762
1780
  videos: 3,
@@ -1796,8 +1814,10 @@ var VIDEO_MODELS = [
1796
1814
  exposure: ["cowork", "space_builder"],
1797
1815
  constraints: {
1798
1816
  aspectRatios: [...H3_REFERENCE_RATIOS],
1799
- resolutions: ["480p", "768p"],
1817
+ resolutions: ["480p", "768p", "1080p"],
1800
1818
  durations: { min: 5, max: 15 },
1819
+ // A start frame routes to image-to-video, which has no aspect_ratio.
1820
+ ratioIgnoredWith: ["firstFrame"],
1801
1821
  references: {
1802
1822
  images: 9,
1803
1823
  videos: 3,
@@ -2354,6 +2374,7 @@ var CatalogDisplaySchema = z3.looseObject({
2354
2374
  var CatalogCapabilitiesSchema = z3.looseObject({
2355
2375
  acceptsSourceVideo: z3.boolean().optional(),
2356
2376
  framesExcludeReferences: z3.boolean().optional(),
2377
+ lastFrameRequiresFirstFrame: z3.boolean().optional(),
2357
2378
  reasoning: z3.boolean().optional(),
2358
2379
  vision: z3.boolean().optional(),
2359
2380
  contextWindow: z3.number().optional()
@@ -4578,7 +4599,12 @@ async function generateOmniVideo(req, ctx, createGenAIClient) {
4578
4599
  const extend = req.taskType === "extend";
4579
4600
  const previousId = extend ? req.previousInteractionId : void 0;
4580
4601
  const chained = previousId !== void 0;
4581
- for (const field of ["referenceImages", "referenceVideos", "firstFrame"]) {
4602
+ for (const field of [
4603
+ "referenceImages",
4604
+ "referenceVideos",
4605
+ "firstFrame",
4606
+ "lastFrame"
4607
+ ]) {
4582
4608
  const value = req[field];
4583
4609
  if (!chained || value === void 0 || Array.isArray(value) && value.length === 0)
4584
4610
  continue;
@@ -4592,6 +4618,14 @@ async function generateOmniVideo(req, ctx, createGenAIClient) {
4592
4618
  const images = chained ? [] : req.referenceImages ?? [];
4593
4619
  const videos = chained ? [] : req.referenceVideos ?? [];
4594
4620
  const firstFrame = chained ? void 0 : req.firstFrame;
4621
+ const lastFrame = firstFrame ? req.lastFrame : void 0;
4622
+ if (!chained && req.lastFrame !== void 0 && lastFrame === void 0) {
4623
+ ctx.warnings.push({
4624
+ code: "option_ignored",
4625
+ field: "lastFrame",
4626
+ message: `${modelId} takes an end frame only with a start frame; lastFrame ignored`
4627
+ });
4628
+ }
4595
4629
  if (extend && !chained && req.sourceVideo === void 0) {
4596
4630
  throw new ModelKitError({
4597
4631
  code: "missing_input",
@@ -4676,27 +4710,7 @@ async function generateOmniVideo(req, ctx, createGenAIClient) {
4676
4710
  mime_type: media.mime
4677
4711
  });
4678
4712
  }
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) {
4713
+ const pushImage = async (ref) => {
4700
4714
  if (ref.label !== void 0) parts.push({ type: "text", text: ref.label });
4701
4715
  const media = await resolveMedia(ref, ctx.fetch, ctx.signal, "image/png", modelId);
4702
4716
  if (media.bytes.length > OMNI_INLINE_LIMIT_BYTES) {
@@ -4708,8 +4722,12 @@ async function generateOmniVideo(req, ctx, createGenAIClient) {
4708
4722
  } else {
4709
4723
  parts.push({ type: "image", data: toBase64(media.bytes), mime_type: media.mime });
4710
4724
  }
4711
- }
4712
- parts.push({ type: "text", text: req.prompt });
4725
+ };
4726
+ if (firstFrame) await pushImage(firstFrame);
4727
+ if (lastFrame) await pushImage(lastFrame);
4728
+ for (const ref of images) await pushImage(ref);
4729
+ const prompt = lastFrame ? `<FIRST_FRAME> <LAST_FRAME> ${req.prompt}` : req.prompt;
4730
+ parts.push({ type: "text", text: prompt });
4713
4731
  try {
4714
4732
  interaction = await withRetry(async () => {
4715
4733
  try {
@@ -5239,6 +5257,26 @@ function videoUsage3(ctx, req, duration) {
5239
5257
  if (req.durationSeconds !== void 0) event.durationSeconds = req.durationSeconds;
5240
5258
  return event;
5241
5259
  }
5260
+ var KLING_ENDPOINT_FIELDS = /* @__PURE__ */ new Map([
5261
+ [
5262
+ "kling-video/v2.5-turbo/pro/image-to-video",
5263
+ { aspectRatio: false, negativePrompt: true, lastFrame: false, sound: false }
5264
+ ],
5265
+ [
5266
+ "kling-video/v3.0/pro/image-to-video",
5267
+ { aspectRatio: false, negativePrompt: false, lastFrame: true, sound: true }
5268
+ ]
5269
+ ]);
5270
+ var UNLISTED_KLING_FIELDS = {
5271
+ aspectRatio: true,
5272
+ negativePrompt: true,
5273
+ lastFrame: true,
5274
+ sound: false
5275
+ };
5276
+ function klingEndpointFields(providerModelId) {
5277
+ const path = (providerModelId.startsWith("/") ? providerModelId.slice(1) : providerModelId).toLowerCase();
5278
+ return KLING_ENDPOINT_FIELDS.get(path) ?? UNLISTED_KLING_FIELDS;
5279
+ }
5242
5280
  var klingCreateSchema = z13.object({ status_url: z13.string() });
5243
5281
  var klingStatusSchema = z13.object({
5244
5282
  status: z13.string().optional(),
@@ -5273,6 +5311,13 @@ async function createRequest(ctx, url, init) {
5273
5311
  }
5274
5312
  return res;
5275
5313
  }
5314
+ function ignoreKlingField(ctx, field) {
5315
+ ctx.warnings.push({
5316
+ code: "option_ignored",
5317
+ field,
5318
+ message: `${ctx.model.id}: the Higgsfield endpoint has no ${field} input; ignored`
5319
+ });
5320
+ }
5276
5321
  async function generateKlingVideo(req, ctx) {
5277
5322
  const firstFrame = req.firstFrame ? refToUrl(req.firstFrame) : void 0;
5278
5323
  if (!firstFrame) {
@@ -5284,16 +5329,29 @@ async function generateKlingVideo(req, ctx) {
5284
5329
  retryable: false
5285
5330
  });
5286
5331
  }
5332
+ const fields = klingEndpointFields(ctx.model.providerModelId);
5287
5333
  const body = {
5288
5334
  prompt: req.prompt.trim(),
5289
5335
  image_url: firstFrame,
5290
- cfg_scale: req.cfgScale ?? 0.5,
5291
- negative_prompt: req.negativePrompt ?? DEFAULT_NEGATIVE_PROMPT
5336
+ cfg_scale: req.cfgScale ?? 0.5
5292
5337
  };
5338
+ if (fields.negativePrompt) {
5339
+ body.negative_prompt = req.negativePrompt ?? DEFAULT_NEGATIVE_PROMPT;
5340
+ } else if (req.negativePrompt !== void 0) {
5341
+ ignoreKlingField(ctx, "negativePrompt");
5342
+ }
5293
5343
  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);
5344
+ if (req.aspectRatio !== void 0 && req.aspectRatio !== "adaptive") {
5345
+ if (fields.aspectRatio) body.aspect_ratio = req.aspectRatio;
5346
+ else ignoreKlingField(ctx, "aspectRatio");
5347
+ }
5348
+ if (req.lastFrame) {
5349
+ if (fields.lastFrame) body.last_image_url = refToUrl(req.lastFrame);
5350
+ else ignoreKlingField(ctx, "lastFrame");
5351
+ }
5352
+ if (fields.sound && req.generateAudio !== void 0) {
5353
+ body.sound = req.generateAudio ? "on" : "off";
5354
+ }
5297
5355
  const endpoint = klingEndpoint(ctx.model.providerModelId, ctx.model.id);
5298
5356
  const retryOptions = mediaRetryOptions(ctx.call, ctx.signal);
5299
5357
  const createRes = await withRetry(
@@ -6595,24 +6653,22 @@ function klingInput(req) {
6595
6653
  return input;
6596
6654
  }
6597
6655
  function klingOmniMode(req) {
6656
+ if (req.resolution === "4k") return "4k";
6598
6657
  if (req.resolution === "1080p") return "pro";
6599
6658
  if (req.resolution === "720p") return "standard";
6600
6659
  return req.mode ?? "standard";
6601
6660
  }
6602
6661
  function klingOmniInput(req) {
6603
- const images = (req.firstFrame ? [req.firstFrame] : []).map(refToUrl);
6604
- if (req.lastFrame) images.push(refToUrl(req.lastFrame));
6605
6662
  const input = {
6606
6663
  prompt: req.prompt,
6607
6664
  duration: req.durationSeconds ?? 10,
6608
6665
  mode: klingOmniMode(req),
6609
6666
  generate_audio: req.generateAudio ?? false
6610
6667
  };
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;
6668
+ const start = req.firstFrame ?? req.lastFrame;
6669
+ if (start) {
6670
+ input.start_image = refToUrl(start);
6671
+ if (req.firstFrame && req.lastFrame) input.end_image = refToUrl(req.lastFrame);
6616
6672
  } else if (req.aspectRatio !== "adaptive") {
6617
6673
  input.aspect_ratio = req.aspectRatio ?? "16:9";
6618
6674
  }
@@ -6860,7 +6916,7 @@ async function generateXaiVideo(req, ctx, deps) {
6860
6916
  retryable: false
6861
6917
  });
6862
6918
  }
6863
- const refUrls = (req.referenceImages ?? []).map(refToUrl).slice(0, 5);
6919
+ const refUrls = (req.referenceImages ?? []).map(refToUrl).slice(0, 7);
6864
6920
  const xaiOptions = {
6865
6921
  pollIntervalMs: POLL_INTERVAL_MS2,
6866
6922
  pollTimeoutMs: POLL_TIMEOUT_MS2
@@ -7574,6 +7630,24 @@ function resolveVideoOptions(model, request, onUnsupported = "error") {
7574
7630
  });
7575
7631
  delete merged.sourceVideo;
7576
7632
  }
7633
+ if (merged.lastFrame !== void 0 && merged.firstFrame === void 0 && model.capabilities?.lastFrameRequiresFirstFrame) {
7634
+ if (onUnsupported === "error") {
7635
+ throw new ModelKitError({
7636
+ code: "missing_input",
7637
+ message: `${model.id} takes an end frame only together with a start frame`,
7638
+ model: model.id,
7639
+ retryable: false
7640
+ });
7641
+ }
7642
+ warnings.push({
7643
+ code: "option_ignored",
7644
+ field: "lastFrame",
7645
+ to: "firstFrame",
7646
+ message: `end frame used as start frame: ${model.id} needs a start frame with an end frame`
7647
+ });
7648
+ merged.firstFrame = merged.lastFrame;
7649
+ delete merged.lastFrame;
7650
+ }
7577
7651
  const hasFrames = merged.firstFrame !== void 0 || merged.lastFrame !== void 0;
7578
7652
  const hasRefs = compact(merged.referenceImages).length + compact(merged.referenceVideos).length + compact(merged.referenceAudios).length > 0;
7579
7653
  if (hasFrames && hasRefs && model.capabilities?.framesExcludeReferences) {