@shop-os/modelkit 0.3.7 → 0.4.0
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 +23 -0
- package/README.md +2 -2
- package/dist/catalog.cjs +3 -1
- package/dist/catalog.cjs.map +1 -1
- package/dist/catalog.d.cts +12 -1
- package/dist/catalog.d.ts +12 -1
- package/dist/catalog.js +3 -1
- package/dist/catalog.js.map +1 -1
- package/dist/index.cjs +118 -35
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +13 -2
- package/dist/index.d.ts +13 -2
- package/dist/index.js +118 -35
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.d.cts
CHANGED
|
@@ -184,6 +184,8 @@ interface VideoToolSubmission {
|
|
|
184
184
|
responseUrl: string;
|
|
185
185
|
cancelUrl: string;
|
|
186
186
|
output: VideoToolOutputSpec;
|
|
187
|
+
/** Epoch ms when the kit submitted the job; the result's usage `latencyMs` is measured from it. */
|
|
188
|
+
submittedAt?: number;
|
|
187
189
|
}
|
|
188
190
|
type VideoToolStatus = "queued" | "running" | "completed";
|
|
189
191
|
interface VideoToolResult {
|
|
@@ -523,6 +525,13 @@ interface UsageEvent {
|
|
|
523
525
|
inputTokens?: number;
|
|
524
526
|
outputTokens?: number;
|
|
525
527
|
reportedCostUsd?: number;
|
|
528
|
+
/**
|
|
529
|
+
* Wall time of the kit call that produced this event, in ms: from dispatch to the
|
|
530
|
+
* provider's final answer (a video's polling included; a multi-step text call's tool
|
|
531
|
+
* steps included). Absent only where the kit never saw the start (a video tool whose
|
|
532
|
+
* submission carries no `submittedAt`).
|
|
533
|
+
*/
|
|
534
|
+
latencyMs?: number;
|
|
526
535
|
raw?: unknown;
|
|
527
536
|
}
|
|
528
537
|
type ProviderKeyName = "openai" | "anthropic" | "google" | "xai" | "perplexity" | "deepseek" | "moonshot" | "minimax" | "ark" | "fal" | "replicate" | "prodia" | "higgsfield" | "cloudflare";
|
|
@@ -605,7 +614,9 @@ interface CallContext {
|
|
|
605
614
|
|
|
606
615
|
/** Provider body or the lifted detail (Gemini blockReason, fal body.detail). */
|
|
607
616
|
type ProviderErrorBody = string | JsonValue;
|
|
608
|
-
type ModelKitErrorCode = "unknown_model" | "unsupported_option" | "missing_input" | "content_blocked" | "provider_rejected"
|
|
617
|
+
type ModelKitErrorCode = "unknown_model" | "unsupported_option" | "missing_input" | "content_blocked" | "provider_rejected"
|
|
618
|
+
/** The provider account is out of credit or quota (HTTP 402 and its body spellings). Never retried. */
|
|
619
|
+
| "provider_billing" | "timeout" | "auth" | "network" | "rate_limited";
|
|
609
620
|
interface ModelKitErrorInit {
|
|
610
621
|
code: ModelKitErrorCode;
|
|
611
622
|
message: string;
|
|
@@ -1230,7 +1241,7 @@ declare function keysFromEnv(env: NodeJS.ProcessEnv, prefix?: string): ProviderK
|
|
|
1230
1241
|
|
|
1231
1242
|
/**
|
|
1232
1243
|
* One classifier for every adapter. Never retry safety blocks,
|
|
1233
|
-
* content_policy_violation, or 4xx other than 429. Retry 429, 5xx, timeouts
|
|
1244
|
+
* content_policy_violation, billing failures, or 4xx other than 429. Retry 429, 5xx, timeouts
|
|
1234
1245
|
* and network failures. Adapters must not stack their own retry loop on top.
|
|
1235
1246
|
*/
|
|
1236
1247
|
declare function classifyHttpError(status: number | undefined, body: ProviderErrorBody, provider: string, model?: string): ModelKitError;
|
package/dist/index.d.ts
CHANGED
|
@@ -184,6 +184,8 @@ interface VideoToolSubmission {
|
|
|
184
184
|
responseUrl: string;
|
|
185
185
|
cancelUrl: string;
|
|
186
186
|
output: VideoToolOutputSpec;
|
|
187
|
+
/** Epoch ms when the kit submitted the job; the result's usage `latencyMs` is measured from it. */
|
|
188
|
+
submittedAt?: number;
|
|
187
189
|
}
|
|
188
190
|
type VideoToolStatus = "queued" | "running" | "completed";
|
|
189
191
|
interface VideoToolResult {
|
|
@@ -523,6 +525,13 @@ interface UsageEvent {
|
|
|
523
525
|
inputTokens?: number;
|
|
524
526
|
outputTokens?: number;
|
|
525
527
|
reportedCostUsd?: number;
|
|
528
|
+
/**
|
|
529
|
+
* Wall time of the kit call that produced this event, in ms: from dispatch to the
|
|
530
|
+
* provider's final answer (a video's polling included; a multi-step text call's tool
|
|
531
|
+
* steps included). Absent only where the kit never saw the start (a video tool whose
|
|
532
|
+
* submission carries no `submittedAt`).
|
|
533
|
+
*/
|
|
534
|
+
latencyMs?: number;
|
|
526
535
|
raw?: unknown;
|
|
527
536
|
}
|
|
528
537
|
type ProviderKeyName = "openai" | "anthropic" | "google" | "xai" | "perplexity" | "deepseek" | "moonshot" | "minimax" | "ark" | "fal" | "replicate" | "prodia" | "higgsfield" | "cloudflare";
|
|
@@ -605,7 +614,9 @@ interface CallContext {
|
|
|
605
614
|
|
|
606
615
|
/** Provider body or the lifted detail (Gemini blockReason, fal body.detail). */
|
|
607
616
|
type ProviderErrorBody = string | JsonValue;
|
|
608
|
-
type ModelKitErrorCode = "unknown_model" | "unsupported_option" | "missing_input" | "content_blocked" | "provider_rejected"
|
|
617
|
+
type ModelKitErrorCode = "unknown_model" | "unsupported_option" | "missing_input" | "content_blocked" | "provider_rejected"
|
|
618
|
+
/** The provider account is out of credit or quota (HTTP 402 and its body spellings). Never retried. */
|
|
619
|
+
| "provider_billing" | "timeout" | "auth" | "network" | "rate_limited";
|
|
609
620
|
interface ModelKitErrorInit {
|
|
610
621
|
code: ModelKitErrorCode;
|
|
611
622
|
message: string;
|
|
@@ -1230,7 +1241,7 @@ declare function keysFromEnv(env: NodeJS.ProcessEnv, prefix?: string): ProviderK
|
|
|
1230
1241
|
|
|
1231
1242
|
/**
|
|
1232
1243
|
* One classifier for every adapter. Never retry safety blocks,
|
|
1233
|
-
* content_policy_violation, or 4xx other than 429. Retry 429, 5xx, timeouts
|
|
1244
|
+
* content_policy_violation, billing failures, or 4xx other than 429. Retry 429, 5xx, timeouts
|
|
1234
1245
|
* and network failures. Adapters must not stack their own retry loop on top.
|
|
1235
1246
|
*/
|
|
1236
1247
|
declare function classifyHttpError(status: number | undefined, body: ProviderErrorBody, provider: string, model?: string): ModelKitError;
|
package/dist/index.js
CHANGED
|
@@ -810,7 +810,9 @@ var OTHER_MODELS = [
|
|
|
810
810
|
},
|
|
811
811
|
{
|
|
812
812
|
id: "bg-remove",
|
|
813
|
-
|
|
813
|
+
// Task ids the two emitters report on usage events (trigger-workflows,
|
|
814
|
+
// vibe-triggers); the usage price lookup resolves them to this row.
|
|
815
|
+
aliases: ["prodia-background-remover", "prodia-remove-background"],
|
|
814
816
|
label: "Background Remover",
|
|
815
817
|
type: "bg-remove",
|
|
816
818
|
adapter: "prodia",
|
|
@@ -3283,6 +3285,36 @@ function describeThrown(cause) {
|
|
|
3283
3285
|
|
|
3284
3286
|
// src/retry.ts
|
|
3285
3287
|
var textBody = z6.string();
|
|
3288
|
+
var BILLING_CODES = /* @__PURE__ */ new Set([
|
|
3289
|
+
"insufficient_quota",
|
|
3290
|
+
"billing_hard_limit_reached",
|
|
3291
|
+
"billing_not_active",
|
|
3292
|
+
"payment_required",
|
|
3293
|
+
"insufficient_balance",
|
|
3294
|
+
"insufficient_credits",
|
|
3295
|
+
"insufficient_credit"
|
|
3296
|
+
]);
|
|
3297
|
+
var errorCodes = z6.object({
|
|
3298
|
+
code: z6.string().optional(),
|
|
3299
|
+
type: z6.string().optional(),
|
|
3300
|
+
error: z6.object({ code: z6.string().optional(), type: z6.string().optional() }).optional()
|
|
3301
|
+
});
|
|
3302
|
+
var BILLING_TEXT = /insufficient_quota|payment[ _]required|exhausted balance|insufficient (?:credits?|balance|funds)|billing_hard_limit/i;
|
|
3303
|
+
var BILLING_TEXT_STATUSES = /* @__PURE__ */ new Set([403, 429]);
|
|
3304
|
+
function billingCode(body) {
|
|
3305
|
+
const parsed = errorCodes.safeParse(body);
|
|
3306
|
+
if (!parsed.success) return false;
|
|
3307
|
+
const { code, type, error } = parsed.data;
|
|
3308
|
+
return [code, type, error?.code, error?.type].some(
|
|
3309
|
+
(value) => value !== void 0 && BILLING_CODES.has(value.toLowerCase())
|
|
3310
|
+
);
|
|
3311
|
+
}
|
|
3312
|
+
function isBillingFailure(status, body, text2) {
|
|
3313
|
+
if (status === 402) return true;
|
|
3314
|
+
if (status === void 0 || status < 400 || status >= 500) return false;
|
|
3315
|
+
if (billingCode(body)) return true;
|
|
3316
|
+
return BILLING_TEXT_STATUSES.has(status) && BILLING_TEXT.test(text2);
|
|
3317
|
+
}
|
|
3286
3318
|
function classifyHttpError(status, body, provider, model) {
|
|
3287
3319
|
const asText = textBody.safeParse(body);
|
|
3288
3320
|
const text2 = asText.success ? asText.data : JSON.stringify(body);
|
|
@@ -3291,6 +3323,7 @@ function classifyHttpError(status, body, provider, model) {
|
|
|
3291
3323
|
);
|
|
3292
3324
|
let code = "provider_rejected";
|
|
3293
3325
|
if (blocked) code = "content_blocked";
|
|
3326
|
+
else if (isBillingFailure(status, body, text2)) code = "provider_billing";
|
|
3294
3327
|
else if (status === 401 || status === 403) code = "auth";
|
|
3295
3328
|
else if (status === 429) code = "rate_limited";
|
|
3296
3329
|
else if (status === 408 || status === 504) code = "timeout";
|
|
@@ -3353,12 +3386,11 @@ function sleep(ms, signal) {
|
|
|
3353
3386
|
rejectAborted();
|
|
3354
3387
|
return;
|
|
3355
3388
|
}
|
|
3356
|
-
let timer;
|
|
3357
3389
|
const onAbort = () => {
|
|
3358
|
-
|
|
3390
|
+
clearTimeout(timer);
|
|
3359
3391
|
rejectAborted();
|
|
3360
3392
|
};
|
|
3361
|
-
timer = setTimeout(() => {
|
|
3393
|
+
const timer = setTimeout(() => {
|
|
3362
3394
|
signal?.removeEventListener("abort", onAbort);
|
|
3363
3395
|
resolve();
|
|
3364
3396
|
}, ms);
|
|
@@ -9148,6 +9180,13 @@ function createModelKit(config, adapters) {
|
|
|
9148
9180
|
model: model.id
|
|
9149
9181
|
});
|
|
9150
9182
|
}
|
|
9183
|
+
function timed(event, startedAt) {
|
|
9184
|
+
if (event.latencyMs !== void 0) return event;
|
|
9185
|
+
return { ...event, latencyMs: Math.max(0, Date.now() - startedAt) };
|
|
9186
|
+
}
|
|
9187
|
+
function textTimed(event, stepCount, startedAt) {
|
|
9188
|
+
return stepCount === 1 ? timed(event, startedAt) : event;
|
|
9189
|
+
}
|
|
9151
9190
|
async function emit(event) {
|
|
9152
9191
|
try {
|
|
9153
9192
|
await config.onUsage?.(event);
|
|
@@ -9231,17 +9270,21 @@ function createModelKit(config, adapters) {
|
|
|
9231
9270
|
const abortSignal = combineSignals(req.abortSignal, call.signal);
|
|
9232
9271
|
if (abortSignal) options.abortSignal = abortSignal;
|
|
9233
9272
|
if (call.maxRetries !== void 0) options.maxRetries = call.maxRetries;
|
|
9273
|
+
const startedAt = Date.now();
|
|
9234
9274
|
const result = await aiGenerateImage(options);
|
|
9235
9275
|
const images = result.images.map(
|
|
9236
9276
|
(file) => toGeneratedImage({ bytes: file.uint8Array, mime: file.mediaType })
|
|
9237
9277
|
);
|
|
9238
|
-
const usage2 =
|
|
9239
|
-
|
|
9240
|
-
|
|
9241
|
-
|
|
9242
|
-
|
|
9243
|
-
|
|
9244
|
-
|
|
9278
|
+
const usage2 = timed(
|
|
9279
|
+
{
|
|
9280
|
+
model: model.modelId,
|
|
9281
|
+
provider: model.provider,
|
|
9282
|
+
modality: "IMAGE",
|
|
9283
|
+
unit: "images",
|
|
9284
|
+
quantity: images.length
|
|
9285
|
+
},
|
|
9286
|
+
startedAt
|
|
9287
|
+
);
|
|
9245
9288
|
await emit(usage2);
|
|
9246
9289
|
return { image: images[0], images, usage: usage2, raw: result, warnings };
|
|
9247
9290
|
}
|
|
@@ -9314,18 +9357,22 @@ function createModelKit(config, adapters) {
|
|
|
9314
9357
|
if (providerOptions) options.providerOptions = providerOptions;
|
|
9315
9358
|
if (abortSignal) options.abortSignal = abortSignal;
|
|
9316
9359
|
logCoercions(logger, model.modelId, warnings);
|
|
9360
|
+
const startedAt = Date.now();
|
|
9317
9361
|
const pending = aiGenerateVideo(options);
|
|
9318
9362
|
let settled;
|
|
9319
9363
|
let failed = false;
|
|
9320
9364
|
const done = pending.then(async (result) => {
|
|
9321
|
-
const usage2 =
|
|
9322
|
-
|
|
9323
|
-
|
|
9324
|
-
|
|
9325
|
-
|
|
9326
|
-
|
|
9327
|
-
|
|
9328
|
-
|
|
9365
|
+
const usage2 = timed(
|
|
9366
|
+
{
|
|
9367
|
+
model: model.modelId,
|
|
9368
|
+
provider: model.provider,
|
|
9369
|
+
modality: "VIDEO",
|
|
9370
|
+
unit: "seconds",
|
|
9371
|
+
quantity: seconds2 ?? 0,
|
|
9372
|
+
...seconds2 !== void 0 && { durationSeconds: seconds2 }
|
|
9373
|
+
},
|
|
9374
|
+
startedAt
|
|
9375
|
+
);
|
|
9329
9376
|
settled = {
|
|
9330
9377
|
video: { bytes: result.video.uint8Array, mime: result.video.mediaType },
|
|
9331
9378
|
usage: usage2,
|
|
@@ -9382,8 +9429,12 @@ function createModelKit(config, adapters) {
|
|
|
9382
9429
|
message: `${adapter.id} cannot generate images`,
|
|
9383
9430
|
model: model.id
|
|
9384
9431
|
});
|
|
9432
|
+
const startedAt = Date.now();
|
|
9385
9433
|
const result = await adapter.generateImage(request, ctx);
|
|
9386
|
-
const usage2 =
|
|
9434
|
+
const usage2 = timed(
|
|
9435
|
+
{ ...result.usage, model: model.id, unit: model.usage.unit },
|
|
9436
|
+
startedAt
|
|
9437
|
+
);
|
|
9387
9438
|
await emit(usage2);
|
|
9388
9439
|
const images = result.images.map(toGeneratedImage);
|
|
9389
9440
|
const first = images[0];
|
|
@@ -9426,6 +9477,7 @@ function createModelKit(config, adapters) {
|
|
|
9426
9477
|
message: `${adapter.id} cannot generate video`,
|
|
9427
9478
|
model: model.id
|
|
9428
9479
|
});
|
|
9480
|
+
const startedAt = Date.now();
|
|
9429
9481
|
const job = await adapter.generateVideo(request, ctx);
|
|
9430
9482
|
const result = job.result.bind(job);
|
|
9431
9483
|
let settled;
|
|
@@ -9435,11 +9487,14 @@ function createModelKit(config, adapters) {
|
|
|
9435
9487
|
result(opts) {
|
|
9436
9488
|
if (settled) return settled;
|
|
9437
9489
|
const pending = result(opts).then(
|
|
9438
|
-
(out) =>
|
|
9439
|
-
|
|
9440
|
-
|
|
9441
|
-
|
|
9442
|
-
|
|
9490
|
+
async (out) => {
|
|
9491
|
+
const usage2 = timed(
|
|
9492
|
+
{ ...out.usage, model: model.id, unit: model.usage.unit },
|
|
9493
|
+
startedAt
|
|
9494
|
+
);
|
|
9495
|
+
await emit(usage2);
|
|
9496
|
+
return { ...out, usage: usage2 };
|
|
9497
|
+
},
|
|
9443
9498
|
(cause) => {
|
|
9444
9499
|
settled = void 0;
|
|
9445
9500
|
throw cause;
|
|
@@ -9476,9 +9531,14 @@ function createModelKit(config, adapters) {
|
|
|
9476
9531
|
model: model.id
|
|
9477
9532
|
});
|
|
9478
9533
|
}
|
|
9534
|
+
const startedAt = Date.now();
|
|
9479
9535
|
const result = await adapter.generateAudio(resolved, ctx);
|
|
9480
|
-
|
|
9481
|
-
|
|
9536
|
+
const usage2 = timed(
|
|
9537
|
+
{ ...result.usage, model: model.id, unit: model.usage.unit },
|
|
9538
|
+
startedAt
|
|
9539
|
+
);
|
|
9540
|
+
await emit(usage2);
|
|
9541
|
+
return { ...result, usage: usage2 };
|
|
9482
9542
|
},
|
|
9483
9543
|
async prepareMediaPart(modelId, ref, mediaType, call = {}) {
|
|
9484
9544
|
mediaRetryOptions(call);
|
|
@@ -9507,11 +9567,13 @@ function createModelKit(config, adapters) {
|
|
|
9507
9567
|
const { webhookUrl, ...rest } = call;
|
|
9508
9568
|
const { adapter, ctx } = videoToolTarget(req.model, rest);
|
|
9509
9569
|
if (!adapter.submitVideoTool) throw noVideoTools(adapter, ctx.model);
|
|
9510
|
-
|
|
9570
|
+
const submittedAt = Date.now();
|
|
9571
|
+
const submission = await adapter.submitVideoTool(
|
|
9511
9572
|
req,
|
|
9512
9573
|
ctx,
|
|
9513
9574
|
webhookUrl !== void 0 ? { webhookUrl } : {}
|
|
9514
9575
|
);
|
|
9576
|
+
return { ...submission, submittedAt };
|
|
9515
9577
|
},
|
|
9516
9578
|
async videoToolStatus(sub, call = {}) {
|
|
9517
9579
|
const { adapter, ctx } = videoToolTarget(sub.model, call);
|
|
@@ -9523,8 +9585,10 @@ function createModelKit(config, adapters) {
|
|
|
9523
9585
|
const { adapter, ctx } = videoToolTarget(sub.model, rest);
|
|
9524
9586
|
if (!adapter.videoToolResult) throw noVideoTools(adapter, ctx.model);
|
|
9525
9587
|
const result = await adapter.videoToolResult(sub, ctx, webhook);
|
|
9526
|
-
|
|
9527
|
-
|
|
9588
|
+
const base = { ...result.usage, model: ctx.model.id, unit: ctx.model.usage.unit };
|
|
9589
|
+
const usage2 = sub.submittedAt === void 0 ? base : timed(base, sub.submittedAt);
|
|
9590
|
+
await emit(usage2);
|
|
9591
|
+
return { ...result, usage: usage2 };
|
|
9528
9592
|
},
|
|
9529
9593
|
async cancelVideoTool(sub, call = {}) {
|
|
9530
9594
|
const { adapter, ctx } = videoToolTarget(sub.model, call);
|
|
@@ -9546,6 +9610,7 @@ function createModelKit(config, adapters) {
|
|
|
9546
9610
|
(model) => textTarget(model, call),
|
|
9547
9611
|
options.providerOptions
|
|
9548
9612
|
);
|
|
9613
|
+
const startedAt = Date.now();
|
|
9549
9614
|
const result = await aiGenerateText2({
|
|
9550
9615
|
...options,
|
|
9551
9616
|
...textAbortOptions(options.abortSignal, call.signal),
|
|
@@ -9555,7 +9620,8 @@ function createModelKit(config, adapters) {
|
|
|
9555
9620
|
options.prepareStep ?? options.experimental_prepareStep
|
|
9556
9621
|
)
|
|
9557
9622
|
});
|
|
9558
|
-
for (const event of steps.usage(result.steps))
|
|
9623
|
+
for (const event of steps.usage(result.steps))
|
|
9624
|
+
await emit(textTimed(withRowUnit(event), result.steps.length, startedAt));
|
|
9559
9625
|
return result;
|
|
9560
9626
|
},
|
|
9561
9627
|
streamText(options, call = {}) {
|
|
@@ -9566,6 +9632,7 @@ function createModelKit(config, adapters) {
|
|
|
9566
9632
|
options.providerOptions
|
|
9567
9633
|
);
|
|
9568
9634
|
const callerOnFinish = options.onFinish;
|
|
9635
|
+
const startedAt = Date.now();
|
|
9569
9636
|
return aiStreamText2({
|
|
9570
9637
|
...options,
|
|
9571
9638
|
...textAbortOptions(options.abortSignal, call.signal),
|
|
@@ -9575,7 +9642,8 @@ function createModelKit(config, adapters) {
|
|
|
9575
9642
|
options.prepareStep ?? options.experimental_prepareStep
|
|
9576
9643
|
),
|
|
9577
9644
|
onFinish: async (event) => {
|
|
9578
|
-
for (const usage2 of steps.usage(event.steps))
|
|
9645
|
+
for (const usage2 of steps.usage(event.steps))
|
|
9646
|
+
await emit(textTimed(withRowUnit(usage2), event.steps.length, startedAt));
|
|
9579
9647
|
await callerOnFinish?.(event);
|
|
9580
9648
|
}
|
|
9581
9649
|
});
|
|
@@ -9588,15 +9656,22 @@ function createModelKit(config, adapters) {
|
|
|
9588
9656
|
model: languageModel2,
|
|
9589
9657
|
providerOptions: mergeTextProviderOptions(entry, options.providerOptions)
|
|
9590
9658
|
};
|
|
9659
|
+
const startedAt = Date.now();
|
|
9591
9660
|
const result = await aiGenerateObject2(sdkOptions);
|
|
9592
9661
|
await emit(
|
|
9593
|
-
|
|
9662
|
+
timed(
|
|
9663
|
+
withRowUnit(
|
|
9664
|
+
textUsageEvent(entry, provider, result.usage, result.providerMetadata)
|
|
9665
|
+
),
|
|
9666
|
+
startedAt
|
|
9667
|
+
)
|
|
9594
9668
|
);
|
|
9595
9669
|
return result;
|
|
9596
9670
|
},
|
|
9597
9671
|
streamObject(options, call = {}) {
|
|
9598
9672
|
const { entry, languageModel: languageModel2, provider } = textTarget(options.model, call);
|
|
9599
9673
|
const callerOnFinish = options.onFinish;
|
|
9674
|
+
const startedAt = Date.now();
|
|
9600
9675
|
const sdkOptions = {
|
|
9601
9676
|
...options,
|
|
9602
9677
|
...textAbortOptions(options.abortSignal, call.signal),
|
|
@@ -9604,8 +9679,16 @@ function createModelKit(config, adapters) {
|
|
|
9604
9679
|
providerOptions: mergeTextProviderOptions(entry, options.providerOptions),
|
|
9605
9680
|
onFinish: async (event) => {
|
|
9606
9681
|
await emit(
|
|
9607
|
-
|
|
9608
|
-
|
|
9682
|
+
timed(
|
|
9683
|
+
withRowUnit(
|
|
9684
|
+
textUsageEvent(
|
|
9685
|
+
entry,
|
|
9686
|
+
provider,
|
|
9687
|
+
event.usage,
|
|
9688
|
+
event.providerMetadata
|
|
9689
|
+
)
|
|
9690
|
+
),
|
|
9691
|
+
startedAt
|
|
9609
9692
|
)
|
|
9610
9693
|
);
|
|
9611
9694
|
await callerOnFinish?.(event);
|