@shop-os/modelkit 0.2.26 → 0.3.0

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/catalog.js CHANGED
@@ -1293,6 +1293,11 @@ var NEGATIVE_PROMPT = "blur, distort, and low quality";
1293
1293
  var FRAMES_EXCLUDE_REFERENCES = { framesExcludeReferences: true };
1294
1294
  var KLING_PROMPT = { referenceSyntax: "numbered", maxChars: 2500 };
1295
1295
  var SEEDANCE_PROMPT = { referenceSyntax: "numbered", maxChars: 3e4 };
1296
+ var SEEDANCE_2_AUDIO = {
1297
+ audioSeconds: { min: 2, max: 15, total: 15 },
1298
+ audioFormats: ["wav", "mp3"],
1299
+ audioMaxBytes: 15 * 1024 * 1024
1300
+ };
1296
1301
  var H3_PROMPT = { referenceSyntax: "numbered", maxChars: 5e4 };
1297
1302
  var OMNI_PROMPT = { referenceSyntax: "caption", maxChars: 5e4 };
1298
1303
  var BYTEDANCE_LOGO = "https://d31vvfpk2ppdyq.cloudfront.net/assets/image/3bba5d81-4e57-4e71-911b-cdfa05acca2c_compressed.png";
@@ -1429,7 +1434,7 @@ var VIDEO_MODELS = [
1429
1434
  description: "Google's conversational video model with native audio, human support, and natural language editing. Aspect ratio only \u2014 resolution and duration are model-determined.",
1430
1435
  logo: GOOGLE_LOGO2
1431
1436
  },
1432
- capabilities: { acceptsSourceVideo: true }
1437
+ capabilities: { acceptsSourceVideo: true, videoEdit: true }
1433
1438
  },
1434
1439
  {
1435
1440
  id: "gemini-omni-1.1-flash",
@@ -1454,7 +1459,7 @@ var VIDEO_MODELS = [
1454
1459
  // with a video task, so those calls carry the prompt alone).
1455
1460
  // First-to-last-frame interpolation: `<LAST_FRAME>` goes with `<FIRST_FRAME>`.
1456
1461
  supports: ["videoExtension", "lastFrame"],
1457
- extension: { segmentSeconds: 10, maxTotalSeconds: 40 },
1462
+ extension: { segmentSeconds: 10, maxTotalSeconds: 40, returns: "joined" },
1458
1463
  prompt: OMNI_PROMPT,
1459
1464
  audio: "always"
1460
1465
  },
@@ -1466,7 +1471,11 @@ var VIDEO_MODELS = [
1466
1471
  logo: GOOGLE_LOGO2,
1467
1472
  description: "Google Gemini Omni Flash 1.1 conversational video generation, editing and extension; text, image, and up to 10-second video input, 3-10 second output at up to 4K and 24 FPS, extendable to 40 seconds."
1468
1473
  },
1469
- capabilities: { acceptsSourceVideo: true, lastFrameRequiresFirstFrame: true }
1474
+ capabilities: {
1475
+ acceptsSourceVideo: true,
1476
+ videoEdit: true,
1477
+ lastFrameRequiresFirstFrame: true
1478
+ }
1470
1479
  },
1471
1480
  {
1472
1481
  // vibe-triggers and Space Builder drop the ratio when a frame is wired; Cowork always sends it.
@@ -1488,7 +1497,8 @@ var VIDEO_MODELS = [
1488
1497
  videos: 3,
1489
1498
  audios: 3,
1490
1499
  audioOnly: false,
1491
- videoSeconds: { min: 2, max: 15, total: 15 }
1500
+ videoSeconds: { min: 2, max: 15, total: 15 },
1501
+ ...SEEDANCE_2_AUDIO
1492
1502
  },
1493
1503
  sourceVideoSeconds: { min: 2, max: 15, inReferenceTotal: true },
1494
1504
  ratioIgnoredWith: ["firstFrame", "sourceVideo"],
@@ -1510,7 +1520,7 @@ var VIDEO_MODELS = [
1510
1520
  description: "High-quality AI video generation with reference image conditioning. Supports 4-15 second clips with human preprocessing.",
1511
1521
  logo: BYTEDANCE_LOGO
1512
1522
  },
1513
- capabilities: { acceptsSourceVideo: true, ...FRAMES_EXCLUDE_REFERENCES }
1523
+ capabilities: { acceptsSourceVideo: true, videoEdit: false, ...FRAMES_EXCLUDE_REFERENCES }
1514
1524
  },
