@shop-os/modelkit 0.2.14 → 0.2.15

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,16 @@ header in the release PR.
9
9
 
10
10
  ## [Unreleased]
11
11
 
12
+ ## [0.2.15] - Unreleased
13
+
14
+ ### Added
15
+ - `MediaRef.durationSeconds`: the measured length of a video or audio ref. The kit never measures it; callers pass it when known.
16
+ - `ReferenceLimits.videoSeconds` (`VideoSecondsLimits`: `min`, `max`, `total`, `editMin`): per-video and combined length limits for reference videos. `editMin` raises the per-video minimum when `taskType` is `edit` (Seedance 2.5: 4 s). Rows without it accept any length.
17
+ - `ModelConstraints.sourceVideoSeconds` (`min`, `max`, `editMin`, `inReferenceTotal`): length limits for the source video. `inReferenceTotal` counts the source toward the combined reference limit where the provider sends it as a reference video (Seedance).
18
+ - `videoLengthViolation(model, input)`: pure checker that returns the first length violation as a user-facing message, else `undefined`. Unknown lengths are skipped. Exported from the root and `/catalog` entries.
19
+ - `resolveVideoOptions` rejects a reference or source video outside the row limits with a non-retryable `unsupported_option` error before any provider call. Videos dropped by the reference caps do not count.
20
+ - Snapshot rows carry the provider limits: MiniMax H3 and H3 Max reference (2-15 s each, 15 s combined), Seedance 2.0 and 2.0 Fast (2-15 s each, 15 s combined), Seedance 2.5 (2-30 s each, 30 s combined, 4 s minimum for edit), Gemini Omni 1.0 and 1.1 (10 s), Kling O1 v2v source video (3-10.05 s).
21
+
12
22
  ## [0.2.14] - Unreleased
13
23
 
14
24
  - Keep AI SDK 6 and its compatible provider packages, including Bedrock and usage tracking.
package/dist/catalog.cjs CHANGED
@@ -48,7 +48,8 @@ __export(catalog_entry_exports, {
48
48
  resolveImageOptions: () => resolveImageOptions,
49
49
  resolveOptions: () => resolveOptions,
50
50
  resolveVideoOptions: () => resolveVideoOptions,
51
- sortCatalogRows: () => sortCatalogRows
51
+ sortCatalogRows: () => sortCatalogRows,
52
+ videoLengthViolation: () => videoLengthViolation
52
53
  });
53
54
  module.exports = __toCommonJS(catalog_entry_exports);
54
55
 
@@ -1416,7 +1417,8 @@ var VIDEO_MODELS = [
1416
1417
  // reference clip keeps its own framing.
1417
1418
  aspectRatios: [...OMNI_RATIOS],
1418
1419
  durations: [3, 4, 5, 6, 7, 8, 9, 10],
1419
- references: { images: 6, videos: 1 },
1420
+ references: { images: 6, videos: 1, videoSeconds: { max: 10 } },
1421
+ sourceVideoSeconds: { max: 10 },
1420
1422
  supports: []
1421
1423
  },
1422
1424
  defaults: {},
@@ -1442,7 +1444,8 @@ var VIDEO_MODELS = [
1442
1444
  aspectRatios: [...OMNI_RATIOS],
1443
1445
  resolutions: ["360p", "720p", "1080p", "4k"],
1444
1446
  durations: [3, 4, 5, 6, 7, 8, 9, 10],
1445
- references: { images: 6, videos: 1 },
1447
+ references: { images: 6, videos: 1, videoSeconds: { max: 10 } },
1448
+ sourceVideoSeconds: { max: 10 },
1446
1449
  supports: []
1447
1450
  },
1448
1451
  defaults: {},
