@shop-os/modelkit 0.13.0 → 0.15.0

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,26 @@ header in the release PR.
9
9
 
10
10
  ## [Unreleased]
11
11
 
12
+ ## [0.15.0] - Unreleased
13
+
14
+ Reference and source videos have a minimum frame size, checked before the provider call (ENG-3453).
15
+
16
+ ### Added
17
+ - `references.videoPixels` and `sourceVideoPixels` constraints (`{ minWidth?, minHeight? }`, `VideoPixelLimits`): the smallest frame a row takes for a reference or source video. Kits before 0.15.0 ignore them (the constraint objects are loose).
18
+ - `videoSizeViolation(model, { referenceVideos?, sourceVideo? })` beside `videoLengthViolation`: the first undersized video as a user-facing message ("Kling 4.0 takes reference videos at least 700 px wide; video 1 is 576 px."), from measured sizes. An unmeasured video passes.
19
+ - Limits from fal's schemas: `kling-4.0` reference videos and `kling-4.0-edit` source 700 px, `kling-o3-edit` source 720 px, `kling-3.0-motion-control-*` motion clip 340 px (width and height each). The stored `model_catalog` rows need the same values (pro-admin) for runtime checks to see them.
20
+
21
+ ## [0.14.0] - Unreleased
22
+
23
+ Video Depth Anything on fal, as a Creative Studio video tool.
24
+
25
+ ### Added
26
+ - Video-tool kind `depth`: a depth map of the source clip at its frame rate and length, H.264, billed per source second at the output tier. `planVideoTool` needs the source's measured frame rate when the row has `maxOutputFrames` (fal keeps the source rate and drops frames past the limit), refuses a clip past it, and shrinks the frame to `maxOutputShortEdge`.
27
+ - `depth-anything-video` (Video Depth Anything) video-tool row on the fal adapter (`fal-ai/depth-anything-video`), listed on `cowork`, sources up to 100 s. The adapter always sends `model: "VDA-Small"` and `colormap: "grayscale"` (Small is the only Apache-2.0 size; fal defaults to the CC-BY-NC Large). fal caps the output at a 1080 px short edge and 2400 frames and bills $0.04 per second. The `credit_pricing_video` rows ship in the consumer bump.
28
+
29
+ ### Changed
30
+ - `submitFalVideoTool` sends `output_format` only for `hdr` rows; a tool kind with no fields of its own gets none.
31
+
12
32
  ## [0.13.0] - Unreleased
13
33
 
14
34
  The fal and Luma keys have one env name each; every provider key now has exactly one name.
package/dist/catalog.cjs CHANGED
@@ -79,6 +79,7 @@ __export(catalog_entry_exports, {
79
79
  sortCatalogRows: () => sortCatalogRows,
80
80
  takesTargetAspectRatio: () => takesTargetAspectRatio,
81
81
  videoLengthViolation: () => videoLengthViolation,
82
+ videoSizeViolation: () => videoSizeViolation,
82
83
  videoToolConstraints: () => videoToolConstraints
83
84
  });
84
85
  module.exports = __toCommonJS(catalog_entry_exports);
@@ -5167,6 +5168,7 @@ function klingMotionControl(tier) {
5167
5168
  videos: 1,
5168
5169
  audios: 0,
5169
5170
  videoSeconds: { min: 3, max: 30.05 },
5171
+ videoPixels: { minWidth: 340, minHeight: 340 },
5170
5172
  min: { images: 1, videos: 1 }
5171
5173
  },
5172
5174
  requires: ["references"],
