@shop-os/modelkit 0.7.2 → 0.7.3

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
@@ -13672,24 +13672,24 @@ import { z as z25 } from "zod";
13672
13672
  function mp4DurationSeconds(bytes) {
13673
13673
  const moov = findBox(bytes, 0, bytes.length, "moov");
13674
13674
  if (!moov) return void 0;
13675
- const mvhd = findBox(bytes, moov.start + 8, moov.end, "mvhd");
13675
+ const mvhd = findBox(bytes, moov.body, moov.end, "mvhd");
13676
13676
  if (!mvhd) return void 0;
13677
13677
  const view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);
13678
- const size = mvhd.end - mvhd.start;
13679
- if (size < 8 + 4) return void 0;
13680
- const version = view.getUint8(mvhd.start + 8);
13678
+ const size = mvhd.end - mvhd.body;
13679
+ if (size < 4) return void 0;
13680
+ const version = view.getUint8(mvhd.body);
13681
13681
  if (version === 0) {
13682
- if (size < 8 + 4 + 4 + 4 + 4 + 4) return void 0;
13683
- const timescale = view.getUint32(mvhd.start + 8 + 4 + 4 + 4);
13684
- const duration = view.getUint32(mvhd.start + 8 + 4 + 4 + 4 + 4);
13682
+ if (size < 4 + 4 + 4 + 4 + 4) return void 0;
13683
+ const timescale = view.getUint32(mvhd.body + 4 + 4 + 4);
13684
+ const duration = view.getUint32(mvhd.body + 4 + 4 + 4 + 4);
13685
13685
  if (timescale === 0) return void 0;
13686
13686
  return duration / timescale;
13687
13687
  }
13688
13688
  if (version === 1) {
13689
- if (size < 8 + 4 + 8 + 8 + 4 + 8) return void 0;
13690
- const timescale = view.getUint32(mvhd.start + 8 + 4 + 8 + 8);
13691
- const high = view.getUint32(mvhd.start + 8 + 4 + 8 + 8 + 4);
13692
- const low = view.getUint32(mvhd.start + 8 + 4 + 8 + 8 + 4 + 4);
13689
+ if (size < 4 + 8 + 8 + 4 + 8) return void 0;
13690
+ const timescale = view.getUint32(mvhd.body + 4 + 8 + 8);
13691
+ const high = view.getUint32(mvhd.body + 4 + 8 + 8 + 4);
13692
+ const low = view.getUint32(mvhd.body + 4 + 8 + 8 + 4 + 4);
13693
13693
  if (timescale === 0) return void 0;
13694
13694
  return (high * 2 ** 32 + low) / timescale;
13695
13695
  }
@@ -13699,15 +13699,23 @@ function findBox(bytes, from, to, type) {
13699
13699
  const view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);
13700
13700
  let offset = from;
13701
13701
  while (offset + 8 <= to) {
13702
- const size = view.getUint32(offset);
13702
+ let size = view.getUint32(offset);
13703
+ let header = 8;
13703
13704
  const name = String.fromCharCode(
13704
13705
  view.getUint8(offset + 4),
13705
13706
  view.getUint8(offset + 5),
13706
13707
  view.getUint8(offset + 6),
13707
13708
  view.getUint8(offset + 7)
13708
13709
  );
13709
- if (size < 8 || offset + size > to) return void 0;
13710
- if (name === type) return { start: offset, end: offset + size };
13710
+ if (size === 1) {
13711
+ if (offset + 16 > to) return void 0;
13712
+ size = view.getUint32(offset + 8) * 2 ** 32 + view.getUint32(offset + 12);
13713
+ header = 16;
13714
+ } else if (size === 0) {
13715
+ size = to - offset;
13716
+ }
13717
+ if (size < header || offset + size > to) return void 0;
13718
+ if (name === type) return { body: offset + header, end: offset + size };
13711
13719
  offset += size;
13712
13720
  }
13713
13721
  return void 0;
@@ -13720,6 +13728,7 @@ var IMAGE_TIMEOUT_MS = 5 * 6e4;
13720
13728
  var VIDEO_POLL_INTERVAL_MS = 5e3;
13721
13729
  var VIDEO_TIMEOUT_MS = 20 * 6e4;
13722
13730
  var PROMPT_MAX_CHARS = 6e3;
