@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/CHANGELOG.md +12 -0
- package/README.md +1 -0
- package/dist/catalog.cjs +62 -9
- package/dist/catalog.cjs.map +1 -1
- package/dist/catalog.js +62 -9
- package/dist/catalog.js.map +1 -1
- package/dist/index.cjs +88 -9
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +19 -1
- package/dist/index.d.ts +19 -1
- package/dist/index.js +88 -9
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -9,6 +9,18 @@ header in the release PR.
|
|
|
9
9
|
|
|
10
10
|
## [Unreleased]
|
|
11
11
|
|
|
12
|
+
## [0.4.5] - Unreleased
|
|
13
|
+
|
|
14
|
+
### Fixed
|
|
15
|
+
- Video length maximums and combined totals forgive ffprobe's container over-read. ffprobe reads a clip a few hundredths over its nominal length (a 10 s clip reads 10.04 s), so a clip at the limit was refused. A length is now over a maximum only when it and its tenth are both over: a source that reads 10.04 s passes Gemini Omni's 10 s cap, one that reads 10.05 s or more is still refused, and a limit that is not a whole tenth (Kling O1's 10.05 s) still takes a clip at it. A combined total adds each clip's tenth, so three 5.04 s references fit a 15 s total. The rule is never stricter than the old raw comparison. It applies to the reference maximum and total and the source maximum in `videoLengthViolation`, and to the source cap in `planVideoTool`. Minimums stay raw: the over-read makes a clip that reads short shorter still, so 3.96 s is still under a 4 s minimum.
|
|
16
|
+
|
|
17
|
+
## [0.4.4] - Unreleased
|
|
18
|
+
|
|
19
|
+
### Added
|
|
20
|
+
- `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.
|
|
21
|
+
|
|
22
|
+
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.
|
|
23
|
+
|
|
12
24
|
## [0.4.3] - Unreleased
|
|
13
25
|
|
|
14
26
|
### Fixed
|
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
|
-
{
|
|
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;
|
|
@@ -3288,6 +3324,20 @@ function resetLiveCatalog() {
|
|
|
3288
3324
|
|
|
3289
3325
|
// src/catalog/video-tools-spec.ts
|
|
3290
3326
|
var import_zod6 = require("zod");
|
|
3327
|
+
|
|
3328
|
+
// src/video-length.ts
|
|
3329
|
+
function tenths(seconds2) {
|
|
3330
|
+
return Math.round(seconds2 * 10) / 10;
|
|
3331
|
+
}
|
|
3332
|
+
function overMax(seconds2, max) {
|
|
3333
|
+
return seconds2 > max && tenths(seconds2) > max;
|
|
3334
|
+
}
|
|
3335
|
+
function overTotal(lengths, total) {
|
|
3336
|
+
const sum = (values) => values.reduce((acc, len) => acc + len, 0);
|
|
3337
|
+
return sum(lengths) > total && tenths(sum(lengths.map(tenths))) > total;
|
|
3338
|
+
}
|
|
3339
|
+
|
|
3340
|
+
// src/catalog/video-tools-spec.ts
|
|
3291
3341
|
var RangeSchema2 = import_zod6.z.object({ min: import_zod6.z.number(), max: import_zod6.z.number() });
|
|
3292
3342
|
var ToolSchema = import_zod6.z.object({
|
|
3293
3343
|
kind: import_zod6.z.enum(["upscale", "interpolate", "hdr"]),
|
|
@@ -3331,7 +3381,7 @@ function planVideoTool(row, req) {
|
|
|
3331
3381
|
return fail("The source video's width, height and duration are required");
|
|
3332
3382
|
}
|
|
3333
3383
|
const maxSeconds = row.constraints.sourceVideoSeconds?.max;
|
|
3334
|
-
if (maxSeconds !== void 0 && durationSeconds
|
|
3384
|
+
if (maxSeconds !== void 0 && overMax(durationSeconds, maxSeconds)) {
|
|
3335
3385
|
return fail(`${row.label} takes source videos up to ${maxSeconds} seconds.`);
|
|
3336
3386
|
}
|
|
3337
3387
|
const base = {
|
|
@@ -3528,7 +3578,7 @@ function videoLengthViolation(model, input) {
|
|
|
3528
3578
|
const lengths = (input.referenceVideos ?? []).map(knownLength);
|
|
3529
3579
|
if (limits?.max !== void 0) {
|
|
3530
3580
|
const max = limits.max;
|
|
3531
|
-
const i = lengths.findIndex((len) => len !== void 0 && len
|
|
3581
|
+
const i = lengths.findIndex((len) => len !== void 0 && overMax(len, max));
|
|
3532
3582
|
if (i >= 0)
|
|
3533
3583
|
return `${model.label} takes reference videos up to ${String(max)} s each; video ${i + 1} is ${seconds(lengths[i], max)} s.`;
|
|
3534
3584
|
}
|
|
@@ -3540,14 +3590,17 @@ function videoLengthViolation(model, input) {
|
|
|
3540
3590
|
const source = knownLength(input.sourceVideo);
|
|
3541
3591
|
const sourceLimits = model.constraints.sourceVideoSeconds;
|
|
3542
3592
|
if (limits?.total !== void 0) {
|
|
3543
|
-
const
|
|
3544
|
-
|
|
3545
|
-
|
|
3593
|
+
const counted = [
|
|
3594
|
+
...lengths.filter((len) => len !== void 0),
|
|
3595
|
+
...sourceLimits?.inReferenceTotal && source !== void 0 ? [source] : []
|
|
3596
|
+
];
|
|
3597
|
+
const sum = counted.reduce((acc, len) => acc + len, 0);
|
|
3598
|
+
if (overTotal(counted, limits.total))
|
|
3546
3599
|
return `${model.label} takes reference videos up to ${String(limits.total)} s combined; these add up to ${seconds(sum, limits.total)} s.`;
|
|
3547
3600
|
}
|
|
3548
3601
|
if (source === void 0) return void 0;
|
|
3549
3602
|
const sourceMin = effectiveMin(sourceLimits, input.taskType);
|
|
3550
|
-
if (sourceLimits?.max !== void 0 && source
|
|
3603
|
+
if (sourceLimits?.max !== void 0 && overMax(source, sourceLimits.max))
|
|
3551
3604
|
return `${model.label} takes a source video up to ${String(sourceLimits.max)} s; this one is ${seconds(source, sourceLimits.max)} s.`;
|
|
3552
3605
|
if (sourceMin !== void 0 && source < sourceMin)
|
|
3553
3606
|
return `${model.label} takes a source video of at least ${String(sourceMin)} s; this one is ${seconds(source, sourceMin)} s.`;
|