1515
1525
  {
1516
1526
  id: "seedance-2.0-fast",
@@ -1530,7 +1540,8 @@ var VIDEO_MODELS = [
1530
1540
  videos: 3,
1531
1541
  audios: 3,
1532
1542
  audioOnly: false,
1533
- videoSeconds: { min: 2, max: 15, total: 15 }
1543
+ videoSeconds: { min: 2, max: 15, total: 15 },
1544
+ ...SEEDANCE_2_AUDIO
1534
1545
  },
1535
1546
  sourceVideoSeconds: { min: 2, max: 15, inReferenceTotal: true },
1536
1547
  ratioIgnoredWith: ["firstFrame", "sourceVideo"],
@@ -1552,7 +1563,7 @@ var VIDEO_MODELS = [
1552
1563
  logo: BYTEDANCE_LOGO,
1553
1564
  description: "Speed- and cost-optimized ByteDance Seedance 2.0 Fast; text plus image, video, and audio references to generate or edit 4-15 second clips."
1554
1565
  },
1555
- capabilities: { acceptsSourceVideo: true, ...FRAMES_EXCLUDE_REFERENCES }
1566
+ capabilities: { acceptsSourceVideo: true, videoEdit: false, ...FRAMES_EXCLUDE_REFERENCES }
1556
1567
  },
1557
1568
  {
1558
1569
  // omni_reference_task_type edit/extend: adaptive ratio, edit sends duration -1.
@@ -1572,12 +1583,20 @@ var VIDEO_MODELS = [
1572
1583
  images: 30,
1573
1584
  videos: 10,
1574
1585
  audios: 10,
1575
- videoSeconds: { min: 2, max: 30, total: 30, editMin: 4 }
1586
+ videoSeconds: { min: 2, max: 30, total: 30, editMin: 4 },
1587
+ // BytePlus: 2-30 s per clip, 30 s in all, wav/mp3, 15 MB each.
1588
+ audioSeconds: { min: 2, max: 30, total: 30 },
1589
+ audioFormats: ["wav", "mp3"],
1590
+ audioMaxBytes: 15 * 1024 * 1024
1576
1591
  },
1577
1592
  sourceVideoSeconds: { min: 2, max: 30, editMin: 4, inReferenceTotal: true },
1578
1593
  ratioIgnoredWith: ["firstFrame", "sourceVideo"],
1579
1594
  // No seed: 2.x echoes it but does not honour it (two seeded runs differed, 2026-10-01).
1580
- supports: ["generateAudio", "lastFrame", "watermark", "taskType"],
1595
+ supports: ["generateAudio", "lastFrame", "watermark", "taskType", "videoExtension"],
1596
+ // Extend (probe 2026-10-02, 480p): returns ONLY the added footage, continuing from
1597
+ // the source's last frame at the source's framing and fps but at the requested
1598
+ // resolution, so the caller joins the clips. Forward only in practice.
1599
+ extension: { segmentSeconds: 30, maxTotalSeconds: 60, returns: "new" },
1581
1600
  prompt: SEEDANCE_PROMPT,
1582
1601
  audio: "optional"
1583
1602
  },
@@ -1594,7 +1613,7 @@ var VIDEO_MODELS = [
1594
1613
  logo: BYTEDANCE_LOGO,
1595
1614
  description: "ByteDance Seedance 2.5 for long-form storytelling and omni-reference generation/editing; up to 50 reference assets and 4-30 second clips at up to 1080p."
1596
1615
  },
1597
- capabilities: { acceptsSourceVideo: true, ...FRAMES_EXCLUDE_REFERENCES }
1616
+ capabilities: { acceptsSourceVideo: true, videoEdit: true, ...FRAMES_EXCLUDE_REFERENCES }
1598
1617
  },
1599
1618
  {
1600
1619
  id: "grok-imagine",
@@ -1958,11 +1977,116 @@ var VIDEO_MODELS = [
1958
1977
  }
1959
1978
  ];