13731
+ var OUTPUT_URL_TTL_MS = 55 * 6e4;
13723
13732
  var LUMA_IMAGE_MODEL_IDS = /* @__PURE__ */ new Set(["uni-1", "uni-1-max"]);
13724
13733
  var LUMA_VIDEO_MODEL_ID = "ray-3.2";
13725
13734
  var LUMA_VIDEO_MODES = /* @__PURE__ */ new Map([
@@ -13738,6 +13747,9 @@ function lumaRowRefusal(row2) {
13738
13747
  }
13739
13748
  return `luma has no ${row2.type} rows`;
13740
13749
  }
13750
+ function lumaModel(ctx) {
13751
+ return ctx.model.providerModelId.toLowerCase();
13752
+ }
13741
13753
  function lumaBase(ctx) {
13742
13754
  return ctx.endpoints?.luma ?? DEFAULT_BASE_URL;
13743
13755
  }
@@ -13780,8 +13792,23 @@ function imageRef(ref) {
13780
13792
  if ("url" in ref) return { url: ref.url };
13781
13793
  return { data: mediaRefToBase64(ref.bytes), media_type: ref.mime };
13782
13794
  }
13795
+ var VIDEO_TYPES_BY_EXTENSION = /* @__PURE__ */ new Map([
13796
+ ["mp4", "video/mp4"],
13797
+ ["m4v", "video/mp4"],
13798
+ ["mov", "video/quicktime"],
13799
+ ["webm", "video/webm"]
13800
+ ]);
13801
+ function videoUrlType(url) {
13802
+ let path = url;
13803
+ try {
13804
+ path = new URL(url).pathname;
13805
+ } catch {
13806
+ }
13807
+ const extension = path.slice(path.lastIndexOf(".") + 1).toLowerCase();
13808
+ return VIDEO_TYPES_BY_EXTENSION.get(extension) ?? "video/mp4";
13809
+ }
13783
13810
  function videoRef(ref) {
13784
- if ("url" in ref) return { url: ref.url, media_type: "video/mp4" };
13811
+ if ("url" in ref) return { url: ref.url, media_type: videoUrlType(ref.url) };
13785
13812
  const mediaType = ref.mime.startsWith("video/") ? ref.mime : "video/mp4";
13786
13813
  return { data: mediaRefToBase64(ref.bytes), media_type: mediaType };
13787
13814
  }
@@ -13945,14 +13972,22 @@ async function lumaRequest(ctx, url, body) {
13945
13972
  }
13946
13973
  return res;
13947
13974
  }