@@ -1470,7 +1473,14 @@ var VIDEO_MODELS = [
1470
1473
  resolutions: ["480p", "720p", "1080p", "4k"],
1471
1474
  durations: { min: 4, max: 15 },
1472
1475
  durationPresets: [4, 6, 8, 10, 12, 15],
1473
- references: { images: 9, videos: 3, audios: 3, audioOnly: false },
1476
+ references: {
1477
+ images: 9,
1478
+ videos: 3,
1479
+ audios: 3,
1480
+ audioOnly: false,
1481
+ videoSeconds: { min: 2, max: 15, total: 15 }
1482
+ },
1483
+ sourceVideoSeconds: { min: 2, max: 15, inReferenceTotal: true },
1474
1484
  ratioIgnoredWith: ["firstFrame", "sourceVideo"],
1475
1485
  supports: ["generateAudio", "seed", "lastFrame", "watermark"]
1476
1486
  },
@@ -1502,7 +1512,14 @@ var VIDEO_MODELS = [
1502
1512
  aspectRatios: [...SEEDANCE_RATIOS],
1503
1513
  resolutions: ["480p", "720p"],
1504
1514
  durations: { min: 4, max: 15 },
1505
- references: { images: 9, videos: 3, audios: 3, audioOnly: false },
1515
+ references: {
1516
+ images: 9,
1517
+ videos: 3,
1518
+ audios: 3,
1519
+ audioOnly: false,
1520
+ videoSeconds: { min: 2, max: 15, total: 15 }
1521
+ },
1522
+ sourceVideoSeconds: { min: 2, max: 15, inReferenceTotal: true },
1506
1523
  ratioIgnoredWith: ["firstFrame", "sourceVideo"],
1507
1524
  supports: ["generateAudio", "seed", "lastFrame", "watermark"]
1508
1525
  },
@@ -1536,7 +1553,13 @@ var VIDEO_MODELS = [
1536
1553
  aspectRatios: [...SEEDANCE_RATIOS],
1537
1554
  resolutions: ["480p", "720p", "1080p"],
1538
1555
  durations: { min: 4, max: 30 },
1539
- references: { images: 30, videos: 10, audios: 10 },
1556
+ references: {
1557
+ images: 30,
1558
+ videos: 10,
1559
+ audios: 10,
1560
+ videoSeconds: { min: 2, max: 30, total: 30, editMin: 4 }
1561
+ },
1562
+ sourceVideoSeconds: { min: 2, max: 30, editMin: 4, inReferenceTotal: true },
1540
1563
  ratioIgnoredWith: ["firstFrame", "sourceVideo"],
1541
1564
  supports: ["generateAudio", "seed", "lastFrame", "watermark", "taskType"]
1542
1565
  },
@@ -1748,7 +1771,14 @@ var VIDEO_MODELS = [
1748
1771
  aspectRatios: [...H3_REFERENCE_RATIOS],
1749
1772
  resolutions: ["480p", "768p", "2k", "4k"],
1750
1773
  durations: { min: 5, max: 15 },
1751
- references: { images: 9, videos: 3, audios: 3, total: 12, audioOnly: false },
1774
+ references: {
1775
+ images: 9,
1776
+ videos: 3,
1777
+ audios: 3,
1778
+ total: 12,
1779
+ audioOnly: false,
1780
+ videoSeconds: { min: 2, max: 15, total: 15 }
1781
+ },
1752
1782
  supports: ["seed", "promptExpansion", "safetyChecker"]
1753
1783
  },
1754
1784
  defaults: {
@@ -1779,7 +1809,14 @@ var VIDEO_MODELS = [
1779
1809
  aspectRatios: [...H3_REFERENCE_RATIOS],
1780
1810
  resolutions: ["480p", "768p"],
1781
1811
  durations: { min: 5, max: 15 },
1782
- references: { images: 9, videos: 3, audios: 3, total: 12, audioOnly: false },
1812
+ references: {
1813
+ images: 9,
1814
+ videos: 3,
1815
+ audios: 3,
1816
+ total: 12,
1817
+ audioOnly: false,
1818
+ videoSeconds: { min: 2, max: 15, total: 15 }
1819
+ },
1783
1820
  supports: ["seed", "promptExpansion", "safetyChecker"]
1784
1821
  },
1785
1822
  defaults: {
@@ -1811,6 +1848,7 @@ var VIDEO_MODELS = [
1811
1848
  aspectRatios: [...KLING_RATIOS],
1812
1849
  durations: { min: 3, max: 10 },
1813
1850
  requires: ["sourceVideo"],
1851
+ sourceVideoSeconds: { min: 3, max: 10.05 },
1814
1852
  supports: []
1815
1853
  },
1816
1854
  defaults: { durationSeconds: 5, aspectRatio: "16:9" },
@@ -2167,13 +2205,20 @@ var ProviderOptionValueSchema = import_zod2.z.lazy(
2167
2205
  ])
2168
2206
  );
