@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/CHANGELOG.md +16 -0
- package/dist/index.cjs +101 -54
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +101 -54
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
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.
|
|
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.
|
|
13679
|
-
if (size <
|
|
13680
|
-
const version = view.getUint8(mvhd.
|
|
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 <
|
|
13683
|
-
const timescale = view.getUint32(mvhd.
|
|
13684
|
-
const duration = view.getUint32(mvhd.
|
|
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 <
|
|
13690
|
-
const timescale = view.getUint32(mvhd.
|
|
13691
|
-
const high = view.getUint32(mvhd.
|
|
13692
|
-
const low = view.getUint32(mvhd.
|
|
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
|
-
|
|
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
|
|
13710
|
-
|
|
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:
|
|
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
|
|
13955
|
-
if (
|
|
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
|
|
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
|
|
13969
|
-
|
|
13970
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
14026
|
-
|
|
14027
|
-
|
|
14028
|
-
|
|
14029
|
-
|
|
14030
|
-
|
|
14031
|
-
|
|
14032
|
-
|
|
14033
|
-
|
|
14034
|
-
|
|
14035
|
-
|
|
14036
|
-
|
|
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
|
|
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
|
|
14055
|
-
if (mode === "generate") return GENERATE_SECONDS;
|
|
14095
|
+
function editedSeconds(req, bytes) {
|
|
14056
14096
|
const measured = mp4DurationSeconds(bytes);
|
|
14057
|
-
const
|
|
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,
|
|
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
|
|
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
|
|
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
|
|
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
|
|
14221
|
-
const
|
|
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
|
-
|
|
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(
|
|
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
|
|
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}"`,
|