@shop-os/modelkit 0.3.2 → 0.3.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/dist/index.cjs CHANGED
@@ -33,6 +33,7 @@ var index_exports = {};
33
33
  __export(index_exports, {
34
34
  ADAPTER_IDS: () => ADAPTER_IDS,
35
35
  ADAPTER_KEYS: () => ADAPTER_KEYS,
36
+ ARRAY_INPUT_SPEC_KEYS: () => ARRAY_INPUT_SPEC_KEYS,
36
37
  AUDIO_INPUT_SPEC_KEYS: () => AUDIO_INPUT_SPEC_KEYS,
37
38
  Catalog: () => Catalog,
38
39
  CatalogModelSchema: () => CatalogModelSchema,
@@ -75,6 +76,7 @@ __export(index_exports, {
75
76
  googleImageAdapter: () => googleImageAdapter,
76
77
  hasAwsCredentials: () => hasAwsCredentials,
77
78
  higgsfieldAdapter: () => higgsfieldAdapter,
79
+ isArrayInputSpecKey: () => isArrayInputSpecKey,
78
80
  isInputSpecKey: () => isInputSpecKey,
79
81
  isListed: () => isListed,
80
82
  isListedOn: () => isListedOn,
@@ -2130,8 +2132,16 @@ var VIDEO_MODELS = [
2130
2132
 
2131
2133
  // src/catalog/video-tools.ts
2132
2134
  var TOPAZ_LOGO = "https://cdn.jsdelivr.net/npm/@lobehub/icons-static-png@1.97.0/light/topazlabs.png";
2135
+ var FLUX_LOGO = "https://cdn.jsdelivr.net/npm/@lobehub/icons-static-png@1.97.0/light/flux.png";
2133
2136
  var TOPAZ_SOURCE_SECONDS = { max: 300 };
2134
- var TOPAZ_USAGE = { unit: "seconds" };
2137
+ var SECONDS_USAGE = { unit: "seconds" };
2138
+ var FLUX_SOURCE_SECONDS = { max: 20 };
2139
+ var FLUX_UPSCALE_TOOL = {
2140
+ kind: "upscale",
2141
+ upscaleFactor: { min: 1.5, max: 3 },
2142
+ maxOutputLongEdge: 3840,
2143
+ prompt: true
2144
+ };
2135
2145
  var VIDEO_TOOL_MODELS = [
2136
2146
  {
2137
2147
  id: "topaz-precision",
@@ -2141,7 +2151,7 @@ var VIDEO_TOOL_MODELS = [
2141
2151
  adapter: "fal",
2142
2152
  providerModelId: "topaz/upscale/video/precision",
2143
2153
  lifecycle: "active",
2144
- exposure: ["cowork"],
2154
+ exposure: ["cowork", "space_builder"],
2145
2155
  constraints: {
2146
2156
  supports: [],
2147
2157
  sourceVideoSeconds: TOPAZ_SOURCE_SECONDS,
@@ -2149,7 +2159,7 @@ var VIDEO_TOOL_MODELS = [
2149
2159
  },
2150
2160
  defaults: {},
2151
2161
  providerOptions: { fal: { model: "Proteus" } },
2152
- usage: TOPAZ_USAGE,
2162
+ usage: SECONDS_USAGE,
2153
2163
  pricingKind: "video_per_second",
2154
2164
  display: {
2155
2165
  provider: "topaz",
@@ -2166,7 +2176,7 @@ var VIDEO_TOOL_MODELS = [
2166
2176
  adapter: "fal",
2167
2177
  providerModelId: "topaz/upscale/video/generative",
2168
2178
  lifecycle: "active",
2169
- exposure: ["cowork"],
2179
+ exposure: ["cowork", "space_builder"],
2170
2180
  constraints: {
2171
2181
  supports: [],
2172
2182
  sourceVideoSeconds: TOPAZ_SOURCE_SECONDS,
@@ -2174,7 +2184,7 @@ var VIDEO_TOOL_MODELS = [
2174
2184
  },
2175
2185
  defaults: {},
2176
2186
  providerOptions: { fal: { model: "Starlight Precise 2.6" } },
2177
- usage: TOPAZ_USAGE,
2187
+ usage: SECONDS_USAGE,
2178
2188
  pricingKind: "video_per_second",
2179
2189
  display: {
2180
2190
  provider: "topaz",
@@ -2199,7 +2209,7 @@ var VIDEO_TOOL_MODELS = [
2199
2209
  },
2200
2210
  defaults: {},
2201
2211
  providerOptions: { fal: { model: "Apollo" } },
2202
- usage: TOPAZ_USAGE,
2212
+ usage: SECONDS_USAGE,
2203
2213
  pricingKind: "video_per_second",
2204
2214
  display: {
2205
2215
  provider: "topaz",
@@ -2222,13 +2232,63 @@ var VIDEO_TOOL_MODELS = [
2222
2232
  tool: { kind: "hdr", outputFormats: ["h265", "prores"] }
2223
2233
  },
2224
2234
  defaults: {},
2225
- usage: TOPAZ_USAGE,
2235
+ usage: SECONDS_USAGE,
2226
2236
  pricingKind: "video_per_second",
2227
2237
  display: {
2228
2238
  provider: "topaz",
2229
2239
  logo: TOPAZ_LOGO,
2230
2240
  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."
2231
2241
  }
2242
+ },
2243
+ {
2244
+ id: "flux-video-upscale-precise",
2245
+ aliases: [],
2246
+ label: "FLUX Upscale Precise",
2247
+ type: "video-tool",
2248
+ adapter: "fal",
2249
+ providerModelId: "blackforestlabs/flux-video-upscale",
2250
+ lifecycle: "active",
2251
+ exposure: ["space_builder"],
2252
+ constraints: {
2253
+ supports: [],
2254
+ sourceVideoSeconds: FLUX_SOURCE_SECONDS,
2255
+ tool: FLUX_UPSCALE_TOOL
2256
+ },
2257
+ defaults: {},
2258
+ providerOptions: { fal: { input: { creativity: 0 } } },
2259
+ usage: SECONDS_USAGE,
2260
+ pricingKind: "video_per_second",
2261
+ display: {
2262
+ provider: "black-forest-labs",
2263
+ logo: FLUX_LOGO,
2264
+ description: "FLUX 3 super-resolution, source-faithful: 1.5x to 3x, up to 4K, for clips up to 20 seconds.",
2265
+ sortOrder: 30
2266
+ }
2267
+ },
2268
+ {
2269
+ id: "flux-video-upscale-creative",
2270
+ aliases: [],
2271
+ label: "FLUX Upscale Creative",
2272
+ type: "video-tool",
2273
+ adapter: "fal",
2274
+ providerModelId: "blackforestlabs/flux-video-upscale",
2275
+ lifecycle: "active",
2276
+ exposure: ["space_builder"],
2277
+ constraints: {
2278
+ supports: [],
2279
+ sourceVideoSeconds: FLUX_SOURCE_SECONDS,
2280
+ tool: FLUX_UPSCALE_TOOL
2281
+ },
2282
+ defaults: {},
2283
+ providerOptions: { fal: { input: { creativity: 1 } } },
2284
+ usage: SECONDS_USAGE,
2285
+ pricingKind: "video_per_second",
2286
+ display: {
2287
+ provider: "black-forest-labs",
2288
+ logo: FLUX_LOGO,
2289
+ description: "FLUX 3 super-resolution that adds fine detail, optionally guided by a prompt: 1.5x to 3x, up to 4K, for clips up to 20 seconds. Costs more than Precise.",
2290
+ sortOrder: 40
2291
+ }
2232
2292
  }
2233
2293
  ];
2234
2294
 
@@ -2355,7 +2415,8 @@ var VIDEO_INPUT_SPEC_KEYS = [
2355
2415
  "generateAudio",
2356
2416
  "cfgScale",
2357
2417
  "promptExpansion",
2358
- "referenceImages"
2418
+ "referenceImages",
2419
+ "sourceVideo"
2359
2420
  ];
2360
2421
  var IMAGE_INPUT_SPEC_KEYS = [
2361
2422
  "prompt",
@@ -2374,6 +2435,12 @@ var AUDIO_INPUT_SPEC_KEYS = [
2374
2435
  "sourceVideo",
2375
2436
  "instrumental"
2376
2437
  ];
2438
+ var ARRAY_INPUT_SPEC_KEYS = [
2439
+ "firstFrame",
2440
+ "referenceImages",
2441
+ "lastFrame",
2442
+ "references"
2443
+ ];
2377
2444
  var INPUT_SPEC_SUPPORTS_KEYS = [
2378
2445
  "lastFrame",
2379
2446
  "seed",
@@ -2387,6 +2454,7 @@ var INPUT_SPEC_SUPPORTS_KEYS = [
2387
2454
  var VIDEO_KEY_SET = new Set(VIDEO_INPUT_SPEC_KEYS);
2388
2455
  var IMAGE_KEY_SET = new Set(IMAGE_INPUT_SPEC_KEYS);
2389
2456
  var AUDIO_KEY_SET = new Set(AUDIO_INPUT_SPEC_KEYS);
2457
+ var ARRAY_KEY_SET = new Set(ARRAY_INPUT_SPEC_KEYS);
2390
2458
  var ALL_KEY_SET = /* @__PURE__ */ new Set([
2391
2459
  ...VIDEO_INPUT_SPEC_KEYS,
2392
2460
  ...IMAGE_INPUT_SPEC_KEYS,
@@ -2431,13 +2499,30 @@ var ValuesSchema = import_zod2.z.record(import_zod2.z.string(), import_zod2.z.re
2431
2499
  }
2432
2500
  });
2433
2501
  var StaticSchema = import_zod2.z.record(import_zod2.z.string(), ProviderScalarSchema);
2502
+ var ArraysSchema = import_zod2.z.record(import_zod2.z.string().min(1), import_zod2.z.array(import_zod2.z.string()).min(1)).superRefine((arrays, ctx) => {
2503
+ for (const [name, keys] of Object.entries(arrays)) {
2504
+ for (const key of keys) {
2505
+ if (!ARRAY_KEY_SET.has(key)) {
2506
+ ctx.addIssue({
2507
+ code: "custom",
2508
+ path: [name],
2509
+ message: `inputSpec.arrays "${name}" has unknown image key "${key}"`
2510
+ });
2511
+ }
2512
+ }
2513
+ }
2514
+ });
2434
2515
  var InputSpecSchema = import_zod2.z.strictObject({
2435
2516
  fields: FieldsSchema,
2436
2517
  values: ValuesSchema.optional(),
2437
2518
  static: StaticSchema.optional(),
2519
+ arrays: ArraysSchema.optional(),
2438
2520
  editEndpoint: import_zod2.z.string().min(1).optional()
2439
2521
  }).superRefine((spec, ctx) => {
2440
- const mappedNames = new Set(Object.values(spec.fields));
2522
+ const mappedNames = /* @__PURE__ */ new Set([
2523
+ ...Object.values(spec.fields),
2524
+ ...Object.keys(spec.arrays ?? {})
2525
+ ]);
2441
2526
  for (const key of Object.keys(spec.static ?? {})) {
2442
2527
  if (mappedNames.has(key)) {
2443
2528
  ctx.addIssue({
@@ -2451,6 +2536,9 @@ var InputSpecSchema = import_zod2.z.strictObject({
2451
2536
  function isInputSpecKey(key) {
2452
2537
  return ALL_KEY_SET.has(key);
2453
2538
  }
2539
+ function isArrayInputSpecKey(key) {
2540
+ return ARRAY_KEY_SET.has(key);
2541
+ }
2454
2542
  function validateInputSpec(raw, row) {
2455
2543
  const parsed = InputSpecSchema.safeParse(raw);
2456
2544
  if (!parsed.success) {
@@ -2529,14 +2617,54 @@ function validateInputSpec(raw, row) {
2529
2617
  });
2530
2618
  }
2531
2619
  }
2620
+ const arrayOwner = /* @__PURE__ */ new Map();
2621
+ for (const [name, keys] of Object.entries(spec.arrays ?? {})) {
2622
+ const fieldKey = seenNames.get(name);
2623
+ if (fieldKey !== void 0) {
2624
+ issues.push({
2625
+ path: ["arrays", name],
2626
+ message: `inputSpec.arrays "${name}" is also the provider field of inputSpec.fields "${fieldKey}"`
2627
+ });
2628
+ }
2629
+ for (const key of keys) {
2630
+ if (allowed !== void 0 && !allowed.has(key)) {
2631
+ issues.push({
2632
+ path: ["arrays", name],
2633
+ message: `inputSpec.arrays "${key}" is not supported for ${row.type} rows`
2634
+ });
2635
+ }
2636
+ if (key in spec.fields) {
2637
+ issues.push({
2638
+ path: ["arrays", name],
2639
+ message: `inputSpec.arrays "${key}" is already mapped in inputSpec.fields`
2640
+ });
2641
+ }
2642
+ const owner = arrayOwner.get(key);
2643
+ if (owner === void 0) arrayOwner.set(key, name);
2644
+ else {
2645
+ issues.push({
2646
+ path: ["arrays", name],
2647
+ message: owner === name ? `inputSpec.arrays "${name}" lists "${key}" twice` : `inputSpec.arrays "${owner}" and "${name}" both take "${key}"`
2648
+ });
2649
+ }
2650
+ }
2651
+ }
2532
2652
  const supports = row.constraints.supports ?? [];
2533
2653
  for (const key of INPUT_SPEC_SUPPORTS_KEYS) {
2534
- if (key in spec.fields && !supports.includes(key)) {
2654
+ if (supports.includes(key)) continue;
2655
+ if (key in spec.fields) {
2535
2656
  issues.push({
2536
2657
  path: ["fields", key],
2537
2658
  message: `inputSpec.fields "${key}" needs "${key}" in constraints.supports`
2538
2659
  });
2539
2660
  }
2661
+ const arrayName = arrayOwner.get(key);
2662
+ if (arrayName !== void 0) {
2663
+ issues.push({
2664
+ path: ["arrays", arrayName],
2665
+ message: `inputSpec.arrays "${key}" needs "${key}" in constraints.supports`
2666
+ });
2667
+ }
2540
2668
  }
2541
2669
  if (issues.length > 0) return { ok: false, issues };
2542
2670
  return { ok: true, spec };
@@ -2661,6 +2789,7 @@ var VideoToolConstraintsSchema = import_zod3.z.looseObject({
2661
2789
  kind: import_zod3.z.enum(["upscale", "interpolate", "hdr"]),
2662
2790
  upscaleFactor: RangeSchema.optional(),
2663
2791
  maxOutputLongEdge: import_zod3.z.number().positive().optional(),
2792
+ prompt: import_zod3.z.boolean().optional(),
2664
2793
  fps: RangeSchema.optional(),
2665
2794
  outputFormats: import_zod3.z.array(import_zod3.z.enum(["h265", "prores"])).min(1).optional()
2666
2795
  });
@@ -2758,6 +2887,7 @@ var CatalogCapabilitiesSchema = import_zod3.z.looseObject({
2758
2887
  acceptsSourceVideo: import_zod3.z.boolean().optional(),
2759
2888
  videoEdit: import_zod3.z.boolean().optional(),
2760
2889
  framesExcludeReferences: import_zod3.z.boolean().optional(),
2890
+ sourceVideoExcludesFrames: import_zod3.z.boolean().optional(),
2761
2891
  lastFrameRequiresFirstFrame: import_zod3.z.boolean().optional(),
2762
2892
  reasoning: import_zod3.z.boolean().optional(),
2763
2893
  vision: import_zod3.z.boolean().optional(),
@@ -3955,6 +4085,7 @@ var ToolSchema = import_zod10.z.object({
3955
4085
  kind: import_zod10.z.enum(["upscale", "interpolate", "hdr"]),
3956
4086
  upscaleFactor: RangeSchema2.optional(),
3957
4087
  maxOutputLongEdge: import_zod10.z.number().positive().optional(),
4088
+ prompt: import_zod10.z.boolean().optional(),
3958
4089
  fps: RangeSchema2.optional(),
3959
4090
  outputFormats: import_zod10.z.array(import_zod10.z.enum(["h265", "prores"])).min(1).optional()
3960
4091
  });
@@ -3962,11 +4093,12 @@ function videoToolConstraints(row) {
3962
4093
  if (row.type !== "video-tool") return void 0;
3963
4094
  const parsed = ToolSchema.safeParse(row.constraints.tool);
3964
4095
  if (!parsed.success) return void 0;
3965
- const { kind, upscaleFactor, maxOutputLongEdge, fps, outputFormats } = parsed.data;
4096
+ const { kind, upscaleFactor, maxOutputLongEdge, prompt, fps, outputFormats } = parsed.data;
3966
4097
  return {
3967
4098
  kind,
3968
4099
  ...upscaleFactor !== void 0 && { upscaleFactor },
3969
4100
  ...maxOutputLongEdge !== void 0 && { maxOutputLongEdge },
4101
+ ...prompt !== void 0 && { prompt },
3970
4102
  ...fps !== void 0 && { fps },
3971
4103
  ...outputFormats !== void 0 && { outputFormats }
3972
4104
  };
@@ -4122,14 +4254,21 @@ async function readJson(ctx, res) {
4122
4254
  function providerModelName(ctx) {
4123
4255
  return import_zod11.z.string().safeParse(ctx.model.providerOptions?.["fal"]?.["model"]).data;
4124
4256
  }
4257
+ function staticInput(ctx) {
4258
+ return JsonObjectSchema.safeParse(ctx.model.providerOptions?.["fal"]?.["input"]).data ?? {};
4259
+ }
4125
4260
  function toolInput(ctx, req, output) {
4126
4261
  const tool = videoToolConstraints(ctx.model);
4127
4262
  const model = providerModelName(ctx);
4128
- const input = { video_url: req.source.url };
4263
+ const input = { ...staticInput(ctx), video_url: req.source.url };
4129
4264
  if (tool?.kind === "upscale") {
4130
- if (model !== void 0) input["model"] = model;
4131
4265
  input["upscale_factor"] = req.upscaleFactor ?? 1;
4132
- input["H264_output"] = true;
4266
+ if (model !== void 0) {
4267
+ input["model"] = model;
4268
+ input["H264_output"] = true;
4269
+ }
4270
+ const prompt = req.prompt?.trim();
4271
+ if (tool.prompt === true && prompt) input["prompt"] = prompt;
4133
4272
  } else if (tool?.kind === "interpolate") {
4134
4273
  if (model !== void 0) input["model"] = model;
4135
4274
  input["target_fps"] = req.targetFps ?? 60;
@@ -4285,6 +4424,17 @@ function applyInputSpec(spec, req, refToUrl2, opts) {
4285
4424
  const mapped = spec.values?.[canonical]?.[String(value)];
4286
4425
  out[providerField] = mapped ?? serialised;
4287
4426
  }
4427
+ for (const [providerField, keys] of Object.entries(spec.arrays ?? {})) {
4428
+ const urls = [];
4429
+ for (const key of keys) {
4430
+ if (!isArrayInputSpecKey(key)) continue;
4431
+ const value = canonicalValue(req, key);
4432
+ if (value === void 0) continue;
4433
+ if (Array.isArray(value)) urls.push(...value.map(refToUrl2));
4434
+ else if (isMediaRef(value)) urls.push(refToUrl2(value));
4435
+ }
4436
+ if (urls.length > 0) out[providerField] = urls;
4437
+ }
4288
4438
  for (const [key, value] of Object.entries(spec.static ?? {})) {
4289
4439
  out[key] = value;
4290
4440
  }
@@ -8404,6 +8554,26 @@ function resolveVideoOptions(model, request, onUnsupported = "error") {
8404
8554
  });
8405
8555
  delete merged.sourceVideo;
8406
8556
  }
8557
+ const imageInputs = ["firstFrame", "lastFrame", "referenceImages"].filter(
8558
+ (field) => field === "referenceImages" ? compact(merged.referenceImages).length > 0 : merged[field] !== void 0
8559
+ );
8560
+ if (merged.sourceVideo !== void 0 && imageInputs.length > 0 && model.capabilities?.sourceVideoExcludesFrames) {
8561
+ if (onUnsupported === "error") {
8562
+ throw new ModelKitError({
8563
+ code: "unsupported_option",
8564
+ message: `${model.id} takes a source video or images, not both`,
8565
+ model: model.id
8566
+ });
8567
+ }
8568
+ for (const field of imageInputs) {
8569
+ warnings.push({
8570
+ code: "option_ignored",
8571
+ field,
8572
+ message: `${model.id} takes a source video or images, not both; ${field} dropped`
8573
+ });
8574
+ delete merged[field];
8575
+ }
8576
+ }
8407
8577
  if (merged.lastFrame !== void 0 && merged.firstFrame === void 0 && model.capabilities?.lastFrameRequiresFirstFrame) {
8408
8578
  if (onUnsupported === "error") {
8409
8579
  throw new ModelKitError({
@@ -9514,6 +9684,7 @@ var describe = (modelId) => createDefaultModelKit().describe(modelId);
9514
9684
  0 && (module.exports = {
9515
9685
  ADAPTER_IDS,
9516
9686
  ADAPTER_KEYS,
9687
+ ARRAY_INPUT_SPEC_KEYS,
9517
9688
  AUDIO_INPUT_SPEC_KEYS,
9518
9689
  Catalog,
9519
9690
  CatalogModelSchema,
@@ -9556,6 +9727,7 @@ var describe = (modelId) => createDefaultModelKit().describe(modelId);
9556
9727
  googleImageAdapter,
9557
9728
  hasAwsCredentials,
9558
9729
  higgsfieldAdapter,
9730
+ isArrayInputSpecKey,
9559
9731
  isInputSpecKey,
9560
9732
  isListed,
9561
9733
  isListedOn,