2169
2207
  var ProviderOptionsSchema = import_zod2.z.record(import_zod2.z.string(), import_zod2.z.record(import_zod2.z.string(), ProviderOptionValueSchema));
2208
+ var VideoSecondsLimitsSchema = import_zod2.z.looseObject({
2209
+ min: import_zod2.z.number().optional(),
2210
+ max: import_zod2.z.number().optional(),
2211
+ total: import_zod2.z.number().optional(),
2212
+ editMin: import_zod2.z.number().optional()
2213
+ });
2170
2214
  var ReferenceLimitsSchema = import_zod2.z.looseObject({
2171
2215
  images: import_zod2.z.number().optional(),
2172
2216
  imagesWithVideo: import_zod2.z.number().optional(),
2173
2217
  videos: import_zod2.z.number().optional(),
2174
2218
  audios: import_zod2.z.number().optional(),
2175
2219
  total: import_zod2.z.number().optional(),
2176
- audioOnly: import_zod2.z.boolean().optional()
2220
+ audioOnly: import_zod2.z.boolean().optional(),
2221
+ videoSeconds: VideoSecondsLimitsSchema.optional()
2177
2222
  });
2178
2223
  var ModelConstraintsSchema = import_zod2.z.looseObject({
2179
2224
  aspectRatios: import_zod2.z.array(AspectRatioSchema).optional(),
@@ -2186,14 +2231,20 @@ var ModelConstraintsSchema = import_zod2.z.looseObject({
2186
2231
  references: ReferenceLimitsSchema.optional(),
2187
2232
  requires: import_zod2.z.array(RequiredInputSchema).optional(),
2188
2233
  supports: import_zod2.z.array(SupportedOptionSchema).optional(),
2189
- ratioIgnoredWith: import_zod2.z.array(import_zod2.z.enum(["firstFrame", "sourceVideo"])).optional()
2234
+ ratioIgnoredWith: import_zod2.z.array(import_zod2.z.enum(["firstFrame", "sourceVideo"])).optional(),
2235
+ sourceVideoSeconds: VideoSecondsLimitsSchema.omit({ total: true }).extend({ inReferenceTotal: import_zod2.z.boolean().optional() }).optional()
2190
2236
  });
2191
2237
  var MediaRefSchema = import_zod2.z.union([
2192
- import_zod2.z.looseObject({ url: import_zod2.z.string(), label: import_zod2.z.string().optional() }),
2238
+ import_zod2.z.looseObject({
2239
+ url: import_zod2.z.string(),
2240
+ label: import_zod2.z.string().optional(),
2241
+ durationSeconds: import_zod2.z.number().optional()
2242
+ }),
2193
2243
  import_zod2.z.looseObject({
2194
2244
  bytes: import_zod2.z.instanceof(Uint8Array),
2195
2245
  mime: import_zod2.z.string(),
2196
- label: import_zod2.z.string().optional()
2246
+ label: import_zod2.z.string().optional(),
2247
+ durationSeconds: import_zod2.z.number().optional()
2197
2248
  })
2198
2249
  ]);
2199
2250
  var ImageSafetySettingSchema = import_zod2.z.looseObject({ category: import_zod2.z.string(), threshold: import_zod2.z.string() });
@@ -2682,6 +2733,49 @@ function trim(field, refs, limit, model, warnings) {
2682
2733
  });
2683
2734
  return refs.slice(0, limit);
2684
2735
  }
