@shop-os/modelkit 0.2.15 → 0.2.16

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
@@ -1512,7 +1512,14 @@ var VIDEO_MODELS = [
1512
1512
  durations: [3, 4, 5, 6, 7, 8, 9, 10],
1513
1513
  references: { images: 6, videos: 1, videoSeconds: { max: 10 } },
1514
1514
  sourceVideoSeconds: { max: 10 },
1515
- supports: []
1515
+ // Extension (probe 2026-09-24, 360p): each `taskType: "extend"` call
1516
+ // renders `durationSeconds` more and returns the FULL cumulative clip
1517
+ // (3 s -> 6 s -> 9 s); the provider caps the clip at 40 s. A first
1518
+ // extension can upload a <= 10 s source with task "extend"; later ones
1519
+ // chain by `previousInteractionId` (the API rejects the id together
1520
+ // with a video task, so those calls carry the prompt alone).
1521
+ supports: ["videoExtension"],
1522
+ extension: { segmentSeconds: 10, maxTotalSeconds: 40 }
1516
1523
  },
1517
1524
  defaults: {},
1518
1525
  usage: { ...VIDEO_USAGE },
@@ -1520,7 +1527,7 @@ var VIDEO_MODELS = [
1520
1527
  display: {
1521
1528
  provider: "google",
1522
1529
  logo: GOOGLE_LOGO2,
1523
- description: "Google Gemini Omni Flash 1.1 conversational video generation and editing; text, image, and up to 10-second video input, 3-10 second output at up to 4K and 24 FPS."
1530
+ 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."
1524
1531
  },
1525
1532
  capabilities: { acceptsSourceVideo: true }
1526
1533
  },
@@ -2254,6 +2261,7 @@ var SupportedOptionSchema = import_zod2.z.enum([
2254
2261
  "watermark",
2255
2262
  "safetyChecker",
2256
2263
  "taskType",
2264
+ "videoExtension",
2257
2265
  "instrumental"
2258
2266
  ]);