@@ -5233,6 +5235,7 @@ var VIDEO_FAMILY_MODELS = [
5233
5235
  references: { ...NO_REFERENCES, images: 4 },
5234
5236
  requires: ["sourceVideo"],
5235
5237
  sourceVideoSeconds: { min: 3, max: 15.05 },
5238
+ sourceVideoPixels: { minWidth: 720, minHeight: 720 },
5236
5239
  // generateAudio keeps (true) or drops (false) the source soundtrack.
5237
5240
  supports: ["generateAudio"],
5238
5241
  prompt: KLING_PROMPT,
@@ -5273,7 +5276,8 @@ var VIDEO_FAMILY_MODELS = [
5273
5276
  ...NO_REFERENCES,
5274
5277
  images: 10,
5275
5278
  videos: 5,
5276
- videoSeconds: { min: 3, max: 30.05, total: 30 }
5279
+ videoSeconds: { min: 3, max: 30.05, total: 30 },
5280
+ videoPixels: { minWidth: 700, minHeight: 700 }
5277
5281
  },
5278
5282
  supports: ["lastFrame"],
5279
5283
  prompt: KLING_V4_PROMPT,
@@ -5307,6 +5311,7 @@ var VIDEO_FAMILY_MODELS = [
5307
5311
  references: { ...NO_REFERENCES, images: 10 },
5308
5312
  requires: ["sourceVideo"],
5309
5313
  sourceVideoSeconds: { min: 3, max: 30.05 },
5314
+ sourceVideoPixels: { minWidth: 700, minHeight: 700 },
5310
5315
  supports: [],
5311
5316
  prompt: KLING_V4_PROMPT,
5312
5317
  audio: KLING_V4_AUDIO
@@ -6643,6 +6648,33 @@ var VIDEO_TOOL_MODELS = [
6643
6648
  description: "Topaz Hyperion 2.5 SDR to HDR: a 10-bit HDR master in H.265 (HDR10) or ProRes 422 HQ, at the source's size and frame rate."
6644
6649
  }
6645
6650
  },
6651
+ {
6652
+ // https://fal.ai/models/fal-ai/depth-anything-video/api: greyscale depth (white = near)
6653
+ // at the source's frame rate, short edge capped at 1080 px (a 2160x3840 source came
6654
+ // back 1080x1920), at most 2400 frames; one fal price of $0.04 per second for every
6655
+ // model size. The adapter always sends VDA-Small, the only Apache-2.0 size. 100 s is
6656
+ // 2400 frames at 24 fps; the frame check refuses faster clips sooner.
6657
+ id: "depth-anything-video",
6658
+ aliases: [],
6659
+ label: "Video Depth Anything",
6660
+ type: "video-tool",
6661
+ adapter: "fal",
6662
+ providerModelId: "fal-ai/depth-anything-video",
6663
+ lifecycle: "active",
6664
+ exposure: ["cowork"],
6665
+ constraints: {
6666
+ supports: [],
6667
+ sourceVideoSeconds: { max: 100 },
6668
+ tool: { kind: "depth", maxOutputShortEdge: 1080, maxOutputFrames: 2400 }
6669
+ },
6670
+ defaults: {},
6671
+ usage: SECONDS_USAGE,
6672
+ pricingKind: "video_per_second",
6673
+ display: {
6674
+ provider: "depth-anything",
6675
+ description: "Video Depth Anything: a greyscale depth map of the clip (near is white), frame for frame, for motion and camera control."
6676
+ }
6677
+ },
6646
6678
  {
6647
6679
  id: "flux-video-upscale-precise",
6648
6680
  aliases: [],
@@ -7830,6 +7862,10 @@ var VideoSecondsLimitsSchema = import_zod3.z.looseObject({
7830
7862
  total: import_zod3.z.number().optional(),
7831
7863
  editMin: import_zod3.z.number().optional()
7832
7864
  });
7865
+ var VideoPixelLimitsSchema = import_zod3.z.looseObject({
7866
+ minWidth: import_zod3.z.number().int().positive().optional(),
7867
+ minHeight: import_zod3.z.number().int().positive().optional()
7868
+ });
7833
7869
  var ReferenceLimitsSchema = import_zod3.z.looseObject({
7834
7870
  images: import_zod3.z.number().optional(),
7835
7871
  imagesWithVideo: import_zod3.z.number().optional(),
@@ -7838,6 +7874,7 @@ var ReferenceLimitsSchema = import_zod3.z.looseObject({
7838
7874
  total: import_zod3.z.number().optional(),
7839
7875
  audioOnly: import_zod3.z.boolean().optional(),
7840
7876
  videoSeconds: VideoSecondsLimitsSchema.optional(),
7877
+ videoPixels: VideoPixelLimitsSchema.optional(),
7841
7878
  audioSeconds: import_zod3.z.looseObject({
7842
7879
  min: import_zod3.z.number().optional(),
7843
7880
  max: import_zod3.z.number().optional(),
@@ -7866,7 +7903,7 @@ var LanguageOptionSchema = import_zod3.z.looseObject({
7866
7903
  name: import_zod3.z.string().min(1)
7867
7904
  });
7868
7905
  var VideoToolConstraintsSchema = import_zod3.z.looseObject({
7869
- kind: import_zod3.z.enum(["upscale", "interpolate", "hdr", "bg-remove", "subtitles"]),
7906
+ kind: import_zod3.z.enum(["upscale", "interpolate", "hdr", "bg-remove", "subtitles", "depth"]),
7870
7907
  upscaleFactor: RangeSchema.optional(),
7871
7908
  maxOutputLongEdge: import_zod3.z.number().positive().optional(),
7872
7909
  prompt: import_zod3.z.boolean().optional(),
@@ -7927,6 +7964,7 @@ var ModelConstraintsSchema = import_zod3.z.looseObject({
7927
7964
  }).optional(),
7928
7965
  languages: import_zod3.z.array(LanguageOptionSchema).optional(),
7929
7966
  outputFormats: import_zod3.z.array(import_zod3.z.string().min(1)).min(1).optional(),
7967
+ sourceVideoPixels: VideoPixelLimitsSchema.optional(),
7930
7968
  sourceVideoSeconds: VideoSecondsLimitsSchema.omit({ total: true }).extend({ inReferenceTotal: import_zod3.z.boolean().optional() }).optional(),
7931
7969
  // segmentSeconds/maxTotalSeconds stay required: a kit before 0.3.0 drops a row whose
7932
7970
  // extension lacks them, so every extension block keeps them.
@@ -8472,7 +8510,7 @@ function subtitleProblem(label, tool, options, resolution, durationSeconds) {
8472
8510
  // src/catalog/video-tools-spec.ts
8473
8511
  var RangeSchema2 = import_zod6.z.object({ min: import_zod6.z.number(), max: import_zod6.z.number() });
8474
8512
  var ToolSchema = import_zod6.z.object({
8475
- kind: import_zod6.z.enum(["upscale", "interpolate", "hdr", "bg-remove", "subtitles"]),
8513
+ kind: import_zod6.z.enum(["upscale", "interpolate", "hdr", "bg-remove", "subtitles", "depth"]),
8476
8514
  upscaleFactor: RangeSchema2.optional(),
8477
8515
  maxOutputLongEdge: import_zod6.z.number().positive().optional(),
8478
8516
  prompt: import_zod6.z.boolean().optional(),
@@ -8610,6 +8648,25 @@ function planVideoTool(row2, req) {
8610
8648
  }
8611
8649
  };
8612
8650
  }
8651
+ if (tool.kind === "depth") {
8652
+ const limit = tool.maxOutputFrames;
8653
+ if (limit !== void 0) {
8654
+ if (fps === void 0) return fail("The source video's frame rate is required");
8655
+ if (durationSeconds * fps > limit) {
8656
+ return fail(`${row2.label} renders up to ${limit} frames (${seconds(limit, fps)}).`);
8657
+ }
8658
+ }
8659
+ const out2 = shrinkToShortEdge(width, height, tool.maxOutputShortEdge);
8660
+ return {
8661
+ ok: true,
8662
+ output: {
8663
+ ...base,
8664
+ ...out2,
8665
+ resolution: resolutionTier(out2.width, out2.height),
8666
+ format: "h264"
8667
+ }
8668
+ };
8669
+ }
8613
8670
  if (tool.kind === "subtitles") {
8614
8671
  const resolution = resolutionTier(width, height);
8615
8672
  const problem = subtitleProblem(
@@ -9432,6 +9489,28 @@ function videoLengthViolation(model, input) {
9432
9489
  return `${model.label} takes a source video of at least ${String(sourceMin)} s; this one is ${seconds2(source, sourceMin)} s.`;
9433
9490
  return void 0;
9434
9491
  }
9492
+ function undersizedSide(frame, limits) {
9493
+ if (!frame || !limits) return void 0;
9494
+ const { width, height } = frame;
9495
+ if (limits.minWidth !== void 0 && width !== void 0 && width > 0 && width < limits.minWidth)
9496
+ return { side: "wide", min: limits.minWidth, actual: width };
9497
+ if (limits.minHeight !== void 0 && height !== void 0 && height > 0 && height < limits.minHeight)
9498
+ return { side: "tall", min: limits.minHeight, actual: height };
9499
+ return void 0;
9500
+ }
9501
+ function videoSizeViolation(model, input) {
9502
+ const refLimits = model.constraints.references?.videoPixels;
9503
+ const refs = input.referenceVideos ?? [];
9504
+ for (let i = 0; i < refs.length; i++) {
9505
+ const under2 = undersizedSide(refs[i], refLimits);
9506
+ if (under2)
9507
+ return `${model.label} takes reference videos at least ${String(under2.min)} px ${under2.side}; video ${i + 1} is ${String(under2.actual)} px.`;
9508
+ }
9509
+ const under = undersizedSide(input.sourceVideo, model.constraints.sourceVideoPixels);
9510
+ if (under)
9511
+ return `${model.label} takes a source video at least ${String(under.min)} px ${under.side}; this one is ${String(under.actual)} px.`;
9512
+ return void 0;
9513
+ }
9435
9514
  function resolveImageOptions(model, request, onUnsupported = "error") {
9436
9515
  const warnings = [];
9437
9516
  const c = model.constraints;