@mux/ai 0.52.0 → 0.53.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/dist/{index-sqJ9PpHw.d.ts → index-BbxJ0X25.d.ts} +154 -17
- package/dist/index-cbaPe-aE.d.ts +225 -0
- package/dist/index.d.ts +8 -5
- package/dist/index.js +441 -175
- package/dist/index.js.map +1 -1
- package/dist/primitives/index.d.ts +2 -2
- package/dist/primitives/index.js +47 -33
- package/dist/primitives/index.js.map +1 -1
- package/dist/{shots-3wmvZnUV.d.ts → transcripts-BCtzC8hZ.d.ts} +200 -1
- package/dist/workflows/index.d.ts +2 -2
- package/dist/workflows/index.js +439 -174
- package/dist/workflows/index.js.map +1 -1
- package/package.json +2 -2
- package/dist/index-nu1UOAMW.d.ts +0 -387
package/dist/index.js
CHANGED
|
@@ -5,7 +5,7 @@ var __export = (target, all) => {
|
|
|
5
5
|
};
|
|
6
6
|
|
|
7
7
|
// package.json
|
|
8
|
-
var version = "0.
|
|
8
|
+
var version = "0.53.0";
|
|
9
9
|
|
|
10
10
|
// src/lib/language-codes.ts
|
|
11
11
|
var ISO639_1_TO_3 = {
|
|
@@ -704,6 +704,9 @@ var MuxAiError = class extends Error {
|
|
|
704
704
|
super(message);
|
|
705
705
|
/** Structural brand for duck-type identification. */
|
|
706
706
|
this.__robots_error = true;
|
|
707
|
+
if (opts?.cause !== void 0) {
|
|
708
|
+
Object.defineProperty(this, "cause", { value: opts.cause, writable: true, configurable: true });
|
|
709
|
+
}
|
|
707
710
|
this.name = "FatalError";
|
|
708
711
|
this.publicType = opts?.type ?? "processing_error";
|
|
709
712
|
this.publicMessage = message;
|
|
@@ -1814,6 +1817,7 @@ __export(primitives_exports, {
|
|
|
1814
1817
|
estimateTokenCount: () => estimateTokenCount,
|
|
1815
1818
|
extractTextFromVTT: () => extractTextFromVTT,
|
|
1816
1819
|
extractTimestampedTranscript: () => extractTimestampedTranscript,
|
|
1820
|
+
fetchTranscriptDataForUrl: () => fetchTranscriptDataForUrl,
|
|
1817
1821
|
fetchTranscriptForAsset: () => fetchTranscriptForAsset,
|
|
1818
1822
|
filterVttByScope: () => filterVttByScope,
|
|
1819
1823
|
findCaptionTrack: () => findCaptionTrack,
|
|
@@ -3128,9 +3132,10 @@ function extractTimestampedTranscript(vttContent) {
|
|
|
3128
3132
|
segments.map((segment) => `[${Math.floor(segment.time)}s] ${segment.text}`).join("\n")
|
|
3129
3133
|
);
|
|
3130
3134
|
}
|
|
3131
|
-
function parseVTTCues(vttContent) {
|
|
3135
|
+
function parseVTTCues(vttContent, options = {}) {
|
|
3132
3136
|
if (!vttContent.trim())
|
|
3133
3137
|
return [];
|
|
3138
|
+
const { capCueText = true } = options;
|
|
3134
3139
|
const lines = vttContent.split("\n");
|
|
3135
3140
|
const cues = [];
|
|
3136
3141
|
let previousCueIdentifier = null;
|
|
@@ -3162,15 +3167,16 @@ function parseVTTCues(vttContent) {
|
|
|
3162
3167
|
}
|
|
3163
3168
|
const textLines = rawTextLines.map((textLine) => textLine.replace(/<[^>]*>/g, "")).filter(Boolean);
|
|
3164
3169
|
if (textLines.length > 0) {
|
|
3170
|
+
const sanitized = sanitizeUntrustedText(textLines.join(" "));
|
|
3165
3171
|
cues.push({
|
|
3166
3172
|
startTime,
|
|
3167
3173
|
endTime,
|
|
3168
3174
|
// Sanitize at the cue boundary so every downstream consumer
|
|
3169
3175
|
// (engagement-insights, translate-captions chunked path, etc.)
|
|
3170
3176
|
// receives text free of invisible / bidi obfuscation. Length-
|
|
3171
|
-
// cap the result so an adversarially oversized cue
|
|
3172
|
-
// smuggle a large injection payload into
|
|
3173
|
-
text: capCueTextLength(
|
|
3177
|
+
// cap the result by default so an adversarially oversized cue
|
|
3178
|
+
// cannot smuggle a large injection payload into a prompt.
|
|
3179
|
+
text: capCueText ? capCueTextLength(sanitized) : sanitized
|
|
3174
3180
|
});
|
|
3175
3181
|
}
|
|
3176
3182
|
previousCueIdentifier = currentCueIdentifier;
|
|
@@ -3294,16 +3300,32 @@ async function buildTranscriptUrl(playbackId, trackId, shouldSign = false, crede
|
|
|
3294
3300
|
}
|
|
3295
3301
|
return baseUrl;
|
|
3296
3302
|
}
|
|
3297
|
-
async function
|
|
3303
|
+
async function fetchTranscriptDataForUrl(transcriptUrl, options = {}) {
|
|
3298
3304
|
"use step";
|
|
3299
|
-
const {
|
|
3300
|
-
|
|
3301
|
-
|
|
3302
|
-
|
|
3303
|
-
|
|
3304
|
-
|
|
3305
|
-
|
|
3306
|
-
|
|
3305
|
+
const { cleanTranscript = true, scope } = options;
|
|
3306
|
+
try {
|
|
3307
|
+
const response = await fetch(transcriptUrl);
|
|
3308
|
+
if (!response.ok) {
|
|
3309
|
+
throw new Error(`Failed to fetch transcript (HTTP ${response.status})`);
|
|
3310
|
+
}
|
|
3311
|
+
const rawVtt = await response.text();
|
|
3312
|
+
const effectiveScope = hasWorkflowScopeBoundaries(scope) ? scope : void 0;
|
|
3313
|
+
const scopedVtt = filterVttByScope(rawVtt, effectiveScope);
|
|
3314
|
+
const transcriptText = cleanTranscript ? extractTextFromVTT(scopedVtt) : scopedVtt;
|
|
3315
|
+
const hasScopedCues = !effectiveScope || parseVTTCues(scopedVtt).length > 0;
|
|
3316
|
+
if (!transcriptText.trim() || !hasScopedCues) {
|
|
3317
|
+
return {
|
|
3318
|
+
transcriptText,
|
|
3319
|
+
emptyReason: hasScopedCues ? "empty_body" : "no_scoped_cues"
|
|
3320
|
+
};
|
|
3321
|
+
}
|
|
3322
|
+
return { transcriptText };
|
|
3323
|
+
} catch (error) {
|
|
3324
|
+
wrapError(error, "Failed to fetch transcript");
|
|
3325
|
+
}
|
|
3326
|
+
}
|
|
3327
|
+
async function fetchTranscriptForAsset(asset, playbackId, options = {}) {
|
|
3328
|
+
const { required = false, languageCode, cleanTranscript, scope, shouldSign, credentials } = options;
|
|
3307
3329
|
const track = findCaptionTrack(asset, languageCode);
|
|
3308
3330
|
if (!track) {
|
|
3309
3331
|
if (required) {
|
|
@@ -3322,33 +3344,25 @@ async function fetchTranscriptForAsset(asset, playbackId, options = {}) {
|
|
|
3322
3344
|
return { transcriptText: "", track };
|
|
3323
3345
|
}
|
|
3324
3346
|
const transcriptUrl = await buildTranscriptUrl(playbackId, track.id, shouldSign, credentials);
|
|
3347
|
+
let result;
|
|
3325
3348
|
try {
|
|
3326
|
-
|
|
3327
|
-
if (!response.ok) {
|
|
3328
|
-
if (required) {
|
|
3329
|
-
throw new Error(`Failed to fetch transcript (HTTP ${response.status})`);
|
|
3330
|
-
}
|
|
3331
|
-
return { transcriptText: "", transcriptUrl, track };
|
|
3332
|
-
}
|
|
3333
|
-
const rawVtt = await response.text();
|
|
3334
|
-
const effectiveScope = hasWorkflowScopeBoundaries(scope) ? scope : void 0;
|
|
3335
|
-
const scopedVtt = filterVttByScope(rawVtt, effectiveScope);
|
|
3336
|
-
const transcriptText = cleanTranscript ? extractTextFromVTT(scopedVtt) : scopedVtt;
|
|
3337
|
-
const hasScopedCues = !effectiveScope || parseVTTCues(scopedVtt).length > 0;
|
|
3338
|
-
if (required && (!transcriptText.trim() || !hasScopedCues)) {
|
|
3339
|
-
throw new MuxAiError(
|
|
3340
|
-
effectiveScope ? "Transcript has no cues in the requested scope." : "Transcript is empty.",
|
|
3341
|
-
{ type: "validation_error" }
|
|
3342
|
-
);
|
|
3343
|
-
}
|
|
3344
|
-
return { transcriptText, transcriptUrl, track };
|
|
3349
|
+
result = await fetchTranscriptDataForUrl(transcriptUrl, { cleanTranscript, scope });
|
|
3345
3350
|
} catch (error) {
|
|
3346
3351
|
if (required) {
|
|
3347
|
-
|
|
3352
|
+
throw error;
|
|
3348
3353
|
}
|
|
3349
3354
|
console.warn("Failed to fetch transcript:", error);
|
|
3350
3355
|
return { transcriptText: "", transcriptUrl, track };
|
|
3351
3356
|
}
|
|
3357
|
+
if (!required || !result.emptyReason) {
|
|
3358
|
+
return { transcriptText: result.transcriptText, transcriptUrl, track };
|
|
3359
|
+
}
|
|
3360
|
+
switch (result.emptyReason) {
|
|
3361
|
+
case "no_scoped_cues":
|
|
3362
|
+
throw new MuxAiError("Transcript has no cues in the requested scope.", { type: "validation_error" });
|
|
3363
|
+
case "empty_body":
|
|
3364
|
+
throw new MuxAiError("Transcript is empty.", { type: "validation_error" });
|
|
3365
|
+
}
|
|
3352
3366
|
}
|
|
3353
3367
|
|
|
3354
3368
|
// src/workflows/index.ts
|
|
@@ -3361,6 +3375,7 @@ __export(workflows_exports, {
|
|
|
3361
3375
|
DEFAULT_DESCRIPTION_LENGTH: () => DEFAULT_DESCRIPTION_LENGTH,
|
|
3362
3376
|
DEFAULT_SUMMARY_KEYWORD_LIMIT: () => DEFAULT_SUMMARY_KEYWORD_LIMIT,
|
|
3363
3377
|
DEFAULT_TITLE_LENGTH: () => DEFAULT_TITLE_LENGTH,
|
|
3378
|
+
DEFAULT_TRANSCRIPT_WINDOWING: () => DEFAULT_TRANSCRIPT_WINDOWING,
|
|
3364
3379
|
GENERATE_TEXT_CALLS_TO_ACTION: () => GENERATE_TEXT_CALLS_TO_ACTION,
|
|
3365
3380
|
GENERATE_TEXT_CHANNELS: () => GENERATE_TEXT_CHANNELS,
|
|
3366
3381
|
GENERATE_TEXT_CHANNEL_CEILINGS: () => GENERATE_TEXT_CHANNEL_CEILINGS,
|
|
@@ -3386,6 +3401,7 @@ __export(workflows_exports, {
|
|
|
3386
3401
|
applyTextRepair: () => applyTextRepair,
|
|
3387
3402
|
askQuestions: () => askQuestions,
|
|
3388
3403
|
buildReplacementRegex: () => buildReplacementRegex,
|
|
3404
|
+
buildTranscriptWindows: () => buildTranscriptWindows,
|
|
3389
3405
|
burnedInCaptionsSchema: () => burnedInCaptionsSchema,
|
|
3390
3406
|
censorVttContent: () => censorVttContent,
|
|
3391
3407
|
chapterSchema: () => chapterSchema,
|
|
@@ -7277,10 +7293,27 @@ ${renderSection({ tag: "revision_request", content: "The previous draft was empt
|
|
|
7277
7293
|
usages.push(...repaired.attempts.flatMap((item) => item.call.usage ? [item.call.usage] : []));
|
|
7278
7294
|
if (repaired.status !== "valid") {
|
|
7279
7295
|
const cause = repaired.attempts.at(-1)?.call.errorCause;
|
|
7280
|
-
|
|
7281
|
-
|
|
7282
|
-
|
|
7283
|
-
})
|
|
7296
|
+
let failure;
|
|
7297
|
+
if (isIncompleteGenerationError(cause)) {
|
|
7298
|
+
failure = new IncompleteGenerationError(cause.finishReason, cause.rawFinishReason);
|
|
7299
|
+
} else if (MuxAiError.is(cause)) {
|
|
7300
|
+
failure = new MuxAiError(cause.publicMessage, { type: cause.publicType, retryable: cause.retryable });
|
|
7301
|
+
} else {
|
|
7302
|
+
const apiError = APICallError3.isInstance(cause) ? cause : void 0;
|
|
7303
|
+
console.warn("[@mux/ai] Bounded text repair failed.", {
|
|
7304
|
+
provider: args.provider,
|
|
7305
|
+
modelId: args.modelId,
|
|
7306
|
+
reason: repaired.reason,
|
|
7307
|
+
errorName: cause instanceof Error ? cause.name : void 0,
|
|
7308
|
+
statusCode: apiError?.statusCode,
|
|
7309
|
+
requestId: apiError?.responseHeaders?.["x-request-id"] ?? apiError?.responseHeaders?.["request-id"]
|
|
7310
|
+
});
|
|
7311
|
+
failure = new MuxAiError("Unable to complete text generation.", {
|
|
7312
|
+
type: "processing_error",
|
|
7313
|
+
retryable: apiError?.isRetryable ?? true,
|
|
7314
|
+
cause
|
|
7315
|
+
});
|
|
7316
|
+
}
|
|
7284
7317
|
rethrowWithTokenUsage(failure, usages);
|
|
7285
7318
|
}
|
|
7286
7319
|
return { content: repaired.content, usages, unexpectedKeys };
|
|
@@ -7458,6 +7491,9 @@ async function generateTextInternal(assetId, rawOptions, collectedUsage) {
|
|
|
7458
7491
|
};
|
|
7459
7492
|
}
|
|
7460
7493
|
|
|
7494
|
+
// src/workflows/moderation.ts
|
|
7495
|
+
import { z as z9 } from "zod";
|
|
7496
|
+
|
|
7461
7497
|
// src/lib/sampling-plan.ts
|
|
7462
7498
|
var DEFAULT_FPS = 30;
|
|
7463
7499
|
function roundToNearestFrameMs(tsMs, fps = DEFAULT_FPS) {
|
|
@@ -7530,6 +7566,15 @@ var OPENAI_MODERATION_BASE_DELAY_MS = 750;
|
|
|
7530
7566
|
var OPENAI_MODERATION_MAX_DELAY_MS = 3e3;
|
|
7531
7567
|
var MIN_SAMPLE_COVERAGE_FOR_CONFIDENT_THRESHOLDING = 0.5;
|
|
7532
7568
|
var MIN_SUCCESSFUL_THUMBNAILS_FOR_CONFIDENT_THRESHOLDING = 3;
|
|
7569
|
+
var DEFAULT_TRANSCRIPT_WINDOWING = {
|
|
7570
|
+
targetWindowCount: 40,
|
|
7571
|
+
minWindowSeconds: 20,
|
|
7572
|
+
maxWindowSeconds: 120,
|
|
7573
|
+
overlapFraction: 0.15,
|
|
7574
|
+
minOverlapSeconds: 5
|
|
7575
|
+
};
|
|
7576
|
+
var TRANSCRIPT_BATCH_MAX_UTF16_CODE_UNITS = 1e5;
|
|
7577
|
+
var TRANSCRIPT_BATCH_MAX_ITEMS = 100;
|
|
7533
7578
|
var GOOGLE_VISION_ENDPOINT = "https://vision.googleapis.com/v1/images:annotate";
|
|
7534
7579
|
var GOOGLE_VISION_LIKELIHOOD_TO_SCORE = {
|
|
7535
7580
|
UNKNOWN: 0,
|
|
@@ -7597,6 +7642,14 @@ function parseOpenAIModerationResponse(bodyText, status, statusText) {
|
|
|
7597
7642
|
);
|
|
7598
7643
|
}
|
|
7599
7644
|
}
|
|
7645
|
+
var OpenAIModerationResponseSchema = z9.object({
|
|
7646
|
+
results: z9.array(z9.object({
|
|
7647
|
+
category_scores: z9.object({
|
|
7648
|
+
sexual: z9.number().finite().nullish(),
|
|
7649
|
+
violence: z9.number().finite().nullish()
|
|
7650
|
+
})
|
|
7651
|
+
}))
|
|
7652
|
+
});
|
|
7600
7653
|
async function callOpenAIModerationApi({
|
|
7601
7654
|
model,
|
|
7602
7655
|
input,
|
|
@@ -7636,7 +7689,21 @@ async function callOpenAIModerationApi({
|
|
|
7636
7689
|
}
|
|
7637
7690
|
);
|
|
7638
7691
|
}
|
|
7639
|
-
|
|
7692
|
+
const parsed = OpenAIModerationResponseSchema.safeParse(json);
|
|
7693
|
+
if (!parsed.success) {
|
|
7694
|
+
throw new OpenAIModerationRequestError(
|
|
7695
|
+
`OpenAI moderation returned an unexpected response shape: ${compactBodySnippet(bodyText)}`,
|
|
7696
|
+
{ status: res.status, retriable: false }
|
|
7697
|
+
);
|
|
7698
|
+
}
|
|
7699
|
+
const expectedResultCount = Array.isArray(input) ? input.length : 1;
|
|
7700
|
+
if (parsed.data.results.length !== expectedResultCount) {
|
|
7701
|
+
throw new OpenAIModerationRequestError(
|
|
7702
|
+
`OpenAI moderation returned ${parsed.data.results.length} result(s) for ${expectedResultCount} input(s).`,
|
|
7703
|
+
{ status: res.status, retriable: false }
|
|
7704
|
+
);
|
|
7705
|
+
}
|
|
7706
|
+
return parsed.data;
|
|
7640
7707
|
},
|
|
7641
7708
|
{
|
|
7642
7709
|
maxRetries: OPENAI_MODERATION_MAX_RETRIES,
|
|
@@ -7660,7 +7727,7 @@ async function processConcurrently(items, processor, maxConcurrent = 5) {
|
|
|
7660
7727
|
async function moderateImageWithOpenAI(entry) {
|
|
7661
7728
|
"use step";
|
|
7662
7729
|
try {
|
|
7663
|
-
const
|
|
7730
|
+
const { results } = await callOpenAIModerationApi({
|
|
7664
7731
|
model: entry.model,
|
|
7665
7732
|
input: [
|
|
7666
7733
|
{
|
|
@@ -7672,12 +7739,12 @@ async function moderateImageWithOpenAI(entry) {
|
|
|
7672
7739
|
],
|
|
7673
7740
|
credentials: entry.credentials
|
|
7674
7741
|
});
|
|
7675
|
-
const
|
|
7742
|
+
const [result] = results;
|
|
7676
7743
|
return {
|
|
7677
7744
|
url: entry.url,
|
|
7678
7745
|
time: entry.time,
|
|
7679
|
-
sexual:
|
|
7680
|
-
violence:
|
|
7746
|
+
sexual: result.category_scores.sexual ?? 0,
|
|
7747
|
+
violence: result.category_scores.violence ?? 0,
|
|
7681
7748
|
error: false
|
|
7682
7749
|
};
|
|
7683
7750
|
} catch (error) {
|
|
@@ -7701,65 +7768,187 @@ async function requestOpenAIModeration(images, model, maxConcurrent = 5, submiss
|
|
|
7701
7768
|
) : images.map((img) => ({ url: img.url, time: img.time, image: img.url, model, credentials }));
|
|
7702
7769
|
return processConcurrently(targetUrls, moderateImageWithOpenAI, maxConcurrent);
|
|
7703
7770
|
}
|
|
7704
|
-
|
|
7705
|
-
|
|
7706
|
-
|
|
7707
|
-
|
|
7708
|
-
|
|
7709
|
-
|
|
7710
|
-
|
|
7771
|
+
var TRANSCRIPT_WINDOW_MAX_UTF16_CODE_UNITS = 1e4;
|
|
7772
|
+
function describeSurfaceFailure(itemLabel, scores) {
|
|
7773
|
+
const messages = Array.from(new Set(scores.map((s) => s.errorMessage || "Unknown error")));
|
|
7774
|
+
return `All ${scores.length} ${itemLabel}(s) failed to moderate: ${messages.join("; ")}`;
|
|
7775
|
+
}
|
|
7776
|
+
function clamp(value, min, max) {
|
|
7777
|
+
return Math.min(Math.max(value, min), max);
|
|
7778
|
+
}
|
|
7779
|
+
function splitCuesUnderCharCeiling(cues, maxUnits = TRANSCRIPT_WINDOW_MAX_UTF16_CODE_UNITS) {
|
|
7780
|
+
const windows = [];
|
|
7781
|
+
let current = [];
|
|
7782
|
+
let currentLength = 0;
|
|
7783
|
+
const flush = () => {
|
|
7784
|
+
if (current.length === 0) {
|
|
7785
|
+
return;
|
|
7786
|
+
}
|
|
7787
|
+
windows.push({
|
|
7788
|
+
startTime: Math.min(...current.map((cue) => cue.startTime)),
|
|
7789
|
+
endTime: Math.max(...current.map((cue) => cue.endTime)),
|
|
7790
|
+
text: current.map((cue) => cue.text).join(" ")
|
|
7711
7791
|
});
|
|
7712
|
-
|
|
7713
|
-
|
|
7714
|
-
|
|
7715
|
-
|
|
7716
|
-
|
|
7717
|
-
|
|
7718
|
-
|
|
7719
|
-
|
|
7720
|
-
|
|
7721
|
-
|
|
7722
|
-
|
|
7723
|
-
sexual: 0,
|
|
7724
|
-
violence: 0,
|
|
7725
|
-
error: true,
|
|
7726
|
-
errorMessage: error instanceof Error ? error.message : String(error)
|
|
7727
|
-
};
|
|
7792
|
+
current = [];
|
|
7793
|
+
currentLength = 0;
|
|
7794
|
+
};
|
|
7795
|
+
for (const cue of cues) {
|
|
7796
|
+
const cueText = cue.text;
|
|
7797
|
+
const addedLength = currentLength === 0 ? cueText.length : currentLength + 1 + cueText.length;
|
|
7798
|
+
if (current.length > 0 && addedLength > maxUnits) {
|
|
7799
|
+
flush();
|
|
7800
|
+
}
|
|
7801
|
+
current.push(cue);
|
|
7802
|
+
currentLength = current.length === 1 ? cueText.length : currentLength + 1 + cueText.length;
|
|
7728
7803
|
}
|
|
7804
|
+
flush();
|
|
7805
|
+
return windows;
|
|
7729
7806
|
}
|
|
7730
|
-
function
|
|
7731
|
-
|
|
7807
|
+
function buildTranscriptWindows(cues, duration) {
|
|
7808
|
+
const usableCues = cues.filter((cue) => cue.text.trim().length > 0).slice().sort((a, b) => a.startTime - b.startTime);
|
|
7809
|
+
if (usableCues.length === 0) {
|
|
7732
7810
|
return [];
|
|
7733
7811
|
}
|
|
7734
|
-
|
|
7735
|
-
|
|
7812
|
+
const lastCueEnd = Math.max(...usableCues.map((cue) => cue.endTime));
|
|
7813
|
+
const effectiveDuration = duration && duration > 0 ? duration : lastCueEnd;
|
|
7814
|
+
const { targetWindowCount, minWindowSeconds, maxWindowSeconds, overlapFraction, minOverlapSeconds } = DEFAULT_TRANSCRIPT_WINDOWING;
|
|
7815
|
+
const windowSeconds = clamp(
|
|
7816
|
+
effectiveDuration / targetWindowCount,
|
|
7817
|
+
minWindowSeconds,
|
|
7818
|
+
maxWindowSeconds
|
|
7819
|
+
);
|
|
7820
|
+
const overlapSeconds = Math.max(
|
|
7821
|
+
minOverlapSeconds,
|
|
7822
|
+
windowSeconds * overlapFraction
|
|
7823
|
+
);
|
|
7824
|
+
const stride = Math.max(windowSeconds - overlapSeconds, 1);
|
|
7825
|
+
const rawWindows = [];
|
|
7826
|
+
for (let k = 0; ; k++) {
|
|
7827
|
+
const windowStart = k * stride;
|
|
7828
|
+
if (windowStart > lastCueEnd) {
|
|
7829
|
+
break;
|
|
7830
|
+
}
|
|
7831
|
+
const windowEnd = windowStart + windowSeconds;
|
|
7832
|
+
const windowCues = usableCues.filter(
|
|
7833
|
+
(cue) => cue.startTime < windowEnd && cue.endTime > windowStart
|
|
7834
|
+
);
|
|
7835
|
+
if (windowCues.length > 0) {
|
|
7836
|
+
rawWindows.push(windowCues);
|
|
7837
|
+
}
|
|
7838
|
+
if (stride <= 0) {
|
|
7839
|
+
break;
|
|
7840
|
+
}
|
|
7736
7841
|
}
|
|
7737
|
-
const
|
|
7738
|
-
|
|
7739
|
-
|
|
7740
|
-
|
|
7741
|
-
|
|
7842
|
+
const dedupedCueGroups = [];
|
|
7843
|
+
const cueGroupKey = (group) => group.map((cue) => `${cue.startTime}:${cue.endTime}`).join("|");
|
|
7844
|
+
let previousKey;
|
|
7845
|
+
for (const group of rawWindows) {
|
|
7846
|
+
const key = cueGroupKey(group);
|
|
7847
|
+
if (key === previousKey) {
|
|
7848
|
+
continue;
|
|
7742
7849
|
}
|
|
7850
|
+
dedupedCueGroups.push(group);
|
|
7851
|
+
previousKey = key;
|
|
7743
7852
|
}
|
|
7744
|
-
|
|
7853
|
+
const windows = [];
|
|
7854
|
+
for (const group of dedupedCueGroups) {
|
|
7855
|
+
const joinedLength = group.reduce(
|
|
7856
|
+
(sum, cue, index) => sum + cue.text.length + (index === 0 ? 0 : 1),
|
|
7857
|
+
0
|
|
7858
|
+
);
|
|
7859
|
+
if (joinedLength > TRANSCRIPT_WINDOW_MAX_UTF16_CODE_UNITS) {
|
|
7860
|
+
windows.push(...splitCuesUnderCharCeiling(group));
|
|
7861
|
+
} else {
|
|
7862
|
+
windows.push({
|
|
7863
|
+
startTime: Math.min(...group.map((cue) => cue.startTime)),
|
|
7864
|
+
endTime: Math.max(...group.map((cue) => cue.endTime)),
|
|
7865
|
+
text: group.map((cue) => cue.text).join(" ")
|
|
7866
|
+
});
|
|
7867
|
+
}
|
|
7868
|
+
}
|
|
7869
|
+
return windows;
|
|
7870
|
+
}
|
|
7871
|
+
function packWindowsIntoBatches(windows) {
|
|
7872
|
+
const batches = [];
|
|
7873
|
+
let current = [];
|
|
7874
|
+
let currentLength = 0;
|
|
7875
|
+
for (const window of windows) {
|
|
7876
|
+
const wouldExceedChars = current.length > 0 && currentLength + window.text.length > TRANSCRIPT_BATCH_MAX_UTF16_CODE_UNITS;
|
|
7877
|
+
const wouldExceedItems = current.length >= TRANSCRIPT_BATCH_MAX_ITEMS;
|
|
7878
|
+
if (wouldExceedChars || wouldExceedItems) {
|
|
7879
|
+
batches.push(current);
|
|
7880
|
+
current = [];
|
|
7881
|
+
currentLength = 0;
|
|
7882
|
+
}
|
|
7883
|
+
current.push(window);
|
|
7884
|
+
currentLength += window.text.length;
|
|
7885
|
+
}
|
|
7886
|
+
if (current.length > 0) {
|
|
7887
|
+
batches.push(current);
|
|
7888
|
+
}
|
|
7889
|
+
return batches;
|
|
7890
|
+
}
|
|
7891
|
+
function transcriptErrorScore(window, error) {
|
|
7892
|
+
return {
|
|
7893
|
+
startTime: window.startTime,
|
|
7894
|
+
endTime: window.endTime,
|
|
7895
|
+
sexual: 0,
|
|
7896
|
+
violence: 0,
|
|
7897
|
+
error: true,
|
|
7898
|
+
errorMessage: error instanceof Error ? error.message : String(error)
|
|
7899
|
+
};
|
|
7745
7900
|
}
|
|
7746
|
-
async function
|
|
7901
|
+
async function moderateTranscriptBatchWithOpenAI(batch) {
|
|
7747
7902
|
"use step";
|
|
7748
|
-
const
|
|
7749
|
-
if (
|
|
7750
|
-
return [
|
|
7751
|
-
|
|
7752
|
-
|
|
7753
|
-
|
|
7754
|
-
|
|
7755
|
-
|
|
7756
|
-
|
|
7757
|
-
|
|
7758
|
-
|
|
7759
|
-
|
|
7760
|
-
|
|
7903
|
+
const { windows, model, credentials } = batch;
|
|
7904
|
+
if (windows.length === 0) {
|
|
7905
|
+
return [];
|
|
7906
|
+
}
|
|
7907
|
+
try {
|
|
7908
|
+
const { results } = await callOpenAIModerationApi({
|
|
7909
|
+
model,
|
|
7910
|
+
input: windows.map((window) => window.text),
|
|
7911
|
+
credentials
|
|
7912
|
+
});
|
|
7913
|
+
return windows.map((window, index) => {
|
|
7914
|
+
const result = results[index];
|
|
7915
|
+
return {
|
|
7916
|
+
startTime: window.startTime,
|
|
7917
|
+
endTime: window.endTime,
|
|
7918
|
+
sexual: result.category_scores.sexual ?? 0,
|
|
7919
|
+
violence: result.category_scores.violence ?? 0,
|
|
7920
|
+
error: false
|
|
7921
|
+
};
|
|
7922
|
+
});
|
|
7923
|
+
} catch (error) {
|
|
7924
|
+
const status = error instanceof OpenAIModerationRequestError ? error.status : void 0;
|
|
7925
|
+
if (status === 400 && windows.length > 1) {
|
|
7926
|
+
const mid = Math.ceil(windows.length / 2);
|
|
7927
|
+
const [left, right] = await Promise.all([
|
|
7928
|
+
moderateTranscriptBatchWithOpenAI({ windows: windows.slice(0, mid), model, credentials }),
|
|
7929
|
+
moderateTranscriptBatchWithOpenAI({ windows: windows.slice(mid), model, credentials })
|
|
7930
|
+
]);
|
|
7931
|
+
return [...left, ...right];
|
|
7932
|
+
}
|
|
7933
|
+
console.error("OpenAI transcript moderation failed:", error);
|
|
7934
|
+
return windows.map((window) => transcriptErrorScore(window, error));
|
|
7935
|
+
}
|
|
7936
|
+
}
|
|
7937
|
+
async function requestOpenAITranscriptModeration(cues, duration, model, maxConcurrent = 5, credentials) {
|
|
7938
|
+
"use step";
|
|
7939
|
+
const windows = buildTranscriptWindows(cues, duration);
|
|
7940
|
+
if (!windows.length) {
|
|
7941
|
+
return [];
|
|
7942
|
+
}
|
|
7943
|
+
const batches = packWindowsIntoBatches(windows).map(
|
|
7944
|
+
(windowBatch) => ({ windows: windowBatch, model, credentials })
|
|
7945
|
+
);
|
|
7946
|
+
const batchResults = await processConcurrently(
|
|
7947
|
+
batches,
|
|
7948
|
+
moderateTranscriptBatchWithOpenAI,
|
|
7761
7949
|
maxConcurrent
|
|
7762
7950
|
);
|
|
7951
|
+
return batchResults.flat();
|
|
7763
7952
|
}
|
|
7764
7953
|
function getHiveCategoryScores(classes, categoryNames) {
|
|
7765
7954
|
const scoreMap = Object.fromEntries(
|
|
@@ -7982,6 +8171,28 @@ async function getThumbnailUrlsFromTimestamps(playbackId, timestampsMs, options)
|
|
|
7982
8171
|
});
|
|
7983
8172
|
return Promise.all(urlPromises);
|
|
7984
8173
|
}
|
|
8174
|
+
async function requestThumbnailModeration(provider, thumbnailUrls, options) {
|
|
8175
|
+
const { model, maxConcurrent, imageSubmissionMode, imageDownloadOptions, credentials } = options;
|
|
8176
|
+
switch (provider) {
|
|
8177
|
+
case "openai":
|
|
8178
|
+
return requestOpenAIModeration(
|
|
8179
|
+
thumbnailUrls,
|
|
8180
|
+
model || "omni-moderation-latest",
|
|
8181
|
+
maxConcurrent,
|
|
8182
|
+
imageSubmissionMode,
|
|
8183
|
+
imageDownloadOptions,
|
|
8184
|
+
credentials
|
|
8185
|
+
);
|
|
8186
|
+
case "hive":
|
|
8187
|
+
return requestHiveModeration(thumbnailUrls, maxConcurrent, imageSubmissionMode, imageDownloadOptions, credentials);
|
|
8188
|
+
case "google-vision-api":
|
|
8189
|
+
return requestGoogleVisionModeration(thumbnailUrls, maxConcurrent, imageSubmissionMode, imageDownloadOptions, credentials);
|
|
8190
|
+
default: {
|
|
8191
|
+
const exhaustiveCheck = provider;
|
|
8192
|
+
throw new Error(`Unsupported moderation provider: ${exhaustiveCheck}`);
|
|
8193
|
+
}
|
|
8194
|
+
}
|
|
8195
|
+
}
|
|
7985
8196
|
async function getModerationScores(assetId, options = {}) {
|
|
7986
8197
|
"use workflow";
|
|
7987
8198
|
const {
|
|
@@ -8014,47 +8225,31 @@ async function getModerationScores(assetId, options = {}) {
|
|
|
8014
8225
|
startTime: Math.min(resolvedScope.startTime, duration),
|
|
8015
8226
|
endTime: Math.min(resolvedScope.endTime, duration)
|
|
8016
8227
|
} : void 0;
|
|
8017
|
-
if (!isAudioOnly && renderableScope && renderableScope.startTime >= renderableScope.endTime) {
|
|
8018
|
-
throw new Error("The requested scope does not include any renderable video.");
|
|
8019
|
-
}
|
|
8020
8228
|
const signingContext = await resolveMuxSigningContext(credentials);
|
|
8021
8229
|
if (policy === "signed" && !signingContext) {
|
|
8022
8230
|
throw new Error(
|
|
8023
8231
|
"Signed playback ID requires signing credentials. Set MUX_SIGNING_KEY and MUX_PRIVATE_KEY environment variables."
|
|
8024
8232
|
);
|
|
8025
8233
|
}
|
|
8026
|
-
let
|
|
8027
|
-
let
|
|
8028
|
-
let
|
|
8234
|
+
let thumbnailModeration = { status: "completed" };
|
|
8235
|
+
let thumbnailUrls = [];
|
|
8236
|
+
let shouldRunThumbnails = false;
|
|
8029
8237
|
if (isAudioOnly) {
|
|
8030
|
-
|
|
8031
|
-
|
|
8032
|
-
|
|
8033
|
-
|
|
8034
|
-
|
|
8035
|
-
|
|
8036
|
-
|
|
8037
|
-
|
|
8038
|
-
|
|
8039
|
-
|
|
8040
|
-
|
|
8041
|
-
transcriptResult.transcriptText,
|
|
8042
|
-
model || "omni-moderation-latest",
|
|
8043
|
-
maxConcurrent,
|
|
8044
|
-
credentials
|
|
8045
|
-
);
|
|
8046
|
-
} else if (provider === "hive") {
|
|
8047
|
-
throw new Error("Hive does not support transcript moderation in this workflow. Use provider: 'openai' for audio-only assets.");
|
|
8048
|
-
} else if (provider === "google-vision-api") {
|
|
8049
|
-
throw new Error("google-vision-api is image-only and does not support transcript moderation. Use provider: 'openai' for audio-only assets.");
|
|
8050
|
-
} else {
|
|
8051
|
-
const exhaustiveCheck = provider;
|
|
8052
|
-
throw new Error(`Unsupported moderation provider: ${exhaustiveCheck}`);
|
|
8053
|
-
}
|
|
8238
|
+
thumbnailModeration = {
|
|
8239
|
+
status: "skipped",
|
|
8240
|
+
skipReason: "no_video_track",
|
|
8241
|
+
skipMessage: "Asset has no video track, so there are no thumbnails to moderate."
|
|
8242
|
+
};
|
|
8243
|
+
} else if (renderableScope && renderableScope.startTime >= renderableScope.endTime) {
|
|
8244
|
+
thumbnailModeration = {
|
|
8245
|
+
status: "skipped",
|
|
8246
|
+
skipReason: "no_video_in_scope",
|
|
8247
|
+
skipMessage: "The requested scope does not include any renderable video, so there are no thumbnails to moderate."
|
|
8248
|
+
};
|
|
8054
8249
|
} else {
|
|
8055
8250
|
const scopedDuration = renderableScope ? renderableScope.endTime - renderableScope.startTime : duration;
|
|
8056
8251
|
const estimatedIntervalCount = scopedDuration <= 50 ? 5 : Math.ceil(scopedDuration / thumbnailInterval);
|
|
8057
|
-
|
|
8252
|
+
thumbnailUrls = maxSamples !== void 0 && estimatedIntervalCount > maxSamples ? await getThumbnailUrlsFromTimestamps(
|
|
8058
8253
|
playbackId,
|
|
8059
8254
|
filterTimestampsWithinExclusiveScope(
|
|
8060
8255
|
planSamplingTimestamps({
|
|
@@ -8079,70 +8274,141 @@ async function getModerationScores(assetId, options = {}) {
|
|
|
8079
8274
|
credentials,
|
|
8080
8275
|
scope: renderableScope
|
|
8081
8276
|
});
|
|
8082
|
-
|
|
8083
|
-
|
|
8084
|
-
|
|
8085
|
-
|
|
8086
|
-
|
|
8087
|
-
|
|
8088
|
-
|
|
8089
|
-
|
|
8090
|
-
|
|
8091
|
-
|
|
8092
|
-
|
|
8093
|
-
|
|
8094
|
-
|
|
8095
|
-
|
|
8096
|
-
|
|
8097
|
-
|
|
8098
|
-
|
|
8099
|
-
|
|
8100
|
-
|
|
8101
|
-
|
|
8102
|
-
|
|
8103
|
-
|
|
8104
|
-
|
|
8105
|
-
|
|
8106
|
-
|
|
8107
|
-
|
|
8277
|
+
shouldRunThumbnails = true;
|
|
8278
|
+
}
|
|
8279
|
+
let transcriptModeration = { status: "completed" };
|
|
8280
|
+
let transcriptCues = [];
|
|
8281
|
+
let shouldRunTranscript = false;
|
|
8282
|
+
const captionTrack = provider === "openai" ? findCaptionTrack(asset, languageCode) : void 0;
|
|
8283
|
+
if (provider !== "openai") {
|
|
8284
|
+
transcriptModeration = {
|
|
8285
|
+
status: "skipped",
|
|
8286
|
+
skipReason: "unsupported_provider",
|
|
8287
|
+
skipMessage: `Provider '${provider}' is image-only and cannot moderate transcript text; use provider 'openai'.`
|
|
8288
|
+
};
|
|
8289
|
+
} else if (!captionTrack) {
|
|
8290
|
+
transcriptModeration = {
|
|
8291
|
+
status: "skipped",
|
|
8292
|
+
skipReason: "no_ready_text_track",
|
|
8293
|
+
skipMessage: languageCode ? `No ready caption/subtitle track found for language '${languageCode}'.` : "No ready caption/subtitle track found for this asset."
|
|
8294
|
+
};
|
|
8295
|
+
} else {
|
|
8296
|
+
if (!captionTrack.id) {
|
|
8297
|
+
throw new MuxAiError("Transcript track is missing an id.", { type: "validation_error" });
|
|
8298
|
+
}
|
|
8299
|
+
const transcriptUrl = await buildTranscriptUrl(playbackId, captionTrack.id, policy === "signed", credentials);
|
|
8300
|
+
const { transcriptText, emptyReason } = await fetchTranscriptDataForUrl(transcriptUrl, {
|
|
8301
|
+
cleanTranscript: false,
|
|
8302
|
+
scope: effectiveScope
|
|
8303
|
+
});
|
|
8304
|
+
if (emptyReason) {
|
|
8305
|
+
transcriptModeration = {
|
|
8306
|
+
status: "skipped",
|
|
8307
|
+
skipReason: "no_cues",
|
|
8308
|
+
skipMessage: emptyReason === "no_scoped_cues" ? "Transcript has no cues in the requested scope." : "Transcript is empty."
|
|
8309
|
+
};
|
|
8108
8310
|
} else {
|
|
8109
|
-
|
|
8110
|
-
|
|
8311
|
+
transcriptCues = parseVTTCues(transcriptText, { capCueText: false });
|
|
8312
|
+
const windows = transcriptCues.length === 0 ? [] : buildTranscriptWindows(transcriptCues, duration);
|
|
8313
|
+
if (windows.length === 0) {
|
|
8314
|
+
transcriptModeration = {
|
|
8315
|
+
status: "skipped",
|
|
8316
|
+
skipReason: "no_cues",
|
|
8317
|
+
skipMessage: "Caption track had no parseable cues."
|
|
8318
|
+
};
|
|
8319
|
+
} else {
|
|
8320
|
+
shouldRunTranscript = true;
|
|
8321
|
+
}
|
|
8111
8322
|
}
|
|
8112
8323
|
}
|
|
8113
|
-
|
|
8114
|
-
|
|
8324
|
+
if (!shouldRunThumbnails && !shouldRunTranscript) {
|
|
8325
|
+
throw new MuxAiError(
|
|
8326
|
+
`Nothing to moderate. Thumbnails skipped: ${thumbnailModeration.skipMessage} Transcript skipped: ${transcriptModeration.skipMessage}`,
|
|
8327
|
+
{ type: "validation_error" }
|
|
8328
|
+
);
|
|
8329
|
+
}
|
|
8330
|
+
const [thumbnailScores, transcriptScores] = await Promise.all([
|
|
8331
|
+
shouldRunThumbnails ? requestThumbnailModeration(provider, thumbnailUrls, {
|
|
8332
|
+
model,
|
|
8333
|
+
maxConcurrent,
|
|
8334
|
+
imageSubmissionMode,
|
|
8335
|
+
imageDownloadOptions,
|
|
8336
|
+
credentials
|
|
8337
|
+
}) : Promise.resolve([]),
|
|
8338
|
+
shouldRunTranscript ? requestOpenAITranscriptModeration(
|
|
8339
|
+
transcriptCues,
|
|
8340
|
+
duration,
|
|
8341
|
+
model || "omni-moderation-latest",
|
|
8342
|
+
maxConcurrent,
|
|
8343
|
+
credentials
|
|
8344
|
+
) : Promise.resolve([])
|
|
8345
|
+
]);
|
|
8346
|
+
const mode = shouldRunThumbnails && shouldRunTranscript ? "combined" : shouldRunTranscript ? "transcript" : "thumbnails";
|
|
8347
|
+
const thumbnailCount = shouldRunThumbnails ? thumbnailUrls.length : void 0;
|
|
8348
|
+
const allScores = [
|
|
8349
|
+
...thumbnailScores.map((s) => ({ ...s, label: s.url })),
|
|
8350
|
+
...transcriptScores.map((s) => ({ ...s, error: s.error ?? false, label: `transcript window ${s.startTime}-${s.endTime}s` }))
|
|
8351
|
+
];
|
|
8352
|
+
const failed = allScores.filter((s) => s.error);
|
|
8353
|
+
const successful = allScores.filter((s) => !s.error);
|
|
8115
8354
|
if (successful.length === 0) {
|
|
8116
|
-
const details = failed.map((s) => `${s.
|
|
8355
|
+
const details = failed.map((s) => `${s.label}: ${s.errorMessage || "Unknown error"}`).join("; ");
|
|
8117
8356
|
throw new Error(
|
|
8118
|
-
`Moderation failed for all ${
|
|
8357
|
+
`Moderation failed for all ${allScores.length} sample(s): ${details}`
|
|
8119
8358
|
);
|
|
8120
8359
|
}
|
|
8121
8360
|
if (failed.length > 0) {
|
|
8122
8361
|
console.warn(
|
|
8123
|
-
`Moderation had partial failures (${failed.length}/${
|
|
8362
|
+
`Moderation had partial failures (${failed.length}/${allScores.length}); continuing with successful samples.`
|
|
8124
8363
|
);
|
|
8125
8364
|
}
|
|
8365
|
+
const thumbnailsFailed = shouldRunThumbnails && thumbnailScores.length > 0 && thumbnailScores.every((s) => s.error);
|
|
8366
|
+
const transcriptFailed = shouldRunTranscript && transcriptScores.length > 0 && transcriptScores.every((s) => s.error);
|
|
8367
|
+
if (thumbnailsFailed) {
|
|
8368
|
+
thumbnailModeration = {
|
|
8369
|
+
status: "failed",
|
|
8370
|
+
errorMessage: describeSurfaceFailure("thumbnail", thumbnailScores)
|
|
8371
|
+
};
|
|
8372
|
+
}
|
|
8373
|
+
if (transcriptFailed) {
|
|
8374
|
+
transcriptModeration = {
|
|
8375
|
+
status: "failed",
|
|
8376
|
+
errorMessage: describeSurfaceFailure("transcript window", transcriptScores)
|
|
8377
|
+
};
|
|
8378
|
+
}
|
|
8126
8379
|
const maxSexual = Math.max(...successful.map((s) => s.sexual));
|
|
8127
8380
|
const maxViolence = Math.max(...successful.map((s) => s.violence));
|
|
8128
8381
|
const finalThresholds = { ...DEFAULT_THRESHOLDS, ...thresholds };
|
|
8129
|
-
const
|
|
8130
|
-
const
|
|
8131
|
-
const
|
|
8132
|
-
const
|
|
8133
|
-
const
|
|
8134
|
-
const
|
|
8382
|
+
const coverageScores = thumbnailScores;
|
|
8383
|
+
const coverageFailed = coverageScores.filter((s) => s.error);
|
|
8384
|
+
const coverageSuccessful = coverageScores.filter((s) => !s.error);
|
|
8385
|
+
const requestedSampleCount = coverageScores.length;
|
|
8386
|
+
const successfulSampleCount = coverageSuccessful.length;
|
|
8387
|
+
const failedSampleCount = coverageFailed.length;
|
|
8388
|
+
const sampleCoverage = requestedSampleCount > 0 ? successfulSampleCount / requestedSampleCount : 0;
|
|
8389
|
+
const hasThumbnails = requestedSampleCount > 0;
|
|
8390
|
+
const hasSuccessfulTranscript = transcriptScores.some((s) => !s.error);
|
|
8391
|
+
let isLowConfidence;
|
|
8392
|
+
if (!hasThumbnails) {
|
|
8393
|
+
isLowConfidence = !hasSuccessfulTranscript;
|
|
8394
|
+
} else {
|
|
8395
|
+
const hasEnoughSuccessfulSamples = successfulSampleCount >= MIN_SUCCESSFUL_THUMBNAILS_FOR_CONFIDENT_THRESHOLDING;
|
|
8396
|
+
isLowConfidence = sampleCoverage < MIN_SAMPLE_COVERAGE_FOR_CONFIDENT_THRESHOLDING || !hasEnoughSuccessfulSamples || transcriptFailed;
|
|
8397
|
+
}
|
|
8135
8398
|
return {
|
|
8136
8399
|
assetId,
|
|
8137
8400
|
mode,
|
|
8138
8401
|
isAudioOnly,
|
|
8139
8402
|
thumbnailScores,
|
|
8403
|
+
transcriptScores,
|
|
8404
|
+
thumbnailModeration,
|
|
8405
|
+
transcriptModeration,
|
|
8140
8406
|
coverage: {
|
|
8141
8407
|
requestedSampleCount,
|
|
8142
8408
|
successfulSampleCount,
|
|
8143
8409
|
failedSampleCount,
|
|
8144
8410
|
sampleCoverage,
|
|
8145
|
-
isPartial:
|
|
8411
|
+
isPartial: failed.length > 0,
|
|
8146
8412
|
isLowConfidence
|
|
8147
8413
|
},
|
|
8148
8414
|
usage: {
|
|
@@ -8162,7 +8428,7 @@ async function getModerationScores(assetId, options = {}) {
|
|
|
8162
8428
|
|
|
8163
8429
|
// src/workflows/summarization.ts
|
|
8164
8430
|
import { generateText as generateText8, Output as Output8 } from "ai";
|
|
8165
|
-
import { z as
|
|
8431
|
+
import { z as z10 } from "zod";
|
|
8166
8432
|
|
|
8167
8433
|
// src/lib/markdown-normalization.ts
|
|
8168
8434
|
var ESCAPED_LINE_BREAK_REGEX = /\\r\\n|\\n|\\r/g;
|
|
@@ -8307,17 +8573,17 @@ function createSummarizationGuidance({
|
|
|
8307
8573
|
}
|
|
8308
8574
|
|
|
8309
8575
|
// src/workflows/summarization.ts
|
|
8310
|
-
var summarySchema =
|
|
8311
|
-
keywords:
|
|
8312
|
-
title:
|
|
8313
|
-
description:
|
|
8576
|
+
var summarySchema = z10.object({
|
|
8577
|
+
keywords: z10.array(z10.string().max(200)),
|
|
8578
|
+
title: z10.string().max(500),
|
|
8579
|
+
description: z10.string().max(1e4)
|
|
8314
8580
|
});
|
|
8315
8581
|
function buildSummarySchema(descriptionLength) {
|
|
8316
8582
|
const descriptionMaxChars = Math.max(2e3, descriptionLength * 15);
|
|
8317
|
-
return
|
|
8318
|
-
keywords:
|
|
8319
|
-
title:
|
|
8320
|
-
description:
|
|
8583
|
+
return z10.object({
|
|
8584
|
+
keywords: z10.array(z10.string().max(200)),
|
|
8585
|
+
title: z10.string().max(500),
|
|
8586
|
+
description: z10.string().max(descriptionMaxChars)
|
|
8321
8587
|
});
|
|
8322
8588
|
}
|
|
8323
8589
|
var VALID_TONES = ["neutral", "playful", "professional"];
|
|
@@ -9436,9 +9702,9 @@ import {
|
|
|
9436
9702
|
RetryError as RetryError2,
|
|
9437
9703
|
TypeValidationError
|
|
9438
9704
|
} from "ai";
|
|
9439
|
-
import { z as
|
|
9440
|
-
var translationSchema =
|
|
9441
|
-
translation:
|
|
9705
|
+
import { z as z11 } from "zod";
|
|
9706
|
+
var translationSchema = z11.object({
|
|
9707
|
+
translation: z11.string()
|
|
9442
9708
|
});
|
|
9443
9709
|
var SYSTEM_PROMPT5 = promptDedent`
|
|
9444
9710
|
You are a subtitle translation expert. Translate VTT subtitle files to the target language specified by the user.
|
|
@@ -9817,8 +10083,8 @@ async function translateCueChunkWithAI({
|
|
|
9817
10083
|
}) {
|
|
9818
10084
|
"use step";
|
|
9819
10085
|
const model = await createLanguageModelFromConfig(provider, modelId, credentials);
|
|
9820
|
-
const schema =
|
|
9821
|
-
translations:
|
|
10086
|
+
const schema = z11.object({
|
|
10087
|
+
translations: z11.array(z11.string().min(1).max(2e3)).length(cues.length)
|
|
9822
10088
|
});
|
|
9823
10089
|
const cuePayload = cues.map((cue, index) => ({
|
|
9824
10090
|
index,
|