@shop-os/modelkit 0.4.5 → 0.4.6
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 +5 -0
- package/README.md +4 -0
- package/dist/index.cjs +155 -95
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +8 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.js +155 -95
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.d.cts
CHANGED
|
@@ -1144,6 +1144,8 @@ interface Adapter {
|
|
|
1144
1144
|
id: string;
|
|
1145
1145
|
generateImage?(req: ImageRequest, ctx: AdapterContext): Promise<ImageResult>;
|
|
1146
1146
|
generateVideo?(req: VideoRequest, ctx: AdapterContext): Promise<VideoJob>;
|
|
1147
|
+
/** Reconnect to a job `generateVideo` submitted earlier; never submits. */
|
|
1148
|
+
resumeVideo?(req: VideoRequest, jobId: string, ctx: AdapterContext): VideoJob;
|
|
1147
1149
|
generateAudio?(req: AudioRequest, ctx: AdapterContext): Promise<AudioResult>;
|
|
1148
1150
|
/** Video tools run as provider jobs the caller tracks; see ModelKit.submitVideoTool. */
|
|
1149
1151
|
submitVideoTool?(req: VideoToolRequest, ctx: AdapterContext, opts: {
|
|
@@ -1350,6 +1352,12 @@ interface ModelKit extends TextMethods {
|
|
|
1350
1352
|
* `model` is a catalog id or an AI SDK video model instance.
|
|
1351
1353
|
*/
|
|
1352
1354
|
generateVideo(req: VideoCallOptions, call?: CallContext): Promise<VideoCallJob>;
|
|
1355
|
+
/**
|
|
1356
|
+
* Reconnect to a video job submitted earlier with `generateVideo` (same request), by its
|
|
1357
|
+
* `jobId`. Never submits a new job. Await `job.result()` as usual; usage is emitted once.
|
|
1358
|
+
* A resumed job reports only the resumed wait in `latencyMs`.
|
|
1359
|
+
*/
|
|
1360
|
+
resumeVideo(req: VideoCallOptions, jobId: string, call?: CallContext): Promise<VideoCallJob>;
|
|
1353
1361
|
/** AI SDK LanguageModel for chat models. Pair with the catalog's providerOptions. */
|
|
1354
1362
|
languageModel(modelId: string, call?: CallContext): LanguageModelHandle;
|
|
1355
1363
|
/** Usage-event `provider` for a catalog model, without building it: "bedrock" on Bedrock, else the vendor. */
|
package/dist/index.d.ts
CHANGED
|
@@ -1144,6 +1144,8 @@ interface Adapter {
|
|
|
1144
1144
|
id: string;
|
|
1145
1145
|
generateImage?(req: ImageRequest, ctx: AdapterContext): Promise<ImageResult>;
|
|
1146
1146
|
generateVideo?(req: VideoRequest, ctx: AdapterContext): Promise<VideoJob>;
|
|
1147
|
+
/** Reconnect to a job `generateVideo` submitted earlier; never submits. */
|
|
1148
|
+
resumeVideo?(req: VideoRequest, jobId: string, ctx: AdapterContext): VideoJob;
|
|
1147
1149
|
generateAudio?(req: AudioRequest, ctx: AdapterContext): Promise<AudioResult>;
|
|
1148
1150
|
/** Video tools run as provider jobs the caller tracks; see ModelKit.submitVideoTool. */
|
|
1149
1151
|
submitVideoTool?(req: VideoToolRequest, ctx: AdapterContext, opts: {
|
|
@@ -1350,6 +1352,12 @@ interface ModelKit extends TextMethods {
|
|
|
1350
1352
|
* `model` is a catalog id or an AI SDK video model instance.
|
|
1351
1353
|
*/
|
|
1352
1354
|
generateVideo(req: VideoCallOptions, call?: CallContext): Promise<VideoCallJob>;
|
|
1355
|
+
/**
|
|
1356
|
+
* Reconnect to a video job submitted earlier with `generateVideo` (same request), by its
|
|
1357
|
+
* `jobId`. Never submits a new job. Await `job.result()` as usual; usage is emitted once.
|
|
1358
|
+
* A resumed job reports only the resumed wait in `latencyMs`.
|
|
1359
|
+
*/
|
|
1360
|
+
resumeVideo(req: VideoCallOptions, jobId: string, call?: CallContext): Promise<VideoCallJob>;
|
|
1353
1361
|
/** AI SDK LanguageModel for chat models. Pair with the catalog's providerOptions. */
|
|
1354
1362
|
languageModel(modelId: string, call?: CallContext): LanguageModelHandle;
|
|
1355
1363
|
/** Usage-event `provider` for a catalog model, without building it: "bedrock" on Bedrock, else the vendor. */
|
package/dist/index.js
CHANGED
|
@@ -3651,6 +3651,11 @@ var arkAdapter = {
|
|
|
3651
3651
|
async generateVideo(req, ctx) {
|
|
3652
3652
|
throwIfAborted(ctx.signal);
|
|
3653
3653
|
return generateSeedanceVideo(req, ctx);
|
|
3654
|
+
},
|
|
3655
|
+
resumeVideo(req, jobId, ctx) {
|
|
3656
|
+
throwIfAborted(ctx.signal);
|
|
3657
|
+
const key = arkKey(ctx);
|
|
3658
|
+
return seedanceJob(ctx, key, jobId, seedanceUsage(ctx, req, seedanceInitialDuration(req)));
|
|
3654
3659
|
}
|
|
3655
3660
|
};
|
|
3656
3661
|
function arkKey(ctx) {
|
|
@@ -3820,6 +3825,9 @@ function seedanceUsage(ctx, req, duration) {
|
|
|
3820
3825
|
if (req.resolution !== void 0) usage2.resolution = req.resolution;
|
|
3821
3826
|
return usage2;
|
|
3822
3827
|
}
|
|
3828
|
+
function seedanceInitialDuration(req) {
|
|
3829
|
+
return req.durationSeconds ?? 5;
|
|
3830
|
+
}
|
|
3823
3831
|
function seedanceErrorMessage(error, status) {
|
|
3824
3832
|
const asString = z7.string().safeParse(error);
|
|
3825
3833
|
if (asString.success) return asString.data;
|
|
@@ -3827,6 +3835,78 @@ function seedanceErrorMessage(error, status) {
|
|
|
3827
3835
|
if (asObject.success && asObject.data.message !== void 0) return asObject.data.message;
|
|
3828
3836
|
return `Seedance task ${status}`;
|
|
3829
3837
|
}
|
|
3838
|
+
function seedanceJob(ctx, key, jobId, usage2) {
|
|
3839
|
+
const modelId = ctx.model.id;
|
|
3840
|
+
const fetchStatus = async () => {
|
|
3841
|
+
const res = await arkFetch(ctx, `${arkBaseUrl(ctx)}${SUBMIT_PATH}/${jobId}`, {
|
|
3842
|
+
headers: { Authorization: `Bearer ${key}` }
|
|
3843
|
+
});
|
|
3844
|
+
if (!res.ok) {
|
|
3845
|
+
if (ctx.call.maxRetries !== 0 && (res.status === 429 || res.status >= 500))
|
|
3846
|
+
return void 0;
|
|
3847
|
+
const text2 = await res.text().catch(() => "");
|
|
3848
|
+
throwArkError(res.status, text2, modelId);
|
|
3849
|
+
}
|
|
3850
|
+
const parsed = SeedanceResponseSchema.safeParse(await res.json());
|
|
3851
|
+
if (!parsed.success) return {};
|
|
3852
|
+
return parsed.data;
|
|
3853
|
+
};
|
|
3854
|
+
return {
|
|
3855
|
+
jobId,
|
|
3856
|
+
async poll() {
|
|
3857
|
+
const polled = await fetchStatus();
|
|
3858
|
+
if (!polled) return { status: "running" };
|
|
3859
|
+
const status = (polled.status ?? "queued").toLowerCase();
|
|
3860
|
+
if (status === "succeeded") return { status: "succeeded", raw: polled };
|
|
3861
|
+
if (status === "failed" || status === "expired" || status === "cancelled") {
|
|
3862
|
+
return { status: "failed", raw: polled };
|
|
3863
|
+
}
|
|
3864
|
+
if (status === "queued" || status === "pending")
|
|
3865
|
+
return { status: "pending", raw: polled };
|
|
3866
|
+
return { status: "running", raw: polled };
|
|
3867
|
+
},
|
|
3868
|
+
async result(opts) {
|
|
3869
|
+
const optSignal = opts?.signal ?? ctx.signal;
|
|
3870
|
+
const pollOpts = {
|
|
3871
|
+
intervalMs: opts?.pollIntervalMs ?? SEEDANCE_POLL_INTERVAL_MS,
|
|
3872
|
+
timeoutMs: opts?.timeoutMs ?? RESULT_TIMEOUT_MS
|
|
3873
|
+
};
|
|
3874
|
+
if (optSignal !== void 0) pollOpts.signal = optSignal;
|
|
3875
|
+
return pollUntil(async () => {
|
|
3876
|
+
const polled = await fetchStatus();
|
|
3877
|
+
if (!polled) return void 0;
|
|
3878
|
+
const status = (polled.status ?? "queued").toLowerCase();
|
|
3879
|
+
if (status === "succeeded") {
|
|
3880
|
+
const url = polled.content?.video_url;
|
|
3881
|
+
if (!url)
|
|
3882
|
+
throwArkError(
|
|
3883
|
+
void 0,
|
|
3884
|
+
"Seedance task succeeded but returned no video_url",
|
|
3885
|
+
modelId
|
|
3886
|
+
);
|
|
3887
|
+
if (polled.duration !== void 0 && polled.duration > 0) {
|
|
3888
|
+
usage2.quantity = polled.duration;
|
|
3889
|
+
usage2.durationSeconds = polled.duration;
|
|
3890
|
+
}
|
|
3891
|
+
const tokens = polled.usage;
|
|
3892
|
+
if (tokens?.total_tokens !== void 0 && tokens.completion_tokens !== void 0) {
|
|
3893
|
+
usage2.inputTokens = tokens.total_tokens - tokens.completion_tokens;
|
|
3894
|
+
usage2.outputTokens = tokens.completion_tokens;
|
|
3895
|
+
}
|
|
3896
|
+
return {
|
|
3897
|
+
video: { url, expiresAt: new Date(Date.now() + VIDEO_URL_TTL_MS) },
|
|
3898
|
+
usage: usage2,
|
|
3899
|
+
raw: polled
|
|
3900
|
+
};
|
|
3901
|
+
}
|
|
3902
|
+
if (status === "failed" || status === "expired" || status === "cancelled") {
|
|
3903
|
+
throwArkError(void 0, seedanceErrorMessage(polled.error, status), modelId);
|
|
3904
|
+
}
|
|
3905
|
+
return void 0;
|
|
3906
|
+
}, pollOpts);
|
|
3907
|
+
}
|
|
3908
|
+
};
|
|
3909
|
+
}
|
|
3830
3910
|
async function generateSeedanceVideo(req, ctx) {
|
|
3831
3911
|
const modelId = ctx.model.id;
|
|
3832
3912
|
const taskType = req.taskType;
|
|
@@ -3841,7 +3921,7 @@ async function generateSeedanceVideo(req, ctx) {
|
|
|
3841
3921
|
});
|
|
3842
3922
|
}
|
|
3843
3923
|
const key = arkKey(ctx);
|
|
3844
|
-
const requestedDuration = req
|
|
3924
|
+
const requestedDuration = seedanceInitialDuration(req);
|
|
3845
3925
|
let duration = requestedDuration;
|
|
3846
3926
|
if (taskType === "edit") duration = -1;
|
|
3847
3927
|
const resolution = req.resolution ?? "720p";
|
|
@@ -3922,75 +4002,7 @@ async function generateSeedanceVideo(req, ctx) {
|
|
|
3922
4002
|
if (!submit.id) throwArkError(void 0, "Seedance task create returned no id", modelId);
|
|
3923
4003
|
const jobId = submit.id;
|
|
3924
4004
|
const usage2 = seedanceUsage(ctx, req, taskType === "edit" ? requestedDuration : duration);
|
|
3925
|
-
|
|
3926
|
-
const res = await arkFetch(ctx, `${arkBaseUrl(ctx)}${SUBMIT_PATH}/${jobId}`, {
|
|
3927
|
-
headers: { Authorization: `Bearer ${key}` }
|
|
3928
|
-
});
|
|
3929
|
-
if (!res.ok) {
|
|
3930
|
-
if (ctx.call.maxRetries !== 0 && (res.status === 429 || res.status >= 500))
|
|
3931
|
-
return void 0;
|
|
3932
|
-
const text2 = await res.text().catch(() => "");
|
|
3933
|
-
throwArkError(res.status, text2, modelId);
|
|
3934
|
-
}
|
|
3935
|
-
const parsed = SeedanceResponseSchema.safeParse(await res.json());
|
|
3936
|
-
if (!parsed.success) return {};
|
|
3937
|
-
return parsed.data;
|
|
3938
|
-
};
|
|
3939
|
-
return {
|
|
3940
|
-
jobId,
|
|
3941
|
-
async poll() {
|
|
3942
|
-
const polled = await fetchStatus();
|
|
3943
|
-
if (!polled) return { status: "running" };
|
|
3944
|
-
const status = (polled.status ?? "queued").toLowerCase();
|
|
3945
|
-
if (status === "succeeded") return { status: "succeeded", raw: polled };
|
|
3946
|
-
if (status === "failed" || status === "expired" || status === "cancelled") {
|
|
3947
|
-
return { status: "failed", raw: polled };
|
|
3948
|
-
}
|
|
3949
|
-
if (status === "queued" || status === "pending")
|
|
3950
|
-
return { status: "pending", raw: polled };
|
|
3951
|
-
return { status: "running", raw: polled };
|
|
3952
|
-
},
|
|
3953
|
-
async result(opts) {
|
|
3954
|
-
const optSignal = opts?.signal ?? ctx.signal;
|
|
3955
|
-
const pollOpts = {
|
|
3956
|
-
intervalMs: opts?.pollIntervalMs ?? SEEDANCE_POLL_INTERVAL_MS,
|
|
3957
|
-
timeoutMs: opts?.timeoutMs ?? RESULT_TIMEOUT_MS
|
|
3958
|
-
};
|
|
3959
|
-
if (optSignal !== void 0) pollOpts.signal = optSignal;
|
|
3960
|
-
return pollUntil(async () => {
|
|
3961
|
-
const polled = await fetchStatus();
|
|
3962
|
-
if (!polled) return void 0;
|
|
3963
|
-
const status = (polled.status ?? "queued").toLowerCase();
|
|
3964
|
-
if (status === "succeeded") {
|
|
3965
|
-
const url = polled.content?.video_url;
|
|
3966
|
-
if (!url)
|
|
3967
|
-
throwArkError(
|
|
3968
|
-
void 0,
|
|
3969
|
-
"Seedance task succeeded but returned no video_url",
|
|
3970
|
-
modelId
|
|
3971
|
-
);
|
|
3972
|
-
if (polled.duration !== void 0 && polled.duration > 0) {
|
|
3973
|
-
usage2.quantity = polled.duration;
|
|
3974
|
-
usage2.durationSeconds = polled.duration;
|
|
3975
|
-
}
|
|
3976
|
-
const tokens = polled.usage;
|
|
3977
|
-
if (tokens?.total_tokens !== void 0 && tokens.completion_tokens !== void 0) {
|
|
3978
|
-
usage2.inputTokens = tokens.total_tokens - tokens.completion_tokens;
|
|
3979
|
-
usage2.outputTokens = tokens.completion_tokens;
|
|
3980
|
-
}
|
|
3981
|
-
return {
|
|
3982
|
-
video: { url, expiresAt: new Date(Date.now() + VIDEO_URL_TTL_MS) },
|
|
3983
|
-
usage: usage2,
|
|
3984
|
-
raw: polled
|
|
3985
|
-
};
|
|
3986
|
-
}
|
|
3987
|
-
if (status === "failed" || status === "expired" || status === "cancelled") {
|
|
3988
|
-
throwArkError(void 0, seedanceErrorMessage(polled.error, status), modelId);
|
|
3989
|
-
}
|
|
3990
|
-
return void 0;
|
|
3991
|
-
}, pollOpts);
|
|
3992
|
-
}
|
|
3993
|
-
};
|
|
4005
|
+
return seedanceJob(ctx, key, jobId, usage2);
|
|
3994
4006
|
}
|
|
3995
4007
|
|
|
3996
4008
|
// src/adapters/fal.ts
|
|
@@ -9579,6 +9591,33 @@ function createModelKit(config, adapters) {
|
|
|
9579
9591
|
result: () => done
|
|
9580
9592
|
};
|
|
9581
9593
|
}
|
|
9594
|
+
function wrapVideoJob(model, job, warnings, startedAt) {
|
|
9595
|
+
const result = job.result.bind(job);
|
|
9596
|
+
let settled;
|
|
9597
|
+
return {
|
|
9598
|
+
...job,
|
|
9599
|
+
warnings,
|
|
9600
|
+
result(opts) {
|
|
9601
|
+
if (settled) return settled;
|
|
9602
|
+
const pending = result(opts).then(
|
|
9603
|
+
async (out) => {
|
|
9604
|
+
const usage2 = timed(
|
|
9605
|
+
{ ...out.usage, model: model.id, unit: model.usage.unit },
|
|
9606
|
+
startedAt
|
|
9607
|
+
);
|
|
9608
|
+
await emit(usage2);
|
|
9609
|
+
return { ...out, usage: usage2 };
|
|
9610
|
+
},
|
|
9611
|
+
(cause) => {
|
|
9612
|
+
settled = void 0;
|
|
9613
|
+
throw cause;
|
|
9614
|
+
}
|
|
9615
|
+
);
|
|
9616
|
+
settled = pending;
|
|
9617
|
+
return pending;
|
|
9618
|
+
}
|
|
9619
|
+
};
|
|
9620
|
+
}
|
|
9582
9621
|
return {
|
|
9583
9622
|
catalog,
|
|
9584
9623
|
describe: (id) => catalog.require(id),
|
|
@@ -9663,31 +9702,52 @@ function createModelKit(config, adapters) {
|
|
|
9663
9702
|
});
|
|
9664
9703
|
const startedAt = Date.now();
|
|
9665
9704
|
const job = await adapter.generateVideo(request, ctx);
|
|
9666
|
-
|
|
9667
|
-
|
|
9668
|
-
|
|
9669
|
-
|
|
9670
|
-
|
|
9671
|
-
|
|
9672
|
-
|
|
9673
|
-
|
|
9674
|
-
|
|
9675
|
-
|
|
9676
|
-
|
|
9677
|
-
|
|
9678
|
-
|
|
9679
|
-
|
|
9680
|
-
|
|
9681
|
-
|
|
9682
|
-
|
|
9683
|
-
|
|
9684
|
-
|
|
9685
|
-
|
|
9686
|
-
|
|
9687
|
-
|
|
9688
|
-
|
|
9689
|
-
}
|
|
9690
|
-
}
|
|
9705
|
+
return wrapVideoJob(model, job, warnings, startedAt);
|
|
9706
|
+
},
|
|
9707
|
+
async resumeVideo(req, jobId, call = {}) {
|
|
9708
|
+
mediaRetryOptions(call);
|
|
9709
|
+
if (!jobId) {
|
|
9710
|
+
throw new ModelKitError({
|
|
9711
|
+
code: "missing_input",
|
|
9712
|
+
message: "resumeVideo needs a jobId",
|
|
9713
|
+
retryable: false
|
|
9714
|
+
});
|
|
9715
|
+
}
|
|
9716
|
+
if (!isCatalogId(req.model)) {
|
|
9717
|
+
throw new ModelKitError({
|
|
9718
|
+
code: "unsupported_option",
|
|
9719
|
+
message: "resumeVideo needs a catalog model id"
|
|
9720
|
+
});
|
|
9721
|
+
}
|
|
9722
|
+
const model = catalog.require(req.model);
|
|
9723
|
+
if (model.type !== "video")
|
|
9724
|
+
throw new ModelKitError({
|
|
9725
|
+
code: "unsupported_option",
|
|
9726
|
+
message: `${model.id} is not a video model`,
|
|
9727
|
+
model: model.id
|
|
9728
|
+
});
|
|
9729
|
+
const { request: input, warnings: inputWarnings } = videoRequestOf({
|
|
9730
|
+
...req,
|
|
9731
|
+
model: req.model
|
|
9732
|
+
});
|
|
9733
|
+
const { request, warnings } = resolveVideoOptions(
|
|
9734
|
+
model,
|
|
9735
|
+
input,
|
|
9736
|
+
call.onUnsupported ?? "error"
|
|
9737
|
+
);
|
|
9738
|
+
warnings.push(...inputWarnings);
|
|
9739
|
+
logCoercions(logger, model.id, warnings);
|
|
9740
|
+
const signal = combineSignals(req.abortSignal, call.signal);
|
|
9741
|
+
const { adapter, ctx } = context(model, signal ? { ...call, signal } : call, warnings);
|
|
9742
|
+
if (!adapter.resumeVideo)
|
|
9743
|
+
throw new ModelKitError({
|
|
9744
|
+
code: "unsupported_option",
|
|
9745
|
+
message: `${adapter.id} cannot resume a video job`,
|
|
9746
|
+
model: model.id
|
|
9747
|
+
});
|
|
9748
|
+
const startedAt = Date.now();
|
|
9749
|
+
const job = adapter.resumeVideo(request, jobId, ctx);
|
|
9750
|
+
return wrapVideoJob(model, { ...job, jobId }, warnings, startedAt);
|
|
9691
9751
|
},
|
|
9692
9752
|
async generateAudio(req, call = {}) {
|
|
9693
9753
|
mediaRetryOptions(call);
|