@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/CHANGELOG.md CHANGED
@@ -9,6 +9,72 @@ header in the release PR.
9
9
 
10
10
  ## [Unreleased]
11
11
 
12
+ ## [0.2.22] - Unreleased
13
+
14
+ ### Added
15
+ - `constraints.prompt.maxChars` on every bundled row with a documented prompt limit, so Studio accepts prompts longer than its 5,000-character default where the model takes them. Each row keeps its reference syntax (`numbered`, or `caption` on the Gemini rows):
16
+ - `seedance-2.0`, `seedance-2.0-fast`, `seedance-2.5`: 30000. BytePlus caps the prompt at 10,000 tokens; 30,000 characters assumes English prompts at about 3 characters per token, with margin. A CJK prompt can reach the token limit sooner.
17
+ - `gpt-image-2`, `gpt-image-2.5-flare`, `gpt-image-2.5-sunburst`: 32000 (OpenAI images API: 32,000 characters for GPT image models).
18
+ - `flux-2-pro`: 32768 (Prodia schema `maxLength`).
19
+ - `minimax-h3`, `minimax-h3-max`, `minimax-h3-reference`, `minimax-h3-max-reference`: 50000 (fal schema `maxLength` 50,000).
20
+ - `gemini-3-pro-image-preview`, `gemini-3.1-flash-image-preview`, `gemini-3.1-flash-lite-image`, `gemini-omni-flash`, `gemini-omni-1.1-flash`: 50000. Their token budgets are far above this; 50,000 is cowork's global prompt ceiling.
21
+ - Unchanged: `kling-2.5`, `kling-3.0`, `kling-v3-omni-video` at 2500 and `veo-3.1` at 3000. No limit on `grok-imagine`, `muse-image-1.0`, `seedream-5-0-pro` or any other row without a documented one, so Studio keeps its 5,000 default there.
22
+
23
+ ### Live rows to edit after publish
24
+ Apps read the pro-backend `model_catalog` rows, not this file. Edit an environment's rows only after the companion cowork + spacelab-mcp change (Studio's global prompt ceiling raised from 5,000 to 50,000) is deployed there. Before that, cowork's projection would report `maxPromptChars: 30000`, while its request schema and spacelab-mcp still refuse anything over 5,000. After pro-backend is on 0.2.22, in each environment:
25
+ - `seedance-2.0`, `seedance-2.0-fast`, `seedance-2.5`: `constraints.prompt` → `{ "referenceSyntax": "numbered", "maxChars": 30000 }` (assumes English prompts).
26
+ - `gpt-image-2`, `gpt-image-2.5-flare`, `gpt-image-2.5-sunburst`: `constraints.prompt` → `{ "referenceSyntax": "numbered", "maxChars": 32000 }`.
27
+ - `flux-2-pro`: `constraints.prompt` → `{ "referenceSyntax": "numbered", "maxChars": 32768 }`.
28
+ - `minimax-h3`, `minimax-h3-max`, `minimax-h3-reference`, `minimax-h3-max-reference`: `constraints.prompt` → `{ "referenceSyntax": "numbered", "maxChars": 50000 }`.
29
+ - `gemini-3-pro-image-preview`, `gemini-3.1-flash-image-preview`, `gemini-3.1-flash-lite-image`, `gemini-omni-flash`, `gemini-omni-1.1-flash`: `constraints.prompt` → `{ "referenceSyntax": "caption", "maxChars": 50000 }`.
30
+
31
+ ## [0.2.21] - Unreleased
32
+
33
+ ### Added
34
+ - `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`.
35
+ - `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.
36
+ - 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).
37
+ - `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).
38
+ - `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.
39
+
40
+ ### Changed
41
+ - 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.
42
+ - `kling-2.5`: no `aspectRatios`, no ratio default, `supports` loses `lastFrame`.
43
+ - `kling-3.0`: no `aspectRatios`, no ratio default, `supports` loses `negativePrompt` (and the default negative prompt).
44
+ - `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`.
45
+ - `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).
46
+ - `seedance-2.5`: default `generateAudio: true` (sound on by default where it is optional).
47
+ - `minimax-h3-max`, `minimax-h3-max-reference`: `resolutions` gain `1080p` (sent as fal's `1080P`).
48
+ - `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`.
49
+ - `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.
50
+
51
+ ### Deferred
52
+ - `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).
53
+
54
+ ### Live rows to edit after publish
55
+ 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.
56
+
57
+ Preconditions:
58
+ - 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.
59
+ - 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.
60
+
61
+ Behaviour after the edit:
62
+ - `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.
63
+ - 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.
64
+
65
+ After pro-backend is on 0.2.21, in each environment:
66
+ - `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).
67
+ - `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.
68
+ - `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).
69
+ - `veo-3.1`: `constraints.prompt` → `{ "referenceSyntax": "numbered", "maxChars": 3000 }`; `capabilities.lastFrameRequiresFirstFrame` → `true`.
70
+ - `gemini-omni-1.1-flash`: `constraints.supports` → `["videoExtension", "lastFrame"]`; `capabilities.lastFrameRequiresFirstFrame` → `true`.
71
+ - `seedance-2.0`, `seedance-2.0-fast`: `constraints.supports` → `["generateAudio", "lastFrame", "watermark"]`.
72
+ - `seedance-2.5`: `constraints.supports` → `["generateAudio", "lastFrame", "watermark", "taskType"]`; `defaults.generateAudio` → `true`.
73
+ - `minimax-h3`, `minimax-h3-reference`: `constraints.ratioIgnoredWith` → `["firstFrame"]`.
74
+ - `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`.
75
+ - `grok-imagine`: `constraints.references.images` → `7`.
76
+ - `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).
77
+
12
78
  ## [0.2.20] - Unreleased
13
79
 
14
80
  ### Added
package/dist/catalog.cjs CHANGED
@@ -217,13 +217,16 @@ var GEMINI_RATIOS = [
217
217
  "5:4",
218
218
  "adaptive"
219
219
  ];
220
- function geminiImage(id, label, aliases, sizes, display, exposure, references) {
220
+ var GEMINI_PROMPT_MAX_CHARS = 5e4;
221
+ var GPT_IMAGE_PROMPT = { referenceSyntax: "numbered", maxChars: 32e3 };
222
+ function geminiImage(id, label, aliases, sizes, display, exposure, references, promptMaxChars) {
223
+ const prompt = { referenceSyntax: "caption" };
224
+ if (promptMaxChars !== void 0) prompt.maxChars = promptMaxChars;
221
225
  const constraints = {
222
226
  aspectRatios: [...GEMINI_RATIOS],
223
227
  sizes,
224
228
  supports: ["safetySettings", "temperature"],
225
- // google-image sends MediaRef.label as a text part beside each reference.
226
- prompt: { referenceSyntax: "caption" }
229
+ prompt
227
230
  };
228
231
  if (references !== void 0) constraints.references = references;
229
232
  const model = {
@@ -255,7 +258,8 @@ var IMAGE_MODELS = [
255
258
  logo: "https://d31vvfpk2ppdyq.cloudfront.net/assets/image/192485d2-a76a-4657-9c06-930b2c70badd_compressed.png"
256
259
  },
257
260
  ["cowork", "space_builder"],
258
- { images: 14 }
261
+ { images: 14 },
262
+ GEMINI_PROMPT_MAX_CHARS
259
263
  ),
260
264
  geminiImage(
261
265
  "gemini-3.1-flash-image-preview",
@@ -269,7 +273,8 @@ var IMAGE_MODELS = [
269
273
  logo: "https://d31vvfpk2ppdyq.cloudfront.net/assets/image/192485d2-a76a-4657-9c06-930b2c70badd_compressed.png"
270
274
  },
271
275
  ["cowork", "space_builder"],
272
- { images: 14 }
276
+ { images: 14 },
277
+ GEMINI_PROMPT_MAX_CHARS
273
278
  ),
274
279
  geminiImage(
275
280
  "gemini-3.1-flash-lite-image",
@@ -282,7 +287,8 @@ var IMAGE_MODELS = [
282
287
  logo: "https://d31vvfpk2ppdyq.cloudfront.net/assets/image/192485d2-a76a-4657-9c06-930b2c70badd_compressed.png"
283
288
  },
284
289
  ["cowork", "space_builder"],
285
- { images: 14 }
290
+ { images: 14 },
291
+ GEMINI_PROMPT_MAX_CHARS
286
292
  ),
287
293
  // trigger-workflows validates 1K only; pricing seeds 2K/4K rows so allow all three.
288
294
  geminiImage(
@@ -320,7 +326,8 @@ var IMAGE_MODELS = [
320
326
  sizes: ["1K", "2K", "4K"],
321
327
  qualityOptions: ["auto", "low", "medium", "high"],
322
328
  references: { images: 16 },
323
- supports: ["quality", "count", "outputFormat"]
329
+ supports: ["quality", "count", "outputFormat"],
330
+ prompt: GPT_IMAGE_PROMPT
324
331
  },
325
332
  defaults: { size: "1K", aspectRatio: "adaptive", quality: "auto" },
326
333
  usage: { ...IMAGE_USAGE, slug: "gpt" },
@@ -355,7 +362,8 @@ var IMAGE_MODELS = [
355
362
  sizes: ["1K", "2K", "4K"],
356
363
  qualityOptions: ["auto", "low", "medium", "high", "xhigh", "max"],
357
364
  references: { images: 16 },
358
- supports: ["quality", "count", "outputFormat"]
365
+ supports: ["quality", "count", "outputFormat"],
366
+ prompt: GPT_IMAGE_PROMPT
359
367
  },
360
368
  defaults: { size: "1K", aspectRatio: "adaptive", quality: "auto" },
361
369
  usage: { ...IMAGE_USAGE },
@@ -389,7 +397,8 @@ var IMAGE_MODELS = [
389
397
  sizes: ["1K", "2K", "4K"],
390
398
  qualityOptions: ["auto", "low", "medium", "high", "xhigh", "max"],
391
399
  references: { images: 16 },
392
- supports: ["quality", "count", "outputFormat"]
400
+ supports: ["quality", "count", "outputFormat"],
401
+ prompt: GPT_IMAGE_PROMPT
393
402
  },
394
403
  defaults: { size: "1K", aspectRatio: "adaptive", quality: "auto" },
395
404
  usage: { ...IMAGE_USAGE },
@@ -512,7 +521,9 @@ var IMAGE_MODELS = [
512
521
  sizes: ["1K"],
513
522
  references: { images: 8 },
514
523
  // flux-2-pro's job type always returns image/png; only the seed is forwarded.
515
- supports: ["seed"]
524
+ supports: ["seed"],
525
+ // Prodia's flux-2-pro schema gives the prompt a maxLength of 32,768.
526
+ prompt: { referenceSyntax: "numbered", maxChars: 32768 }
516
527
  },
517
528
  defaults: { size: "1K", aspectRatio: "1:1" },
518
529
  usage: { ...IMAGE_USAGE },
@@ -1306,12 +1317,17 @@ var TEXT_MODELS = [
1306
1317
  var VIDEO_USAGE = { unit: "seconds" };
1307
1318
  var SEEDANCE_RATIOS = ["21:9", "16:9", "4:3", "1:1", "3:4", "9:16", "adaptive"];
1308
1319
  var KLING_RATIOS = ["16:9", "9:16", "1:1", "adaptive"];
1320
+ var KLING_OMNI_RATIOS = ["16:9", "9:16", "1:1"];
1309
1321
  var OMNI_RATIOS = ["16:9", "9:16"];
1310
1322
  var GROK_RATIOS = ["1:1", "16:9", "9:16", "4:3", "3:4", "3:2", "2:3"];
1311
1323
  var H3_RATIOS = ["21:9", "16:9", "4:3", "1:1", "3:4", "9:16"];
1312
1324
  var H3_REFERENCE_RATIOS = ["adaptive", ...H3_RATIOS];
1313
1325
  var NEGATIVE_PROMPT = "blur, distort, and low quality";
1314
1326
  var FRAMES_EXCLUDE_REFERENCES = { framesExcludeReferences: true };
1327
+ var KLING_PROMPT = { referenceSyntax: "numbered", maxChars: 2500 };
1328
+ var SEEDANCE_PROMPT = { referenceSyntax: "numbered", maxChars: 3e4 };
1329
+ var H3_PROMPT = { referenceSyntax: "numbered", maxChars: 5e4 };
1330
+ var OMNI_PROMPT = { referenceSyntax: "caption", maxChars: 5e4 };
1315
1331
  var BYTEDANCE_LOGO = "https://d31vvfpk2ppdyq.cloudfront.net/assets/image/3bba5d81-4e57-4e71-911b-cdfa05acca2c_compressed.png";
1316
1332
  var GOOGLE_LOGO2 = "https://d31vvfpk2ppdyq.cloudfront.net/assets/image/192485d2-a76a-4657-9c06-930b2c70badd_compressed.png";
1317
1333
  var VIDEO_MODELS = [
@@ -1332,9 +1348,13 @@ var VIDEO_MODELS = [
1332
1348
  constraints: {
1333
1349
  aspectRatios: [...OMNI_RATIOS],
1334
1350
  resolutions: ["720p", "1080p", "4k"],
1351
+ // Veo also takes 4 and 6, but must be 8 at 1080p/4k or with reference images;
1352
+ // [8] until the kit has a rule that forces it there (CHANGELOG 0.2.21).
1335
1353
  durations: [8],
1336
1354
  references: { images: 3 },
1337
1355
  supports: ["lastFrame", "seed"],
1356
+ // 1,024 text tokens; 3,000 characters assumes ~2.9 characters per token.
1357
+ prompt: { referenceSyntax: "numbered", maxChars: 3e3 },
1338
1358
  audio: "always"
1339
1359
  },
1340
1360
  defaults: { durationSeconds: 8, resolution: "720p", aspectRatio: "16:9" },
@@ -1345,7 +1365,8 @@ var VIDEO_MODELS = [
1345
1365
  description: "Google's highest-quality video generation model. Supports up to 4K resolution and 8-second clips with reference image conditioning.",
1346
1366
  logo: GOOGLE_LOGO2
1347
1367
  },
1348
- capabilities: { ...FRAMES_EXCLUDE_REFERENCES }
1368
+ // Veo's lastFrame "must be used in combination with the image parameter".
1369
+ capabilities: { ...FRAMES_EXCLUDE_REFERENCES, lastFrameRequiresFirstFrame: true }
1349
1370
  },
1350
1371
  {
1351
1372
  id: "veo-3.1-fast",
@@ -1430,8 +1451,7 @@ var VIDEO_MODELS = [
1430
1451
  references: { images: 6, videos: 1, videoSeconds: { max: 10 } },
1431
1452
  sourceVideoSeconds: { max: 10 },
1432
1453
  supports: [],
1433
- // generateOmniVideo sends MediaRef.label as a text part beside each image.
1434
- prompt: { referenceSyntax: "caption" },
1454
+ prompt: OMNI_PROMPT,
1435
1455
  audio: "always"
1436
1456
  },
1437
1457
  defaults: {},
@@ -1465,10 +1485,10 @@ var VIDEO_MODELS = [
1465
1485
  // extension can upload a <= 10 s source with task "extend"; later ones
1466
1486
  // chain by `previousInteractionId` (the API rejects the id together
1467
1487
  // with a video task, so those calls carry the prompt alone).
1468
- supports: ["videoExtension"],
1488
+ // First-to-last-frame interpolation: `<LAST_FRAME>` goes with `<FIRST_FRAME>`.
1489
+ supports: ["videoExtension", "lastFrame"],
1469
1490
  extension: { segmentSeconds: 10, maxTotalSeconds: 40 },
1470
- // generateOmniVideo sends MediaRef.label as a text part beside each image.
1471
- prompt: { referenceSyntax: "caption" },
1491
+ prompt: OMNI_PROMPT,
1472
1492
  audio: "always"
1473
1493
  },
1474
1494
  defaults: {},
@@ -1479,7 +1499,7 @@ var VIDEO_MODELS = [
1479
1499
  logo: GOOGLE_LOGO2,
1480
1500
  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
1501
  },
1482
- capabilities: { acceptsSourceVideo: true }
1502
+ capabilities: { acceptsSourceVideo: true, lastFrameRequiresFirstFrame: true }
1483
1503
  },
1484
1504
  {
1485
1505
  // vibe-triggers and Space Builder drop the ratio when a frame is wired; Cowork always sends it.
@@ -1505,7 +1525,9 @@ var VIDEO_MODELS = [
1505
1525
  },
1506
1526
  sourceVideoSeconds: { min: 2, max: 15, inReferenceTotal: true },
1507
1527
  ratioIgnoredWith: ["firstFrame", "sourceVideo"],
1508
- supports: ["generateAudio", "seed", "lastFrame", "watermark"],
1528
+ // No seed: 2.x echoes it but does not honour it (two seeded runs differed, 2026-10-01).
1529
+ supports: ["generateAudio", "lastFrame", "watermark"],
1530
+ prompt: SEEDANCE_PROMPT,
1509
1531
  audio: "optional"
1510
1532
  },
1511
1533
  defaults: {
@@ -1545,7 +1567,9 @@ var VIDEO_MODELS = [
1545
1567
  },
1546
1568
  sourceVideoSeconds: { min: 2, max: 15, inReferenceTotal: true },
1547
1569
  ratioIgnoredWith: ["firstFrame", "sourceVideo"],
1548
- supports: ["generateAudio", "seed", "lastFrame", "watermark"],
1570
+ // No seed: 2.x echoes it but does not honour it (two seeded runs differed, 2026-10-01).
1571
+ supports: ["generateAudio", "lastFrame", "watermark"],
1572
+ prompt: SEEDANCE_PROMPT,
1549
1573
  audio: "optional"
1550
1574
  },
1551
1575
  defaults: {
@@ -1564,7 +1588,6 @@ var VIDEO_MODELS = [
1564
1588
  capabilities: { acceptsSourceVideo: true, ...FRAMES_EXCLUDE_REFERENCES }
1565
1589
  },
1566
1590
  {
1567
- // generateAudio defaults to false here (trigger-workflows); Space Builder defaults it to true.
1568
1591
  // omni_reference_task_type edit/extend: adaptive ratio, edit sends duration -1.
1569
1592
  id: "seedance-2.5",
1570
1593
  aliases: ["seedance-2-5", "dreamina-seedance-2-5-260628"],
@@ -1586,14 +1609,16 @@ var VIDEO_MODELS = [
1586
1609
  },
1587
1610
  sourceVideoSeconds: { min: 2, max: 30, editMin: 4, inReferenceTotal: true },
1588
1611
  ratioIgnoredWith: ["firstFrame", "sourceVideo"],
1589
- supports: ["generateAudio", "seed", "lastFrame", "watermark", "taskType"],
1612
+ // No seed: 2.x echoes it but does not honour it (two seeded runs differed, 2026-10-01).
1613
+ supports: ["generateAudio", "lastFrame", "watermark", "taskType"],
1614
+ prompt: SEEDANCE_PROMPT,
1590
1615
  audio: "optional"
1591
1616
  },
1592
1617
  defaults: {
1593
1618
  durationSeconds: 5,
1594
1619
  resolution: "720p",
1595
1620
  aspectRatio: "16:9",
1596
- generateAudio: false
1621
+ generateAudio: true
1597
1622
  },
1598
1623
  usage: { ...VIDEO_USAGE },
1599
1624
  pricingKind: "video_per_second",
@@ -1605,7 +1630,6 @@ var VIDEO_MODELS = [
1605
1630
  capabilities: { acceptsSourceVideo: true, ...FRAMES_EXCLUDE_REFERENCES }
1606
1631
  },
1607
1632
  {
1608
- // Cowork allows 7 reference images and durations 4/6/8/10; Space Builder allows 5 and 1-15s.
1609
1633
  id: "grok-imagine",
1610
1634
  aliases: ["grok-imagine-video", "grokImagine"],
1611
1635
  label: "Grok Imagine",
@@ -1619,7 +1643,8 @@ var VIDEO_MODELS = [
1619
1643
  resolutions: ["480p", "720p"],
1620
1644
  durations: { min: 1, max: 15 },
1621
1645
  durationPresets: [4, 6, 8, 10],
1622
- references: { images: 5 },
1646
+ // xAI: "A maximum of 7 reference images can be provided per request."
1647
+ references: { images: 7 },
1623
1648
  supports: [],
1624
1649
  // xAI can switch sound off (generate_audio); xai-video.ts never sends it.
1625
1650
  audio: "always"
@@ -1644,16 +1669,16 @@ var VIDEO_MODELS = [
1644
1669
  providerModelId: "kling-video/v2.5-turbo/pro/image-to-video",
1645
1670
  lifecycle: "active",
1646
1671
  exposure: ["cowork", "space_builder"],
1672
+ // The endpoint has no aspect_ratio or last_image_url: the start image sets the framing.
1647
1673
  constraints: {
1648
- aspectRatios: [...KLING_RATIOS],
1649
1674
  durations: [5, 10],
1650
1675
  requires: ["firstFrame"],
1651
- supports: ["cfgScale", "negativePrompt", "lastFrame"],
1676
+ supports: ["cfgScale", "negativePrompt"],
1677
+ prompt: KLING_PROMPT,
1652
1678
  audio: "never"
1653
1679
  },
1654
1680
  defaults: {
1655
1681
  durationSeconds: 5,
1656
- aspectRatio: "16:9",
1657
1682
  cfgScale: 0.5,
1658
1683
  negativePrompt: NEGATIVE_PROMPT
1659
1684
  },
@@ -1662,7 +1687,7 @@ var VIDEO_MODELS = [
1662
1687
  display: {
1663
1688
  provider: "kuaishou",
1664
1689
  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."
1690
+ 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
1691
  },
1667
1692
  capabilities: { ...FRAMES_EXCLUDE_REFERENCES }
1668
1693
  },
@@ -1676,19 +1701,18 @@ var VIDEO_MODELS = [
1676
1701
  providerModelId: "kling-video/v3.0/pro/image-to-video",
1677
1702
  lifecycle: "active",
1678
1703
  exposure: ["cowork", "space_builder"],
1704
+ // The endpoint has no aspect_ratio or negative_prompt; sound is "on"/"off" (default on).
1679
1705
  constraints: {
1680
- aspectRatios: [...KLING_RATIOS],
1681
1706
  durations: { min: 3, max: 15 },
1682
1707
  requires: ["firstFrame"],
1683
- supports: ["cfgScale", "negativePrompt", "lastFrame"],
1684
- // Higgsfield has a `sound` switch (default on); KlingCreate doesn't send it.
1685
- audio: "always"
1708
+ supports: ["cfgScale", "lastFrame", "generateAudio"],
1709
+ prompt: KLING_PROMPT,
1710
+ audio: "optional"
1686
1711
  },
1687
1712
  defaults: {
1688
1713
  durationSeconds: 5,
1689
- aspectRatio: "16:9",
1690
1714
  cfgScale: 0.5,
1691
- negativePrompt: NEGATIVE_PROMPT
1715
+ generateAudio: true
1692
1716
  },
1693
1717
  usage: { ...VIDEO_USAGE },
1694
1718
  pricingKind: "video_per_second",
@@ -1700,7 +1724,7 @@ var VIDEO_MODELS = [
1700
1724
  capabilities: { ...FRAMES_EXCLUDE_REFERENCES }
1701
1725
  },
1702
1726
  {
1703
- // mode standard/pro selects 720p/1080p; the endpoint takes no separate resolution.
1727
+ // mode standard/pro/4k selects 720p/1080p/4K; the endpoint takes no separate resolution.
1704
1728
  // Replicate: "Max 7 [images] without video, 4 with video"; 4K does not support
1705
1729
  // reference_video. No `videos` limit yet: the adapter sends no reference video,
1706
1730
  // so `imagesWithVideo` stays dormant until one is wired through.
@@ -1713,22 +1737,25 @@ var VIDEO_MODELS = [
1713
1737
  lifecycle: "active",
1714
1738
  exposure: ["cowork", "space_builder"],
1715
1739
  constraints: {
1716
- aspectRatios: [...KLING_RATIOS],
1717
- resolutions: ["720p", "1080p"],
1740
+ aspectRatios: [...KLING_OMNI_RATIOS],
1741
+ resolutions: ["720p", "1080p", "4k"],
1718
1742
  durations: { min: 3, max: 15 },
1719
1743
  references: { images: 7, imagesWithVideo: 4 },
1720
1744
  supports: ["generateAudio", "lastFrame"],
1745
+ prompt: KLING_PROMPT,
1721
1746
  audio: "optional"
1722
1747
  },
1723
- defaults: { durationSeconds: 5, aspectRatio: "16:9", generateAudio: false, mode: "pro" },
1748
+ defaults: { durationSeconds: 5, aspectRatio: "16:9", generateAudio: true, mode: "pro" },
1724
1749
  usage: { unit: "compute_seconds" },
1725
1750
  pricingKind: "video_per_second",
1726
1751
  display: {
1727
1752
  provider: "kuaishou",
1728
1753
  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: {}
1754
+ 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."
1755
+ }
1756
+ // end_image "Requires start_image", but no lastFrameRequiresFirstFrame: SpaceLab resolves
1757
+ // replicate rows in error mode, so the flag would refuse its end-frame-only nodes, which
1758
+ // the adapter sends as start_image.
1732
1759
  },
1733
1760
  {
1734
1761
  id: "minimax-h3",
@@ -1743,7 +1770,10 @@ var VIDEO_MODELS = [
1743
1770
  aspectRatios: [...H3_RATIOS],
1744
1771
  resolutions: ["480p", "768p", "2k", "4k"],
1745
1772
  durations: { min: 5, max: 15 },
1773
+ // fal's image-to-video endpoint has no aspect_ratio; the frame sets it.
1774
+ ratioIgnoredWith: ["firstFrame"],
1746
1775
  supports: ["seed", "promptExpansion", "lastFrame", "safetyChecker"],
1776
+ prompt: H3_PROMPT,
1747
1777
  audio: "always"
1748
1778
  },
1749
1779
  defaults: {
@@ -1772,9 +1802,12 @@ var VIDEO_MODELS = [
1772
1802
  exposure: ["cowork", "space_builder"],
1773
1803
  constraints: {
1774
1804
  aspectRatios: [...H3_RATIOS],
1775
- resolutions: ["480p", "768p"],
1805
+ resolutions: ["480p", "768p", "1080p"],
1776
1806
  durations: { min: 5, max: 15 },
1807
+ // fal's image-to-video endpoint has no aspect_ratio; the frame sets it.
1808
+ ratioIgnoredWith: ["firstFrame"],
1777
1809
  supports: ["seed", "promptExpansion", "lastFrame", "safetyChecker"],
1810
+ prompt: H3_PROMPT,
1778
1811
  audio: "always"
1779
1812
  },
1780
1813
  defaults: {
@@ -1788,7 +1821,7 @@ var VIDEO_MODELS = [
1788
1821
  display: {
1789
1822
  provider: "minimax",
1790
1823
  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."
1824
+ description: "High-speed MiniMax H3 Max variant for text-to-video and image-to-video (first/last frame) only, up to 1080p."
1792
1825
  },
1793
1826
  capabilities: { ...FRAMES_EXCLUDE_REFERENCES }
1794
1827
  },
@@ -1805,6 +1838,8 @@ var VIDEO_MODELS = [
1805
1838
  aspectRatios: [...H3_REFERENCE_RATIOS],
1806
1839
  resolutions: ["480p", "768p", "2k", "4k"],
1807
1840
  durations: { min: 5, max: 15 },
1841
+ // A start frame routes to image-to-video, which has no aspect_ratio.
1842
+ ratioIgnoredWith: ["firstFrame"],
1808
1843
  references: {
1809
1844
  images: 9,
1810
1845
  videos: 3,
@@ -1814,6 +1849,7 @@ var VIDEO_MODELS = [
1814
1849
  videoSeconds: { min: 2, max: 15, total: 15 }
1815
1850
  },
1816
1851
  supports: ["seed", "promptExpansion", "safetyChecker"],
1852
+ prompt: H3_PROMPT,
1817
1853
  audio: "always"
1818
1854
  },
1819
1855
  defaults: {
@@ -1844,8 +1880,10 @@ var VIDEO_MODELS = [
1844
1880
  exposure: ["cowork", "space_builder"],
1845
1881
  constraints: {
1846
1882
  aspectRatios: [...H3_REFERENCE_RATIOS],
1847
- resolutions: ["480p", "768p"],
1883
+ resolutions: ["480p", "768p", "1080p"],
1848
1884
  durations: { min: 5, max: 15 },
1885
+ // A start frame routes to image-to-video, which has no aspect_ratio.
1886
+ ratioIgnoredWith: ["firstFrame"],
1849
1887
  references: {
1850
1888
  images: 9,
1851
1889
  videos: 3,
@@ -1855,6 +1893,7 @@ var VIDEO_MODELS = [
1855
1893
  videoSeconds: { min: 2, max: 15, total: 15 }
1856
1894
  },
1857
1895
  supports: ["seed", "promptExpansion", "safetyChecker"],
1896
+ prompt: H3_PROMPT,
1858
1897
  audio: "always"
1859
1898
  },
1860
1899
  defaults: {
@@ -2403,6 +2442,7 @@ var CatalogDisplaySchema = import_zod3.z.looseObject({
2403
2442
  var CatalogCapabilitiesSchema = import_zod3.z.looseObject({
2404
2443
  acceptsSourceVideo: import_zod3.z.boolean().optional(),
2405
2444
  framesExcludeReferences: import_zod3.z.boolean().optional(),
2445
+ lastFrameRequiresFirstFrame: import_zod3.z.boolean().optional(),
2406
2446
  reasoning: import_zod3.z.boolean().optional(),
2407
2447
  vision: import_zod3.z.boolean().optional(),
2408
2448
  contextWindow: import_zod3.z.number().optional()
@@ -2984,6 +3024,24 @@ function resolveVideoOptions(model, request, onUnsupported = "error") {
2984
3024
  });
2985
3025
  delete merged.sourceVideo;
2986
3026
  }
3027
+ if (merged.lastFrame !== void 0 && merged.firstFrame === void 0 && model.capabilities?.lastFrameRequiresFirstFrame) {
3028
+ if (onUnsupported === "error") {
3029
+ throw new ModelKitError({
3030
+ code: "missing_input",
3031
+ message: `${model.id} takes an end frame only together with a start frame`,
3032
+ model: model.id,
3033
+ retryable: false
3034
+ });
3035
+ }
3036
+ warnings.push({
3037
+ code: "option_ignored",
3038
+ field: "lastFrame",
3039
+ to: "firstFrame",
3040
+ message: `end frame used as start frame: ${model.id} needs a start frame with an end frame`
3041
+ });
3042
+ merged.firstFrame = merged.lastFrame;
3043
+ delete merged.lastFrame;
3044
+ }
2987
3045
  const hasFrames = merged.firstFrame !== void 0 || merged.lastFrame !== void 0;
2988
3046
  const hasRefs = compact(merged.referenceImages).length + compact(merged.referenceVideos).length + compact(merged.referenceAudios).length > 0;
2989
3047
  if (hasFrames && hasRefs && model.capabilities?.framesExcludeReferences) {