@shop-os/modelkit 0.4.3 → 0.4.5

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.cjs CHANGED
@@ -2617,16 +2617,26 @@ var ArraysSchema = import_zod2.z.record(import_zod2.z.string().min(1), import_zo
2617
2617
  }
2618
2618
  }
2619
2619
  });
2620
+ var DimensionsSchema = import_zod2.z.strictObject({
2621
+ field: import_zod2.z.string().min(1),
2622
+ areas: import_zod2.z.record(import_zod2.z.string(), import_zod2.z.number().int().positive()).refine((a) => Object.keys(a).length > 0, {
2623
+ message: "inputSpec.dimensions.areas must name at least one size"
2624
+ }),
2625
+ maxSide: import_zod2.z.number().int().positive().optional(),
2626
+ multipleOf: import_zod2.z.number().int().positive().optional()
2627
+ });
2620
2628
  var InputSpecSchema = import_zod2.z.strictObject({
2621
2629
  fields: FieldsSchema,
2622
2630
  values: ValuesSchema.optional(),
2623
2631
  static: StaticSchema.optional(),
2624
2632
  arrays: ArraysSchema.optional(),
2625
- editEndpoint: import_zod2.z.string().min(1).optional()
2633
+ editEndpoint: import_zod2.z.string().min(1).optional(),
2634
+ dimensions: DimensionsSchema.optional()
2626
2635
  }).superRefine((spec, ctx) => {
2627
2636
  const mappedNames = /* @__PURE__ */ new Set([
2628
2637
  ...Object.values(spec.fields),
2629
- ...Object.keys(spec.arrays ?? {})
2638
+ ...Object.keys(spec.arrays ?? {}),
2639
+ ...spec.dimensions ? [spec.dimensions.field] : []
2630
2640
  ]);
2631
2641
  for (const key of Object.keys(spec.static ?? {})) {
2632
2642
  if (mappedNames.has(key)) {
@@ -2661,7 +2671,10 @@ function validateInputSpec(raw, row) {
2661
2671
  return {
2662
2672
  ok: false,
2663
2673
  issues: [
2664
- { path: [], message: "inputSpec is only supported on replicate and fal rows" }
2674
+ {
2675
+ path: [],
2676
+ message: "inputSpec is only supported on replicate and fal rows"
2677
+ }
2665
2678
  ]
2666
2679
  };
2667
2680
  }
@@ -2754,6 +2767,29 @@ function validateInputSpec(raw, row) {
2754
2767
  }
2755
2768
  }
2756
2769
  }
2770
+ if (spec.dimensions !== void 0) {
2771
+ if (row.type !== "image") {
2772
+ issues.push({
2773
+ path: ["dimensions"],
2774
+ message: "inputSpec.dimensions is only supported on image rows"
2775
+ });
2776
+ }
2777
+ for (const key of ["aspectRatio", "size"]) {
2778
+ if (key in spec.fields) {
2779
+ issues.push({
2780
+ path: ["fields", key],
2781
+ message: `inputSpec.fields "${key}" cannot be set with inputSpec.dimensions, which sends it`
2782
+ });
2783
+ }
2784
+ }
2785
+ const owner = seenNames.get(spec.dimensions.field);
2786
+ if (owner !== void 0 || spec.dimensions.field in (spec.arrays ?? {})) {
2787
+ issues.push({
2788
+ path: ["dimensions", "field"],
2789
+ message: `inputSpec.dimensions.field "${spec.dimensions.field}" is already a mapped provider field`
2790
+ });
2791
+ }
2792
+ }
2757
2793
  const supports = row.constraints.supports ?? [];
2758
2794
  for (const key of INPUT_SPEC_SUPPORTS_KEYS) {
2759
2795
  if (supports.includes(key)) continue;
@@ -4215,6 +4251,20 @@ var import_zod11 = require("zod");
4215
4251
 
4216
4252
  // src/catalog/video-tools-spec.ts
4217
4253
  var import_zod10 = require("zod");
4254
+
4255
+ // src/video-length.ts
4256
+ function tenths(seconds2) {
4257
+ return Math.round(seconds2 * 10) / 10;
4258
+ }
4259
+ function overMax(seconds2, max) {
4260
+ return seconds2 > max && tenths(seconds2) > max;
4261
+ }
4262
+ function overTotal(lengths, total) {
4263
+ const sum = (values) => values.reduce((acc, len) => acc + len, 0);
4264
+ return sum(lengths) > total && tenths(sum(lengths.map(tenths))) > total;
4265
+ }
4266
+
4267
+ // src/catalog/video-tools-spec.ts
4218
4268
  var RangeSchema2 = import_zod10.z.object({ min: import_zod10.z.number(), max: import_zod10.z.number() });
4219
4269
  var ToolSchema = import_zod10.z.object({
4220
4270
  kind: import_zod10.z.enum(["upscale", "interpolate", "hdr"]),
@@ -4258,7 +4308,7 @@ function planVideoTool(row, req) {
4258
4308
  return fail("The source video's width, height and duration are required");
4259
4309
  }
4260
4310
  const maxSeconds = row.constraints.sourceVideoSeconds?.max;
4261
- if (maxSeconds !== void 0 && durationSeconds > maxSeconds) {
4311
+ if (maxSeconds !== void 0 && overMax(durationSeconds, maxSeconds)) {
4262
4312
  return fail(`${row.label} takes source videos up to ${maxSeconds} seconds.`);
4263
4313
  }
4264
4314
  const base = {
@@ -4570,11 +4620,37 @@ function applyInputSpec(spec, req, refToUrl2, opts) {
4570
4620
  }
4571
4621
  if (urls.length > 0) out[providerField] = urls;
4572
4622
  }
4623
+ if (spec.dimensions !== void 0) {
4624
+ const dims = dimensionsFor(spec.dimensions, req);
4625
+ if (dims !== void 0) out[spec.dimensions.field] = dims;
4626
+ }
4573
4627
  for (const [key, value] of Object.entries(spec.static ?? {})) {
4574
4628
  out[key] = value;
4575
4629
  }
4576
4630
  return out;
4577
4631
  }
4632
+ function dimensionsFor(dims, req) {
4633
+ const ratio = req.aspectRatio;
4634
+ const size = req.size;
4635
+ if (ratio === void 0 || ratio === "adaptive" || size === void 0) return void 0;
4636
+ const area = dims.areas[size];
4637
+ const match = /^(\d+):(\d+)$/.exec(ratio);
4638
+ if (area === void 0 || match === null) return void 0;
4639
+ const r = Number(match[1]) / Number(match[2]);
4640
+ let width = Math.sqrt(area * r);
4641
+ let height = width / r;
4642
+ const longest = Math.max(width, height);
4643
+ if (dims.maxSide !== void 0 && longest > dims.maxSide) {
4644
+ const scale = dims.maxSide / longest;
4645
+ width *= scale;
4646
+ height *= scale;
4647
+ }
4648
+ const step = dims.multipleOf ?? 1;
4649
+ return {
4650
+ width: Math.max(step, Math.floor(width / step) * step),
4651
+ height: Math.max(step, Math.floor(height / step) * step)
4652
+ };
4653
+ }
4578
4654
  function resolveRowInputSpec(req, ctx) {
4579
4655
  const raw = ctx.model.providerOptions?.["modelkit"]?.["inputSpec"];
4580
4656
  if (raw === void 0) return { kind: "absent" };
@@ -8634,7 +8710,7 @@ function videoLengthViolation(model, input) {
8634
8710
  const lengths = (input.referenceVideos ?? []).map(knownLength);
8635
8711
  if (limits?.max !== void 0) {
8636
8712
  const max = limits.max;
8637
- const i = lengths.findIndex((len) => len !== void 0 && len > max);
8713
+ const i = lengths.findIndex((len) => len !== void 0 && overMax(len, max));
8638
8714
  if (i >= 0)
8639
8715
  return `${model.label} takes reference videos up to ${String(max)} s each; video ${i + 1} is ${seconds(lengths[i], max)} s.`;
8640
8716
  }
@@ -8646,14 +8722,17 @@ function videoLengthViolation(model, input) {
8646
8722
  const source = knownLength(input.sourceVideo);
8647
8723
  const sourceLimits = model.constraints.sourceVideoSeconds;
8648
8724
  if (limits?.total !== void 0) {
8649
- const refSum = lengths.reduce((acc, len) => acc + (len ?? 0), 0);
8650
- const sum = refSum + (sourceLimits?.inReferenceTotal ? source ?? 0 : 0);
8651
- if (sum > limits.total)
8725
+ const counted = [
8726
+ ...lengths.filter((len) => len !== void 0),
8727
+ ...sourceLimits?.inReferenceTotal && source !== void 0 ? [source] : []
8728
+ ];
8729
+ const sum = counted.reduce((acc, len) => acc + len, 0);
8730
+ if (overTotal(counted, limits.total))
8652
8731
  return `${model.label} takes reference videos up to ${String(limits.total)} s combined; these add up to ${seconds(sum, limits.total)} s.`;
8653
8732
  }
8654
8733
  if (source === void 0) return void 0;
8655
8734
  const sourceMin = effectiveMin(sourceLimits, input.taskType);
8656
- if (sourceLimits?.max !== void 0 && source > sourceLimits.max)
8735
+ if (sourceLimits?.max !== void 0 && overMax(source, sourceLimits.max))
8657
8736
  return `${model.label} takes a source video up to ${String(sourceLimits.max)} s; this one is ${seconds(source, sourceLimits.max)} s.`;
8658
8737
  if (sourceMin !== void 0 && source < sourceMin)
8659
8738
  return `${model.label} takes a source video of at least ${String(sourceMin)} s; this one is ${seconds(source, sourceMin)} s.`;