@shop-os/modelkit 0.4.2 → 0.4.4

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,18 @@ header in the release PR.
9
9
 
10
10
  ## [Unreleased]
11
11
 
12
+ ## [0.4.4] - Unreleased
13
+
14
+ ### Added
15
+ - `inputSpec.dimensions` on image rows: `{ field, areas, maxSide?, multipleOf? }` sends one `{ width, height }` object in `field`, computed from the request's aspect ratio and size. `areas` is the target pixel count per size tier; the long side is capped at `maxSide` and both sides round down to `multipleOf`. Nothing is sent when the ratio is missing or `adaptive`, or the size has no area, so the provider default applies (on an edit endpoint, the first reference's size). A spec with `dimensions` cannot also map `aspectRatio` or `size` in `fields`, and `field` cannot be a name another mapping already sends. This lets a fal row whose endpoint only takes `image_size` presets or `{ width, height }` (FLUX 3 Image Klein) offer 2K and 4K as data.
16
+
17
+ Add a row that carries `inputSpec.dimensions` only after every app that will call it runs 0.4.4. An older kit rejects the unknown key, so the row is quarantined there.
18
+
19
+ ## [0.4.3] - Unreleased
20
+
21
+ ### Fixed
22
+ - Gemini Omni edits with no requested duration report the source video's length (`sourceVideo.durationSeconds`) as usage instead of the 10 s text-to-video default. Omni renders an edit at its source's length, also below 3 s (live probe 2026-10-16), so a 2 s edit was billed as 10 s. The fix applies only to callers that send a measured length: without one, or with a length that is not a positive finite number, the 10 s default stays. Extensions are unchanged.
23
+
12
24
  ## [0.4.2] - Unreleased
13
25
 
14
26
  ### Added
package/README.md CHANGED
@@ -224,6 +224,7 @@ last. Video rows use `VIDEO_INPUT_SPEC_KEYS` (`prompt`, `durationSeconds`,
224
224
  with `values`); audio rows use
225
225
  `AUDIO_INPUT_SPEC_KEYS` (`prompt`, `durationSeconds`, `seed`, `negativePrompt`,
226
226
  `references`, `sourceVideo`, `instrumental`). `aspectRatio: "adaptive"` is omitted. A fal image row has no kind and builds its whole input from the spec; the optional `editEndpoint` (fal rows only) is the fal endpoint called instead of `providerModelId` when the request has references. fal rows without an `inputSpec` keep the Muse-shaped request and Ideogram keeps its own path; an `inputSpec` on an Ideogram row replaces that path. `editEndpoint` needs a `references` entry in `fields`. `inputSpec` on fal video rows is not supported.
227
+ `dimensions` (image rows only, from 0.4.4) sends one `{ width, height }` object for a provider that takes pixel sizes instead of a ratio and a tier: `dimensions: { field: "image_size", areas: { "1K": 1048576, "2K": 4194304, "4K": 16777216 }, maxSide: 4096, multipleOf: 16 }`. Each `areas` entry is the target pixel count for that size; the long side is capped at `maxSide`, and both sides round down to `multipleOf`. With no ratio, `adaptive`, or a size with no area, the field is omitted. A spec with `dimensions` maps neither `aspectRatio` nor `size` in `fields`.
227
228
  `arrays` joins several image inputs into one provider array field, in the listed order: `arrays: { images: ["firstFrame", "referenceImages", "lastFrame"] }` (video rows: `firstFrame`, `referenceImages`, `lastFrame`; image rows: `references`). A key is mapped once, in `fields` or in one array, and the field is omitted when none of its inputs is set. A row that maps `sourceVideo` also needs `capabilities.acceptsSourceVideo`; set `capabilities.sourceVideoExcludesFrames` when the provider rejects a source video next to images.
228
229
  `validateInputSpec` is the shared contract the write schema, `dataOnlyCapable`, and
229
230
  the Replicate mapper all check. `dataOnlyCapable(row)` reports whether such a row would
package/dist/catalog.cjs CHANGED
@@ -2563,16 +2563,26 @@ var ArraysSchema = import_zod2.z.record(import_zod2.z.string().min(1), import_zo
2563
2563
  }
2564
2564
  }
2565
2565
  });
2566
+ var DimensionsSchema = import_zod2.z.strictObject({
2567
+ field: import_zod2.z.string().min(1),
2568
+ areas: import_zod2.z.record(import_zod2.z.string(), import_zod2.z.number().int().positive()).refine((a) => Object.keys(a).length > 0, {
2569
+ message: "inputSpec.dimensions.areas must name at least one size"
2570
+ }),
2571
+ maxSide: import_zod2.z.number().int().positive().optional(),
2572
+ multipleOf: import_zod2.z.number().int().positive().optional()
2573
+ });
2566
2574
  var InputSpecSchema = import_zod2.z.strictObject({
2567
2575
  fields: FieldsSchema,
2568
2576
  values: ValuesSchema.optional(),
2569
2577
  static: StaticSchema.optional(),
2570
2578
  arrays: ArraysSchema.optional(),
2571
- editEndpoint: import_zod2.z.string().min(1).optional()
2579
+ editEndpoint: import_zod2.z.string().min(1).optional(),
2580
+ dimensions: DimensionsSchema.optional()
2572
2581
  }).superRefine((spec, ctx) => {
2573
2582
  const mappedNames = /* @__PURE__ */ new Set([
2574
2583
  ...Object.values(spec.fields),
2575
- ...Object.keys(spec.arrays ?? {})
2584
+ ...Object.keys(spec.arrays ?? {}),
2585
+ ...spec.dimensions ? [spec.dimensions.field] : []
2576
2586
  ]);
2577
2587
  for (const key of Object.keys(spec.static ?? {})) {
2578
2588
  if (mappedNames.has(key)) {
@@ -2601,7 +2611,10 @@ function validateInputSpec(raw, row) {
2601
2611
  return {
2602
2612
  ok: false,
2603
2613
  issues: [
2604
- { path: [], message: "inputSpec is only supported on replicate and fal rows" }
2614
+ {
2615
+ path: [],
2616
+ message: "inputSpec is only supported on replicate and fal rows"
2617
+ }
2605
2618
  ]
2606
2619
  };
2607
2620
  }
@@ -2694,6 +2707,29 @@ function validateInputSpec(raw, row) {
2694
2707
  }
2695
2708
  }
2696
2709
  }
2710
+ if (spec.dimensions !== void 0) {
2711
+ if (row.type !== "image") {
2712
+ issues.push({
2713
+ path: ["dimensions"],
2714
+ message: "inputSpec.dimensions is only supported on image rows"
2715
+ });
2716
+ }
2717
+ for (const key of ["aspectRatio", "size"]) {
2718
+ if (key in spec.fields) {
2719
+ issues.push({
2720
+ path: ["fields", key],
2721
+ message: `inputSpec.fields "${key}" cannot be set with inputSpec.dimensions, which sends it`
2722
+ });
2723
+ }
2724
+ }
2725
+ const owner = seenNames.get(spec.dimensions.field);
2726
+ if (owner !== void 0 || spec.dimensions.field in (spec.arrays ?? {})) {
2727
+ issues.push({
2728
+ path: ["dimensions", "field"],
2729
+ message: `inputSpec.dimensions.field "${spec.dimensions.field}" is already a mapped provider field`
2730
+ });
2731
+ }
2732
+ }
2697
2733
  const supports = row.constraints.supports ?? [];
2698
2734
  for (const key of INPUT_SPEC_SUPPORTS_KEYS) {
2699
2735
  if (supports.includes(key)) continue;