@shop-os/modelkit 0.2.13 → 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 +16 -0
- package/README.md +31 -2
- package/dist/catalog.cjs +124 -13
- package/dist/catalog.cjs.map +1 -1
- package/dist/catalog.d.cts +59 -1
- package/dist/catalog.d.ts +59 -1
- package/dist/catalog.js +124 -13
- package/dist/catalog.js.map +1 -1
- package/dist/index.cjs +497 -176
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +59 -1
- package/dist/index.d.ts +59 -1
- package/dist/index.js +496 -176
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -9,6 +9,22 @@ 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
|
+
|
|
22
|
+
## [0.2.14] - Unreleased
|
|
23
|
+
|
|
24
|
+
- Keep AI SDK 6 and its compatible provider packages, including Bedrock and usage tracking.
|
|
25
|
+
- Add per-call media retry limits so durable workflow controllers can own retries without duplicate provider submissions.
|
|
26
|
+
|
|
27
|
+
|
|
12
28
|
## [0.2.13] - 2026-09-18
|
|
13
29
|
|
|
14
30
|
### Added
|
package/README.md
CHANGED
|
@@ -110,11 +110,40 @@ for a model the catalog does not carry yet; the next step is a catalog row.
|
|
|
110
110
|
| `experimental_generateSpeech`, `transcribe`, `embed`, `embedMany`, `rerank`, agents, UI streams | Re-exported from `ai` unchanged; the catalog has no rows for them. |
|
|
111
111
|
|
|
112
112
|
An optional second `CallContext` argument selects enterprise keys, adds cancellation
|
|
113
|
-
(combined with `abortSignal`), and sets `onUnsupported`. Package-level functions read
|
|
113
|
+
(combined with `abortSignal`), sets media `maxRetries`, and sets `onUnsupported`. Package-level functions read
|
|
114
114
|
keys from the environment; a configured kit adds routing, catalog overrides, custom
|
|
115
115
|
fetch, provider endpoint overrides (`endpoints.ark` for the Ark base url), logging, and
|
|
116
116
|
the `onUsage` sink.
|
|
117
117
|
|
|
118
|
+
### Durable media execution
|
|
119
|
+
|
|
120
|
+
Goal Mode owns technical recovery. Every image, video, audio, and media-preparation
|
|
121
|
+
call in that host must pass `{ maxRetries: 0 }` as the second argument:
|
|
122
|
+
|
|
123
|
+
```ts
|
|
124
|
+
const result = await kit.generateImage(
|
|
125
|
+
{ model: "gpt-image-2", prompt: "A product photo" },
|
|
126
|
+
{ maxRetries: 0, signal },
|
|
127
|
+
);
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
`maxRetries` counts retries after the first request. Zero disables adapter and SDK
|
|
131
|
+
retries, failed status-read retries, and Google's Files-to-inline fallback. Successful
|
|
132
|
+
pending-job responses still poll the same job. Unset retains existing adapter defaults.
|
|
133
|
+
An explicit nonzero limit allows bounded submission retries; a later read failure never
|
|
134
|
+
resubmits an already-created fal job. Text calls instead use AI SDK's first-argument
|
|
135
|
+
`maxRetries: 0` option.
|
|
136
|
+
|
|
137
|
+
Replicate 1.x has no retry switch. fal 1.x queue methods override its client retry
|
|
138
|
+
setting. With an explicit limit, ModelKit uses their REST prediction/queue endpoints
|
|
139
|
+
and owns retries itself; the usual SDK paths remain when the option is unset. Google
|
|
140
|
+
models and Google Interactions have separate retry controls, both disabled beneath
|
|
141
|
+
ModelKit's policy. SDK image/video model instances also receive the explicit limit.
|
|
142
|
+
The fal Cloudflare passthrough sets `cf-aig-max-attempts: 1` with an explicit limit,
|
|
143
|
+
using [Cloudflare request handling](https://developers.cloudflare.com/ai-gateway/configuration/request-handling/)
|
|
144
|
+
headers so gateway retries do not multiply local attempts. Custom adapters and custom
|
|
145
|
+
provider instances must honor their own retry contract.
|
|
146
|
+
|
|
118
147
|
`providerOptions` on `generateImage`/`generateVideo` reach the provider only for
|
|
119
148
|
pass-through instances; catalog rows carry their own and the caller's are dropped
|
|
120
149
|
with an `option_ignored` warning.
|
|
@@ -213,7 +242,7 @@ const defaultKit = createDefaultModelKit({ keys: { pro: keysFromEnv(process.env)
|
|
|
213
242
|
| Options | Image tiers are `512` (Gemini 3.1 Flash only), `1K`, `2K`, `4K`. `resolveOptions` fills catalog defaults, errors on an unsupported aspect ratio, size, resolution or duration (`onUnsupported: "coerce"` snaps to the nearest supported value with a warning instead; since 0.1.6 ratio and duration honour this too, so pass `"coerce"` where the old code snapped silently), trims references (audio first, then video), and drops options the model ignores with a warning. Audio requests go through the same resolver and take `abortSignal` (a row without `supports` supports nothing). `aspectRatio: "adaptive"` means "send no ratio": it is never coerced or replaced by the row default, and Gemini, Muse, Kling and Kling O1 omit the field so the model infers it from the references. Every coercion is also logged once per call through `logger.warn`. Gemini image `safetySettings` and `temperature` are sent only when the caller passes them. A reference with a `label` gets a text part with that label right before the image on Gemini image and Omni ("Image 2:", "Here is the brand logo:"). Topaz takes `upscale` (`enhanceModel`, `subjectDetection`, `faceEnhancement` and its creativity/strength) next to `scaleFactor`/`outputFormat`; Seedance takes `watermark`; MiniMax takes `lastFrame` and `safetyChecker`; Seedream 4.5 goes to `4K` and `9:21`. |
|
|
214
243
|
| Keys | Enterprise keys win only when every key the adapter (and its route, e.g. the cloudflare token for a vendor that really goes through the gateway) needs is set on the enterprise tier; a fallback to pro keys is logged once per call. |
|
|
215
244
|
| Routing | Cloudflare AI Gateway for text only. Never for Gemini images or Omni. fal passthrough for geo-blocked regions; the fal direct route submits to the queue and polls, so `job.result({ timeoutMs, signal })` is live. |
|
|
216
|
-
| Retry | One classifier, one policy. Never on content blocks, aborts, or 4xx except 429; SDKs with their own retry (Replicate) run under a single kit attempt; a 429 on a status poll keeps polling.
|
|
245
|
+
| Retry | One classifier, one policy. Never on content blocks, aborts, or 4xx except 429; SDKs with their own retry (Replicate) run under a single kit attempt; a 429 on a status poll keeps polling unless `CallContext.maxRetries` is `0`. See durable media execution above. |
|
|
217
246
|
| Errors | `ModelKitError` with `code`, `provider`, `status`, `body`. Gemini `blockReason` and fal `body.detail` are lifted into `body`. A throw with no HTTP status (fetch failed, reset, DNS) is a retryable `network` error on every adapter. |
|
|
218
247
|
| Usage | One event per call with the canonical model id and the row's `usage.unit` (adapters supply the quantity in that unit). Gemini image events carry the token counts too (`inputTokens`/`outputTokens`); Google's raw `usageMetadata` rides on `raw.providerMetadata.google`. Video usage is emitted on completion; Seedance keeps `usage`, `duration`, `framespersecond` and `resolution` from the poll on `raw`. Audio: Lyria 3 returns no duration, so it emits `unit: "images", quantity: 1` (token counts ride along as `inputTokens`/`outputTokens`); Replicate audio (Lyria 2, MMAudio) emits `unit: "compute_seconds"` with the prediction time. |
|
|
219
248
|
| Media parts | `prepareMediaPart` turns a `MediaRef` into an AI SDK file part. Google inlines bytes up to 20 MB and uploads larger payloads through the Files API; call the returned `cleanup` after the request. Other providers get the reference inline. |
|
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
|
|
|
@@ -180,7 +181,9 @@ function normalizeExposure(value) {
|
|
|
180
181
|
if (value === "catalog") return ["cowork", "space_builder"];
|
|
181
182
|
if (value === "internal") return [];
|
|
182
183
|
if (Array.isArray(value)) {
|
|
183
|
-
if (value.every(
|
|
184
|
+
if (value.every(
|
|
185
|
+
(entry) => SURFACES.includes(entry)
|
|
186
|
+
)) {
|
|
184
187
|
const present = new Set(value);
|
|
185
188
|
return SURFACES.filter((surface) => present.has(surface));
|
|
186
189
|
}
|
|
@@ -1414,7 +1417,8 @@ var VIDEO_MODELS = [
|
|
|
1414
1417
|
// reference clip keeps its own framing.
|
|
1415
1418
|
aspectRatios: [...OMNI_RATIOS],
|
|
1416
1419
|
durations: [3, 4, 5, 6, 7, 8, 9, 10],
|
|
1417
|
-
references: { images: 6, videos: 1 },
|
|
1420
|
+
references: { images: 6, videos: 1, videoSeconds: { max: 10 } },
|
|
1421
|
+
sourceVideoSeconds: { max: 10 },
|
|
1418
1422
|
supports: []
|
|
1419
1423
|
},
|
|
1420
1424
|
defaults: {},
|
|
@@ -1440,7 +1444,8 @@ var VIDEO_MODELS = [
|
|
|
1440
1444
|
aspectRatios: [...OMNI_RATIOS],
|
|
1441
1445
|
resolutions: ["360p", "720p", "1080p", "4k"],
|
|
1442
1446
|
durations: [3, 4, 5, 6, 7, 8, 9, 10],
|
|
1443
|
-
references: { images: 6, videos: 1 },
|
|
1447
|
+
references: { images: 6, videos: 1, videoSeconds: { max: 10 } },
|
|
1448
|
+
sourceVideoSeconds: { max: 10 },
|
|
1444
1449
|
supports: []
|
|
1445
1450
|
},
|
|
1446
1451
|
defaults: {},
|
|
@@ -1468,7 +1473,14 @@ var VIDEO_MODELS = [
|
|
|
1468
1473
|
resolutions: ["480p", "720p", "1080p", "4k"],
|
|
1469
1474
|
durations: { min: 4, max: 15 },
|
|
1470
1475
|
durationPresets: [4, 6, 8, 10, 12, 15],
|
|
1471
|
-
references: {
|
|
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 },
|
|
1472
1484
|
ratioIgnoredWith: ["firstFrame", "sourceVideo"],
|
|
1473
1485
|
supports: ["generateAudio", "seed", "lastFrame", "watermark"]
|
|
1474
1486
|
},
|
|
@@ -1500,7 +1512,14 @@ var VIDEO_MODELS = [
|
|
|
1500
1512
|
aspectRatios: [...SEEDANCE_RATIOS],
|
|
1501
1513
|
resolutions: ["480p", "720p"],
|
|
1502
1514
|
durations: { min: 4, max: 15 },
|
|
1503
|
-
references: {
|
|
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 },
|
|
1504
1523
|
ratioIgnoredWith: ["firstFrame", "sourceVideo"],
|
|
1505
1524
|
supports: ["generateAudio", "seed", "lastFrame", "watermark"]
|
|
1506
1525
|
},
|
|
@@ -1534,7 +1553,13 @@ var VIDEO_MODELS = [
|
|
|
1534
1553
|
aspectRatios: [...SEEDANCE_RATIOS],
|
|
1535
1554
|
resolutions: ["480p", "720p", "1080p"],
|
|
1536
1555
|
durations: { min: 4, max: 30 },
|
|
1537
|
-
references: {
|
|
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 },
|
|
1538
1563
|
ratioIgnoredWith: ["firstFrame", "sourceVideo"],
|
|
1539
1564
|
supports: ["generateAudio", "seed", "lastFrame", "watermark", "taskType"]
|
|
1540
1565
|
},
|
|
@@ -1746,7 +1771,14 @@ var VIDEO_MODELS = [
|
|
|
1746
1771
|
aspectRatios: [...H3_REFERENCE_RATIOS],
|
|
1747
1772
|
resolutions: ["480p", "768p", "2k", "4k"],
|
|
1748
1773
|
durations: { min: 5, max: 15 },
|
|
1749
|
-
references: {
|
|
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
|
+
},
|
|
1750
1782
|
supports: ["seed", "promptExpansion", "safetyChecker"]
|
|
1751
1783
|
},
|
|
1752
1784
|
defaults: {
|
|
@@ -1777,7 +1809,14 @@ var VIDEO_MODELS = [
|
|
|
1777
1809
|
aspectRatios: [...H3_REFERENCE_RATIOS],
|
|
1778
1810
|
resolutions: ["480p", "768p"],
|
|
1779
1811
|
durations: { min: 5, max: 15 },
|
|
1780
|
-
references: {
|
|
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
|
+
},
|
|
1781
1820
|
supports: ["seed", "promptExpansion", "safetyChecker"]
|
|
1782
1821
|
},
|
|
1783
1822
|
defaults: {
|
|
@@ -1809,6 +1848,7 @@ var VIDEO_MODELS = [
|
|
|
1809
1848
|
aspectRatios: [...KLING_RATIOS],
|
|
1810
1849
|
durations: { min: 3, max: 10 },
|
|
1811
1850
|
requires: ["sourceVideo"],
|
|
1851
|
+
sourceVideoSeconds: { min: 3, max: 10.05 },
|
|
1812
1852
|
supports: []
|
|
1813
1853
|
},
|
|
1814
1854
|
defaults: { durationSeconds: 5, aspectRatio: "16:9" },
|
|
@@ -2165,13 +2205,20 @@ var ProviderOptionValueSchema = import_zod2.z.lazy(
|
|
|
2165
2205
|
])
|
|
2166
2206
|
);
|
|
2167
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
|
+
});
|
|
2168
2214
|
var ReferenceLimitsSchema = import_zod2.z.looseObject({
|
|
2169
2215
|
images: import_zod2.z.number().optional(),
|
|
2170
2216
|
imagesWithVideo: import_zod2.z.number().optional(),
|
|
2171
2217
|
videos: import_zod2.z.number().optional(),
|
|
2172
2218
|
audios: import_zod2.z.number().optional(),
|
|
2173
2219
|
total: import_zod2.z.number().optional(),
|
|
2174
|
-
audioOnly: import_zod2.z.boolean().optional()
|
|
2220
|
+
audioOnly: import_zod2.z.boolean().optional(),
|
|
2221
|
+
videoSeconds: VideoSecondsLimitsSchema.optional()
|
|
2175
2222
|
});
|
|
2176
2223
|
var ModelConstraintsSchema = import_zod2.z.looseObject({
|
|
2177
2224
|
aspectRatios: import_zod2.z.array(AspectRatioSchema).optional(),
|
|
@@ -2184,14 +2231,20 @@ var ModelConstraintsSchema = import_zod2.z.looseObject({
|
|
|
2184
2231
|
references: ReferenceLimitsSchema.optional(),
|
|
2185
2232
|
requires: import_zod2.z.array(RequiredInputSchema).optional(),
|
|
2186
2233
|
supports: import_zod2.z.array(SupportedOptionSchema).optional(),
|
|
2187
|
-
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()
|
|
2188
2236
|
});
|
|
2189
2237
|
var MediaRefSchema = import_zod2.z.union([
|
|
2190
|
-
import_zod2.z.looseObject({
|
|
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
|
+
}),
|
|
2191
2243
|
import_zod2.z.looseObject({
|
|
2192
2244
|
bytes: import_zod2.z.instanceof(Uint8Array),
|
|
2193
2245
|
mime: import_zod2.z.string(),
|
|
2194
|
-
label: import_zod2.z.string().optional()
|
|
2246
|
+
label: import_zod2.z.string().optional(),
|
|
2247
|
+
durationSeconds: import_zod2.z.number().optional()
|
|
2195
2248
|
})
|
|
2196
2249
|
]);
|
|
2197
2250
|
var ImageSafetySettingSchema = import_zod2.z.looseObject({ category: import_zod2.z.string(), threshold: import_zod2.z.string() });
|
|
@@ -2680,6 +2733,49 @@ function trim(field, refs, limit, model, warnings) {
|
|
|
2680
2733
|
});
|
|
2681
2734
|
return refs.slice(0, limit);
|
|
2682
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
|
+
}
|
|
2683
2779
|
function resolveImageOptions(model, request, onUnsupported = "error") {
|
|
2684
2780
|
const warnings = [];
|
|
2685
2781
|
const c = model.constraints;
|
|
@@ -2865,6 +2961,21 @@ function resolveVideoOptions(model, request, onUnsupported = "error") {
|
|
|
2865
2961
|
model: model.id
|
|
2866
2962
|
});
|
|
2867
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
|
+
}
|
|
2868
2979
|
setOrDelete(merged, "referenceImages", images);
|
|
2869
2980
|
setOrDelete(merged, "referenceVideos", videos);
|
|
2870
2981
|
setOrDelete(merged, "referenceAudios", audios);
|