1960
1979
 
1980
+ // src/catalog/video-tools.ts
1981
+ var TOPAZ_LOGO = "https://cdn.jsdelivr.net/npm/@lobehub/icons-static-png@1.97.0/light/topazlabs.png";
1982
+ var TOPAZ_SOURCE_SECONDS = { max: 300 };
1983
+ var TOPAZ_USAGE = { unit: "seconds" };
1984
+ var VIDEO_TOOL_MODELS = [
1985
+ {
1986
+ id: "topaz-precision",
1987
+ aliases: [],
1988
+ label: "Topaz Precision",
1989
+ type: "video-tool",
1990
+ adapter: "fal",
1991
+ providerModelId: "topaz/upscale/video/precision",
1992
+ lifecycle: "active",
1993
+ exposure: ["cowork"],
1994
+ constraints: {
1995
+ supports: [],
1996
+ sourceVideoSeconds: TOPAZ_SOURCE_SECONDS,
1997
+ tool: { kind: "upscale", upscaleFactor: { min: 1, max: 4 }, maxOutputLongEdge: 3840 }
1998
+ },
1999
+ defaults: {},
2000
+ providerOptions: { fal: { model: "Proteus" } },
2001
+ usage: TOPAZ_USAGE,
2002
+ pricingKind: "video_per_second",
2003
+ display: {
2004
+ provider: "topaz",
2005
+ logo: TOPAZ_LOGO,
2006
+ description: "Topaz Proteus upscaling: sharper, cleaner footage up to 4K while staying faithful to the source.",
2007
+ sortOrder: 20
2008
+ }
2009
+ },
2010
+ {
2011
+ id: "topaz-starlight",
2012
+ aliases: [],
2013
+ label: "Topaz Starlight",
2014
+ type: "video-tool",
2015
+ adapter: "fal",
2016
+ providerModelId: "topaz/upscale/video/generative",
2017
+ lifecycle: "active",
2018
+ exposure: ["cowork"],
2019
+ constraints: {
2020
+ supports: [],
2021
+ sourceVideoSeconds: TOPAZ_SOURCE_SECONDS,
2022
+ tool: { kind: "upscale", upscaleFactor: { min: 1, max: 4 }, maxOutputLongEdge: 3840 }
2023
+ },
2024
+ defaults: {},
2025
+ providerOptions: { fal: { model: "Starlight Precise 2.6" } },
2026
+ usage: TOPAZ_USAGE,
2027
+ pricingKind: "video_per_second",
2028
+ display: {
2029
+ provider: "topaz",
2030
+ logo: TOPAZ_LOGO,
2031
+ description: "Topaz Starlight Precise 2.6: generative upscaling that rebuilds fine detail, up to 4K. Slower and costlier than Precision.",
2032
+ sortOrder: 10
2033
+ }
2034
+ },
2035
+ {
2036
+ id: "topaz-apollo",
2037
+ aliases: [],
2038
+ label: "Topaz Apollo",
2039
+ type: "video-tool",
2040
+ adapter: "fal",
2041
+ providerModelId: "topaz/interpolate/video",
2042
+ lifecycle: "active",
2043
+ exposure: ["cowork"],
2044
+ constraints: {
2045
+ supports: [],
2046
+ sourceVideoSeconds: TOPAZ_SOURCE_SECONDS,
2047
+ tool: { kind: "interpolate", fps: { min: 16, max: 120 } }
2048
+ },
2049
+ defaults: {},
2050
+ providerOptions: { fal: { model: "Apollo" } },
2051
+ usage: TOPAZ_USAGE,
2052
+ pricingKind: "video_per_second",
2053
+ display: {
2054
+ provider: "topaz",
2055
+ logo: TOPAZ_LOGO,
2056
+ description: "Topaz Apollo frame interpolation: smoother motion at up to 120 fps, same length as the source."
2057
+ }
2058
+ },
2059
+ {
2060
+ id: "topaz-hyperion-2.5",
2061
+ aliases: [],
2062
+ label: "Topaz Hyperion 2.5",
2063
+ type: "video-tool",
2064
+ adapter: "fal",
2065
+ providerModelId: "topaz/sdr-to-hdr/video",
2066
+ lifecycle: "active",
2067
+ exposure: ["cowork"],
2068
+ constraints: {
2069
+ supports: [],
2070
+ sourceVideoSeconds: TOPAZ_SOURCE_SECONDS,
2071
+ tool: { kind: "hdr", outputFormats: ["h265", "prores"] }
2072
+ },
2073
+ defaults: {},
2074
+ usage: TOPAZ_USAGE,
2075
+ pricingKind: "video_per_second",
2076
+ display: {
2077
+ provider: "topaz",
2078
+ logo: TOPAZ_LOGO,
2079
+ 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."
2080
+ }
2081
+ }
2082
+ ];
2083
+
1961
2084
  // src/catalog/models.ts