2736
+ function knownLength(value) {
2737
+ return value !== void 0 && Number.isFinite(value) ? value : void 0;
2738
+ }
2739
+ function seconds(len, limit) {
2740
+ let digits = 1;
2741
+ while (digits < 3 && Number(len.toFixed(digits)) === limit) digits++;
2742
+ return len.toFixed(digits);
2743
+ }
2744
+ function effectiveMin(limits, taskType) {
2745
+ if (taskType !== "edit" || limits?.editMin === void 0) return limits?.min;
2746
+ return Math.max(limits.min ?? 0, limits.editMin);
2747
+ }
2748
+ function videoLengthViolation(model, input) {
2749
+ const limits = model.constraints.references?.videoSeconds;
2750
+ const refMin = effectiveMin(limits, input.taskType);
2751
+ const lengths = (input.referenceVideos ?? []).map(knownLength);
2752
+ if (limits?.max !== void 0) {
2753
+ const max = limits.max;
2754
+ const i = lengths.findIndex((len) => len !== void 0 && len > max);
2755
+ if (i >= 0)
2756
+ return `${model.label} takes reference videos up to ${String(max)} s each; video ${i + 1} is ${seconds(lengths[i], max)} s.`;
2757
+ }
2758
+ if (refMin !== void 0) {
2759
+ const i = lengths.findIndex((len) => len !== void 0 && len < refMin);
2760
+ if (i >= 0)
2761
+ return `${model.label} takes reference videos of at least ${String(refMin)} s each; video ${i + 1} is ${seconds(lengths[i], refMin)} s.`;
2762
+ }
2763
+ const source = knownLength(input.sourceVideo);
2764
+ const sourceLimits = model.constraints.sourceVideoSeconds;
2765
+ if (limits?.total !== void 0) {
2766
+ const refSum = lengths.reduce((acc, len) => acc + (len ?? 0), 0);
2767
+ const sum = refSum + (sourceLimits?.inReferenceTotal ? source ?? 0 : 0);
2768
+ if (sum > limits.total)
2769
+ return `${model.label} takes reference videos up to ${String(limits.total)} s combined; these add up to ${seconds(sum, limits.total)} s.`;
2770
+ }
2771
+ if (source === void 0) return void 0;
2772
+ const sourceMin = effectiveMin(sourceLimits, input.taskType);
2773
+ if (sourceLimits?.max !== void 0 && source > sourceLimits.max)
2774
+ return `${model.label} takes a source video up to ${String(sourceLimits.max)} s; this one is ${seconds(source, sourceLimits.max)} s.`;
2775
+ if (sourceMin !== void 0 && source < sourceMin)
2776
+ return `${model.label} takes a source video of at least ${String(sourceMin)} s; this one is ${seconds(source, sourceMin)} s.`;
2777
+ return void 0;
2778
+ }
2685
2779
  function resolveImageOptions(model, request, onUnsupported = "error") {
2686
2780
  const warnings = [];
2687
2781
  const c = model.constraints;
@@ -2867,6 +2961,21 @@ function resolveVideoOptions(model, request, onUnsupported = "error") {
2867
2961
  model: model.id
2868
2962
  });
2869
2963
  }
2964
+ const lengthViolation = videoLengthViolation(model, {
2965
+ referenceVideos: videos.map((v) => v.durationSeconds),
2966
+ taskType: merged.taskType,
2967
+ ...merged.sourceVideo?.durationSeconds !== void 0 && {
2968
+ sourceVideo: merged.sourceVideo.durationSeconds
2969
+ }
2970
+ });
2971
+ if (lengthViolation !== void 0) {
2972
+ throw new ModelKitError({
2973
+ code: "unsupported_option",
2974
+ message: lengthViolation,
2975
+ model: model.id,
2976
+ retryable: false
2977
+ });
2978
+ }
2870
2979
  setOrDelete(merged, "referenceImages", images);
2871
2980
  setOrDelete(merged, "referenceVideos", videos);
2872
2981
  setOrDelete(merged, "referenceAudios", audios);