13975
+ async function parseGeneration(res) {
13976
+ try {
13977
+ const parsed = lumaGenerationSchema.safeParse(await res.json());
13978
+ return parsed.success ? parsed.data : void 0;
13979
+ } catch {
13980
+ return "not_json";
13981
+ }
13982
+ }
13948
13983
  async function submitGeneration(ctx, body) {
13949
13984
  const retryOptions = mediaRetryOptions(ctx.call, ctx.signal);
13950
13985
  const res = await withRetry(
13951
13986
  () => lumaRequest(ctx, `${lumaBase(ctx)}/v1/generations`, JSON.stringify(body)),
13952
13987
  retryOptions
13953
13988
  );
13954
- const parsed = lumaGenerationSchema.safeParse(await res.json());
13955
- if (!parsed.success) {
13989
+ const generation = await parseGeneration(res);
13990
+ if (generation === void 0 || generation === "not_json") {
13956
13991
  throw new ModelKitError({
13957
13992
  code: "provider_rejected",
13958
13993
  message: "Luma submit returned no generation id",
@@ -13961,13 +13996,20 @@ async function submitGeneration(ctx, body) {
13961
13996
  retryable: false
13962
13997
  });
13963
13998
  }
13964
- return parsed.data;
13999
+ return generation;
13965
14000
  }
13966
14001
  async function readGeneration(ctx, id) {
13967
14002
  const res = await lumaRequest(ctx, `${lumaBase(ctx)}/v1/generations/${id}`);
13968
- const payload = JsonValueSchema.parse(await res.json());
13969
- const parsed = lumaGenerationSchema.safeParse(payload);
13970
- if (!parsed.success) {
14003
+ const generation = await parseGeneration(res);
14004
+ if (generation === "not_json") {
14005
+ throw new ModelKitError({
14006
+ code: "network",
14007
+ message: "Luma returned a poll response that is not JSON",
14008
+ provider: "luma",
14009
+ model: ctx.model.id
14010
+ });
14011
+ }
14012
+ if (generation === void 0) {
13971
14013
  throw new ModelKitError({
13972
14014
  code: "provider_rejected",
13973
14015
  message: "Luma returned an unreadable generation",
@@ -13976,14 +14018,12 @@ async function readGeneration(ctx, id) {
13976
14018
  retryable: false
13977
14019
  });
13978
14020
  }
13979
- return parsed.data;
14021
+ return generation;
13980
14022
  }
13981
14023
  function outputUrl(generation, type) {
13982
14024
  const outputs = generation.output ?? [];
13983
- const match = outputs.find((item) => item.type === type && item.url);
13984
- if (match?.url) return match.url;
13985
- const fallback = outputs.find((item) => item.url);
13986
- return fallback?.url;
14025
+ const match = outputs.find((item) => item.type === type && item.url) ?? outputs.find((item) => item.type === void 0 && item.url);
14026
+ return match?.url;
13987
14027
  }
13988
14028
  async function readGenerationOnce(ctx, id) {
13989
14029
  try {
@@ -14022,21 +14062,22 @@ async function checkGeneration(ctx, id, type) {
14022
14062
  return void 0;
14023
14063
  }
14024
14064
  async function downloadBytes(ctx, url, fallback) {
14025
- let bytes;
14026
- let header;
14027
- try {
14028
- const out = await mediaRefToBytes({ url }, ctx.fetch, {
14029
- ...ctx.signal !== void 0 && { signal: ctx.signal },
14030
- provider: "luma",
14031
- model: ctx.model.id
14032
- });
14033
- bytes = out.bytes;
14034
- header = out.mime;
14035
- } catch (cause) {
14036
- throw classifyThrown("luma", cause, ctx.model.id);
14037
- }
14065
+ const { bytes, mime: header } = await withRetry(
14066
+ async () => {
14067
+ try {
14068
+ return await mediaRefToBytes({ url }, ctx.fetch, {
14069
+ ...ctx.signal !== void 0 && { signal: ctx.signal },
14070
+ provider: "luma",
14071
+ model: ctx.model.id
14072
+ });
14073
+ } catch (cause) {
14074
+ throw classifyThrown("luma", cause, ctx.model.id);
14075
+ }
14076
+ },
14077
+ mediaRetryOptions(ctx.call, ctx.signal)
14078
+ );
14038
14079
  const kind = fallback.split("/")[0];
14039
- const usable = header !== void 0 && header.startsWith(`${kind}/`);
14080
+ const usable = header.startsWith(`${kind}/`);
14040
14081
  return { bytes, mime: usable ? header : fallback };
14041
14082
  }
14042
14083
  function imageUsage2(ctx) {
@@ -14051,15 +14092,12 @@ function imageUsage2(ctx) {
14051
14092
  }
14052
14093
  var GENERATE_SECONDS = 5;
14053
14094
  var MAX_PLAUSIBLE_SECONDS = 60;
14054
- function billedSeconds(req, mode, bytes) {
14055
- if (mode === "generate") return GENERATE_SECONDS;
14095
+ function editedSeconds(req, bytes) {
14056
14096
  const measured = mp4DurationSeconds(bytes);
14057
- const source2 = req.sourceVideo;
14058
- const length = measured !== void 0 && measured > 0 && measured <= MAX_PLAUSIBLE_SECONDS ? measured : source2 !== void 0 && "url" in source2 ? source2.durationSeconds : void 0;
14097
+ const length = measured !== void 0 && measured > 0 && measured <= MAX_PLAUSIBLE_SECONDS ? measured : req.sourceVideo?.durationSeconds;
14059
14098
  return Math.max(length ?? 0, GENERATE_SECONDS);
14060
14099
  }
14061
- function videoUsage4(ctx, req, mode, bytes) {
14062
- const seconds3 = billedSeconds(req, mode, bytes);
14100
+ function videoUsage4(ctx, req, mode, seconds3) {
14063
14101
  const event = {
14064
14102
  model: ctx.model.id,
14065
14103
  provider: "luma",
@@ -14074,7 +14112,7 @@ function videoUsage4(ctx, req, mode, bytes) {
14074
14112
  }
14075
14113
  function imageBody(req, ctx, outputFormat) {
14076
14114
  const prompt = checkPrompt(req, ctx);
14077
- const model = ctx.model.providerModelId;
14115
+ const model = lumaModel(ctx);
14078
14116
  const ratio = req.aspectRatio !== "adaptive" ? req.aspectRatio : void 0;
14079
14117
  const [source2, ...rest] = req.references ?? [];
14080
14118
  if (source2 === void 0) {
@@ -14102,7 +14140,7 @@ function imageBody(req, ctx, outputFormat) {
14102
14140
  }
14103
14141
  function videoBody(req, ctx, mode) {
14104
14142
  const prompt = checkPrompt(req, ctx);
14105
- const model = ctx.model.providerModelId;
14143
+ const model = lumaModel(ctx);
14106
14144
  const hasReferences = (req.referenceImages ?? []).length + (req.referenceVideos ?? []).length + (req.referenceAudios ?? []).length > 0;
14107
14145
  if (hasReferences) {
14108
14146
  refuseOrDrop(
@@ -14184,7 +14222,7 @@ function videoBody(req, ctx, mode) {
14184
14222
  async function generateLumaImage(req, ctx) {
14185
14223
  throwIfAborted(ctx.signal);
14186
14224
  lumaKey(ctx);
14187
- if (!LUMA_IMAGE_MODEL_IDS.has(ctx.model.providerModelId.toLowerCase())) {
14225
+ if (!LUMA_IMAGE_MODEL_IDS.has(lumaModel(ctx))) {
14188
14226
  throw new ModelKitError({
14189
14227
  code: "unsupported_option",
14190
14228
  message: `luma has no image mapping for "${ctx.model.providerModelId}"`,
@@ -14217,16 +14255,25 @@ function lumaVideoJob(ctx, req, mode, id) {
14217
14255
  return { status: "pending" };
14218
14256
  },
14219
14257
  async result(opts) {
14220
- const signal = opts?.signal ?? ctx.signal;
14221
- const url = await pollUntil(checkGeneration.bind(null, ctx, id, "video"), {
14258
+ const signal = combineSignals(ctx.signal, opts?.signal);
14259
+ const jobCtx = { ...ctx, ...signal !== void 0 && { signal } };
14260
+ const url = await pollUntil(checkGeneration.bind(null, jobCtx, id, "video"), {
14222
14261
  intervalMs: opts?.pollIntervalMs ?? VIDEO_POLL_INTERVAL_MS,
14223
14262
  timeoutMs: opts?.timeoutMs ?? VIDEO_TIMEOUT_MS,
14224
14263
  ...signal !== void 0 && { signal }
14225
14264
  });
14226
- const video = await downloadBytes(ctx, url, "video/mp4");
14265
+ if (mode === "generate") {
14266
+ const result2 = {
14267
+ video: { url, expiresAt: new Date(Date.now() + OUTPUT_URL_TTL_MS) },
14268
+ usage: videoUsage4(jobCtx, req, mode, GENERATE_SECONDS),
14269
+ raw: { id }
14270
+ };
14271
+ return result2;
14272
+ }
14273
+ const video = await downloadBytes(jobCtx, url, "video/mp4");
14227
14274
  const result = {
14228
14275
  video,
14229
- usage: videoUsage4(ctx, req, mode, video.bytes),
14276
+ usage: videoUsage4(jobCtx, req, mode, editedSeconds(req, video.bytes)),
14230
14277
  raw: { id }
14231
14278
  };
14232
14279
  return result;
@@ -14236,7 +14283,7 @@ function lumaVideoJob(ctx, req, mode, id) {
14236
14283
  async function generateLumaVideo(req, ctx) {
14237
14284
  throwIfAborted(ctx.signal);
14238
14285
  lumaKey(ctx);
14239
- if (ctx.model.providerModelId.toLowerCase() !== LUMA_VIDEO_MODEL_ID) {
14286
+ if (lumaModel(ctx) !== LUMA_VIDEO_MODEL_ID) {
14240
14287
  throw new ModelKitError({
14241
14288
  code: "unsupported_option",
14242
14289
  message: `luma has no video mapping for "${ctx.model.providerModelId}"`,