1962
2085
  var MODELS = [
1963
2086
  ...TEXT_MODELS,
1964
2087
  ...IMAGE_MODELS,
1965
2088
  ...VIDEO_MODELS,
2089
+ ...VIDEO_TOOL_MODELS,
1966
2090
  ...OTHER_MODELS
1967
2091
  ];
1968
2092
 
@@ -2268,7 +2392,15 @@ var ADAPTER_IDS = [
2268
2392
  "higgsfield",
2269
2393
  "xai-video"
2270
2394
  ];
2271
- var ModelTypeSchema = z3.enum(["text", "image", "video", "audio", "upscale", "bg-remove"]);
2395
+ var ModelTypeSchema = z3.enum([
2396
+ "text",
2397
+ "image",
2398
+ "video",
2399
+ "audio",
2400
+ "upscale",
2401
+ "bg-remove",
2402
+ "video-tool"
2403
+ ]);
2272
2404
  var LifecycleSchema = z3.enum(["active", "deprecated", "retired"]);
2273
2405
  var SurfaceSchema = z3.enum(["cowork", "space_builder"]);
2274
2406
  var SURFACES2 = SurfaceSchema.options;
@@ -2348,7 +2480,22 @@ var ReferenceLimitsSchema = z3.looseObject({
2348
2480
  audios: z3.number().optional(),
2349
2481
  total: z3.number().optional(),
2350
2482
  audioOnly: z3.boolean().optional(),
2351
- videoSeconds: VideoSecondsLimitsSchema.optional()
2483
+ videoSeconds: VideoSecondsLimitsSchema.optional(),
2484
+ audioSeconds: z3.looseObject({
2485
+ min: z3.number().optional(),
2486
+ max: z3.number().optional(),
2487
+ total: z3.number().optional()
2488
+ }).optional(),
2489
+ audioFormats: z3.array(z3.string()).optional(),
2490
+ audioMaxBytes: z3.number().positive().optional()
2491
+ });
2492
+ var RangeSchema = z3.looseObject({ min: z3.number(), max: z3.number() });
2493
+ var VideoToolConstraintsSchema = z3.looseObject({
2494
+ kind: z3.enum(["upscale", "interpolate", "hdr"]),
2495
+ upscaleFactor: RangeSchema.optional(),
2496
+ maxOutputLongEdge: z3.number().positive().optional(),
2497
+ fps: RangeSchema.optional(),
2498
+ outputFormats: z3.array(z3.enum(["h265", "prores"])).min(1).optional()
2352
2499
  });
