@shop-os/modelkit 0.7.7 → 0.7.10

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
@@ -5267,8 +5267,10 @@ var RAY_RESOLUTIONS = ["360p", "720p", "1080p"];
5267
5267
  var RAY_PROMPT = { referenceSyntax: "numbered", maxChars: 6e3 };
5268
5268
  var LUMA_VIDEO_MODELS = [
5269
5269
  {
5270
- // https://docs.agents.lumalabs.ai: type "video" with a fixed 5 s duration;
5271
- // firstFrame and lastFrame ride as start_frame and end_frame.
5270
+ // https://docs.agents.lumalabs.ai: type "video" with a 5 s or 10 s duration;
5271
+ // firstFrame and lastFrame ride as start_frame and end_frame. Luma bills a
5272
+ // 10 s clip at 3x the 5 s price; pro-backend prices it that way, so this row
5273
+ // must not offer 10 s before that pricing is deployed (ENG-3324).
5272
5274
  id: "ray-3.2",
5273
5275
  aliases: [],
5274
5276
  label: "Ray 3.2",
@@ -5280,7 +5282,7 @@ var LUMA_VIDEO_MODELS = [
5280
5282
  constraints: {
5281
5283
  aspectRatios: ["adaptive", ...RAY_RATIOS],
5282
5284
  resolutions: [...RAY_RESOLUTIONS],
5283
- durations: [5],
5285
+ durations: [5, 10],
5284
5286
  supports: ["lastFrame"],
5285
5287
  prompt: { ...RAY_PROMPT },
5286
5288
  audio: "never"
@@ -5290,7 +5292,7 @@ var LUMA_VIDEO_MODELS = [
5290
5292
  pricingKind: "video_per_second",
5291
5293
  display: {
5292
5294
  provider: "luma",
5293
- description: "Luma Ray 3.2 text- and image-to-video with optional first and last frames, 5-second clips at up to 1080p.",
5295
+ description: "Luma Ray 3.2 text- and image-to-video with optional first and last frames, 5- or 10-second clips at up to 1080p.",
5294
5296
  logo: LUMA_LOGO2
5295
5297
  },
5296
5298
  capabilities: {}
@@ -5800,38 +5802,38 @@ var VIDEO_MODELS = [
5800
5802
  capabilities: { ...FRAMES_EXCLUDE_REFERENCES }
5801
5803
  },
5802
5804
  {
5803
- // mode standard/pro/4k selects 720p/1080p/4K; the endpoint takes no separate resolution.
5804
- // Replicate: "Max 7 [images] without video, 4 with video"; 4K does not support
5805
- // reference_video. No `videos` limit yet: the adapter sends no reference video,
5806
- // so `imagesWithVideo` stays dormant until one is wired through.
5805
+ // fal Kling O3 (adapters/fal-kling-omni.ts): mode standard/pro/4k selects 720p/1080p/4K
5806
+ // in the endpoint path. Moved off Replicate kwaivgi/kling-v3-omni-video, which fal sells
5807
+ // at half the price; the id and inputs stay. The Replicate mapper stays for deployed
5808
+ // model_catalog rows not yet switched. Frames and reference images share fal's cap of 7.
5809
+ // Reference video, multi-shot and keep-sound were never wired, so stay off.
5807
5810
  id: "kling-v3-omni-video",
5808
5811
  aliases: ["kwaivgi/kling-v3-omni-video", "kling-v3-omni"],
5809
5812
  label: "Kling V3 Omni",
5810
5813
  type: "video",
5811
- adapter: "replicate",
5812
- providerModelId: "kwaivgi/kling-v3-omni-video",
5814
+ adapter: "fal",
5815
+ providerModelId: "fal-ai/kling-video/o3",
5813
5816
  lifecycle: "active",
5814
5817
  exposure: ["cowork", "space_builder"],
5815
5818
  constraints: {
5816
5819
  aspectRatios: [...KLING_OMNI_RATIOS],
5817
5820
  resolutions: ["720p", "1080p", "4k"],
5818
5821
  durations: { min: 3, max: 15 },
5819
- references: { images: 7, imagesWithVideo: 4 },
5822
+ references: { images: 7, videos: 0, audios: 0 },
5820
5823
  supports: ["generateAudio", "lastFrame"],
5821
5824
  prompt: KLING_PROMPT2,
5822
5825
  audio: "optional"
5823
5826
  },
5824
5827
  defaults: { durationSeconds: 5, aspectRatio: "16:9", generateAudio: true, mode: "pro" },
5825
- usage: { unit: "compute_seconds" },
5828
+ usage: { ...VIDEO_USAGE3 },
5826
5829
  pricingKind: "video_per_second",
5827
5830
  display: {
5828
5831
  provider: "kuaishou",
5829
5832
  logo: "https://cdn.jsdelivr.net/npm/@lobehub/icons-static-png@1.97.0/light/kling-color.png",
5830
- description: "Kuaishou Kling Video 3.0 Omni unified generation and editing from text, start/end images, up to 7 reference images, and reference video; up to 15 seconds at up to 4K."
5833
+ description: "Kuaishou Kling Video 3.0 Omni generation from text, start/end images and up to 7 reference images, with optional native audio; up to 15 seconds at up to 4K."
5831
5834
  }
5832
- // end_image "Requires start_image", but no lastFrameRequiresFirstFrame: SpaceLab resolves
5833
- // replicate rows in error mode, so the flag would refuse its end-frame-only nodes, which
5834
- // the adapter sends as start_image.
5835
+ // No lastFrameRequiresFirstFrame: fal image-to-video needs a start frame, so the adapter
5836
+ // sends a lone end frame as the start frame, as the Replicate model took it.
5835
5837
  },
5836
5838
  {
5837
5839
  id: "minimax-h3",
@@ -9446,11 +9448,11 @@ function toolInput(ctx, req, output) {
9446
9448
  }
9447
9449
  var EXR_DEFAULT_PROMPT = "The same video in high dynamic range, with unchanged content, motion, framing and timing.";
9448
9450
  async function submitFalVideoTool(req, ctx, opts) {
9449
- const plan3 = planVideoTool(ctx.model, req);
9450
- if (!plan3.ok) {
9451
+ const plan4 = planVideoTool(ctx.model, req);
9452
+ if (!plan4.ok) {
9451
9453
  throw new ModelKitError({
9452
9454
  code: "unsupported_option",
9453
- message: plan3.error,
9455
+ message: plan4.error,
9454
9456
  provider: "fal",
9455
9457
  model: ctx.model.id,
9456
9458
  retryable: false
@@ -9458,7 +9460,7 @@ async function submitFalVideoTool(req, ctx, opts) {
9458
9460
  }
9459
9461
  const target = new URL(`${FAL_QUEUE_ORIGIN}/${ctx.model.providerModelId}`);
9460
9462
  if (opts.webhookUrl !== void 0) target.searchParams.set("fal_webhook", opts.webhookUrl);
9461
- const res = await queueFetch(ctx, target.toString(), "POST", toolInput(ctx, req, plan3.output));
9463
+ const res = await queueFetch(ctx, target.toString(), "POST", toolInput(ctx, req, plan4.output));
9462
9464
  const parsed = submitResponseSchema.safeParse(await readJson(ctx, res));
9463
9465
  if (!parsed.success) {
9464
9466
  throw new ModelKitError({
@@ -9476,7 +9478,7 @@ async function submitFalVideoTool(req, ctx, opts) {
9476
9478
  statusUrl: parsed.data.status_url,
9477
9479
  responseUrl: parsed.data.response_url,
9478
9480
  cancelUrl: parsed.data.cancel_url,
9479
- output: plan3.output
9481
+ output: plan4.output
9480
9482
  };
9481
9483
  }
9482
9484
  async function falVideoToolStatus(sub, ctx) {
@@ -10165,14 +10167,14 @@ function planFalImage(req, providerModelId, model) {
10165
10167
  const exact = falExactSize(spec, req);
10166
10168
  const call = { label: spec.label, req, refs, edit: edit2, ...exact !== void 0 && { exact } };
10167
10169
  const built = family.build(call, model);
10168
- const plan3 = {
10170
+ const plan4 = {
10169
10171
  endpoint: edit2 && family.editEndpoint !== void 0 ? family.editEndpoint : id,
10170
10172
  input: built.input,
10171
10173
  output: family.output ?? "images"
10172
10174
  };
10173
- if (built.imageQuality !== void 0) plan3.imageQuality = built.imageQuality;
10174
- if (built.imageSize !== void 0) plan3.imageSize = built.imageSize;
10175
- return plan3;
10175
+ if (built.imageQuality !== void 0) plan4.imageQuality = built.imageQuality;
10176
+ if (built.imageSize !== void 0) plan4.imageSize = built.imageSize;
10177
+ return plan4;
10176
10178
  }
10177
10179
  var FalFileSchema = import_zod14.z.object({ url: import_zod14.z.string(), content_type: import_zod14.z.string().nullish() });
10178
10180
  var ImagesPayloadSchema = import_zod14.z.object({ images: import_zod14.z.array(FalFileSchema) });
@@ -10194,18 +10196,18 @@ async function downloadFalFile(ctx, file) {
10194
10196
  const mime = res.headers.get("content-type") ?? file.content_type ?? "";
10195
10197
  return { bytes: new Uint8Array(await res.arrayBuffer()), mime };
10196
10198
  }
10197
- function filesOf(payload, plan3) {
10198
- if (plan3.output === "image") {
10199
+ function filesOf(payload, plan4) {
10200
+ if (plan4.output === "image") {
10199
10201
  const parsed2 = ImagePayloadSchema.safeParse(payload);
10200
10202
  return parsed2.success ? [parsed2.data.image] : [];
10201
10203
  }
10202
10204
  const parsed = ImagesPayloadSchema.safeParse(payload);
10203
10205
  return parsed.success ? parsed.data.images : [];
10204
10206
  }
10205
- async function collectFalPlanImages(payload, plan3, ctx, req) {
10206
- const files = filesOf(payload, plan3);
10207
+ async function collectFalPlanImages(payload, plan4, ctx, req) {
10208
+ const files = filesOf(payload, plan4);
10207
10209
  if (files.length === 0) {
10208
- throw fail2(ctx.model.id, `fal ${plan3.endpoint} returned no image`, "provider_rejected");
10210
+ throw fail2(ctx.model.id, `fal ${plan4.endpoint} returned no image`, "provider_rejected");
10209
10211
  }
10210
10212
  const images = await Promise.all(
10211
10213
  files.map(async (file) => {
@@ -10220,18 +10222,18 @@ async function collectFalPlanImages(payload, plan3, ctx, req) {
10220
10222
  unit: "images",
10221
10223
  quantity: images.length
10222
10224
  };
10223
- const billedSize = plan3.imageSize ?? req.size;
10225
+ const billedSize = plan4.imageSize ?? req.size;
10224
10226
  if (billedSize !== void 0 && ctx.model.type === "image") usage3.imageSize = billedSize;
10225
- if (plan3.imageQuality !== void 0) usage3.imageQuality = plan3.imageQuality;
10227
+ if (plan4.imageQuality !== void 0) usage3.imageQuality = plan4.imageQuality;
10226
10228
  const result = { images, usage: usage3, raw: payload };
10227
10229
  const remote = files.find((f) => !f.url.startsWith("data:"));
10228
10230
  if (remote !== void 0) result.outputUrl = remote.url;
10229
10231
  return result;
10230
10232
  }
10231
10233
  async function generateFalFamilyImage(req, ctx, run) {
10232
- const plan3 = planFalImage(req, ctx.model.providerModelId, ctx.model.id);
10233
- const payload = await run(plan3.endpoint, plan3.input);
10234
- return collectFalPlanImages(payload, plan3, ctx, req);
10234
+ const plan4 = planFalImage(req, ctx.model.providerModelId, ctx.model.id);
10235
+ const payload = await run(plan4.endpoint, plan4.input);
10236
+ return collectFalPlanImages(payload, plan4, ctx, req);
10235
10237
  }
10236
10238
 
10237
10239
  // src/adapters/fal-mask.ts
@@ -11329,10 +11331,50 @@ var KLING_TIERS = [
11329
11331
  { mode: "pro", resolution: "1080p" },
11330
11332
  { mode: "4k", resolution: "4k" }
11331
11333
  ];
11334
+ var KLING_O3_OMNI = "fal-ai/kling-video/o3";
11332
11335
  function klingTier(req) {
11333
11336
  return KLING_TIERS.find((tier) => tier.resolution === req.resolution) ?? KLING_TIERS.find((tier) => tier.mode === req.mode) ?? KLING_TIERS[0];
11334
11337
  }
11335
11338
 
11339
+ // src/adapters/fal-kling-omni.ts
11340
+ function plan(req, ctx) {
11341
+ const tier = klingTier(req);
11342
+ const duration = wholeSeconds(req, ctx);
11343
+ const input = {
11344
+ prompt: req.prompt.trim(),
11345
+ duration: String(duration),
11346
+ generate_audio: req.generateAudio ?? true
11347
+ };
11348
+ const start = req.firstFrame ?? req.lastFrame;
11349
+ const end = req.firstFrame ? req.lastFrame : void 0;
11350
+ const images = urls(req.referenceImages);
11351
+ let task;
11352
+ if (images.length > 0) {
11353
+ task = "reference-to-video";
11354
+ input["image_urls"] = images;
11355
+ if (start) input["start_image_url"] = refToUrl(start);
11356
+ if (end) input["end_image_url"] = refToUrl(end);
11357
+ } else if (start) {
11358
+ task = "image-to-video";
11359
+ input["image_url"] = refToUrl(start);
11360
+ if (end) input["end_image_url"] = refToUrl(end);
11361
+ } else {
11362
+ task = "text-to-video";
11363
+ }
11364
+ const ratio = concreteRatio(req);
11365
+ if (ratio !== void 0 && start === void 0) input["aspect_ratio"] = ratio;
11366
+ return {
11367
+ endpoint: `${KLING_O3_OMNI}/${tier.mode}/${task}`,
11368
+ input,
11369
+ resolution: tier.resolution,
11370
+ seconds: requestedSeconds(duration)
11371
+ };
11372
+ }
11373
+ var klingOmniFamily = {
11374
+ ids: /* @__PURE__ */ new Set([KLING_O3_OMNI]),
11375
+ plan
11376
+ };
11377
+
11336
11378
  // src/resolve-video-rules.ts
11337
11379
  var MOTION_PRO2 = "fal-ai/kling-video/v3/pro/motion-control";
11338
11380
  var MOTION_STANDARD2 = "fal-ai/kling-video/v3/standard/motion-control";
@@ -11477,6 +11519,7 @@ var RULES = /* @__PURE__ */ new Map([
11477
11519
  [MOTION_PRO2, motionControl2],
11478
11520
  [MOTION_STANDARD2, motionControl2],
11479
11521
  [O3_REFERENCE2, o3Reference2],
11522
+ [KLING_O3_OMNI, o3Reference2],
11480
11523
  [O3_EDIT2, (req) => needsSource(req) ?? needsPrompt(req)],
11481
11524
  [H3_TURBO, h3Turbo],
11482
11525
  [MINIMAX_H3_INSERT, h3Insert],
@@ -11506,13 +11549,14 @@ function h3EndFrameRatioWarning(model, ratio) {
11506
11549
  message: `${model.id} follows the end frame's aspect ratio`
11507
11550
  };
11508
11551
  }
11509
- function isReplicateKling(model) {
11552
+ function takesKlingMode(model) {
11553
+ if (model.adapter === "fal") return model.providerModelId.toLowerCase() === KLING_O3_OMNI;
11510
11554
  if (model.adapter !== "replicate") return false;
11511
11555
  const kind = videoKind(model.providerModelId);
11512
11556
  return kind === "kling" || kind === "kling-omni";
11513
11557
  }
11514
- function fillReplicateKlingResolution(model, req) {
11515
- if (isReplicateKling(model) && req.resolution === void 0) {
11558
+ function fillKlingModeResolution(model, req) {
11559
+ if (takesKlingMode(model) && req.resolution === void 0) {
11516
11560
  req.resolution = klingTier(req).resolution;
11517
11561
  }
11518
11562
  }
@@ -11651,7 +11695,7 @@ var minimaxH3MaxFamily = {
11651
11695
 
11652
11696
  // src/adapters/fal-omnihuman.ts
11653
11697
  var OMNIHUMAN_V22 = "bytedance/omnihuman/v2";
11654
- function plan(req, ctx) {
11698
+ function plan2(req, ctx) {
11655
11699
  const [image] = req.referenceImages ?? [];
11656
11700
  const [audio] = req.referenceAudios ?? [];
11657
11701
  if (image === void 0 || audio === void 0) {
@@ -11662,14 +11706,14 @@ function plan(req, ctx) {
11662
11706
  return { endpoint: OMNIHUMAN_V22, input, seconds: inputLengthSeconds(req, audio) };
11663
11707
  }
11664
11708
  var OMNIHUMAN_MODEL_IDS = /* @__PURE__ */ new Set([OMNIHUMAN_V22]);
11665
- var omnihumanFamily = { ids: OMNIHUMAN_MODEL_IDS, plan };
11709
+ var omnihumanFamily = { ids: OMNIHUMAN_MODEL_IDS, plan: plan2 };
11666
11710
 
11667
11711
  // src/adapters/fal-vidu.ts
11668
11712
  var FAMILY2 = "fal-ai/vidu/q4";
11669
11713
  function viduTier(resolution) {
11670
11714
  return resolution === "2k" || resolution === "4k" ? upperTier(resolution) : resolution;
11671
11715
  }
11672
- function plan2(req, ctx) {
11716
+ function plan3(req, ctx) {
11673
11717
  const images = urls(req.referenceImages);
11674
11718
  const audios = urls(req.referenceAudios);
11675
11719
  const duration = wholeSeconds(req, ctx);
@@ -11696,11 +11740,12 @@ function plan2(req, ctx) {
11696
11740
  return { endpoint, input, resolution, seconds: requestedSeconds(duration) };
11697
11741
  }
11698
11742
  var VIDU_MODEL_IDS = /* @__PURE__ */ new Set([FAMILY2]);
11699
- var viduFamily = { ids: VIDU_MODEL_IDS, plan: plan2 };
11743
+ var viduFamily = { ids: VIDU_MODEL_IDS, plan: plan3 };
11700
11744
 
11701
11745
  // src/adapters/fal-video-families.ts
11702
11746
  var FAMILIES2 = [
11703
11747
  klingV3Family,
11748
+ klingOmniFamily,
11704
11749
  minimaxH3MaxFamily,
11705
11750
  happyHorseFamily,
11706
11751
  viduFamily,
@@ -12177,7 +12222,7 @@ function extractVideoUrl(result, endpoint, model) {
12177
12222
  }
12178
12223
  async function generateFalVideo(req, ctx, deps) {
12179
12224
  const providerModelId = ctx.model.providerModelId;
12180
- const plan3 = planFalFamilyVideo(req, ctx);
12225
+ const plan4 = planFalFamilyVideo(req, ctx);
12181
12226
  const start = (endpoint2, input, usageOf2) => startFalVideoJob(
12182
12227
  ctx,
12183
12228
  () => falClientOf(ctx, deps),
@@ -12185,7 +12230,7 @@ async function generateFalVideo(req, ctx, deps) {
12185
12230
  input,
12186
12231
  videoFileDecoder(ctx, req, endpoint2, usageOf2)
12187
12232
  );
12188
- if (plan3 !== void 0) return start(plan3.endpoint, plan3.input, planUsage(ctx, plan3));
12233
+ if (plan4 !== void 0) return start(plan4.endpoint, plan4.input, planUsage(ctx, plan4));
12189
12234
  if (isKling(providerModelId)) return start(KLING_O1_ENDPOINT, klingO1Input(req));
12190
12235
  if (isWan(providerModelId)) {
12191
12236
  const { endpoint: endpoint2, input } = wanVideo(req, providerModelId.toLowerCase());
@@ -12235,9 +12280,9 @@ function videoFileDecoder(ctx, req, endpoint, usageOf2) {
12235
12280
  raw: payload
12236
12281
  });
12237
12282
  }
12238
- function planUsage(ctx, plan3) {
12283
+ function planUsage(ctx, plan4) {
12239
12284
  return (payload) => {
12240
- const seconds3 = plan3.seconds(payload);
12285
+ const seconds3 = plan4.seconds(payload);
12241
12286
  return {
12242
12287
  model: ctx.model.id,
12243
12288
  provider: "fal",
@@ -12245,7 +12290,7 @@ function planUsage(ctx, plan3) {
12245
12290
  unit: "seconds",
12246
12291
  quantity: seconds3,
12247
12292
  durationSeconds: seconds3,
12248
- ...plan3.resolution !== void 0 && { resolution: plan3.resolution }
12293
+ ...plan4.resolution !== void 0 && { resolution: plan4.resolution }
12249
12294
  };
12250
12295
  };
12251
12296
  }
@@ -14268,6 +14313,21 @@ function imageUsage2(ctx) {
14268
14313
  };
14269
14314
  }
14270
14315
  var GENERATE_SECONDS = 5;
14316
+ var GENERATE_LENGTHS = [5, 10];
14317
+ function generateSeconds(req) {
14318
+ return req.durationSeconds ?? GENERATE_SECONDS;
14319
+ }
14320
+ function checkGenerateSeconds(req, ctx) {
14321
+ const seconds3 = generateSeconds(req);
14322
+ if (GENERATE_LENGTHS.includes(seconds3)) return;
14323
+ throw new ModelKitError({
14324
+ code: "unsupported_option",
14325
+ message: `${ctx.model.id} renders ${GENERATE_LENGTHS.join(" or ")} s clips, not ${seconds3} s`,
14326
+ provider: "luma",
14327
+ model: ctx.model.id,
14328
+ retryable: false
14329
+ });
14330
+ }
14271
14331
  var MAX_PLAUSIBLE_SECONDS = 60;
14272
14332
  function editedSeconds(req, bytes) {
14273
14333
  const measured = mp4DurationSeconds(bytes);
@@ -14333,7 +14393,7 @@ function videoBody(req, ctx, mode) {
14333
14393
  prompt,
14334
14394
  video: {
14335
14395
  ...req.resolution !== void 0 && { resolution: req.resolution },
14336
- duration: "5s",
14396
+ duration: `${generateSeconds(req)}s`,
14337
14397
  ...req.firstFrame !== void 0 && { start_frame: imageRef(req.firstFrame) },
14338
14398
  ...req.lastFrame !== void 0 && { end_frame: imageRef(req.lastFrame) }
14339
14399
  }
@@ -14442,7 +14502,7 @@ function lumaVideoJob(ctx, req, mode, id) {
14442
14502
  if (mode === "generate") {
14443
14503
  const result2 = {
14444
14504
  video: { url, expiresAt: new Date(Date.now() + OUTPUT_URL_TTL_MS) },
14445
- usage: videoUsage4(jobCtx, req, mode, GENERATE_SECONDS),
14505
+ usage: videoUsage4(jobCtx, req, mode, generateSeconds(req)),
14446
14506
  raw: { id }
14447
14507
  };
14448
14508
  return result2;
@@ -14470,6 +14530,7 @@ async function generateLumaVideo(req, ctx) {
14470
14530
  });
14471
14531
  }
14472
14532
  const mode = lumaVideoMode(ctx);
14533
+ if (mode === "generate") checkGenerateSeconds(req, ctx);
14473
14534
  const created = await submitGeneration(ctx, videoBody(req, ctx, mode));
14474
14535
  return lumaVideoJob(ctx, req, mode, created.id);
14475
14536
  }
@@ -16681,10 +16742,10 @@ function resolveVideoOptions(model, request, onUnsupported = "error") {
16681
16742
  warnings
16682
16743
  );
16683
16744
  if (aspectRatio !== void 0) merged.aspectRatio = aspectRatio;
16684
- fillReplicateKlingResolution(model, merged);
16745
+ fillKlingModeResolution(model, merged);
16685
16746
  const resolution = resolveVideoResolution(merged.resolution, model, onUnsupported, warnings);
16686
16747
  if (resolution !== void 0) merged.resolution = resolution;
16687
- if (isReplicateKling(model)) delete merged.mode;
16748
+ if (takesKlingMode(model)) delete merged.mode;
16688
16749
  const duration = resolveDuration(
16689
16750
  merged.durationSeconds,
16690
16751
  c.durations,