@mux/ai 0.52.0 → 0.54.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-CmTC1w4V.d.ts} +162 -17
- package/dist/index-cbaPe-aE.d.ts +225 -0
- package/dist/index.d.ts +8 -5
- package/dist/index.js +449 -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 +447 -174
- package/dist/workflows/index.js.map +1 -1
- package/package.json +2 -2
- package/dist/index-nu1UOAMW.d.ts +0 -387
package/dist/workflows/index.js
CHANGED
|
@@ -531,6 +531,9 @@ var MuxAiError = class extends Error {
|
|
|
531
531
|
super(message);
|
|
532
532
|
/** Structural brand for duck-type identification. */
|
|
533
533
|
this.__robots_error = true;
|
|
534
|
+
if (opts?.cause !== void 0) {
|
|
535
|
+
Object.defineProperty(this, "cause", { value: opts.cause, writable: true, configurable: true });
|
|
536
|
+
}
|
|
534
537
|
this.name = "FatalError";
|
|
535
538
|
this.publicType = opts?.type ?? "processing_error";
|
|
536
539
|
this.publicMessage = message;
|
|
@@ -2601,9 +2604,10 @@ function extractTimestampedTranscript(vttContent) {
|
|
|
2601
2604
|
segments.map((segment) => `[${Math.floor(segment.time)}s] ${segment.text}`).join("\n")
|
|
2602
2605
|
);
|
|
2603
2606
|
}
|
|
2604
|
-
function parseVTTCues(vttContent) {
|
|
2607
|
+
function parseVTTCues(vttContent, options = {}) {
|
|
2605
2608
|
if (!vttContent.trim())
|
|
2606
2609
|
return [];
|
|
2610
|
+
const { capCueText = true } = options;
|
|
2607
2611
|
const lines = vttContent.split("\n");
|
|
2608
2612
|
const cues = [];
|
|
2609
2613
|
let previousCueIdentifier = null;
|
|
@@ -2635,15 +2639,16 @@ function parseVTTCues(vttContent) {
|
|
|
2635
2639
|
}
|
|
2636
2640
|
const textLines = rawTextLines.map((textLine) => textLine.replace(/<[^>]*>/g, "")).filter(Boolean);
|
|
2637
2641
|
if (textLines.length > 0) {
|
|
2642
|
+
const sanitized = sanitizeUntrustedText(textLines.join(" "));
|
|
2638
2643
|
cues.push({
|
|
2639
2644
|
startTime,
|
|
2640
2645
|
endTime,
|
|
2641
2646
|
// Sanitize at the cue boundary so every downstream consumer
|
|
2642
2647
|
// (engagement-insights, translate-captions chunked path, etc.)
|
|
2643
2648
|
// receives text free of invisible / bidi obfuscation. Length-
|
|
2644
|
-
// cap the result so an adversarially oversized cue
|
|
2645
|
-
// smuggle a large injection payload into
|
|
2646
|
-
text: capCueTextLength(
|
|
2649
|
+
// cap the result by default so an adversarially oversized cue
|
|
2650
|
+
// cannot smuggle a large injection payload into a prompt.
|
|
2651
|
+
text: capCueText ? capCueTextLength(sanitized) : sanitized
|
|
2647
2652
|
});
|
|
2648
2653
|
}
|
|
2649
2654
|
previousCueIdentifier = currentCueIdentifier;
|
|
@@ -2767,16 +2772,32 @@ async function buildTranscriptUrl(playbackId, trackId, shouldSign = false, crede
|
|
|
2767
2772
|
}
|
|
2768
2773
|
return baseUrl;
|
|
2769
2774
|
}
|
|
2770
|
-
async function
|
|
2775
|
+
async function fetchTranscriptDataForUrl(transcriptUrl, options = {}) {
|
|
2771
2776
|
"use step";
|
|
2772
|
-
const {
|
|
2773
|
-
|
|
2774
|
-
|
|
2775
|
-
|
|
2776
|
-
|
|
2777
|
-
|
|
2778
|
-
|
|
2779
|
-
|
|
2777
|
+
const { cleanTranscript = true, scope } = options;
|
|
2778
|
+
try {
|
|
2779
|
+
const response = await fetch(transcriptUrl);
|
|
2780
|
+
if (!response.ok) {
|
|
2781
|
+
throw new Error(`Failed to fetch transcript (HTTP ${response.status})`);
|
|
2782
|
+
}
|
|
2783
|
+
const rawVtt = await response.text();
|
|
2784
|
+
const effectiveScope = hasWorkflowScopeBoundaries(scope) ? scope : void 0;
|
|
2785
|
+
const scopedVtt = filterVttByScope(rawVtt, effectiveScope);
|
|
2786
|
+
const transcriptText = cleanTranscript ? extractTextFromVTT(scopedVtt) : scopedVtt;
|
|
2787
|
+
const hasScopedCues = !effectiveScope || parseVTTCues(scopedVtt).length > 0;
|
|
2788
|
+
if (!transcriptText.trim() || !hasScopedCues) {
|
|
2789
|
+
return {
|
|
2790
|
+
transcriptText,
|
|
2791
|
+
emptyReason: hasScopedCues ? "empty_body" : "no_scoped_cues"
|
|
2792
|
+
};
|
|
2793
|
+
}
|
|
2794
|
+
return { transcriptText };
|
|
2795
|
+
} catch (error) {
|
|
2796
|
+
wrapError(error, "Failed to fetch transcript");
|
|
2797
|
+
}
|
|
2798
|
+
}
|
|
2799
|
+
async function fetchTranscriptForAsset(asset, playbackId, options = {}) {
|
|
2800
|
+
const { required = false, languageCode, cleanTranscript, scope, shouldSign, credentials } = options;
|
|
2780
2801
|
const track = findCaptionTrack(asset, languageCode);
|
|
2781
2802
|
if (!track) {
|
|
2782
2803
|
if (required) {
|
|
@@ -2795,33 +2816,25 @@ async function fetchTranscriptForAsset(asset, playbackId, options = {}) {
|
|
|
2795
2816
|
return { transcriptText: "", track };
|
|
2796
2817
|
}
|
|
2797
2818
|
const transcriptUrl = await buildTranscriptUrl(playbackId, track.id, shouldSign, credentials);
|
|
2819
|
+
let result;
|
|
2798
2820
|
try {
|
|
2799
|
-
|
|
2800
|
-
if (!response.ok) {
|
|
2801
|
-
if (required) {
|
|
2802
|
-
throw new Error(`Failed to fetch transcript (HTTP ${response.status})`);
|
|
2803
|
-
}
|
|
2804
|
-
return { transcriptText: "", transcriptUrl, track };
|
|
2805
|
-
}
|
|
2806
|
-
const rawVtt = await response.text();
|
|
2807
|
-
const effectiveScope = hasWorkflowScopeBoundaries(scope) ? scope : void 0;
|
|
2808
|
-
const scopedVtt = filterVttByScope(rawVtt, effectiveScope);
|
|
2809
|
-
const transcriptText = cleanTranscript ? extractTextFromVTT(scopedVtt) : scopedVtt;
|
|
2810
|
-
const hasScopedCues = !effectiveScope || parseVTTCues(scopedVtt).length > 0;
|
|
2811
|
-
if (required && (!transcriptText.trim() || !hasScopedCues)) {
|
|
2812
|
-
throw new MuxAiError(
|
|
2813
|
-
effectiveScope ? "Transcript has no cues in the requested scope." : "Transcript is empty.",
|
|
2814
|
-
{ type: "validation_error" }
|
|
2815
|
-
);
|
|
2816
|
-
}
|
|
2817
|
-
return { transcriptText, transcriptUrl, track };
|
|
2821
|
+
result = await fetchTranscriptDataForUrl(transcriptUrl, { cleanTranscript, scope });
|
|
2818
2822
|
} catch (error) {
|
|
2819
2823
|
if (required) {
|
|
2820
|
-
|
|
2824
|
+
throw error;
|
|
2821
2825
|
}
|
|
2822
2826
|
console.warn("Failed to fetch transcript:", error);
|
|
2823
2827
|
return { transcriptText: "", transcriptUrl, track };
|
|
2824
2828
|
}
|
|
2829
|
+
if (!required || !result.emptyReason) {
|
|
2830
|
+
return { transcriptText: result.transcriptText, transcriptUrl, track };
|
|
2831
|
+
}
|
|
2832
|
+
switch (result.emptyReason) {
|
|
2833
|
+
case "no_scoped_cues":
|
|
2834
|
+
throw new MuxAiError("Transcript has no cues in the requested scope.", { type: "validation_error" });
|
|
2835
|
+
case "empty_body":
|
|
2836
|
+
throw new MuxAiError("Transcript is empty.", { type: "validation_error" });
|
|
2837
|
+
}
|
|
2825
2838
|
}
|
|
2826
2839
|
|
|
2827
2840
|
// src/workflows/ask-questions.ts
|
|
@@ -6839,10 +6852,27 @@ ${renderSection({ tag: "revision_request", content: "The previous draft was empt
|
|
|
6839
6852
|
usages.push(...repaired.attempts.flatMap((item) => item.call.usage ? [item.call.usage] : []));
|
|
6840
6853
|
if (repaired.status !== "valid") {
|
|
6841
6854
|
const cause = repaired.attempts.at(-1)?.call.errorCause;
|
|
6842
|
-
|
|
6843
|
-
|
|
6844
|
-
|
|
6845
|
-
})
|
|
6855
|
+
let failure;
|
|
6856
|
+
if (isIncompleteGenerationError(cause)) {
|
|
6857
|
+
failure = new IncompleteGenerationError(cause.finishReason, cause.rawFinishReason);
|
|
6858
|
+
} else if (MuxAiError.is(cause)) {
|
|
6859
|
+
failure = new MuxAiError(cause.publicMessage, { type: cause.publicType, retryable: cause.retryable });
|
|
6860
|
+
} else {
|
|
6861
|
+
const apiError = APICallError3.isInstance(cause) ? cause : void 0;
|
|
6862
|
+
console.warn("[@mux/ai] Bounded text repair failed.", {
|
|
6863
|
+
provider: args.provider,
|
|
6864
|
+
modelId: args.modelId,
|
|
6865
|
+
reason: repaired.reason,
|
|
6866
|
+
errorName: cause instanceof Error ? cause.name : void 0,
|
|
6867
|
+
statusCode: apiError?.statusCode,
|
|
6868
|
+
requestId: apiError?.responseHeaders?.["x-request-id"] ?? apiError?.responseHeaders?.["request-id"]
|
|
6869
|
+
});
|
|
6870
|
+
failure = new MuxAiError("Unable to complete text generation.", {
|
|
6871
|
+
type: "processing_error",
|
|
6872
|
+
retryable: apiError?.isRetryable ?? true,
|
|
6873
|
+
cause
|
|
6874
|
+
});
|
|
6875
|
+
}
|
|
6846
6876
|
rethrowWithTokenUsage(failure, usages);
|
|
6847
6877
|
}
|
|
6848
6878
|
return { content: repaired.content, usages, unexpectedKeys };
|
|
@@ -7020,6 +7050,9 @@ async function generateTextInternal(assetId, rawOptions, collectedUsage) {
|
|
|
7020
7050
|
};
|
|
7021
7051
|
}
|
|
7022
7052
|
|
|
7053
|
+
// src/workflows/moderation.ts
|
|
7054
|
+
import { z as z9 } from "zod";
|
|
7055
|
+
|
|
7023
7056
|
// src/lib/sampling-plan.ts
|
|
7024
7057
|
var DEFAULT_FPS = 30;
|
|
7025
7058
|
function roundToNearestFrameMs(tsMs, fps = DEFAULT_FPS) {
|
|
@@ -7133,6 +7166,15 @@ var OPENAI_MODERATION_BASE_DELAY_MS = 750;
|
|
|
7133
7166
|
var OPENAI_MODERATION_MAX_DELAY_MS = 3e3;
|
|
7134
7167
|
var MIN_SAMPLE_COVERAGE_FOR_CONFIDENT_THRESHOLDING = 0.5;
|
|
7135
7168
|
var MIN_SUCCESSFUL_THUMBNAILS_FOR_CONFIDENT_THRESHOLDING = 3;
|
|
7169
|
+
var DEFAULT_TRANSCRIPT_WINDOWING = {
|
|
7170
|
+
targetWindowCount: 40,
|
|
7171
|
+
minWindowSeconds: 20,
|
|
7172
|
+
maxWindowSeconds: 120,
|
|
7173
|
+
overlapFraction: 0.15,
|
|
7174
|
+
minOverlapSeconds: 5
|
|
7175
|
+
};
|
|
7176
|
+
var TRANSCRIPT_BATCH_MAX_UTF16_CODE_UNITS = 1e5;
|
|
7177
|
+
var TRANSCRIPT_BATCH_MAX_ITEMS = 100;
|
|
7136
7178
|
var GOOGLE_VISION_ENDPOINT = "https://vision.googleapis.com/v1/images:annotate";
|
|
7137
7179
|
var GOOGLE_VISION_LIKELIHOOD_TO_SCORE = {
|
|
7138
7180
|
UNKNOWN: 0,
|
|
@@ -7200,6 +7242,14 @@ function parseOpenAIModerationResponse(bodyText, status, statusText) {
|
|
|
7200
7242
|
);
|
|
7201
7243
|
}
|
|
7202
7244
|
}
|
|
7245
|
+
var OpenAIModerationResponseSchema = z9.object({
|
|
7246
|
+
results: z9.array(z9.object({
|
|
7247
|
+
category_scores: z9.object({
|
|
7248
|
+
sexual: z9.number().finite().nullish(),
|
|
7249
|
+
violence: z9.number().finite().nullish()
|
|
7250
|
+
})
|
|
7251
|
+
}))
|
|
7252
|
+
});
|
|
7203
7253
|
async function callOpenAIModerationApi({
|
|
7204
7254
|
model,
|
|
7205
7255
|
input,
|
|
@@ -7239,7 +7289,21 @@ async function callOpenAIModerationApi({
|
|
|
7239
7289
|
}
|
|
7240
7290
|
);
|
|
7241
7291
|
}
|
|
7242
|
-
|
|
7292
|
+
const parsed = OpenAIModerationResponseSchema.safeParse(json);
|
|
7293
|
+
if (!parsed.success) {
|
|
7294
|
+
throw new OpenAIModerationRequestError(
|
|
7295
|
+
`OpenAI moderation returned an unexpected response shape: ${compactBodySnippet(bodyText)}`,
|
|
7296
|
+
{ status: res.status, retriable: false }
|
|
7297
|
+
);
|
|
7298
|
+
}
|
|
7299
|
+
const expectedResultCount = Array.isArray(input) ? input.length : 1;
|
|
7300
|
+
if (parsed.data.results.length !== expectedResultCount) {
|
|
7301
|
+
throw new OpenAIModerationRequestError(
|
|
7302
|
+
`OpenAI moderation returned ${parsed.data.results.length} result(s) for ${expectedResultCount} input(s).`,
|
|
7303
|
+
{ status: res.status, retriable: false }
|
|
7304
|
+
);
|
|
7305
|
+
}
|
|
7306
|
+
return parsed.data;
|
|
7243
7307
|
},
|
|
7244
7308
|
{
|
|
7245
7309
|
maxRetries: OPENAI_MODERATION_MAX_RETRIES,
|
|
@@ -7263,7 +7327,7 @@ async function processConcurrently(items, processor, maxConcurrent = 5) {
|
|
|
7263
7327
|
async function moderateImageWithOpenAI(entry) {
|
|
7264
7328
|
"use step";
|
|
7265
7329
|
try {
|
|
7266
|
-
const
|
|
7330
|
+
const { results } = await callOpenAIModerationApi({
|
|
7267
7331
|
model: entry.model,
|
|
7268
7332
|
input: [
|
|
7269
7333
|
{
|
|
@@ -7275,12 +7339,12 @@ async function moderateImageWithOpenAI(entry) {
|
|
|
7275
7339
|
],
|
|
7276
7340
|
credentials: entry.credentials
|
|
7277
7341
|
});
|
|
7278
|
-
const
|
|
7342
|
+
const [result] = results;
|
|
7279
7343
|
return {
|
|
7280
7344
|
url: entry.url,
|
|
7281
7345
|
time: entry.time,
|
|
7282
|
-
sexual:
|
|
7283
|
-
violence:
|
|
7346
|
+
sexual: result.category_scores.sexual ?? 0,
|
|
7347
|
+
violence: result.category_scores.violence ?? 0,
|
|
7284
7348
|
error: false
|
|
7285
7349
|
};
|
|
7286
7350
|
} catch (error) {
|
|
@@ -7304,65 +7368,187 @@ async function requestOpenAIModeration(images, model, maxConcurrent = 5, submiss
|
|
|
7304
7368
|
) : images.map((img) => ({ url: img.url, time: img.time, image: img.url, model, credentials }));
|
|
7305
7369
|
return processConcurrently(targetUrls, moderateImageWithOpenAI, maxConcurrent);
|
|
7306
7370
|
}
|
|
7307
|
-
|
|
7308
|
-
|
|
7309
|
-
|
|
7310
|
-
|
|
7311
|
-
|
|
7312
|
-
|
|
7313
|
-
|
|
7371
|
+
var TRANSCRIPT_WINDOW_MAX_UTF16_CODE_UNITS = 1e4;
|
|
7372
|
+
function describeSurfaceFailure(itemLabel, scores) {
|
|
7373
|
+
const messages = Array.from(new Set(scores.map((s) => s.errorMessage || "Unknown error")));
|
|
7374
|
+
return `All ${scores.length} ${itemLabel}(s) failed to moderate: ${messages.join("; ")}`;
|
|
7375
|
+
}
|
|
7376
|
+
function clamp(value, min, max) {
|
|
7377
|
+
return Math.min(Math.max(value, min), max);
|
|
7378
|
+
}
|
|
7379
|
+
function splitCuesUnderCharCeiling(cues, maxUnits = TRANSCRIPT_WINDOW_MAX_UTF16_CODE_UNITS) {
|
|
7380
|
+
const windows = [];
|
|
7381
|
+
let current = [];
|
|
7382
|
+
let currentLength = 0;
|
|
7383
|
+
const flush = () => {
|
|
7384
|
+
if (current.length === 0) {
|
|
7385
|
+
return;
|
|
7386
|
+
}
|
|
7387
|
+
windows.push({
|
|
7388
|
+
startTime: Math.min(...current.map((cue) => cue.startTime)),
|
|
7389
|
+
endTime: Math.max(...current.map((cue) => cue.endTime)),
|
|
7390
|
+
text: current.map((cue) => cue.text).join(" ")
|
|
7314
7391
|
});
|
|
7315
|
-
|
|
7316
|
-
|
|
7317
|
-
|
|
7318
|
-
|
|
7319
|
-
|
|
7320
|
-
|
|
7321
|
-
|
|
7322
|
-
|
|
7323
|
-
|
|
7324
|
-
|
|
7325
|
-
|
|
7326
|
-
sexual: 0,
|
|
7327
|
-
violence: 0,
|
|
7328
|
-
error: true,
|
|
7329
|
-
errorMessage: error instanceof Error ? error.message : String(error)
|
|
7330
|
-
};
|
|
7392
|
+
current = [];
|
|
7393
|
+
currentLength = 0;
|
|
7394
|
+
};
|
|
7395
|
+
for (const cue of cues) {
|
|
7396
|
+
const cueText = cue.text;
|
|
7397
|
+
const addedLength = currentLength === 0 ? cueText.length : currentLength + 1 + cueText.length;
|
|
7398
|
+
if (current.length > 0 && addedLength > maxUnits) {
|
|
7399
|
+
flush();
|
|
7400
|
+
}
|
|
7401
|
+
current.push(cue);
|
|
7402
|
+
currentLength = current.length === 1 ? cueText.length : currentLength + 1 + cueText.length;
|
|
7331
7403
|
}
|
|
7404
|
+
flush();
|
|
7405
|
+
return windows;
|
|
7332
7406
|
}
|
|
7333
|
-
function
|
|
7334
|
-
|
|
7407
|
+
function buildTranscriptWindows(cues, duration) {
|
|
7408
|
+
const usableCues = cues.filter((cue) => cue.text.trim().length > 0).slice().sort((a, b) => a.startTime - b.startTime);
|
|
7409
|
+
if (usableCues.length === 0) {
|
|
7335
7410
|
return [];
|
|
7336
7411
|
}
|
|
7337
|
-
|
|
7338
|
-
|
|
7412
|
+
const lastCueEnd = Math.max(...usableCues.map((cue) => cue.endTime));
|
|
7413
|
+
const effectiveDuration = duration && duration > 0 ? duration : lastCueEnd;
|
|
7414
|
+
const { targetWindowCount, minWindowSeconds, maxWindowSeconds, overlapFraction, minOverlapSeconds } = DEFAULT_TRANSCRIPT_WINDOWING;
|
|
7415
|
+
const windowSeconds = clamp(
|
|
7416
|
+
effectiveDuration / targetWindowCount,
|
|
7417
|
+
minWindowSeconds,
|
|
7418
|
+
maxWindowSeconds
|
|
7419
|
+
);
|
|
7420
|
+
const overlapSeconds = Math.max(
|
|
7421
|
+
minOverlapSeconds,
|
|
7422
|
+
windowSeconds * overlapFraction
|
|
7423
|
+
);
|
|
7424
|
+
const stride = Math.max(windowSeconds - overlapSeconds, 1);
|
|
7425
|
+
const rawWindows = [];
|
|
7426
|
+
for (let k = 0; ; k++) {
|
|
7427
|
+
const windowStart = k * stride;
|
|
7428
|
+
if (windowStart > lastCueEnd) {
|
|
7429
|
+
break;
|
|
7430
|
+
}
|
|
7431
|
+
const windowEnd = windowStart + windowSeconds;
|
|
7432
|
+
const windowCues = usableCues.filter(
|
|
7433
|
+
(cue) => cue.startTime < windowEnd && cue.endTime > windowStart
|
|
7434
|
+
);
|
|
7435
|
+
if (windowCues.length > 0) {
|
|
7436
|
+
rawWindows.push(windowCues);
|
|
7437
|
+
}
|
|
7438
|
+
if (stride <= 0) {
|
|
7439
|
+
break;
|
|
7440
|
+
}
|
|
7339
7441
|
}
|
|
7340
|
-
const
|
|
7341
|
-
|
|
7342
|
-
|
|
7343
|
-
|
|
7344
|
-
|
|
7442
|
+
const dedupedCueGroups = [];
|
|
7443
|
+
const cueGroupKey = (group) => group.map((cue) => `${cue.startTime}:${cue.endTime}`).join("|");
|
|
7444
|
+
let previousKey;
|
|
7445
|
+
for (const group of rawWindows) {
|
|
7446
|
+
const key = cueGroupKey(group);
|
|
7447
|
+
if (key === previousKey) {
|
|
7448
|
+
continue;
|
|
7345
7449
|
}
|
|
7450
|
+
dedupedCueGroups.push(group);
|
|
7451
|
+
previousKey = key;
|
|
7346
7452
|
}
|
|
7347
|
-
|
|
7453
|
+
const windows = [];
|
|
7454
|
+
for (const group of dedupedCueGroups) {
|
|
7455
|
+
const joinedLength = group.reduce(
|
|
7456
|
+
(sum, cue, index) => sum + cue.text.length + (index === 0 ? 0 : 1),
|
|
7457
|
+
0
|
|
7458
|
+
);
|
|
7459
|
+
if (joinedLength > TRANSCRIPT_WINDOW_MAX_UTF16_CODE_UNITS) {
|
|
7460
|
+
windows.push(...splitCuesUnderCharCeiling(group));
|
|
7461
|
+
} else {
|
|
7462
|
+
windows.push({
|
|
7463
|
+
startTime: Math.min(...group.map((cue) => cue.startTime)),
|
|
7464
|
+
endTime: Math.max(...group.map((cue) => cue.endTime)),
|
|
7465
|
+
text: group.map((cue) => cue.text).join(" ")
|
|
7466
|
+
});
|
|
7467
|
+
}
|
|
7468
|
+
}
|
|
7469
|
+
return windows;
|
|
7470
|
+
}
|
|
7471
|
+
function packWindowsIntoBatches(windows) {
|
|
7472
|
+
const batches = [];
|
|
7473
|
+
let current = [];
|
|
7474
|
+
let currentLength = 0;
|
|
7475
|
+
for (const window of windows) {
|
|
7476
|
+
const wouldExceedChars = current.length > 0 && currentLength + window.text.length > TRANSCRIPT_BATCH_MAX_UTF16_CODE_UNITS;
|
|
7477
|
+
const wouldExceedItems = current.length >= TRANSCRIPT_BATCH_MAX_ITEMS;
|
|
7478
|
+
if (wouldExceedChars || wouldExceedItems) {
|
|
7479
|
+
batches.push(current);
|
|
7480
|
+
current = [];
|
|
7481
|
+
currentLength = 0;
|
|
7482
|
+
}
|
|
7483
|
+
current.push(window);
|
|
7484
|
+
currentLength += window.text.length;
|
|
7485
|
+
}
|
|
7486
|
+
if (current.length > 0) {
|
|
7487
|
+
batches.push(current);
|
|
7488
|
+
}
|
|
7489
|
+
return batches;
|
|
7490
|
+
}
|
|
7491
|
+
function transcriptErrorScore(window, error) {
|
|
7492
|
+
return {
|
|
7493
|
+
startTime: window.startTime,
|
|
7494
|
+
endTime: window.endTime,
|
|
7495
|
+
sexual: 0,
|
|
7496
|
+
violence: 0,
|
|
7497
|
+
error: true,
|
|
7498
|
+
errorMessage: error instanceof Error ? error.message : String(error)
|
|
7499
|
+
};
|
|
7348
7500
|
}
|
|
7349
|
-
async function
|
|
7501
|
+
async function moderateTranscriptBatchWithOpenAI(batch) {
|
|
7350
7502
|
"use step";
|
|
7351
|
-
const
|
|
7352
|
-
if (
|
|
7353
|
-
return [
|
|
7354
|
-
|
|
7355
|
-
|
|
7356
|
-
|
|
7357
|
-
|
|
7358
|
-
|
|
7359
|
-
|
|
7360
|
-
|
|
7361
|
-
|
|
7362
|
-
|
|
7363
|
-
|
|
7503
|
+
const { windows, model, credentials } = batch;
|
|
7504
|
+
if (windows.length === 0) {
|
|
7505
|
+
return [];
|
|
7506
|
+
}
|
|
7507
|
+
try {
|
|
7508
|
+
const { results } = await callOpenAIModerationApi({
|
|
7509
|
+
model,
|
|
7510
|
+
input: windows.map((window) => window.text),
|
|
7511
|
+
credentials
|
|
7512
|
+
});
|
|
7513
|
+
return windows.map((window, index) => {
|
|
7514
|
+
const result = results[index];
|
|
7515
|
+
return {
|
|
7516
|
+
startTime: window.startTime,
|
|
7517
|
+
endTime: window.endTime,
|
|
7518
|
+
sexual: result.category_scores.sexual ?? 0,
|
|
7519
|
+
violence: result.category_scores.violence ?? 0,
|
|
7520
|
+
error: false
|
|
7521
|
+
};
|
|
7522
|
+
});
|
|
7523
|
+
} catch (error) {
|
|
7524
|
+
const status = error instanceof OpenAIModerationRequestError ? error.status : void 0;
|
|
7525
|
+
if (status === 400 && windows.length > 1) {
|
|
7526
|
+
const mid = Math.ceil(windows.length / 2);
|
|
7527
|
+
const [left, right] = await Promise.all([
|
|
7528
|
+
moderateTranscriptBatchWithOpenAI({ windows: windows.slice(0, mid), model, credentials }),
|
|
7529
|
+
moderateTranscriptBatchWithOpenAI({ windows: windows.slice(mid), model, credentials })
|
|
7530
|
+
]);
|
|
7531
|
+
return [...left, ...right];
|
|
7532
|
+
}
|
|
7533
|
+
console.error("OpenAI transcript moderation failed:", error);
|
|
7534
|
+
return windows.map((window) => transcriptErrorScore(window, error));
|
|
7535
|
+
}
|
|
7536
|
+
}
|
|
7537
|
+
async function requestOpenAITranscriptModeration(cues, duration, model, maxConcurrent = 5, credentials) {
|
|
7538
|
+
"use step";
|
|
7539
|
+
const windows = buildTranscriptWindows(cues, duration);
|
|
7540
|
+
if (!windows.length) {
|
|
7541
|
+
return [];
|
|
7542
|
+
}
|
|
7543
|
+
const batches = packWindowsIntoBatches(windows).map(
|
|
7544
|
+
(windowBatch) => ({ windows: windowBatch, model, credentials })
|
|
7545
|
+
);
|
|
7546
|
+
const batchResults = await processConcurrently(
|
|
7547
|
+
batches,
|
|
7548
|
+
moderateTranscriptBatchWithOpenAI,
|
|
7364
7549
|
maxConcurrent
|
|
7365
7550
|
);
|
|
7551
|
+
return batchResults.flat();
|
|
7366
7552
|
}
|
|
7367
7553
|
function getHiveCategoryScores(classes, categoryNames) {
|
|
7368
7554
|
const scoreMap = Object.fromEntries(
|
|
@@ -7585,12 +7771,35 @@ async function getThumbnailUrlsFromTimestamps(playbackId, timestampsMs, options)
|
|
|
7585
7771
|
});
|
|
7586
7772
|
return Promise.all(urlPromises);
|
|
7587
7773
|
}
|
|
7774
|
+
async function requestThumbnailModeration(provider, thumbnailUrls, options) {
|
|
7775
|
+
const { model, maxConcurrent, imageSubmissionMode, imageDownloadOptions, credentials } = options;
|
|
7776
|
+
switch (provider) {
|
|
7777
|
+
case "openai":
|
|
7778
|
+
return requestOpenAIModeration(
|
|
7779
|
+
thumbnailUrls,
|
|
7780
|
+
model || "omni-moderation-latest",
|
|
7781
|
+
maxConcurrent,
|
|
7782
|
+
imageSubmissionMode,
|
|
7783
|
+
imageDownloadOptions,
|
|
7784
|
+
credentials
|
|
7785
|
+
);
|
|
7786
|
+
case "hive":
|
|
7787
|
+
return requestHiveModeration(thumbnailUrls, maxConcurrent, imageSubmissionMode, imageDownloadOptions, credentials);
|
|
7788
|
+
case "google-vision-api":
|
|
7789
|
+
return requestGoogleVisionModeration(thumbnailUrls, maxConcurrent, imageSubmissionMode, imageDownloadOptions, credentials);
|
|
7790
|
+
default: {
|
|
7791
|
+
const exhaustiveCheck = provider;
|
|
7792
|
+
throw new Error(`Unsupported moderation provider: ${exhaustiveCheck}`);
|
|
7793
|
+
}
|
|
7794
|
+
}
|
|
7795
|
+
}
|
|
7588
7796
|
async function getModerationScores(assetId, options = {}) {
|
|
7589
7797
|
"use workflow";
|
|
7590
7798
|
const {
|
|
7591
7799
|
provider = DEFAULT_PROVIDER2,
|
|
7592
7800
|
model = provider === "openai" ? "omni-moderation-latest" : void 0,
|
|
7593
7801
|
languageCode,
|
|
7802
|
+
includeTranscript = true,
|
|
7594
7803
|
thresholds = DEFAULT_THRESHOLDS,
|
|
7595
7804
|
thumbnailInterval = 10,
|
|
7596
7805
|
thumbnailWidth = 640,
|
|
@@ -7617,47 +7826,31 @@ async function getModerationScores(assetId, options = {}) {
|
|
|
7617
7826
|
startTime: Math.min(resolvedScope.startTime, duration),
|
|
7618
7827
|
endTime: Math.min(resolvedScope.endTime, duration)
|
|
7619
7828
|
} : void 0;
|
|
7620
|
-
if (!isAudioOnly && renderableScope && renderableScope.startTime >= renderableScope.endTime) {
|
|
7621
|
-
throw new Error("The requested scope does not include any renderable video.");
|
|
7622
|
-
}
|
|
7623
7829
|
const signingContext = await resolveMuxSigningContext(credentials);
|
|
7624
7830
|
if (policy === "signed" && !signingContext) {
|
|
7625
7831
|
throw new Error(
|
|
7626
7832
|
"Signed playback ID requires signing credentials. Set MUX_SIGNING_KEY and MUX_PRIVATE_KEY environment variables."
|
|
7627
7833
|
);
|
|
7628
7834
|
}
|
|
7629
|
-
let
|
|
7630
|
-
let
|
|
7631
|
-
let
|
|
7835
|
+
let thumbnailModeration = { status: "completed" };
|
|
7836
|
+
let thumbnailUrls = [];
|
|
7837
|
+
let shouldRunThumbnails = false;
|
|
7632
7838
|
if (isAudioOnly) {
|
|
7633
|
-
|
|
7634
|
-
|
|
7635
|
-
|
|
7636
|
-
|
|
7637
|
-
|
|
7638
|
-
|
|
7639
|
-
|
|
7640
|
-
|
|
7641
|
-
|
|
7642
|
-
|
|
7643
|
-
|
|
7644
|
-
transcriptResult.transcriptText,
|
|
7645
|
-
model || "omni-moderation-latest",
|
|
7646
|
-
maxConcurrent,
|
|
7647
|
-
credentials
|
|
7648
|
-
);
|
|
7649
|
-
} else if (provider === "hive") {
|
|
7650
|
-
throw new Error("Hive does not support transcript moderation in this workflow. Use provider: 'openai' for audio-only assets.");
|
|
7651
|
-
} else if (provider === "google-vision-api") {
|
|
7652
|
-
throw new Error("google-vision-api is image-only and does not support transcript moderation. Use provider: 'openai' for audio-only assets.");
|
|
7653
|
-
} else {
|
|
7654
|
-
const exhaustiveCheck = provider;
|
|
7655
|
-
throw new Error(`Unsupported moderation provider: ${exhaustiveCheck}`);
|
|
7656
|
-
}
|
|
7839
|
+
thumbnailModeration = {
|
|
7840
|
+
status: "skipped",
|
|
7841
|
+
skipReason: "no_video_track",
|
|
7842
|
+
skipMessage: "Asset has no video track, so there are no thumbnails to moderate."
|
|
7843
|
+
};
|
|
7844
|
+
} else if (renderableScope && renderableScope.startTime >= renderableScope.endTime) {
|
|
7845
|
+
thumbnailModeration = {
|
|
7846
|
+
status: "skipped",
|
|
7847
|
+
skipReason: "no_video_in_scope",
|
|
7848
|
+
skipMessage: "The requested scope does not include any renderable video, so there are no thumbnails to moderate."
|
|
7849
|
+
};
|
|
7657
7850
|
} else {
|
|
7658
7851
|
const scopedDuration = renderableScope ? renderableScope.endTime - renderableScope.startTime : duration;
|
|
7659
7852
|
const estimatedIntervalCount = scopedDuration <= 50 ? 5 : Math.ceil(scopedDuration / thumbnailInterval);
|
|
7660
|
-
|
|
7853
|
+
thumbnailUrls = maxSamples !== void 0 && estimatedIntervalCount > maxSamples ? await getThumbnailUrlsFromTimestamps(
|
|
7661
7854
|
playbackId,
|
|
7662
7855
|
filterTimestampsWithinExclusiveScope(
|
|
7663
7856
|
planSamplingTimestamps({
|
|
@@ -7682,70 +7875,148 @@ async function getModerationScores(assetId, options = {}) {
|
|
|
7682
7875
|
credentials,
|
|
7683
7876
|
scope: renderableScope
|
|
7684
7877
|
});
|
|
7685
|
-
|
|
7686
|
-
|
|
7687
|
-
|
|
7688
|
-
|
|
7689
|
-
|
|
7690
|
-
|
|
7691
|
-
|
|
7692
|
-
|
|
7693
|
-
|
|
7694
|
-
|
|
7695
|
-
|
|
7696
|
-
|
|
7697
|
-
|
|
7698
|
-
|
|
7699
|
-
|
|
7700
|
-
|
|
7701
|
-
|
|
7702
|
-
|
|
7703
|
-
}
|
|
7704
|
-
|
|
7705
|
-
|
|
7706
|
-
|
|
7707
|
-
|
|
7708
|
-
|
|
7709
|
-
|
|
7710
|
-
|
|
7878
|
+
shouldRunThumbnails = true;
|
|
7879
|
+
}
|
|
7880
|
+
let transcriptModeration = { status: "completed" };
|
|
7881
|
+
let transcriptCues = [];
|
|
7882
|
+
let shouldRunTranscript = false;
|
|
7883
|
+
const transcriptEnabled = includeTranscript || isAudioOnly;
|
|
7884
|
+
const captionTrack = transcriptEnabled && provider === "openai" ? findCaptionTrack(asset, languageCode) : void 0;
|
|
7885
|
+
if (!transcriptEnabled) {
|
|
7886
|
+
transcriptModeration = {
|
|
7887
|
+
status: "skipped",
|
|
7888
|
+
skipReason: "disabled",
|
|
7889
|
+
skipMessage: "Transcript moderation was disabled with includeTranscript: false."
|
|
7890
|
+
};
|
|
7891
|
+
} else if (provider !== "openai") {
|
|
7892
|
+
transcriptModeration = {
|
|
7893
|
+
status: "skipped",
|
|
7894
|
+
skipReason: "unsupported_provider",
|
|
7895
|
+
skipMessage: `Provider '${provider}' is image-only and cannot moderate transcript text; use provider 'openai'.`
|
|
7896
|
+
};
|
|
7897
|
+
} else if (!captionTrack) {
|
|
7898
|
+
transcriptModeration = {
|
|
7899
|
+
status: "skipped",
|
|
7900
|
+
skipReason: "no_ready_text_track",
|
|
7901
|
+
skipMessage: languageCode ? `No ready caption/subtitle track found for language '${languageCode}'.` : "No ready caption/subtitle track found for this asset."
|
|
7902
|
+
};
|
|
7903
|
+
} else {
|
|
7904
|
+
if (!captionTrack.id) {
|
|
7905
|
+
throw new MuxAiError("Transcript track is missing an id.", { type: "validation_error" });
|
|
7906
|
+
}
|
|
7907
|
+
const transcriptUrl = await buildTranscriptUrl(playbackId, captionTrack.id, policy === "signed", credentials);
|
|
7908
|
+
const { transcriptText, emptyReason } = await fetchTranscriptDataForUrl(transcriptUrl, {
|
|
7909
|
+
cleanTranscript: false,
|
|
7910
|
+
scope: effectiveScope
|
|
7911
|
+
});
|
|
7912
|
+
if (emptyReason) {
|
|
7913
|
+
transcriptModeration = {
|
|
7914
|
+
status: "skipped",
|
|
7915
|
+
skipReason: "no_cues",
|
|
7916
|
+
skipMessage: emptyReason === "no_scoped_cues" ? "Transcript has no cues in the requested scope." : "Transcript is empty."
|
|
7917
|
+
};
|
|
7711
7918
|
} else {
|
|
7712
|
-
|
|
7713
|
-
|
|
7919
|
+
transcriptCues = parseVTTCues(transcriptText, { capCueText: false });
|
|
7920
|
+
const windows = transcriptCues.length === 0 ? [] : buildTranscriptWindows(transcriptCues, duration);
|
|
7921
|
+
if (windows.length === 0) {
|
|
7922
|
+
transcriptModeration = {
|
|
7923
|
+
status: "skipped",
|
|
7924
|
+
skipReason: "no_cues",
|
|
7925
|
+
skipMessage: "Caption track had no parseable cues."
|
|
7926
|
+
};
|
|
7927
|
+
} else {
|
|
7928
|
+
shouldRunTranscript = true;
|
|
7929
|
+
}
|
|
7714
7930
|
}
|
|
7715
7931
|
}
|
|
7716
|
-
|
|
7717
|
-
|
|
7932
|
+
if (!shouldRunThumbnails && !shouldRunTranscript) {
|
|
7933
|
+
throw new MuxAiError(
|
|
7934
|
+
`Nothing to moderate. Thumbnails skipped: ${thumbnailModeration.skipMessage} Transcript skipped: ${transcriptModeration.skipMessage}`,
|
|
7935
|
+
{ type: "validation_error" }
|
|
7936
|
+
);
|
|
7937
|
+
}
|
|
7938
|
+
const [thumbnailScores, transcriptScores] = await Promise.all([
|
|
7939
|
+
shouldRunThumbnails ? requestThumbnailModeration(provider, thumbnailUrls, {
|
|
7940
|
+
model,
|
|
7941
|
+
maxConcurrent,
|
|
7942
|
+
imageSubmissionMode,
|
|
7943
|
+
imageDownloadOptions,
|
|
7944
|
+
credentials
|
|
7945
|
+
}) : Promise.resolve([]),
|
|
7946
|
+
shouldRunTranscript ? requestOpenAITranscriptModeration(
|
|
7947
|
+
transcriptCues,
|
|
7948
|
+
duration,
|
|
7949
|
+
model || "omni-moderation-latest",
|
|
7950
|
+
maxConcurrent,
|
|
7951
|
+
credentials
|
|
7952
|
+
) : Promise.resolve([])
|
|
7953
|
+
]);
|
|
7954
|
+
const mode = shouldRunThumbnails && shouldRunTranscript ? "combined" : shouldRunTranscript ? "transcript" : "thumbnails";
|
|
7955
|
+
const thumbnailCount = shouldRunThumbnails ? thumbnailUrls.length : void 0;
|
|
7956
|
+
const allScores = [
|
|
7957
|
+
...thumbnailScores.map((s) => ({ ...s, label: s.url })),
|
|
7958
|
+
...transcriptScores.map((s) => ({ ...s, error: s.error ?? false, label: `transcript window ${s.startTime}-${s.endTime}s` }))
|
|
7959
|
+
];
|
|
7960
|
+
const failed = allScores.filter((s) => s.error);
|
|
7961
|
+
const successful = allScores.filter((s) => !s.error);
|
|
7718
7962
|
if (successful.length === 0) {
|
|
7719
|
-
const details = failed.map((s) => `${s.
|
|
7963
|
+
const details = failed.map((s) => `${s.label}: ${s.errorMessage || "Unknown error"}`).join("; ");
|
|
7720
7964
|
throw new Error(
|
|
7721
|
-
`Moderation failed for all ${
|
|
7965
|
+
`Moderation failed for all ${allScores.length} sample(s): ${details}`
|
|
7722
7966
|
);
|
|
7723
7967
|
}
|
|
7724
7968
|
if (failed.length > 0) {
|
|
7725
7969
|
console.warn(
|
|
7726
|
-
`Moderation had partial failures (${failed.length}/${
|
|
7970
|
+
`Moderation had partial failures (${failed.length}/${allScores.length}); continuing with successful samples.`
|
|
7727
7971
|
);
|
|
7728
7972
|
}
|
|
7973
|
+
const thumbnailsFailed = shouldRunThumbnails && thumbnailScores.length > 0 && thumbnailScores.every((s) => s.error);
|
|
7974
|
+
const transcriptFailed = shouldRunTranscript && transcriptScores.length > 0 && transcriptScores.every((s) => s.error);
|
|
7975
|
+
if (thumbnailsFailed) {
|
|
7976
|
+
thumbnailModeration = {
|
|
7977
|
+
status: "failed",
|
|
7978
|
+
errorMessage: describeSurfaceFailure("thumbnail", thumbnailScores)
|
|
7979
|
+
};
|
|
7980
|
+
}
|
|
7981
|
+
if (transcriptFailed) {
|
|
7982
|
+
transcriptModeration = {
|
|
7983
|
+
status: "failed",
|
|
7984
|
+
errorMessage: describeSurfaceFailure("transcript window", transcriptScores)
|
|
7985
|
+
};
|
|
7986
|
+
}
|
|
7729
7987
|
const maxSexual = Math.max(...successful.map((s) => s.sexual));
|
|
7730
7988
|
const maxViolence = Math.max(...successful.map((s) => s.violence));
|
|
7731
7989
|
const finalThresholds = { ...DEFAULT_THRESHOLDS, ...thresholds };
|
|
7732
|
-
const
|
|
7733
|
-
const
|
|
7734
|
-
const
|
|
7735
|
-
const
|
|
7736
|
-
const
|
|
7737
|
-
const
|
|
7990
|
+
const coverageScores = thumbnailScores;
|
|
7991
|
+
const coverageFailed = coverageScores.filter((s) => s.error);
|
|
7992
|
+
const coverageSuccessful = coverageScores.filter((s) => !s.error);
|
|
7993
|
+
const requestedSampleCount = coverageScores.length;
|
|
7994
|
+
const successfulSampleCount = coverageSuccessful.length;
|
|
7995
|
+
const failedSampleCount = coverageFailed.length;
|
|
7996
|
+
const sampleCoverage = requestedSampleCount > 0 ? successfulSampleCount / requestedSampleCount : 0;
|
|
7997
|
+
const hasThumbnails = requestedSampleCount > 0;
|
|
7998
|
+
const hasSuccessfulTranscript = transcriptScores.some((s) => !s.error);
|
|
7999
|
+
let isLowConfidence;
|
|
8000
|
+
if (!hasThumbnails) {
|
|
8001
|
+
isLowConfidence = !hasSuccessfulTranscript;
|
|
8002
|
+
} else {
|
|
8003
|
+
const hasEnoughSuccessfulSamples = successfulSampleCount >= MIN_SUCCESSFUL_THUMBNAILS_FOR_CONFIDENT_THRESHOLDING;
|
|
8004
|
+
isLowConfidence = sampleCoverage < MIN_SAMPLE_COVERAGE_FOR_CONFIDENT_THRESHOLDING || !hasEnoughSuccessfulSamples || transcriptFailed;
|
|
8005
|
+
}
|
|
7738
8006
|
return {
|
|
7739
8007
|
assetId,
|
|
7740
8008
|
mode,
|
|
7741
8009
|
isAudioOnly,
|
|
7742
8010
|
thumbnailScores,
|
|
8011
|
+
transcriptScores,
|
|
8012
|
+
thumbnailModeration,
|
|
8013
|
+
transcriptModeration,
|
|
7743
8014
|
coverage: {
|
|
7744
8015
|
requestedSampleCount,
|
|
7745
8016
|
successfulSampleCount,
|
|
7746
8017
|
failedSampleCount,
|
|
7747
8018
|
sampleCoverage,
|
|
7748
|
-
isPartial:
|
|
8019
|
+
isPartial: failed.length > 0,
|
|
7749
8020
|
isLowConfidence
|
|
7750
8021
|
},
|
|
7751
8022
|
usage: {
|
|
@@ -7765,7 +8036,7 @@ async function getModerationScores(assetId, options = {}) {
|
|
|
7765
8036
|
|
|
7766
8037
|
// src/workflows/summarization.ts
|
|
7767
8038
|
import { generateText as generateText8, Output as Output8 } from "ai";
|
|
7768
|
-
import { z as
|
|
8039
|
+
import { z as z10 } from "zod";
|
|
7769
8040
|
|
|
7770
8041
|
// src/lib/markdown-normalization.ts
|
|
7771
8042
|
var ESCAPED_LINE_BREAK_REGEX = /\\r\\n|\\n|\\r/g;
|
|
@@ -7910,17 +8181,17 @@ function createSummarizationGuidance({
|
|
|
7910
8181
|
}
|
|
7911
8182
|
|
|
7912
8183
|
// src/workflows/summarization.ts
|
|
7913
|
-
var summarySchema =
|
|
7914
|
-
keywords:
|
|
7915
|
-
title:
|
|
7916
|
-
description:
|
|
8184
|
+
var summarySchema = z10.object({
|
|
8185
|
+
keywords: z10.array(z10.string().max(200)),
|
|
8186
|
+
title: z10.string().max(500),
|
|
8187
|
+
description: z10.string().max(1e4)
|
|
7917
8188
|
});
|
|
7918
8189
|
function buildSummarySchema(descriptionLength) {
|
|
7919
8190
|
const descriptionMaxChars = Math.max(2e3, descriptionLength * 15);
|
|
7920
|
-
return
|
|
7921
|
-
keywords:
|
|
7922
|
-
title:
|
|
7923
|
-
description:
|
|
8191
|
+
return z10.object({
|
|
8192
|
+
keywords: z10.array(z10.string().max(200)),
|
|
8193
|
+
title: z10.string().max(500),
|
|
8194
|
+
description: z10.string().max(descriptionMaxChars)
|
|
7924
8195
|
});
|
|
7925
8196
|
}
|
|
7926
8197
|
var VALID_TONES = ["neutral", "playful", "professional"];
|
|
@@ -9091,9 +9362,9 @@ import {
|
|
|
9091
9362
|
RetryError as RetryError2,
|
|
9092
9363
|
TypeValidationError
|
|
9093
9364
|
} from "ai";
|
|
9094
|
-
import { z as
|
|
9095
|
-
var translationSchema =
|
|
9096
|
-
translation:
|
|
9365
|
+
import { z as z11 } from "zod";
|
|
9366
|
+
var translationSchema = z11.object({
|
|
9367
|
+
translation: z11.string()
|
|
9097
9368
|
});
|
|
9098
9369
|
var SYSTEM_PROMPT5 = promptDedent`
|
|
9099
9370
|
You are a subtitle translation expert. Translate VTT subtitle files to the target language specified by the user.
|
|
@@ -9472,8 +9743,8 @@ async function translateCueChunkWithAI({
|
|
|
9472
9743
|
}) {
|
|
9473
9744
|
"use step";
|
|
9474
9745
|
const model = await createLanguageModelFromConfig(provider, modelId, credentials);
|
|
9475
|
-
const schema =
|
|
9476
|
-
translations:
|
|
9746
|
+
const schema = z11.object({
|
|
9747
|
+
translations: z11.array(z11.string().min(1).max(2e3)).length(cues.length)
|
|
9477
9748
|
});
|
|
9478
9749
|
const cuePayload = cues.map((cue, index) => ({
|
|
9479
9750
|
index,
|
|
@@ -9931,6 +10202,7 @@ export {
|
|
|
9931
10202
|
DEFAULT_DESCRIPTION_LENGTH,
|
|
9932
10203
|
DEFAULT_SUMMARY_KEYWORD_LIMIT,
|
|
9933
10204
|
DEFAULT_TITLE_LENGTH,
|
|
10205
|
+
DEFAULT_TRANSCRIPT_WINDOWING,
|
|
9934
10206
|
GENERATE_TEXT_CALLS_TO_ACTION,
|
|
9935
10207
|
GENERATE_TEXT_CHANNELS,
|
|
9936
10208
|
GENERATE_TEXT_CHANNEL_CEILINGS,
|
|
@@ -9956,6 +10228,7 @@ export {
|
|
|
9956
10228
|
applyTextRepair,
|
|
9957
10229
|
askQuestions,
|
|
9958
10230
|
buildReplacementRegex,
|
|
10231
|
+
buildTranscriptWindows,
|
|
9959
10232
|
burnedInCaptionsSchema,
|
|
9960
10233
|
censorVttContent,
|
|
9961
10234
|
chapterSchema,
|