2259
2267
  var UsageUnitSchema = import_zod2.z.enum([
@@ -2301,7 +2309,8 @@ var ModelConstraintsSchema = import_zod2.z.looseObject({
2301
2309
  requires: import_zod2.z.array(RequiredInputSchema).optional(),
2302
2310
  supports: import_zod2.z.array(SupportedOptionSchema).optional(),
2303
2311
  ratioIgnoredWith: import_zod2.z.array(import_zod2.z.enum(["firstFrame", "sourceVideo"])).optional(),
2304
- sourceVideoSeconds: VideoSecondsLimitsSchema.omit({ total: true }).extend({ inReferenceTotal: import_zod2.z.boolean().optional() }).optional()
2312
+ sourceVideoSeconds: VideoSecondsLimitsSchema.omit({ total: true }).extend({ inReferenceTotal: import_zod2.z.boolean().optional() }).optional(),
2313
+ extension: import_zod2.z.looseObject({ segmentSeconds: import_zod2.z.number(), maxTotalSeconds: import_zod2.z.number() }).optional()
2305
2314
  });
2306
2315
  var MediaRefSchema = import_zod2.z.union([
2307
2316
  import_zod2.z.looseObject({
@@ -4568,12 +4577,34 @@ async function generateOmniVideo(req, ctx, createGenAIClient) {
4568
4577
  const modelId = ctx.model.id;
4569
4578
  const providerModelId = ctx.model.providerModelId;
4570
4579
  const duration = req.durationSeconds ?? 10;
4571
- const images = req.referenceImages ?? [];
4572
- const videos = req.referenceVideos ?? [];
4573
- const task = req.sourceVideo ? "edit" : req.firstFrame ? "image_to_video" : images.length > 0 || videos.length > 0 ? "reference_to_video" : "text_to_video";
4580
+ const extend = req.taskType === "extend";
4581
+ const previousId = extend ? req.previousInteractionId : void 0;
4582
+ const chained = previousId !== void 0;
4583
+ for (const field of ["referenceImages", "referenceVideos", "firstFrame"]) {
4584
+ const value = req[field];
4585
+ if (!chained || value === void 0 || Array.isArray(value) && value.length === 0)
4586
+ continue;
4587
+ ctx.warnings.push({
4588
+ code: "option_ignored",
4589
+ field,
4590
+ from: Array.isArray(value) ? value.length : "set",
4591
+ message: `${modelId} chained extension sends the prompt alone; ${field} ignored`
4592
+ });
4593
+ }
4594
+ const images = chained ? [] : req.referenceImages ?? [];
4595
+ const videos = chained ? [] : req.referenceVideos ?? [];
4596
+ const firstFrame = chained ? void 0 : req.firstFrame;
4597
+ if (extend && !chained && req.sourceVideo === void 0) {
4598
+ throw new ModelKitError({
4599
+ code: "missing_input",
4600
+ message: `${modelId} extend needs a source video or a previousInteractionId`,
4601
+ model: modelId
4602
+ });
4603
+ }
4604
+ const task = extend ? "extend" : req.sourceVideo ? "edit" : firstFrame ? "image_to_video" : images.length > 0 || videos.length > 0 ? "reference_to_video" : "text_to_video";
4574
4605
  const isOmniV1 = providerModelId === "gemini-omni-flash-preview";
4575
4606
  const responseFormat = { type: "video", delivery: "uri" };
4576
- if (req.sourceVideo === void 0 && req.aspectRatio !== void 0 && req.aspectRatio !== "adaptive") {
4607
+ if (req.sourceVideo === void 0 && !extend && req.aspectRatio !== void 0 && req.aspectRatio !== "adaptive") {
4577
4608
  responseFormat.aspect_ratio = toOmniAspect(req.aspectRatio);
4578
4609
  }
4579
4610
  if (!isOmniV1) {
@@ -4605,18 +4636,26 @@ async function generateOmniVideo(req, ctx, createGenAIClient) {
4605
4636
  const cleanupUploads = async () => {
4606
4637
  for (const fileName of uploadedNames) await deleteGoogleFile(fileName, apiKey, ctx.fetch);
4607
4638
  };
4608
- const callCreate = (input) => client2.interactions.create({
4609
- model: providerModelId,
4610
- input,
4611
- response_format: responseFormat,
4612
- generation_config: { video_config: { task } },
4613
- store: true
4614
- });
4639
+ const callCreate = (input) => client2.interactions.create(
4640
+ previousId !== void 0 ? {
4641
+ model: providerModelId,
4642
+ input,
4643
+ response_format: responseFormat,
4644
+ store: true,
4645
+ previous_interaction_id: previousId
4646
+ } : {
4647
+ model: providerModelId,
4648
+ input,
4649
+ response_format: responseFormat,
4650
+ generation_config: { video_config: { task } },
4651
+ store: true
4652
+ }
4653
+ );
4615
4654
  const createRetryOptions = mediaRetryOptions(ctx.call, ctx.signal);
4616
4655
  let interaction;
4617
4656
  try {
4618
4657
  const parts = [];
4619
- if (req.sourceVideo) {
4658
+ if (req.sourceVideo && !chained) {
4620
4659
  const media = await resolveMedia(
4621
4660
  req.sourceVideo,
4622
4661
  ctx.fetch,
@@ -4639,11 +4678,11 @@ async function generateOmniVideo(req, ctx, createGenAIClient) {
4639
4678
  mime_type: media.mime
4640
4679
  });
4641
4680
  }
4642
- if (req.firstFrame) {
4643
- if (req.firstFrame.label !== void 0)
4644
- parts.push({ type: "text", text: req.firstFrame.label });
4681
+ if (firstFrame) {
4682
+ if (firstFrame.label !== void 0)
4683
+ parts.push({ type: "text", text: firstFrame.label });
4645
4684
  const media = await resolveMedia(
4646
- req.firstFrame,
4685
+ firstFrame,
4647
4686
  ctx.fetch,
4648
4687
  ctx.signal,
4649
4688
  "image/png",
@@ -4746,6 +4785,7 @@ async function generateOmniVideo(req, ctx, createGenAIClient) {
4746
4785
  return void 0;
4747
4786
  }, pollOptions);
4748
4787
  const video = extractOmniVideo(done, modelId);
4788
+ const providerRef = { interactionId: done.id ?? jobId };
4749
4789
  if (video.data) {
4750
4790
  return {
4751
4791
  video: {
@@ -4753,14 +4793,16 @@ async function generateOmniVideo(req, ctx, createGenAIClient) {
4753
4793
  mime: video.mime_type ?? "video/mp4"
4754
4794
  },
4755
4795
  usage: usage2,
4756
- raw: done
4796
+ raw: done,
4797
+ providerRef
4757
4798
  };
4758
4799
  }
4759
4800
  if (video.uri)
4760
4801
  return {
4761
4802
  video: await downloadVideo(ctx, video.uri, apiKey, signal),
4762
4803
  usage: usage2,
4763
- raw: done
4804
+ raw: done,
4805
+ providerRef
4764
4806
  };
4765
4807
  throw classifyHttpError(
4766
4808
  void 0,
@@ -7652,6 +7694,7 @@ function resolveVideoOptions(model, request, onUnsupported = "error") {
7652
7694
  setOrDelete(merged, "referenceImages", images);
7653
7695
  setOrDelete(merged, "referenceVideos", videos);
7654
7696
  setOrDelete(merged, "referenceAudios", audios);
7697
+ const keepExtend = merged.taskType === "extend" && (model.constraints.supports ?? []).includes("videoExtension");
7655
7698
  dropUnsupported(merged, model, warnings, [
7656
7699
  "seed",
7657
7700
  "negativePrompt",
@@ -7661,8 +7704,17 @@ function resolveVideoOptions(model, request, onUnsupported = "error") {
7661
7704
  "lastFrame",
7662
7705
  "watermark",
7663
7706
  "safetyChecker",
7664
- "taskType"
7707
+ ...keepExtend ? [] : ["taskType"]
7665
7708
  ]);
7709
+ if (merged.previousInteractionId !== void 0 && !keepExtend) {
7710
+ warnings.push({
7711
+ code: "option_ignored",
7712
+ field: "previousInteractionId",
7713
+ from: merged.previousInteractionId,
7714
+ message: `${model.id} does not support previousInteractionId; ignored`
7715
+ });
7716
+ delete merged.previousInteractionId;
7717
+ }
7666
7718
  return { request: merged, warnings };
7667
7719
  }
7668
7720
  function resolveAudioOptions(model, request, onUnsupported = "error") {