@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.
@@ -149,6 +149,8 @@ interface VideoToolRequest {
149
149
  source: VideoToolSource;
150
150
  /** Upscale rows. */
151
151
  upscaleFactor?: number;
152
+ /** Upscale rows whose `constraints.tool.prompt` is set; ignored by every other row. */
153
+ prompt?: string;
152
154
  /** Interpolate rows. */
153
155
  targetFps?: number;
154
156
  /** HDR rows; the row's first `outputFormats` entry when unset. */
@@ -271,6 +273,8 @@ interface VideoToolConstraints {
271
273
  };
272
274
  /** Upscale: the output's long edge may not pass this, in pixels. */
273
275
  maxOutputLongEdge?: number;
276
+ /** Upscale: the row takes an optional prompt that guides added detail. */
277
+ prompt?: boolean;
274
278
  /** Interpolate: allowed target frame rate range. */
275
279
  fps?: {
276
280
  min: number;
@@ -393,6 +397,8 @@ interface CatalogCapabilities {
393
397
  videoEdit?: boolean;
394
398
  /** Video rows: first/last frames and reference images are mutually exclusive inputs. */
395
399
  framesExcludeReferences?: boolean;
400
+ /** Video rows: a source video is never sent with first/last frames or reference images. */
401
+ sourceVideoExcludesFrames?: boolean;
396
402
  /** Video rows: a last frame is accepted only together with a first frame. */
397
403
  lastFrameRequiresFirstFrame?: boolean;
398
404
  /** Text rows. `reasoning`: the model streams reasoning the UI should surface. */
@@ -792,6 +798,7 @@ declare const CatalogModelSchemaBase: z.ZodObject<{
792
798
  max: z.ZodNumber;
793
799
  }, z.core.$loose>>;
794
800
  maxOutputLongEdge: z.ZodOptional<z.ZodNumber>;
801
+ prompt: z.ZodOptional<z.ZodBoolean>;
795
802
  fps: z.ZodOptional<z.ZodObject<{
796
803
  min: z.ZodNumber;
797
804
  max: z.ZodNumber;
@@ -994,6 +1001,7 @@ declare const CatalogModelSchemaBase: z.ZodObject<{
994
1001
  acceptsSourceVideo: z.ZodOptional<z.ZodBoolean>;
995
1002
  videoEdit: z.ZodOptional<z.ZodBoolean>;
996
1003
  framesExcludeReferences: z.ZodOptional<z.ZodBoolean>;
1004
+ sourceVideoExcludesFrames: z.ZodOptional<z.ZodBoolean>;
997
1005
  lastFrameRequiresFirstFrame: z.ZodOptional<z.ZodBoolean>;
998
1006
  reasoning: z.ZodOptional<z.ZodBoolean>;
999
1007
  vision: z.ZodOptional<z.ZodBoolean>;
@@ -1062,7 +1070,11 @@ declare const VIDEO_MODELS: CatalogModel[];
1062
1070
 
1063
1071
  /**
1064
1072
  * Topaz Labs video tools on fal. `providerOptions.fal.model` names the Topaz model the
1065
- * endpoint runs; the adapter sends it as `model`. Exposed on cowork only (Creative Studio).
1073
+ * endpoint runs; the adapter sends it as `model`. Precision and Starlight are also listed in
1074
+ * Space Builder's video upscale node.
1075
+ *
1076
+ * FLUX video upscale on fal: one endpoint, two rows, because fal prices its `creativity`
1077
+ * modes differently and pricing is keyed by model. `providerOptions.fal.input` fixes the mode.
1066
1078
  */
1067
1079
  declare const VIDEO_TOOL_MODELS: CatalogModel[];
1068
1080
 
package/dist/catalog.d.ts CHANGED
@@ -149,6 +149,8 @@ interface VideoToolRequest {
149
149
  source: VideoToolSource;
150
150
  /** Upscale rows. */
151
151
  upscaleFactor?: number;
152
+ /** Upscale rows whose `constraints.tool.prompt` is set; ignored by every other row. */
153
+ prompt?: string;
152
154
  /** Interpolate rows. */
153
155
  targetFps?: number;
154
156
  /** HDR rows; the row's first `outputFormats` entry when unset. */
@@ -271,6 +273,8 @@ interface VideoToolConstraints {
271
273
  };
272
274
  /** Upscale: the output's long edge may not pass this, in pixels. */
273
275
  maxOutputLongEdge?: number;
276
+ /** Upscale: the row takes an optional prompt that guides added detail. */
277
+ prompt?: boolean;
274
278
  /** Interpolate: allowed target frame rate range. */
275
279
  fps?: {
276
280
  min: number;
@@ -393,6 +397,8 @@ interface CatalogCapabilities {
393
397
  videoEdit?: boolean;
394
398
  /** Video rows: first/last frames and reference images are mutually exclusive inputs. */
395
399
  framesExcludeReferences?: boolean;
400
+ /** Video rows: a source video is never sent with first/last frames or reference images. */
401
+ sourceVideoExcludesFrames?: boolean;
396
402
  /** Video rows: a last frame is accepted only together with a first frame. */
397
403
  lastFrameRequiresFirstFrame?: boolean;
398
404
  /** Text rows. `reasoning`: the model streams reasoning the UI should surface. */
@@ -792,6 +798,7 @@ declare const CatalogModelSchemaBase: z.ZodObject<{
792
798
  max: z.ZodNumber;
793
799
  }, z.core.$loose>>;
794
800
  maxOutputLongEdge: z.ZodOptional<z.ZodNumber>;
801
+ prompt: z.ZodOptional<z.ZodBoolean>;
795
802
  fps: z.ZodOptional<z.ZodObject<{
796
803
  min: z.ZodNumber;
797
804
  max: z.ZodNumber;
@@ -994,6 +1001,7 @@ declare const CatalogModelSchemaBase: z.ZodObject<{
994
1001
  acceptsSourceVideo: z.ZodOptional<z.ZodBoolean>;
995
1002
  videoEdit: z.ZodOptional<z.ZodBoolean>;
996
1003
  framesExcludeReferences: z.ZodOptional<z.ZodBoolean>;
1004
+ sourceVideoExcludesFrames: z.ZodOptional<z.ZodBoolean>;
997
1005
  lastFrameRequiresFirstFrame: z.ZodOptional<z.ZodBoolean>;
998
1006
  reasoning: z.ZodOptional<z.ZodBoolean>;
999
1007
  vision: z.ZodOptional<z.ZodBoolean>;
@@ -1062,7 +1070,11 @@ declare const VIDEO_MODELS: CatalogModel[];
1062
1070
 
1063
1071
  /**
1064
1072
  * Topaz Labs video tools on fal. `providerOptions.fal.model` names the Topaz model the
1065
- * endpoint runs; the adapter sends it as `model`. Exposed on cowork only (Creative Studio).
1073
+ * endpoint runs; the adapter sends it as `model`. Precision and Starlight are also listed in
1074
+ * Space Builder's video upscale node.
1075
+ *
1076
+ * FLUX video upscale on fal: one endpoint, two rows, because fal prices its `creativity`
1077
+ * modes differently and pricing is keyed by model. `providerOptions.fal.input` fixes the mode.
1066
1078
  */
1067
1079
  declare const VIDEO_TOOL_MODELS: CatalogModel[];
1068
1080
 
package/dist/catalog.js CHANGED
@@ -2002,8 +2002,16 @@ var VIDEO_MODELS = [
2002
2002
 
2003
2003
  // src/catalog/video-tools.ts
2004
2004
  var TOPAZ_LOGO = "https://cdn.jsdelivr.net/npm/@lobehub/icons-static-png@1.97.0/light/topazlabs.png";
2005
+ var FLUX_LOGO = "https://cdn.jsdelivr.net/npm/@lobehub/icons-static-png@1.97.0/light/flux.png";
2005
2006
  var TOPAZ_SOURCE_SECONDS = { max: 300 };
2006
- var TOPAZ_USAGE = { unit: "seconds" };
2007
+ var SECONDS_USAGE = { unit: "seconds" };
2008
+ var FLUX_SOURCE_SECONDS = { max: 20 };
2009
+ var FLUX_UPSCALE_TOOL = {
2010
+ kind: "upscale",
2011
+ upscaleFactor: { min: 1.5, max: 3 },
2012
+ maxOutputLongEdge: 3840,
2013
+ prompt: true
2014
+ };
2007
2015
  var VIDEO_TOOL_MODELS = [
2008
2016
  {
2009
2017
  id: "topaz-precision",
@@ -2013,7 +2021,7 @@ var VIDEO_TOOL_MODELS = [
2013
2021
  adapter: "fal",
2014
2022
  providerModelId: "topaz/upscale/video/precision",
2015
2023
  lifecycle: "active",
2016
- exposure: ["cowork"],
2024
+ exposure: ["cowork", "space_builder"],
2017
2025
  constraints: {
2018
2026
  supports: [],
2019
2027
  sourceVideoSeconds: TOPAZ_SOURCE_SECONDS,
@@ -2021,7 +2029,7 @@ var VIDEO_TOOL_MODELS = [
2021
2029
  },
2022
2030
  defaults: {},
2023
2031
  providerOptions: { fal: { model: "Proteus" } },
2024
- usage: TOPAZ_USAGE,
2032
+ usage: SECONDS_USAGE,
2025
2033
  pricingKind: "video_per_second",
2026
2034
  display: {
2027
2035
  provider: "topaz",
@@ -2038,7 +2046,7 @@ var VIDEO_TOOL_MODELS = [
2038
2046
  adapter: "fal",
2039
2047
  providerModelId: "topaz/upscale/video/generative",
2040
2048
  lifecycle: "active",
2041
- exposure: ["cowork"],
2049
+ exposure: ["cowork", "space_builder"],
2042
2050
  constraints: {
2043
2051
  supports: [],
2044
2052
  sourceVideoSeconds: TOPAZ_SOURCE_SECONDS,
@@ -2046,7 +2054,7 @@ var VIDEO_TOOL_MODELS = [
2046
2054
  },
2047
2055
  defaults: {},
2048
2056
  providerOptions: { fal: { model: "Starlight Precise 2.6" } },
2049
- usage: TOPAZ_USAGE,
2057
+ usage: SECONDS_USAGE,
2050
2058
  pricingKind: "video_per_second",
2051
2059
  display: {
2052
2060
  provider: "topaz",
@@ -2071,7 +2079,7 @@ var VIDEO_TOOL_MODELS = [
2071
2079
  },
2072
2080
  defaults: {},
2073
2081
  providerOptions: { fal: { model: "Apollo" } },
2074
- usage: TOPAZ_USAGE,
2082
+ usage: SECONDS_USAGE,
2075
2083
  pricingKind: "video_per_second",
2076
2084
  display: {
2077
2085
  provider: "topaz",
@@ -2094,13 +2102,63 @@ var VIDEO_TOOL_MODELS = [
2094
2102
  tool: { kind: "hdr", outputFormats: ["h265", "prores"] }
2095
2103
  },
2096
2104
  defaults: {},
2097
- usage: TOPAZ_USAGE,
2105
+ usage: SECONDS_USAGE,
2098
2106
  pricingKind: "video_per_second",
2099
2107
  display: {
2100
2108
  provider: "topaz",
2101
2109
  logo: TOPAZ_LOGO,
2102
2110
  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."
2103
2111
  }
2112
+ },
2113
+ {
2114
+ id: "flux-video-upscale-precise",
2115
+ aliases: [],
2116
+ label: "FLUX Upscale Precise",
2117
+ type: "video-tool",
2118
+ adapter: "fal",
2119
+ providerModelId: "blackforestlabs/flux-video-upscale",
2120
+ lifecycle: "active",
2121
+ exposure: ["space_builder"],
2122
+ constraints: {
2123
+ supports: [],
2124
+ sourceVideoSeconds: FLUX_SOURCE_SECONDS,
2125
+ tool: FLUX_UPSCALE_TOOL
2126
+ },
2127
+ defaults: {},
2128
+ providerOptions: { fal: { input: { creativity: 0 } } },
2129
+ usage: SECONDS_USAGE,
2130
+ pricingKind: "video_per_second",
2131
+ display: {
2132
+ provider: "black-forest-labs",
2133
+ logo: FLUX_LOGO,
2134
+ description: "FLUX 3 super-resolution, source-faithful: 1.5x to 3x, up to 4K, for clips up to 20 seconds.",
2135
+ sortOrder: 30
2136
+ }
2137
+ },
2138
+ {
2139
+ id: "flux-video-upscale-creative",
2140
+ aliases: [],
2141
+ label: "FLUX Upscale Creative",
2142
+ type: "video-tool",
2143
+ adapter: "fal",
2144
+ providerModelId: "blackforestlabs/flux-video-upscale",
2145
+ lifecycle: "active",
2146
+ exposure: ["space_builder"],
2147
+ constraints: {
2148
+ supports: [],
2149
+ sourceVideoSeconds: FLUX_SOURCE_SECONDS,
2150
+ tool: FLUX_UPSCALE_TOOL
2151
+ },
2152
+ defaults: {},
2153
+ providerOptions: { fal: { input: { creativity: 1 } } },
2154
+ usage: SECONDS_USAGE,
2155
+ pricingKind: "video_per_second",
2156
+ display: {
2157
+ provider: "black-forest-labs",
2158
+ logo: FLUX_LOGO,
2159
+ 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.",
2160
+ sortOrder: 40
2161
+ }
2104
2162
  }
2105
2163
  ];
2106
2164
 
@@ -2239,7 +2297,8 @@ var VIDEO_INPUT_SPEC_KEYS = [
2239
2297
  "generateAudio",
2240
2298
  "cfgScale",
2241
2299
  "promptExpansion",
2242
- "referenceImages"
2300
+ "referenceImages",
2301
+ "sourceVideo"
2243
2302
  ];
2244
2303
  var IMAGE_INPUT_SPEC_KEYS = [
2245
2304
  "prompt",
@@ -2258,6 +2317,12 @@ var AUDIO_INPUT_SPEC_KEYS = [
2258
2317
  "sourceVideo",
2259
2318
  "instrumental"
2260
2319
  ];
2320
+ var ARRAY_INPUT_SPEC_KEYS = [
2321
+ "firstFrame",
2322
+ "referenceImages",
2323
+ "lastFrame",
2324
+ "references"
2325
+ ];
2261
2326
  var INPUT_SPEC_SUPPORTS_KEYS = [
2262
2327
  "lastFrame",
2263
2328
  "seed",
@@ -2271,6 +2336,7 @@ var INPUT_SPEC_SUPPORTS_KEYS = [
2271
2336
  var VIDEO_KEY_SET = new Set(VIDEO_INPUT_SPEC_KEYS);
2272
2337
  var IMAGE_KEY_SET = new Set(IMAGE_INPUT_SPEC_KEYS);
2273
2338
  var AUDIO_KEY_SET = new Set(AUDIO_INPUT_SPEC_KEYS);
2339
+ var ARRAY_KEY_SET = new Set(ARRAY_INPUT_SPEC_KEYS);
2274
2340
  var ALL_KEY_SET = /* @__PURE__ */ new Set([
2275
2341
  ...VIDEO_INPUT_SPEC_KEYS,
2276
2342
  ...IMAGE_INPUT_SPEC_KEYS,
@@ -2315,13 +2381,30 @@ var ValuesSchema = z2.record(z2.string(), z2.record(z2.string(), ProviderScalarS
2315
2381
  }
2316
2382
  });
2317
2383
  var StaticSchema = z2.record(z2.string(), ProviderScalarSchema);
2384
+ var ArraysSchema = z2.record(z2.string().min(1), z2.array(z2.string()).min(1)).superRefine((arrays, ctx) => {
2385
+ for (const [name, keys] of Object.entries(arrays)) {
2386
+ for (const key of keys) {
2387
+ if (!ARRAY_KEY_SET.has(key)) {
2388
+ ctx.addIssue({
2389
+ code: "custom",
2390
+ path: [name],
2391
+ message: `inputSpec.arrays "${name}" has unknown image key "${key}"`
2392
+ });
2393
+ }
2394
+ }
2395
+ }
2396
+ });
2318
2397
  var InputSpecSchema = z2.strictObject({
2319
2398
  fields: FieldsSchema,
2320
2399
  values: ValuesSchema.optional(),
2321
2400
  static: StaticSchema.optional(),
2401
+ arrays: ArraysSchema.optional(),
2322
2402
  editEndpoint: z2.string().min(1).optional()
2323
2403
  }).superRefine((spec, ctx) => {
2324
- const mappedNames = new Set(Object.values(spec.fields));
2404
+ const mappedNames = /* @__PURE__ */ new Set([
2405
+ ...Object.values(spec.fields),
2406
+ ...Object.keys(spec.arrays ?? {})
2407
+ ]);
2325
2408
  for (const key of Object.keys(spec.static ?? {})) {
2326
2409
  if (mappedNames.has(key)) {
2327
2410
  ctx.addIssue({
@@ -2410,14 +2493,54 @@ function validateInputSpec(raw, row) {
2410
2493
  });
2411
2494
  }
2412
2495
  }
2496
+ const arrayOwner = /* @__PURE__ */ new Map();
2497
+ for (const [name, keys] of Object.entries(spec.arrays ?? {})) {
2498
+ const fieldKey = seenNames.get(name);
2499
+ if (fieldKey !== void 0) {
2500
+ issues.push({
2501
+ path: ["arrays", name],
2502
+ message: `inputSpec.arrays "${name}" is also the provider field of inputSpec.fields "${fieldKey}"`
2503
+ });
2504
+ }
2505
+ for (const key of keys) {
2506
+ if (allowed !== void 0 && !allowed.has(key)) {
2507
+ issues.push({
2508
+ path: ["arrays", name],
2509
+ message: `inputSpec.arrays "${key}" is not supported for ${row.type} rows`
2510
+ });
2511
+ }
2512
+ if (key in spec.fields) {
2513
+ issues.push({
2514
+ path: ["arrays", name],
2515
+ message: `inputSpec.arrays "${key}" is already mapped in inputSpec.fields`
2516
+ });
2517
+ }
2518
+ const owner = arrayOwner.get(key);
2519
+ if (owner === void 0) arrayOwner.set(key, name);
2520
+ else {
2521
+ issues.push({
2522
+ path: ["arrays", name],
2523
+ message: owner === name ? `inputSpec.arrays "${name}" lists "${key}" twice` : `inputSpec.arrays "${owner}" and "${name}" both take "${key}"`
2524
+ });
2525
+ }
2526
+ }
2527
+ }
2413
2528
  const supports = row.constraints.supports ?? [];
2414
2529
  for (const key of INPUT_SPEC_SUPPORTS_KEYS) {
2415
- if (key in spec.fields && !supports.includes(key)) {
2530
+ if (supports.includes(key)) continue;
2531
+ if (key in spec.fields) {
2416
2532
  issues.push({
2417
2533
  path: ["fields", key],
2418
2534
  message: `inputSpec.fields "${key}" needs "${key}" in constraints.supports`
2419
2535
  });
2420
2536
  }
2537
+ const arrayName = arrayOwner.get(key);
2538
+ if (arrayName !== void 0) {
2539
+ issues.push({
2540
+ path: ["arrays", arrayName],
2541
+ message: `inputSpec.arrays "${key}" needs "${key}" in constraints.supports`
2542
+ });
2543
+ }
2421
2544
  }
2422
2545
  if (issues.length > 0) return { ok: false, issues };
2423
2546
  return { ok: true, spec };
@@ -2542,6 +2665,7 @@ var VideoToolConstraintsSchema = z3.looseObject({
2542
2665
  kind: z3.enum(["upscale", "interpolate", "hdr"]),
2543
2666
  upscaleFactor: RangeSchema.optional(),
2544
2667
  maxOutputLongEdge: z3.number().positive().optional(),
2668
+ prompt: z3.boolean().optional(),
2545
2669
  fps: RangeSchema.optional(),
2546
2670
  outputFormats: z3.array(z3.enum(["h265", "prores"])).min(1).optional()
2547
2671
  });
@@ -2639,6 +2763,7 @@ var CatalogCapabilitiesSchema = z3.looseObject({
2639
2763
  acceptsSourceVideo: z3.boolean().optional(),
2640
2764
  videoEdit: z3.boolean().optional(),
2641
2765
  framesExcludeReferences: z3.boolean().optional(),
2766
+ sourceVideoExcludesFrames: z3.boolean().optional(),
2642
2767
  lastFrameRequiresFirstFrame: z3.boolean().optional(),
2643
2768
  reasoning: z3.boolean().optional(),
2644
2769
  vision: z3.boolean().optional(),
@@ -2999,6 +3124,7 @@ var ToolSchema = z6.object({
2999
3124
  kind: z6.enum(["upscale", "interpolate", "hdr"]),
3000
3125
  upscaleFactor: RangeSchema2.optional(),
3001
3126
  maxOutputLongEdge: z6.number().positive().optional(),
3127
+ prompt: z6.boolean().optional(),
3002
3128
  fps: RangeSchema2.optional(),
3003
3129
  outputFormats: z6.array(z6.enum(["h265", "prores"])).min(1).optional()
3004
3130
  });
@@ -3006,11 +3132,12 @@ function videoToolConstraints(row) {
3006
3132
  if (row.type !== "video-tool") return void 0;
3007
3133
  const parsed = ToolSchema.safeParse(row.constraints.tool);
3008
3134
  if (!parsed.success) return void 0;
3009
- const { kind, upscaleFactor, maxOutputLongEdge, fps, outputFormats } = parsed.data;
3135
+ const { kind, upscaleFactor, maxOutputLongEdge, prompt, fps, outputFormats } = parsed.data;
3010
3136
  return {
3011
3137
  kind,
3012
3138
  ...upscaleFactor !== void 0 && { upscaleFactor },
3013
3139
  ...maxOutputLongEdge !== void 0 && { maxOutputLongEdge },
3140
+ ...prompt !== void 0 && { prompt },
3014
3141
  ...fps !== void 0 && { fps },
3015
3142
  ...outputFormats !== void 0 && { outputFormats }
3016
3143
  };
@@ -3336,6 +3463,26 @@ function resolveVideoOptions(model, request, onUnsupported = "error") {
3336
3463
  });
3337
3464
  delete merged.sourceVideo;
3338
3465
  }
3466
+ const imageInputs = ["firstFrame", "lastFrame", "referenceImages"].filter(
3467
+ (field) => field === "referenceImages" ? compact(merged.referenceImages).length > 0 : merged[field] !== void 0
3468
+ );
3469
+ if (merged.sourceVideo !== void 0 && imageInputs.length > 0 && model.capabilities?.sourceVideoExcludesFrames) {
3470
+ if (onUnsupported === "error") {
3471
+ throw new ModelKitError({
3472
+ code: "unsupported_option",
3473
+ message: `${model.id} takes a source video or images, not both`,
3474
+ model: model.id
3475
+ });
3476
+ }
3477
+ for (const field of imageInputs) {
3478
+ warnings.push({
3479
+ code: "option_ignored",
3480
+ field,
3481
+ message: `${model.id} takes a source video or images, not both; ${field} dropped`
3482
+ });
3483
+ delete merged[field];
3484
+ }
3485
+ }
3339
3486
  if (merged.lastFrame !== void 0 && merged.firstFrame === void 0 && model.capabilities?.lastFrameRequiresFirstFrame) {
3340
3487
  if (onUnsupported === "error") {
3341
3488
  throw new ModelKitError({