2353
2500
  var ModelConstraintsSchema = z3.looseObject({
2354
2501
  aspectRatios: z3.array(AspectRatioSchema).optional(),
@@ -2363,7 +2510,14 @@ var ModelConstraintsSchema = z3.looseObject({
2363
2510
  supports: z3.array(SupportedOptionSchema).optional(),
2364
2511
  ratioIgnoredWith: z3.array(z3.enum(["firstFrame", "sourceVideo"])).optional(),
2365
2512
  sourceVideoSeconds: VideoSecondsLimitsSchema.omit({ total: true }).extend({ inReferenceTotal: z3.boolean().optional() }).optional(),
2366
- extension: z3.looseObject({ segmentSeconds: z3.number(), maxTotalSeconds: z3.number() }).optional(),
2513
+ // segmentSeconds/maxTotalSeconds stay required: a kit before 0.3.0 drops a row whose
2514
+ // extension lacks them, so every extension block keeps them.
2515
+ extension: z3.looseObject({
2516
+ segmentSeconds: z3.number(),
2517
+ maxTotalSeconds: z3.number(),
2518
+ returns: z3.enum(["new", "joined"]).optional()
2519
+ }).optional(),
2520
+ tool: VideoToolConstraintsSchema.optional(),
2367
2521
  // Loose on purpose: the read path keeps a row whose value is invalid (the accessors in
2368
2522
  // conventions.ts read it as "not set"); CatalogModelSchema below rejects it on write.
2369
2523
  audio: z3.unknown().optional(),
@@ -2435,6 +2589,7 @@ var CatalogDisplaySchema = z3.looseObject({
2435
2589
  });
2436
2590
  var CatalogCapabilitiesSchema = z3.looseObject({
2437
2591
  acceptsSourceVideo: z3.boolean().optional(),
2592
+ videoEdit: z3.boolean().optional(),
2438
2593
  framesExcludeReferences: z3.boolean().optional(),
2439
2594
  lastFrameRequiresFirstFrame: z3.boolean().optional(),
2440
2595
  reasoning: z3.boolean().optional(),
@@ -2477,6 +2632,13 @@ var CatalogModelSchemaBase = z3.looseObject({
2477
2632
  message: "active text rows need contextWindow"
2478
2633
  });
2479
2634
  }
2635
+ if (row.type === "video-tool" && row.constraints.tool === void 0) {
2636
+ ctx.addIssue({
2637
+ code: "custom",
2638
+ path: ["constraints", "tool"],
2639
+ message: "video-tool rows need constraints.tool"
2640
+ });
2641
+ }
2480
2642
  if (row.replacedBy !== void 0 && row.replacedBy === row.id) {
2481
2643
  ctx.addIssue({
2482
2644
  code: "custom",
@@ -2782,6 +2944,114 @@ function resetLiveCatalog() {
2782
2944
  liveCatalog.replace([...MODELS]);
2783
2945
  }
2784
2946
 
2947
+ // src/catalog/video-tools-spec.ts
2948
+ import { z as z6 } from "zod";
2949
+ var RangeSchema2 = z6.object({ min: z6.number(), max: z6.number() });
2950
+ var ToolSchema = z6.object({
2951
+ kind: z6.enum(["upscale", "interpolate", "hdr"]),
2952
+ upscaleFactor: RangeSchema2.optional(),
2953
+ maxOutputLongEdge: z6.number().positive().optional(),
2954
+ fps: RangeSchema2.optional(),
2955
+ outputFormats: z6.array(z6.enum(["h265", "prores"])).min(1).optional()
2956
+ });
2957
+ function videoToolConstraints(row) {
2958
+ if (row.type !== "video-tool") return void 0;
2959
+ const parsed = ToolSchema.safeParse(row.constraints.tool);
2960
+ if (!parsed.success) return void 0;
2961
+ const { kind, upscaleFactor, maxOutputLongEdge, fps, outputFormats } = parsed.data;
2962
+ return {
2963
+ kind,
2964
+ ...upscaleFactor !== void 0 && { upscaleFactor },
2965
+ ...maxOutputLongEdge !== void 0 && { maxOutputLongEdge },
2966
+ ...fps !== void 0 && { fps },
2967
+ ...outputFormats !== void 0 && { outputFormats }
2968
+ };
2969
+ }
2970
+ function resolutionTier(width, height) {
2971
+ const shortEdge = Math.min(width, height);
2972
+ if (shortEdge <= 360) return "360p";
2973
+ if (shortEdge <= 480) return "480p";
2974
+ if (shortEdge <= 720) return "720p";
2975
+ if (shortEdge <= 1080) return "1080p";
2976
+ if (shortEdge <= 1440) return "2k";
2977
+ return "4k";
2978
+ }
2979
+ function fail(error) {
2980
+ return { ok: false, error };
2981
+ }
2982
+ function planVideoTool(row, req) {
2983
+ const tool = videoToolConstraints(row);
2984
+ if (!tool) return fail(`${row.id} is not a video tool`);
2985
+ const { width, height, durationSeconds, fps } = req.source;
2986
+ if (!(width > 0 && height > 0 && durationSeconds > 0)) {
2987
+ return fail("The source video's width, height and duration are required");
2988
+ }
2989
+ const maxSeconds = row.constraints.sourceVideoSeconds?.max;
2990
+ if (maxSeconds !== void 0 && durationSeconds > maxSeconds) {
2991
+ return fail(`${row.label} takes source videos up to ${maxSeconds} seconds.`);
2992
+ }
2993
+ const base = {
2994
+ durationSeconds,
2995
+ ...fps !== void 0 && { fps }
2996
+ };
2997
+ if (tool.kind === "upscale") {
2998
+ const factor = req.upscaleFactor;
2999
+ if (factor === void 0) return fail("An upscale factor is required");
3000
+ const range = tool.upscaleFactor;
3001
+ if (range && (factor < range.min || factor > range.max)) {
3002
+ return fail(`${row.label} upscales by ${range.min}x to ${range.max}x.`);
3003
+ }
3004
+ const outWidth = Math.round(width * factor);
3005
+ const outHeight = Math.round(height * factor);
3006
+ const limit = tool.maxOutputLongEdge;
3007
+ if (limit !== void 0 && Math.max(outWidth, outHeight) > limit) {
3008
+ return fail(
3009
+ `A ${factor}x upscale of a ${width}x${height} video passes the ${limit}px limit.`
3010
+ );
3011
+ }
3012
+ return {
3013
+ ok: true,
3014
+ output: {
3015
+ ...base,
3016
+ width: outWidth,
3017
+ height: outHeight,
3018
+ resolution: resolutionTier(outWidth, outHeight),
3019
+ format: "h264"
3020
+ }
3021
+ };
3022
+ }
3023
+ if (tool.kind === "interpolate") {
3024
+ const target = req.targetFps;
3025
+ if (target === void 0 || !Number.isInteger(target)) {
3026
+ return fail("A whole-number target frame rate is required");
3027
+ }
3028
+ const range = tool.fps;
3029
+ if (range && (target < range.min || target > range.max)) {
3030
+ return fail(`${row.label} outputs ${range.min} to ${range.max} fps.`);
3031
+ }
3032
+ return {
3033
+ ok: true,
3034
+ output: {
3035
+ durationSeconds,
3036
+ fps: target,
3037
+ width,
3038
+ height,
3039
+ resolution: resolutionTier(width, height),
3040
+ format: "h264"
3041
+ }
3042
+ };
3043
+ }
3044
+ const formats = tool.outputFormats ?? ["h265"];
3045
+ const format = req.outputFormat ?? formats[0] ?? "h265";
3046
+ if (!formats.includes(format)) {
3047
+ return fail(`${row.label} writes ${formats.join(" or ")}.`);
3048
+ }
3049
+ return {
3050
+ ok: true,
3051
+ output: { ...base, width, height, resolution: resolutionTier(width, height), format }
3052
+ };
3053
+ }
3054
+
2785
3055
  // src/resolve-options.ts
2786
3056
  var SIZE_ORDER = ["512", "1K", "2K", "4K"];
2787
3057
  var RESOLUTION_ORDER = ["360p", "480p", "720p", "768p", "1080p", "2k", "4k"];
@@ -3270,6 +3540,7 @@ export {
3270
3540
  SurfaceSchema,
3271
3541
  TEXT_MODELS,
3272
3542
  VIDEO_MODELS,
3543
+ VIDEO_TOOL_MODELS,
3273
3544
  billedAudio,
3274
3545
  compareCatalogOrder,
3275
3546
  isListed,
@@ -3280,14 +3551,17 @@ export {
3280
3551
  nearestAspectRatio,
3281
3552
  normalizeExposure,
3282
3553
  outputAudio,
3554
+ planVideoTool,
3283
3555
  primeCatalog,
3284
3556
  promptConventions,
3285
3557
  resetLiveCatalog,
3558
+ resolutionTier,
3286
3559
  resolveAudioOptions,
3287
3560
  resolveImageOptions,
3288
3561
  resolveOptions,
3289
3562
  resolveVideoOptions,
3290
3563
  sortCatalogRows,
3291
- videoLengthViolation
3564
+ videoLengthViolation,
3565
+ videoToolConstraints
3292
3566
  };
3293
3567
  //# sourceMappingURL=catalog.js.map