@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.js CHANGED
@@ -5129,8 +5129,10 @@ var RAY_RESOLUTIONS = ["360p", "720p", "1080p"];
5129
5129
  var RAY_PROMPT = { referenceSyntax: "numbered", maxChars: 6e3 };
5130
5130
  var LUMA_VIDEO_MODELS = [
5131
5131
  {
5132
- // https://docs.agents.lumalabs.ai: type "video" with a fixed 5 s duration;
5133
- // firstFrame and lastFrame ride as start_frame and end_frame.
5132
+ // https://docs.agents.lumalabs.ai: type "video" with a 5 s or 10 s duration;
5133
+ // firstFrame and lastFrame ride as start_frame and end_frame. Luma bills a
5134
+ // 10 s clip at 3x the 5 s price; pro-backend prices it that way, so this row
5135
+ // must not offer 10 s before that pricing is deployed (ENG-3324).
5134
5136
  id: "ray-3.2",
5135
5137
  aliases: [],
5136
5138
  label: "Ray 3.2",
@@ -5142,7 +5144,7 @@ var LUMA_VIDEO_MODELS = [
5142
5144
  constraints: {
5143
5145
  aspectRatios: ["adaptive", ...RAY_RATIOS],
5144
5146
  resolutions: [...RAY_RESOLUTIONS],
5145
- durations: [5],
5147
+ durations: [5, 10],
5146
5148
  supports: ["lastFrame"],
5147
5149
  prompt: { ...RAY_PROMPT },
5148
5150
  audio: "never"
@@ -5152,7 +5154,7 @@ var LUMA_VIDEO_MODELS = [
5152
5154
  pricingKind: "video_per_second",
5153
5155
  display: {
5154
5156
  provider: "luma",
5155
- description: "Luma Ray 3.2 text- and image-to-video with optional first and last frames, 5-second clips at up to 1080p.",
5157
+ 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.",
5156
5158
  logo: LUMA_LOGO2
5157
5159
  },
5158
5160
  capabilities: {}
@@ -5662,38 +5664,38 @@ var VIDEO_MODELS = [
5662
5664
  capabilities: { ...FRAMES_EXCLUDE_REFERENCES }
5663
5665
  },
5664
5666
  {
5665
- // mode standard/pro/4k selects 720p/1080p/4K; the endpoint takes no separate resolution.
5666
- // Replicate: "Max 7 [images] without video, 4 with video"; 4K does not support
5667
- // reference_video. No `videos` limit yet: the adapter sends no reference video,
5668
- // so `imagesWithVideo` stays dormant until one is wired through.
5667
+ // fal Kling O3 (adapters/fal-kling-omni.ts): mode standard/pro/4k selects 720p/1080p/4K
5668
+ // in the endpoint path. Moved off Replicate kwaivgi/kling-v3-omni-video, which fal sells
5669
+ // at half the price; the id and inputs stay. The Replicate mapper stays for deployed
5670
+ // model_catalog rows not yet switched. Frames and reference images share fal's cap of 7.
5671
+ // Reference video, multi-shot and keep-sound were never wired, so stay off.
5669
5672
  id: "kling-v3-omni-video",
5670
5673
  aliases: ["kwaivgi/kling-v3-omni-video", "kling-v3-omni"],
5671
5674
  label: "Kling V3 Omni",
5672
5675
  type: "video",
5673
- adapter: "replicate",
5674
- providerModelId: "kwaivgi/kling-v3-omni-video",
5676
+ adapter: "fal",
5677
+ providerModelId: "fal-ai/kling-video/o3",
5675
5678
  lifecycle: "active",
5676
5679
  exposure: ["cowork", "space_builder"],
5677
5680
  constraints: {
5678
5681
  aspectRatios: [...KLING_OMNI_RATIOS],
5679
5682
  resolutions: ["720p", "1080p", "4k"],
5680
5683
  durations: { min: 3, max: 15 },
5681
- references: { images: 7, imagesWithVideo: 4 },
5684
+ references: { images: 7, videos: 0, audios: 0 },
5682
5685
  supports: ["generateAudio", "lastFrame"],
5683
5686
  prompt: KLING_PROMPT2,
5684
5687
  audio: "optional"
5685
5688
  },
5686
5689
  defaults: { durationSeconds: 5, aspectRatio: "16:9", generateAudio: true, mode: "pro" },
5687
- usage: { unit: "compute_seconds" },
5690
+ usage: { ...VIDEO_USAGE3 },
5688
5691
  pricingKind: "video_per_second",
5689
5692
  display: {
5690
5693
  provider: "kuaishou",
5691
5694
  logo: "https://cdn.jsdelivr.net/npm/@lobehub/icons-static-png@1.97.0/light/kling-color.png",
5692
- 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."
5695
+ 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."
5693
5696
  }
5694
- // end_image "Requires start_image", but no lastFrameRequiresFirstFrame: SpaceLab resolves
5695
- // replicate rows in error mode, so the flag would refuse its end-frame-only nodes, which
5696
- // the adapter sends as start_image.
5697
+ // No lastFrameRequiresFirstFrame: fal image-to-video needs a start frame, so the adapter
5698
+ // sends a lone end frame as the start frame, as the Replicate model took it.
5697
5699
  },
5698
5700
  {
5699
5701
  id: "minimax-h3",
@@ -9308,11 +9310,11 @@ function toolInput(ctx, req, output) {
9308
9310
  }
9309
9311
  var EXR_DEFAULT_PROMPT = "The same video in high dynamic range, with unchanged content, motion, framing and timing.";
9310
9312
  async function submitFalVideoTool(req, ctx, opts) {
9311
- const plan3 = planVideoTool(ctx.model, req);
9312
- if (!plan3.ok) {
9313
+ const plan4 = planVideoTool(ctx.model, req);
9314
+ if (!plan4.ok) {
9313
9315
  throw new ModelKitError({
9314
9316
  code: "unsupported_option",
9315
- message: plan3.error,
9317
+ message: plan4.error,
9316
9318
  provider: "fal",
9317
9319
  model: ctx.model.id,
9318
9320
  retryable: false
@@ -9320,7 +9322,7 @@ async function submitFalVideoTool(req, ctx, opts) {
9320
9322
  }
9321
9323
  const target = new URL(`${FAL_QUEUE_ORIGIN}/${ctx.model.providerModelId}`);
9322
9324
  if (opts.webhookUrl !== void 0) target.searchParams.set("fal_webhook", opts.webhookUrl);
9323
- const res = await queueFetch(ctx, target.toString(), "POST", toolInput(ctx, req, plan3.output));
9325
+ const res = await queueFetch(ctx, target.toString(), "POST", toolInput(ctx, req, plan4.output));
9324
9326
  const parsed = submitResponseSchema.safeParse(await readJson(ctx, res));
9325
9327
  if (!parsed.success) {
9326
9328
  throw new ModelKitError({
@@ -9338,7 +9340,7 @@ async function submitFalVideoTool(req, ctx, opts) {
9338
9340
  statusUrl: parsed.data.status_url,
9339
9341
  responseUrl: parsed.data.response_url,
9340
9342
  cancelUrl: parsed.data.cancel_url,
9341
- output: plan3.output
9343
+ output: plan4.output
9342
9344
  };
9343
9345
  }
9344
9346
  async function falVideoToolStatus(sub, ctx) {
@@ -10027,14 +10029,14 @@ function planFalImage(req, providerModelId, model) {
10027
10029
  const exact = falExactSize(spec, req);
10028
10030
  const call = { label: spec.label, req, refs, edit: edit2, ...exact !== void 0 && { exact } };
10029
10031
  const built = family.build(call, model);
10030
- const plan3 = {
10032
+ const plan4 = {
10031
10033
  endpoint: edit2 && family.editEndpoint !== void 0 ? family.editEndpoint : id,
10032
10034
  input: built.input,
10033
10035
  output: family.output ?? "images"
10034
10036
  };
10035
- if (built.imageQuality !== void 0) plan3.imageQuality = built.imageQuality;
10036
- if (built.imageSize !== void 0) plan3.imageSize = built.imageSize;
10037
- return plan3;
10037
+ if (built.imageQuality !== void 0) plan4.imageQuality = built.imageQuality;
10038
+ if (built.imageSize !== void 0) plan4.imageSize = built.imageSize;
10039
+ return plan4;
10038
10040
  }
10039
10041
  var FalFileSchema = z14.object({ url: z14.string(), content_type: z14.string().nullish() });
10040
10042
  var ImagesPayloadSchema = z14.object({ images: z14.array(FalFileSchema) });
@@ -10056,18 +10058,18 @@ async function downloadFalFile(ctx, file) {
10056
10058
  const mime = res.headers.get("content-type") ?? file.content_type ?? "";
10057
10059
  return { bytes: new Uint8Array(await res.arrayBuffer()), mime };
10058
10060
  }
10059
- function filesOf(payload, plan3) {
10060
- if (plan3.output === "image") {
10061
+ function filesOf(payload, plan4) {
10062
+ if (plan4.output === "image") {
10061
10063
  const parsed2 = ImagePayloadSchema.safeParse(payload);
10062
10064
  return parsed2.success ? [parsed2.data.image] : [];
10063
10065
  }
10064
10066
  const parsed = ImagesPayloadSchema.safeParse(payload);
10065
10067
  return parsed.success ? parsed.data.images : [];
10066
10068
  }
10067
- async function collectFalPlanImages(payload, plan3, ctx, req) {
10068
- const files = filesOf(payload, plan3);
10069
+ async function collectFalPlanImages(payload, plan4, ctx, req) {
10070
+ const files = filesOf(payload, plan4);
10069
10071
  if (files.length === 0) {
10070
- throw fail2(ctx.model.id, `fal ${plan3.endpoint} returned no image`, "provider_rejected");
10072
+ throw fail2(ctx.model.id, `fal ${plan4.endpoint} returned no image`, "provider_rejected");
10071
10073
  }
10072
10074
  const images = await Promise.all(
10073
10075
  files.map(async (file) => {
@@ -10082,18 +10084,18 @@ async function collectFalPlanImages(payload, plan3, ctx, req) {
10082
10084
  unit: "images",
10083
10085
  quantity: images.length
10084
10086
  };
10085
- const billedSize = plan3.imageSize ?? req.size;
10087
+ const billedSize = plan4.imageSize ?? req.size;
10086
10088
  if (billedSize !== void 0 && ctx.model.type === "image") usage3.imageSize = billedSize;
10087
- if (plan3.imageQuality !== void 0) usage3.imageQuality = plan3.imageQuality;
10089
+ if (plan4.imageQuality !== void 0) usage3.imageQuality = plan4.imageQuality;
10088
10090
  const result = { images, usage: usage3, raw: payload };
10089
10091
  const remote = files.find((f) => !f.url.startsWith("data:"));
10090
10092
  if (remote !== void 0) result.outputUrl = remote.url;
10091
10093
  return result;
10092
10094
  }
10093
10095
  async function generateFalFamilyImage(req, ctx, run) {
10094
- const plan3 = planFalImage(req, ctx.model.providerModelId, ctx.model.id);
10095
- const payload = await run(plan3.endpoint, plan3.input);
10096
- return collectFalPlanImages(payload, plan3, ctx, req);
10096
+ const plan4 = planFalImage(req, ctx.model.providerModelId, ctx.model.id);
10097
+ const payload = await run(plan4.endpoint, plan4.input);
10098
+ return collectFalPlanImages(payload, plan4, ctx, req);
10097
10099
  }
10098
10100
 
10099
10101
  // src/adapters/fal-mask.ts
@@ -11191,10 +11193,50 @@ var KLING_TIERS = [
11191
11193
  { mode: "pro", resolution: "1080p" },
11192
11194
  { mode: "4k", resolution: "4k" }
11193
11195
  ];
11196
+ var KLING_O3_OMNI = "fal-ai/kling-video/o3";
11194
11197
  function klingTier(req) {
11195
11198
  return KLING_TIERS.find((tier) => tier.resolution === req.resolution) ?? KLING_TIERS.find((tier) => tier.mode === req.mode) ?? KLING_TIERS[0];
11196
11199
  }
11197
11200
 
11201
+ // src/adapters/fal-kling-omni.ts
11202
+ function plan(req, ctx) {
11203
+ const tier = klingTier(req);
11204
+ const duration = wholeSeconds(req, ctx);
11205
+ const input = {
11206
+ prompt: req.prompt.trim(),
11207
+ duration: String(duration),
11208
+ generate_audio: req.generateAudio ?? true
11209
+ };
11210
+ const start = req.firstFrame ?? req.lastFrame;
11211
+ const end = req.firstFrame ? req.lastFrame : void 0;
11212
+ const images = urls(req.referenceImages);
11213
+ let task;
11214
+ if (images.length > 0) {
11215
+ task = "reference-to-video";
11216
+ input["image_urls"] = images;
11217
+ if (start) input["start_image_url"] = refToUrl(start);
11218
+ if (end) input["end_image_url"] = refToUrl(end);
11219
+ } else if (start) {
11220
+ task = "image-to-video";
11221
+ input["image_url"] = refToUrl(start);
11222
+ if (end) input["end_image_url"] = refToUrl(end);
11223
+ } else {
11224
+ task = "text-to-video";
11225
+ }
11226
+ const ratio = concreteRatio(req);
11227
+ if (ratio !== void 0 && start === void 0) input["aspect_ratio"] = ratio;
11228
+ return {
11229
+ endpoint: `${KLING_O3_OMNI}/${tier.mode}/${task}`,
11230
+ input,
11231
+ resolution: tier.resolution,
11232
+ seconds: requestedSeconds(duration)
11233
+ };
11234
+ }
11235
+ var klingOmniFamily = {
11236
+ ids: /* @__PURE__ */ new Set([KLING_O3_OMNI]),
11237
+ plan
11238
+ };
11239
+
11198
11240
  // src/resolve-video-rules.ts
11199
11241
  var MOTION_PRO2 = "fal-ai/kling-video/v3/pro/motion-control";
11200
11242
  var MOTION_STANDARD2 = "fal-ai/kling-video/v3/standard/motion-control";
@@ -11339,6 +11381,7 @@ var RULES = /* @__PURE__ */ new Map([
11339
11381
  [MOTION_PRO2, motionControl2],
11340
11382
  [MOTION_STANDARD2, motionControl2],
11341
11383
  [O3_REFERENCE2, o3Reference2],
11384
+ [KLING_O3_OMNI, o3Reference2],
11342
11385
  [O3_EDIT2, (req) => needsSource(req) ?? needsPrompt(req)],
11343
11386
  [H3_TURBO, h3Turbo],
11344
11387
  [MINIMAX_H3_INSERT, h3Insert],
@@ -11368,13 +11411,14 @@ function h3EndFrameRatioWarning(model, ratio) {
11368
11411
  message: `${model.id} follows the end frame's aspect ratio`
11369
11412
  };
11370
11413
  }
11371
- function isReplicateKling(model) {
11414
+ function takesKlingMode(model) {
11415
+ if (model.adapter === "fal") return model.providerModelId.toLowerCase() === KLING_O3_OMNI;
11372
11416
  if (model.adapter !== "replicate") return false;
11373
11417
  const kind = videoKind(model.providerModelId);
11374
11418
  return kind === "kling" || kind === "kling-omni";
11375
11419
  }
11376
- function fillReplicateKlingResolution(model, req) {
11377
- if (isReplicateKling(model) && req.resolution === void 0) {
11420
+ function fillKlingModeResolution(model, req) {
11421
+ if (takesKlingMode(model) && req.resolution === void 0) {
11378
11422
  req.resolution = klingTier(req).resolution;
11379
11423
  }
11380
11424
  }
@@ -11513,7 +11557,7 @@ var minimaxH3MaxFamily = {
11513
11557
 
11514
11558
  // src/adapters/fal-omnihuman.ts
11515
11559
  var OMNIHUMAN_V22 = "bytedance/omnihuman/v2";
11516
- function plan(req, ctx) {
11560
+ function plan2(req, ctx) {
11517
11561
  const [image] = req.referenceImages ?? [];
11518
11562
  const [audio] = req.referenceAudios ?? [];
11519
11563
  if (image === void 0 || audio === void 0) {
@@ -11524,14 +11568,14 @@ function plan(req, ctx) {
11524
11568
  return { endpoint: OMNIHUMAN_V22, input, seconds: inputLengthSeconds(req, audio) };
11525
11569
  }
11526
11570
  var OMNIHUMAN_MODEL_IDS = /* @__PURE__ */ new Set([OMNIHUMAN_V22]);
11527
- var omnihumanFamily = { ids: OMNIHUMAN_MODEL_IDS, plan };
11571
+ var omnihumanFamily = { ids: OMNIHUMAN_MODEL_IDS, plan: plan2 };
11528
11572
 
11529
11573
  // src/adapters/fal-vidu.ts
11530
11574
  var FAMILY2 = "fal-ai/vidu/q4";
11531
11575
  function viduTier(resolution) {
11532
11576
  return resolution === "2k" || resolution === "4k" ? upperTier(resolution) : resolution;
11533
11577
  }
11534
- function plan2(req, ctx) {
11578
+ function plan3(req, ctx) {
11535
11579
  const images = urls(req.referenceImages);
11536
11580
  const audios = urls(req.referenceAudios);
11537
11581
  const duration = wholeSeconds(req, ctx);
@@ -11558,11 +11602,12 @@ function plan2(req, ctx) {
11558
11602
  return { endpoint, input, resolution, seconds: requestedSeconds(duration) };
11559
11603
  }
11560
11604
  var VIDU_MODEL_IDS = /* @__PURE__ */ new Set([FAMILY2]);
11561
- var viduFamily = { ids: VIDU_MODEL_IDS, plan: plan2 };
11605
+ var viduFamily = { ids: VIDU_MODEL_IDS, plan: plan3 };
11562
11606
 
11563
11607
  // src/adapters/fal-video-families.ts
11564
11608
  var FAMILIES2 = [
11565
11609
  klingV3Family,
11610
+ klingOmniFamily,
11566
11611
  minimaxH3MaxFamily,
11567
11612
  happyHorseFamily,
11568
11613
  viduFamily,
@@ -12039,7 +12084,7 @@ function extractVideoUrl(result, endpoint, model) {
12039
12084
  }
12040
12085
  async function generateFalVideo(req, ctx, deps) {
12041
12086
  const providerModelId = ctx.model.providerModelId;
12042
- const plan3 = planFalFamilyVideo(req, ctx);
12087
+ const plan4 = planFalFamilyVideo(req, ctx);
12043
12088
  const start = (endpoint2, input, usageOf2) => startFalVideoJob(
12044
12089
  ctx,
12045
12090
  () => falClientOf(ctx, deps),
@@ -12047,7 +12092,7 @@ async function generateFalVideo(req, ctx, deps) {
12047
12092
  input,
12048
12093
  videoFileDecoder(ctx, req, endpoint2, usageOf2)
12049
12094
  );
12050
- if (plan3 !== void 0) return start(plan3.endpoint, plan3.input, planUsage(ctx, plan3));
12095
+ if (plan4 !== void 0) return start(plan4.endpoint, plan4.input, planUsage(ctx, plan4));
12051
12096
  if (isKling(providerModelId)) return start(KLING_O1_ENDPOINT, klingO1Input(req));
12052
12097
  if (isWan(providerModelId)) {
12053
12098
  const { endpoint: endpoint2, input } = wanVideo(req, providerModelId.toLowerCase());
@@ -12097,9 +12142,9 @@ function videoFileDecoder(ctx, req, endpoint, usageOf2) {
12097
12142
  raw: payload
12098
12143
  });
12099
12144
  }
12100
- function planUsage(ctx, plan3) {
12145
+ function planUsage(ctx, plan4) {
12101
12146
  return (payload) => {
12102
- const seconds3 = plan3.seconds(payload);
12147
+ const seconds3 = plan4.seconds(payload);
12103
12148
  return {
12104
12149
  model: ctx.model.id,
12105
12150
  provider: "fal",
@@ -12107,7 +12152,7 @@ function planUsage(ctx, plan3) {
12107
12152
  unit: "seconds",
12108
12153
  quantity: seconds3,
12109
12154
  durationSeconds: seconds3,
12110
- ...plan3.resolution !== void 0 && { resolution: plan3.resolution }
12155
+ ...plan4.resolution !== void 0 && { resolution: plan4.resolution }
12111
12156
  };
12112
12157
  };
12113
12158
  }
@@ -14132,6 +14177,21 @@ function imageUsage2(ctx) {
14132
14177
  };
14133
14178
  }
14134
14179
  var GENERATE_SECONDS = 5;
14180
+ var GENERATE_LENGTHS = [5, 10];
14181
+ function generateSeconds(req) {
14182
+ return req.durationSeconds ?? GENERATE_SECONDS;
14183
+ }
14184
+ function checkGenerateSeconds(req, ctx) {
14185
+ const seconds3 = generateSeconds(req);
14186
+ if (GENERATE_LENGTHS.includes(seconds3)) return;
14187
+ throw new ModelKitError({
14188
+ code: "unsupported_option",
14189
+ message: `${ctx.model.id} renders ${GENERATE_LENGTHS.join(" or ")} s clips, not ${seconds3} s`,
14190
+ provider: "luma",
14191
+ model: ctx.model.id,
14192
+ retryable: false
14193
+ });
14194
+ }
14135
14195
  var MAX_PLAUSIBLE_SECONDS = 60;
14136
14196
  function editedSeconds(req, bytes) {
14137
14197
  const measured = mp4DurationSeconds(bytes);
@@ -14197,7 +14257,7 @@ function videoBody(req, ctx, mode) {
14197
14257
  prompt,
14198
14258
  video: {
14199
14259
  ...req.resolution !== void 0 && { resolution: req.resolution },
14200
- duration: "5s",
14260
+ duration: `${generateSeconds(req)}s`,
14201
14261
  ...req.firstFrame !== void 0 && { start_frame: imageRef(req.firstFrame) },
14202
14262
  ...req.lastFrame !== void 0 && { end_frame: imageRef(req.lastFrame) }
14203
14263
  }
@@ -14306,7 +14366,7 @@ function lumaVideoJob(ctx, req, mode, id) {
14306
14366
  if (mode === "generate") {
14307
14367
  const result2 = {
14308
14368
  video: { url, expiresAt: new Date(Date.now() + OUTPUT_URL_TTL_MS) },
14309
- usage: videoUsage4(jobCtx, req, mode, GENERATE_SECONDS),
14369
+ usage: videoUsage4(jobCtx, req, mode, generateSeconds(req)),
14310
14370
  raw: { id }
14311
14371
  };
14312
14372
  return result2;
@@ -14334,6 +14394,7 @@ async function generateLumaVideo(req, ctx) {
14334
14394
  });
14335
14395
  }
14336
14396
  const mode = lumaVideoMode(ctx);
14397
+ if (mode === "generate") checkGenerateSeconds(req, ctx);
14337
14398
  const created = await submitGeneration(ctx, videoBody(req, ctx, mode));
14338
14399
  return lumaVideoJob(ctx, req, mode, created.id);
14339
14400
  }
@@ -16545,10 +16606,10 @@ function resolveVideoOptions(model, request, onUnsupported = "error") {
16545
16606
  warnings
16546
16607
  );
16547
16608
  if (aspectRatio !== void 0) merged.aspectRatio = aspectRatio;
16548
- fillReplicateKlingResolution(model, merged);
16609
+ fillKlingModeResolution(model, merged);
16549
16610
  const resolution = resolveVideoResolution(merged.resolution, model, onUnsupported, warnings);
16550
16611
  if (resolution !== void 0) merged.resolution = resolution;
16551
- if (isReplicateKling(model)) delete merged.mode;
16612
+ if (takesKlingMode(model)) delete merged.mode;
16552
16613
  const duration = resolveDuration(
16553
16614
  merged.durationSeconds,
16554
16615
  c.durations,