@mux/ai 0.29.0 → 0.30.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/README.md +1 -1
- package/dist/{index-CXwsu679.d.ts → index-CJtQiGj7.d.ts} +1 -1
- package/dist/{index-DJirK4fh.d.ts → index-D3cB2ltG.d.ts} +35 -3
- package/dist/index.d.ts +7 -6
- package/dist/index.js +340 -145
- package/dist/index.js.map +1 -1
- package/dist/primitives/index.d.ts +2 -2
- package/dist/primitives/index.js +26 -6
- package/dist/primitives/index.js.map +1 -1
- package/dist/{shots-BKd7TMFN.d.ts → shots-Bh0GrvLx.d.ts} +2 -0
- package/dist/workflows/index.d.ts +2 -2
- package/dist/workflows/index.js +448 -251
- package/dist/workflows/index.js.map +1 -1
- package/package.json +1 -1
package/dist/workflows/index.js
CHANGED
|
@@ -1,114 +1,11 @@
|
|
|
1
1
|
// src/workflows/ask-questions.ts
|
|
2
2
|
import { generateText, Output } from "ai";
|
|
3
3
|
import dedent2 from "dedent";
|
|
4
|
-
import { z as
|
|
4
|
+
import { z as z3 } from "zod";
|
|
5
5
|
|
|
6
|
-
// src/lib/
|
|
7
|
-
import
|
|
8
|
-
|
|
9
|
-
function bytesToBase64(bytes) {
|
|
10
|
-
if (bytes.length === 0) {
|
|
11
|
-
return "";
|
|
12
|
-
}
|
|
13
|
-
let binary = "";
|
|
14
|
-
for (let i = 0; i < bytes.length; i += BASE64_CHUNK_SIZE) {
|
|
15
|
-
const chunk = bytes.subarray(i, i + BASE64_CHUNK_SIZE);
|
|
16
|
-
binary += String.fromCharCode(...chunk);
|
|
17
|
-
}
|
|
18
|
-
if (typeof globalThis.btoa !== "function") {
|
|
19
|
-
throw new TypeError("Base64 encoder is not available in this environment.");
|
|
20
|
-
}
|
|
21
|
-
return globalThis.btoa(binary);
|
|
22
|
-
}
|
|
23
|
-
var DEFAULT_OPTIONS = {
|
|
24
|
-
timeout: 1e4,
|
|
25
|
-
retries: 3,
|
|
26
|
-
retryDelay: 1e3,
|
|
27
|
-
maxRetryDelay: 1e4,
|
|
28
|
-
exponentialBackoff: true
|
|
29
|
-
};
|
|
30
|
-
async function downloadImageAsBase64(url, options = {}) {
|
|
31
|
-
"use step";
|
|
32
|
-
const opts = { ...DEFAULT_OPTIONS, ...options };
|
|
33
|
-
let attemptCount = 0;
|
|
34
|
-
return pRetry(
|
|
35
|
-
async () => {
|
|
36
|
-
attemptCount++;
|
|
37
|
-
const controller = new AbortController();
|
|
38
|
-
const timeoutId = setTimeout(() => controller.abort(), opts.timeout);
|
|
39
|
-
try {
|
|
40
|
-
const response = await fetch(url, {
|
|
41
|
-
signal: controller.signal,
|
|
42
|
-
headers: {
|
|
43
|
-
"User-Agent": "@mux/ai image downloader"
|
|
44
|
-
}
|
|
45
|
-
});
|
|
46
|
-
clearTimeout(timeoutId);
|
|
47
|
-
if (!response.ok) {
|
|
48
|
-
if (response.status >= 400 && response.status < 500 && response.status !== 429) {
|
|
49
|
-
throw new AbortError(`HTTP ${response.status}: ${response.statusText}`);
|
|
50
|
-
}
|
|
51
|
-
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
|
|
52
|
-
}
|
|
53
|
-
const contentType = response.headers.get("content-type");
|
|
54
|
-
if (!contentType?.startsWith("image/")) {
|
|
55
|
-
throw new AbortError(`Invalid content type: ${contentType}. Expected image/*`);
|
|
56
|
-
}
|
|
57
|
-
const arrayBuffer = await response.arrayBuffer();
|
|
58
|
-
const buffer = new Uint8Array(arrayBuffer);
|
|
59
|
-
if (buffer.length === 0) {
|
|
60
|
-
throw new AbortError("Downloaded image is empty");
|
|
61
|
-
}
|
|
62
|
-
const base64Data = `data:${contentType};base64,${bytesToBase64(buffer)}`;
|
|
63
|
-
return {
|
|
64
|
-
base64Data,
|
|
65
|
-
buffer,
|
|
66
|
-
url,
|
|
67
|
-
contentType,
|
|
68
|
-
sizeBytes: buffer.length,
|
|
69
|
-
attempts: attemptCount
|
|
70
|
-
};
|
|
71
|
-
} catch (error) {
|
|
72
|
-
clearTimeout(timeoutId);
|
|
73
|
-
if (error instanceof AbortError) {
|
|
74
|
-
throw error;
|
|
75
|
-
}
|
|
76
|
-
if (error instanceof Error) {
|
|
77
|
-
if (error.name === "AbortError") {
|
|
78
|
-
throw new Error(`Request timeout after ${opts.timeout}ms`);
|
|
79
|
-
}
|
|
80
|
-
throw new Error(`Download failed: ${error.message}`);
|
|
81
|
-
}
|
|
82
|
-
throw new Error("Unknown download error");
|
|
83
|
-
}
|
|
84
|
-
},
|
|
85
|
-
{
|
|
86
|
-
retries: opts.retries,
|
|
87
|
-
minTimeout: opts.retryDelay,
|
|
88
|
-
maxTimeout: opts.maxRetryDelay,
|
|
89
|
-
factor: opts.exponentialBackoff ? 2 : 1,
|
|
90
|
-
randomize: true,
|
|
91
|
-
// Add jitter to prevent thundering herd
|
|
92
|
-
onFailedAttempt: (error) => {
|
|
93
|
-
console.warn(`Image download attempt ${error.attemptNumber} failed for ${url}`);
|
|
94
|
-
if (error.retriesLeft > 0) {
|
|
95
|
-
console.warn(`Retrying... (${error.retriesLeft} attempts left)`);
|
|
96
|
-
}
|
|
97
|
-
}
|
|
98
|
-
}
|
|
99
|
-
);
|
|
100
|
-
}
|
|
101
|
-
async function downloadImagesAsBase64(urls, options = {}, maxConcurrent = 5) {
|
|
102
|
-
"use step";
|
|
103
|
-
const results = [];
|
|
104
|
-
for (let i = 0; i < urls.length; i += maxConcurrent) {
|
|
105
|
-
const batch = urls.slice(i, i + maxConcurrent);
|
|
106
|
-
const batchPromises = batch.map((url) => downloadImageAsBase64(url, options));
|
|
107
|
-
const batchResults = await Promise.all(batchPromises);
|
|
108
|
-
results.push(...batchResults);
|
|
109
|
-
}
|
|
110
|
-
return results;
|
|
111
|
-
}
|
|
6
|
+
// src/lib/content-policy-error.ts
|
|
7
|
+
import { APICallError, NoObjectGeneratedError, RetryError } from "ai";
|
|
8
|
+
import { z as z2 } from "zod";
|
|
112
9
|
|
|
113
10
|
// src/lib/prompt-fragments.ts
|
|
114
11
|
import dedent from "dedent";
|
|
@@ -179,7 +76,7 @@ var EnvSchema = z.object({
|
|
|
179
76
|
MUX_TEST_ASSET_ID_VIOLENT_AUDIO_ONLY: optionalString("Mux test asset ID for audio-only assets with violent content.", "Mux test asset id for audio-only assets with violent content for testing"),
|
|
180
77
|
// Eval config
|
|
181
78
|
MUX_AI_EVAL_MODEL_SET: optionalString("Eval model selection mode.", "Choose between 'default' (provider defaults only) or 'all' (all configured models)"),
|
|
182
|
-
MUX_AI_EVAL_MODELS: optionalString("Comma-separated eval model pairs.", "Comma-separated provider:model pairs (e.g. 'openai:gpt-5.
|
|
79
|
+
MUX_AI_EVAL_MODELS: optionalString("Comma-separated eval model pairs.", "Comma-separated provider:model pairs (e.g. 'openai:gpt-5.6-luna,anthropic:claude-sonnet-4-5,google:gemini-3-flash-preview')"),
|
|
183
80
|
// AI Providers
|
|
184
81
|
OPENAI_API_KEY: optionalString("OpenAI API key for OpenAI-backed workflows.", "OpenAI API key"),
|
|
185
82
|
ANTHROPIC_API_KEY: optionalString("Anthropic API key for Claude-backed workflows.", "Anthropic API key"),
|
|
@@ -498,7 +395,8 @@ var TOKEN_USAGE_FIELDS = [
|
|
|
498
395
|
"outputTokens",
|
|
499
396
|
"totalTokens",
|
|
500
397
|
"reasoningTokens",
|
|
501
|
-
"cachedInputTokens"
|
|
398
|
+
"cachedInputTokens",
|
|
399
|
+
"cacheWriteTokens"
|
|
502
400
|
];
|
|
503
401
|
function isDefinedTokenUsageValue(value) {
|
|
504
402
|
return typeof value === "number";
|
|
@@ -540,6 +438,12 @@ function getErrorTokenUsage(error) {
|
|
|
540
438
|
extracted.cachedInputTokens = cacheReadTokens;
|
|
541
439
|
}
|
|
542
440
|
}
|
|
441
|
+
if (extracted.cacheWriteTokens === void 0) {
|
|
442
|
+
const cacheWriteTokens = source.inputTokenDetails?.cacheWriteTokens;
|
|
443
|
+
if (typeof cacheWriteTokens === "number") {
|
|
444
|
+
extracted.cacheWriteTokens = cacheWriteTokens;
|
|
445
|
+
}
|
|
446
|
+
}
|
|
543
447
|
return Object.keys(extracted).length > 0 ? extracted : void 0;
|
|
544
448
|
}
|
|
545
449
|
function rethrowWithTokenUsage(error, collectedUsage) {
|
|
@@ -565,6 +469,9 @@ var MuxAiError = class extends Error {
|
|
|
565
469
|
this.publicMessage = message;
|
|
566
470
|
this.retryable = opts?.retryable ?? false;
|
|
567
471
|
}
|
|
472
|
+
static is(value) {
|
|
473
|
+
return typeof value === "object" && value !== null && "__robots_error" in value && value.__robots_error === true;
|
|
474
|
+
}
|
|
568
475
|
};
|
|
569
476
|
function extractErrorDetail(error) {
|
|
570
477
|
if (error instanceof Error && error.message) {
|
|
@@ -582,7 +489,7 @@ function extractErrorDetail(error) {
|
|
|
582
489
|
return "Unknown error";
|
|
583
490
|
}
|
|
584
491
|
function wrapError(error, message) {
|
|
585
|
-
if (error
|
|
492
|
+
if (MuxAiError.is(error)) {
|
|
586
493
|
throw error;
|
|
587
494
|
}
|
|
588
495
|
const rawDetail = extractErrorDetail(error);
|
|
@@ -600,6 +507,260 @@ function wrapError(error, message) {
|
|
|
600
507
|
throw wrapped;
|
|
601
508
|
}
|
|
602
509
|
|
|
510
|
+
// src/lib/content-policy-error.ts
|
|
511
|
+
var PolicyTokenSchema = z2.string().trim().min(1).max(64).regex(/^[A-Z0-9_]+$/);
|
|
512
|
+
var SafetyRatingSchema = z2.object({
|
|
513
|
+
category: PolicyTokenSchema.nullish(),
|
|
514
|
+
blocked: z2.boolean().nullish()
|
|
515
|
+
});
|
|
516
|
+
var ContentPolicyResponseSchema = z2.object({
|
|
517
|
+
promptFeedback: z2.object({
|
|
518
|
+
blockReason: PolicyTokenSchema.nullish(),
|
|
519
|
+
safetyRatings: z2.array(SafetyRatingSchema).max(32).nullish()
|
|
520
|
+
}).nullish(),
|
|
521
|
+
candidates: z2.array(z2.object({
|
|
522
|
+
finishReason: PolicyTokenSchema.nullish(),
|
|
523
|
+
safetyRatings: z2.array(SafetyRatingSchema).max(32).nullish()
|
|
524
|
+
})).max(8).nullish()
|
|
525
|
+
});
|
|
526
|
+
var ContentPolicyUsageResponseSchema = z2.object({
|
|
527
|
+
usageMetadata: z2.object({
|
|
528
|
+
promptTokenCount: z2.number().finite().nonnegative().nullish(),
|
|
529
|
+
candidatesTokenCount: z2.number().finite().nonnegative().nullish(),
|
|
530
|
+
totalTokenCount: z2.number().finite().nonnegative().nullish(),
|
|
531
|
+
cachedContentTokenCount: z2.number().finite().nonnegative().nullish(),
|
|
532
|
+
thoughtsTokenCount: z2.number().finite().nonnegative().nullish()
|
|
533
|
+
}).nullish()
|
|
534
|
+
});
|
|
535
|
+
var CONTENT_POLICY_REASONS = /* @__PURE__ */ new Set([
|
|
536
|
+
"BLOCKLIST",
|
|
537
|
+
"ESCALATION",
|
|
538
|
+
"IMAGE_PROHIBITED_CONTENT",
|
|
539
|
+
"IMAGE_RECITATION",
|
|
540
|
+
"IMAGE_SAFETY",
|
|
541
|
+
"JAILBREAK",
|
|
542
|
+
"MODEL_ARMOR",
|
|
543
|
+
"PROHIBITED_CONTENT",
|
|
544
|
+
"RECITATION",
|
|
545
|
+
"SAFETY",
|
|
546
|
+
"SPII"
|
|
547
|
+
]);
|
|
548
|
+
async function withContentPolicyErrorHandling(operation) {
|
|
549
|
+
try {
|
|
550
|
+
return await operation();
|
|
551
|
+
} catch (error) {
|
|
552
|
+
rethrowContentPolicyError(error);
|
|
553
|
+
}
|
|
554
|
+
}
|
|
555
|
+
function rethrowContentPolicyError(error) {
|
|
556
|
+
const block = extractContentPolicyBlock(error);
|
|
557
|
+
if (!block) {
|
|
558
|
+
throw error;
|
|
559
|
+
}
|
|
560
|
+
throwContentPolicyError(block, getContentPolicyTokenUsage(error));
|
|
561
|
+
}
|
|
562
|
+
function getGeneratedOutputWithContentPolicyHandling(response) {
|
|
563
|
+
if (response.finishReason === "content-filter") {
|
|
564
|
+
const rawReason = PolicyTokenSchema.safeParse(response.rawFinishReason);
|
|
565
|
+
const reason = rawReason.success && isContentPolicyReason(rawReason.data) ? rawReason.data : "CONTENT_FILTER";
|
|
566
|
+
throwContentPolicyError({ reason }, getErrorTokenUsage(response));
|
|
567
|
+
}
|
|
568
|
+
return response.output;
|
|
569
|
+
}
|
|
570
|
+
function throwContentPolicyError(block, usage) {
|
|
571
|
+
const category = block.category ? `; category: ${block.category}` : "";
|
|
572
|
+
const contentPolicyError = new MuxAiError(
|
|
573
|
+
`The supplied content was blocked by a content policy (reason: ${block.reason}${category}).`,
|
|
574
|
+
{
|
|
575
|
+
type: "content_policy_error",
|
|
576
|
+
retryable: false
|
|
577
|
+
}
|
|
578
|
+
);
|
|
579
|
+
if (usage) {
|
|
580
|
+
contentPolicyError.usage = usage;
|
|
581
|
+
}
|
|
582
|
+
throw contentPolicyError;
|
|
583
|
+
}
|
|
584
|
+
function extractContentPolicyBlock(error) {
|
|
585
|
+
if (RetryError.isInstance(error)) {
|
|
586
|
+
return extractContentPolicyBlock(error.lastError);
|
|
587
|
+
}
|
|
588
|
+
if (NoObjectGeneratedError.isInstance(error) && error.finishReason === "content-filter") {
|
|
589
|
+
return { reason: "CONTENT_FILTER" };
|
|
590
|
+
}
|
|
591
|
+
if (!APICallError.isInstance(error) || !error.responseBody) {
|
|
592
|
+
return void 0;
|
|
593
|
+
}
|
|
594
|
+
try {
|
|
595
|
+
const parsed = ContentPolicyResponseSchema.safeParse(JSON.parse(error.responseBody));
|
|
596
|
+
if (!parsed.success) {
|
|
597
|
+
return void 0;
|
|
598
|
+
}
|
|
599
|
+
const promptReason = parsed.data.promptFeedback?.blockReason;
|
|
600
|
+
if (isContentPolicyReason(promptReason)) {
|
|
601
|
+
return {
|
|
602
|
+
reason: promptReason,
|
|
603
|
+
category: findBlockedCategory(parsed.data.promptFeedback?.safetyRatings)
|
|
604
|
+
};
|
|
605
|
+
}
|
|
606
|
+
for (const candidate of parsed.data.candidates ?? []) {
|
|
607
|
+
if (isContentPolicyReason(candidate.finishReason)) {
|
|
608
|
+
return {
|
|
609
|
+
reason: candidate.finishReason,
|
|
610
|
+
category: findBlockedCategory(candidate.safetyRatings)
|
|
611
|
+
};
|
|
612
|
+
}
|
|
613
|
+
}
|
|
614
|
+
} catch {
|
|
615
|
+
return void 0;
|
|
616
|
+
}
|
|
617
|
+
return void 0;
|
|
618
|
+
}
|
|
619
|
+
function isContentPolicyReason(reason) {
|
|
620
|
+
return Boolean(reason && CONTENT_POLICY_REASONS.has(reason));
|
|
621
|
+
}
|
|
622
|
+
function getContentPolicyTokenUsage(error) {
|
|
623
|
+
const errorUsage = getErrorTokenUsage(error);
|
|
624
|
+
if (errorUsage) {
|
|
625
|
+
return errorUsage;
|
|
626
|
+
}
|
|
627
|
+
if (RetryError.isInstance(error)) {
|
|
628
|
+
return getContentPolicyTokenUsage(error.lastError);
|
|
629
|
+
}
|
|
630
|
+
if (!APICallError.isInstance(error) || !error.responseBody) {
|
|
631
|
+
return void 0;
|
|
632
|
+
}
|
|
633
|
+
try {
|
|
634
|
+
const parsed = ContentPolicyUsageResponseSchema.safeParse(JSON.parse(error.responseBody));
|
|
635
|
+
const usage = parsed.success ? parsed.data.usageMetadata : void 0;
|
|
636
|
+
if (!usage) {
|
|
637
|
+
return void 0;
|
|
638
|
+
}
|
|
639
|
+
const inputTokens = usage.promptTokenCount ?? 0;
|
|
640
|
+
const reasoningTokens = usage.thoughtsTokenCount ?? 0;
|
|
641
|
+
const outputTokens = (usage.candidatesTokenCount ?? 0) + reasoningTokens;
|
|
642
|
+
return {
|
|
643
|
+
inputTokens,
|
|
644
|
+
outputTokens,
|
|
645
|
+
totalTokens: usage.totalTokenCount ?? inputTokens + outputTokens,
|
|
646
|
+
reasoningTokens,
|
|
647
|
+
cachedInputTokens: usage.cachedContentTokenCount ?? 0
|
|
648
|
+
};
|
|
649
|
+
} catch {
|
|
650
|
+
return void 0;
|
|
651
|
+
}
|
|
652
|
+
}
|
|
653
|
+
function findBlockedCategory(ratings) {
|
|
654
|
+
return ratings?.find((rating) => rating.blocked === true)?.category ?? void 0;
|
|
655
|
+
}
|
|
656
|
+
|
|
657
|
+
// src/lib/image-download.ts
|
|
658
|
+
import pRetry, { AbortError } from "p-retry";
|
|
659
|
+
var BASE64_CHUNK_SIZE = 32768;
|
|
660
|
+
function bytesToBase64(bytes) {
|
|
661
|
+
if (bytes.length === 0) {
|
|
662
|
+
return "";
|
|
663
|
+
}
|
|
664
|
+
let binary = "";
|
|
665
|
+
for (let i = 0; i < bytes.length; i += BASE64_CHUNK_SIZE) {
|
|
666
|
+
const chunk = bytes.subarray(i, i + BASE64_CHUNK_SIZE);
|
|
667
|
+
binary += String.fromCharCode(...chunk);
|
|
668
|
+
}
|
|
669
|
+
if (typeof globalThis.btoa !== "function") {
|
|
670
|
+
throw new TypeError("Base64 encoder is not available in this environment.");
|
|
671
|
+
}
|
|
672
|
+
return globalThis.btoa(binary);
|
|
673
|
+
}
|
|
674
|
+
var DEFAULT_OPTIONS = {
|
|
675
|
+
timeout: 1e4,
|
|
676
|
+
retries: 3,
|
|
677
|
+
retryDelay: 1e3,
|
|
678
|
+
maxRetryDelay: 1e4,
|
|
679
|
+
exponentialBackoff: true
|
|
680
|
+
};
|
|
681
|
+
async function downloadImageAsBase64(url, options = {}) {
|
|
682
|
+
"use step";
|
|
683
|
+
const opts = { ...DEFAULT_OPTIONS, ...options };
|
|
684
|
+
let attemptCount = 0;
|
|
685
|
+
return pRetry(
|
|
686
|
+
async () => {
|
|
687
|
+
attemptCount++;
|
|
688
|
+
const controller = new AbortController();
|
|
689
|
+
const timeoutId = setTimeout(() => controller.abort(), opts.timeout);
|
|
690
|
+
try {
|
|
691
|
+
const response = await fetch(url, {
|
|
692
|
+
signal: controller.signal,
|
|
693
|
+
headers: {
|
|
694
|
+
"User-Agent": "@mux/ai image downloader"
|
|
695
|
+
}
|
|
696
|
+
});
|
|
697
|
+
clearTimeout(timeoutId);
|
|
698
|
+
if (!response.ok) {
|
|
699
|
+
if (response.status >= 400 && response.status < 500 && response.status !== 429) {
|
|
700
|
+
throw new AbortError(`HTTP ${response.status}: ${response.statusText}`);
|
|
701
|
+
}
|
|
702
|
+
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
|
|
703
|
+
}
|
|
704
|
+
const contentType = response.headers.get("content-type");
|
|
705
|
+
if (!contentType?.startsWith("image/")) {
|
|
706
|
+
throw new AbortError(`Invalid content type: ${contentType}. Expected image/*`);
|
|
707
|
+
}
|
|
708
|
+
const arrayBuffer = await response.arrayBuffer();
|
|
709
|
+
const buffer = new Uint8Array(arrayBuffer);
|
|
710
|
+
if (buffer.length === 0) {
|
|
711
|
+
throw new AbortError("Downloaded image is empty");
|
|
712
|
+
}
|
|
713
|
+
const base64Data = `data:${contentType};base64,${bytesToBase64(buffer)}`;
|
|
714
|
+
return {
|
|
715
|
+
base64Data,
|
|
716
|
+
buffer,
|
|
717
|
+
url,
|
|
718
|
+
contentType,
|
|
719
|
+
sizeBytes: buffer.length,
|
|
720
|
+
attempts: attemptCount
|
|
721
|
+
};
|
|
722
|
+
} catch (error) {
|
|
723
|
+
clearTimeout(timeoutId);
|
|
724
|
+
if (error instanceof AbortError) {
|
|
725
|
+
throw error;
|
|
726
|
+
}
|
|
727
|
+
if (error instanceof Error) {
|
|
728
|
+
if (error.name === "AbortError") {
|
|
729
|
+
throw new Error(`Request timeout after ${opts.timeout}ms`);
|
|
730
|
+
}
|
|
731
|
+
throw new Error(`Download failed: ${error.message}`);
|
|
732
|
+
}
|
|
733
|
+
throw new Error("Unknown download error");
|
|
734
|
+
}
|
|
735
|
+
},
|
|
736
|
+
{
|
|
737
|
+
retries: opts.retries,
|
|
738
|
+
minTimeout: opts.retryDelay,
|
|
739
|
+
maxTimeout: opts.maxRetryDelay,
|
|
740
|
+
factor: opts.exponentialBackoff ? 2 : 1,
|
|
741
|
+
randomize: true,
|
|
742
|
+
// Add jitter to prevent thundering herd
|
|
743
|
+
onFailedAttempt: (error) => {
|
|
744
|
+
console.warn(`Image download attempt ${error.attemptNumber} failed for ${url}`);
|
|
745
|
+
if (error.retriesLeft > 0) {
|
|
746
|
+
console.warn(`Retrying... (${error.retriesLeft} attempts left)`);
|
|
747
|
+
}
|
|
748
|
+
}
|
|
749
|
+
}
|
|
750
|
+
);
|
|
751
|
+
}
|
|
752
|
+
async function downloadImagesAsBase64(urls, options = {}, maxConcurrent = 5) {
|
|
753
|
+
"use step";
|
|
754
|
+
const results = [];
|
|
755
|
+
for (let i = 0; i < urls.length; i += maxConcurrent) {
|
|
756
|
+
const batch = urls.slice(i, i + maxConcurrent);
|
|
757
|
+
const batchPromises = batch.map((url) => downloadImageAsBase64(url, options));
|
|
758
|
+
const batchResults = await Promise.all(batchPromises);
|
|
759
|
+
results.push(...batchResults);
|
|
760
|
+
}
|
|
761
|
+
return results;
|
|
762
|
+
}
|
|
763
|
+
|
|
603
764
|
// src/lib/providers.ts
|
|
604
765
|
import { createAnthropic } from "@ai-sdk/anthropic";
|
|
605
766
|
import { createGoogleGenerativeAI } from "@ai-sdk/google";
|
|
@@ -936,7 +1097,8 @@ async function resolveMuxSigningContext(credentials) {
|
|
|
936
1097
|
|
|
937
1098
|
// src/lib/providers.ts
|
|
938
1099
|
var DEFAULT_LANGUAGE_MODELS = {
|
|
939
|
-
|
|
1100
|
+
// GPT-5.6 models default to medium reasoning when no effort is specified.
|
|
1101
|
+
openai: "gpt-5.6-luna",
|
|
940
1102
|
anthropic: "claude-sonnet-4-5",
|
|
941
1103
|
google: "gemini-3-flash-preview"
|
|
942
1104
|
};
|
|
@@ -945,11 +1107,20 @@ var DEFAULT_EMBEDDING_MODELS = {
|
|
|
945
1107
|
google: "gemini-embedding-001"
|
|
946
1108
|
};
|
|
947
1109
|
var LANGUAGE_MODELS = {
|
|
948
|
-
openai: ["gpt-5.1", "gpt-5-mini"],
|
|
1110
|
+
openai: ["gpt-5.6-luna", "gpt-5.1", "gpt-5-mini"],
|
|
949
1111
|
anthropic: ["claude-sonnet-4-5"],
|
|
950
1112
|
google: ["gemini-3-flash-preview", "gemini-3.1-flash-lite", "gemini-2.5-flash"]
|
|
951
1113
|
};
|
|
952
1114
|
var LANGUAGE_MODEL_DEPRECATIONS = [
|
|
1115
|
+
{
|
|
1116
|
+
provider: "openai",
|
|
1117
|
+
modelId: "gpt-5.1",
|
|
1118
|
+
replacementModelId: "gpt-5.6-luna",
|
|
1119
|
+
phase: "warn",
|
|
1120
|
+
deprecatedOn: "2026-08-01",
|
|
1121
|
+
sunsetOn: "2026-10-01",
|
|
1122
|
+
reason: "GPT-5.6 Luna with its default medium reasoning effort is the new OpenAI default."
|
|
1123
|
+
},
|
|
953
1124
|
{
|
|
954
1125
|
provider: "google",
|
|
955
1126
|
modelId: "gemini-2.5-flash",
|
|
@@ -1010,7 +1181,7 @@ function parseEvalModelPair(value) {
|
|
|
1010
1181
|
const modelId = modelIdRaw?.trim();
|
|
1011
1182
|
if (!provider || !modelId) {
|
|
1012
1183
|
throw new Error(
|
|
1013
|
-
`Invalid eval model pair "${value}". Use "provider:model" (example: "openai:gpt-5.
|
|
1184
|
+
`Invalid eval model pair "${value}". Use "provider:model" (example: "openai:gpt-5.6-luna").`
|
|
1014
1185
|
);
|
|
1015
1186
|
}
|
|
1016
1187
|
if (!isSupportedProvider(provider)) {
|
|
@@ -1283,19 +1454,20 @@ async function withRetry(fn, {
|
|
|
1283
1454
|
try {
|
|
1284
1455
|
return await fn();
|
|
1285
1456
|
} catch (error) {
|
|
1286
|
-
lastError = error
|
|
1457
|
+
lastError = error;
|
|
1458
|
+
const retryError = error instanceof Error ? error : new Error(String(error));
|
|
1287
1459
|
const isLastAttempt = attempt === maxRetries;
|
|
1288
|
-
if (isLastAttempt || !shouldRetry(
|
|
1289
|
-
throw
|
|
1460
|
+
if (isLastAttempt || !shouldRetry(retryError, attempt + 1)) {
|
|
1461
|
+
throw error;
|
|
1290
1462
|
}
|
|
1291
1463
|
const delay = calculateDelay(attempt + 1, baseDelay, maxDelay);
|
|
1292
1464
|
console.warn(
|
|
1293
|
-
`Attempt ${attempt + 1} failed: ${
|
|
1465
|
+
`Attempt ${attempt + 1} failed: ${retryError.message}. Retrying in ${Math.round(delay)}ms...`
|
|
1294
1466
|
);
|
|
1295
1467
|
await new Promise((resolve) => setTimeout(resolve, delay));
|
|
1296
1468
|
}
|
|
1297
1469
|
}
|
|
1298
|
-
throw lastError
|
|
1470
|
+
throw lastError ?? new Error("Retry failed with unknown error");
|
|
1299
1471
|
}
|
|
1300
1472
|
|
|
1301
1473
|
// src/lib/workflow-scope.ts
|
|
@@ -2074,17 +2246,20 @@ async function fetchTranscriptForAsset(asset, playbackId, options = {}) {
|
|
|
2074
2246
|
}
|
|
2075
2247
|
|
|
2076
2248
|
// src/workflows/ask-questions.ts
|
|
2077
|
-
var
|
|
2078
|
-
|
|
2079
|
-
|
|
2080
|
-
|
|
2081
|
-
|
|
2082
|
-
|
|
2249
|
+
var ASK_QUESTIONS_MAX_QUESTION_LENGTH = 600;
|
|
2250
|
+
var ASK_QUESTIONS_DEFAULT_MAX_ANSWER_OPTION_LENGTH = 150;
|
|
2251
|
+
var ASK_QUESTIONS_DEFAULT_MAX_FREE_FORM_ANSWER_LENGTH = 500;
|
|
2252
|
+
var questionAnswerSchema = z3.object({
|
|
2253
|
+
question: z3.string().describe("The full text of the original question"),
|
|
2254
|
+
answer: z3.string().nullable(),
|
|
2255
|
+
confidence: z3.number(),
|
|
2256
|
+
reasoning: z3.string().max(1e3),
|
|
2257
|
+
skipped: z3.boolean()
|
|
2083
2258
|
});
|
|
2084
2259
|
var SKIP_SENTINEL = "__SKIPPED__";
|
|
2085
2260
|
function createAskQuestionsSchema(answerSchema) {
|
|
2086
|
-
return
|
|
2087
|
-
answers:
|
|
2261
|
+
return z3.object({
|
|
2262
|
+
answers: z3.array(
|
|
2088
2263
|
questionAnswerSchema.extend({
|
|
2089
2264
|
answer: answerSchema
|
|
2090
2265
|
})
|
|
@@ -2434,12 +2609,12 @@ function buildResponseSchemaForQuestions(normalizedQuestions, maxFreeFormAnswerL
|
|
|
2434
2609
|
SKIP_SENTINEL.length,
|
|
2435
2610
|
longestConstrained
|
|
2436
2611
|
);
|
|
2437
|
-
return createAskQuestionsSchema(
|
|
2612
|
+
return createAskQuestionsSchema(z3.string().min(1).max(schemaMax));
|
|
2438
2613
|
}
|
|
2439
2614
|
const allowed = Array.from(
|
|
2440
2615
|
new Set(normalizedQuestions.flatMap(allowedAnswersForQuestion))
|
|
2441
2616
|
);
|
|
2442
|
-
return createAskQuestionsSchema(
|
|
2617
|
+
return createAskQuestionsSchema(z3.enum([...allowed, SKIP_SENTINEL]));
|
|
2443
2618
|
}
|
|
2444
2619
|
async function analyzeQuestions({
|
|
2445
2620
|
provider,
|
|
@@ -2457,7 +2632,7 @@ async function analyzeQuestions({
|
|
|
2457
2632
|
normalizedQuestions,
|
|
2458
2633
|
maxFreeFormAnswerLength
|
|
2459
2634
|
);
|
|
2460
|
-
const response = await generateText({
|
|
2635
|
+
const response = await withContentPolicyErrorHandling(() => generateText({
|
|
2461
2636
|
model,
|
|
2462
2637
|
output: Output.object({ schema: responseSchema }),
|
|
2463
2638
|
experimental_telemetry: { isEnabled: true },
|
|
@@ -2474,11 +2649,12 @@ async function analyzeQuestions({
|
|
|
2474
2649
|
] : userPrompt
|
|
2475
2650
|
}
|
|
2476
2651
|
]
|
|
2477
|
-
});
|
|
2478
|
-
|
|
2652
|
+
}));
|
|
2653
|
+
const output = getGeneratedOutputWithContentPolicyHandling(response);
|
|
2654
|
+
if (!output) {
|
|
2479
2655
|
throw new Error("Ask-questions output missing");
|
|
2480
2656
|
}
|
|
2481
|
-
const parsed = responseSchema.parse(
|
|
2657
|
+
const parsed = responseSchema.parse(output);
|
|
2482
2658
|
const unexpectedRootKeys = detectUnexpectedKeysFromRawText(
|
|
2483
2659
|
response.text,
|
|
2484
2660
|
responseSchema.keyof().options
|
|
@@ -2500,7 +2676,8 @@ async function analyzeQuestions({
|
|
|
2500
2676
|
outputTokens: response.usage.outputTokens,
|
|
2501
2677
|
totalTokens: response.usage.totalTokens,
|
|
2502
2678
|
reasoningTokens: response.usage.reasoningTokens,
|
|
2503
|
-
cachedInputTokens: response.usage.cachedInputTokens
|
|
2679
|
+
cachedInputTokens: response.usage.cachedInputTokens,
|
|
2680
|
+
cacheWriteTokens: response.usage.inputTokenDetails?.cacheWriteTokens
|
|
2504
2681
|
},
|
|
2505
2682
|
unexpectedRootKeys,
|
|
2506
2683
|
unexpectedAnswerKeys
|
|
@@ -2519,7 +2696,6 @@ async function askQuestionsInternal(assetId, questions, options, collectedUsage)
|
|
|
2519
2696
|
if (!questions || questions.length === 0) {
|
|
2520
2697
|
throw new MuxAiError("At least one question must be provided.", { type: "validation_error" });
|
|
2521
2698
|
}
|
|
2522
|
-
const MAX_QUESTION_LENGTH = 500;
|
|
2523
2699
|
questions.forEach((q, idx) => {
|
|
2524
2700
|
if (!q.question || typeof q.question !== "string" || !q.question.trim()) {
|
|
2525
2701
|
throw new MuxAiError(
|
|
@@ -2527,15 +2703,14 @@ async function askQuestionsInternal(assetId, questions, options, collectedUsage)
|
|
|
2527
2703
|
{ type: "validation_error" }
|
|
2528
2704
|
);
|
|
2529
2705
|
}
|
|
2530
|
-
if (q.question.length >
|
|
2706
|
+
if (q.question.length > ASK_QUESTIONS_MAX_QUESTION_LENGTH) {
|
|
2531
2707
|
throw new MuxAiError(
|
|
2532
|
-
`Question at index ${idx} exceeds the ${
|
|
2708
|
+
`Question at index ${idx} exceeds the ${ASK_QUESTIONS_MAX_QUESTION_LENGTH}-character limit (received ${q.question.length}).`,
|
|
2533
2709
|
{ type: "validation_error" }
|
|
2534
2710
|
);
|
|
2535
2711
|
}
|
|
2536
2712
|
});
|
|
2537
|
-
const
|
|
2538
|
-
const maxAnswerOptionLength = options?.maxAnswerOptionLength ?? DEFAULT_MAX_ANSWER_OPTION_LENGTH;
|
|
2713
|
+
const maxAnswerOptionLength = options?.maxAnswerOptionLength ?? ASK_QUESTIONS_DEFAULT_MAX_ANSWER_OPTION_LENGTH;
|
|
2539
2714
|
if (!Number.isFinite(maxAnswerOptionLength) || maxAnswerOptionLength <= 0) {
|
|
2540
2715
|
throw new MuxAiError(
|
|
2541
2716
|
`maxAnswerOptionLength must be a positive number (received ${maxAnswerOptionLength}).`,
|
|
@@ -2554,8 +2729,7 @@ async function askQuestionsInternal(assetId, questions, options, collectedUsage)
|
|
|
2554
2729
|
}
|
|
2555
2730
|
}
|
|
2556
2731
|
});
|
|
2557
|
-
const
|
|
2558
|
-
const maxFreeFormAnswerLength = options?.maxFreeFormAnswerLength ?? DEFAULT_MAX_FREE_FORM_ANSWER_LENGTH;
|
|
2732
|
+
const maxFreeFormAnswerLength = options?.maxFreeFormAnswerLength ?? ASK_QUESTIONS_DEFAULT_MAX_FREE_FORM_ANSWER_LENGTH;
|
|
2559
2733
|
if (!Number.isFinite(maxFreeFormAnswerLength) || maxFreeFormAnswerLength <= 0) {
|
|
2560
2734
|
throw new MuxAiError(
|
|
2561
2735
|
`maxFreeFormAnswerLength must be a positive number (received ${maxFreeFormAnswerLength}).`,
|
|
@@ -2757,11 +2931,11 @@ async function askQuestionsInternal(assetId, questions, options, collectedUsage)
|
|
|
2757
2931
|
// src/workflows/burned-in-captions.ts
|
|
2758
2932
|
import { generateText as generateText2, Output as Output2 } from "ai";
|
|
2759
2933
|
import dedent3 from "dedent";
|
|
2760
|
-
import { z as
|
|
2761
|
-
var burnedInCaptionsSchema =
|
|
2762
|
-
hasBurnedInCaptions:
|
|
2763
|
-
confidence:
|
|
2764
|
-
detectedLanguage:
|
|
2934
|
+
import { z as z4 } from "zod";
|
|
2935
|
+
var burnedInCaptionsSchema = z4.object({
|
|
2936
|
+
hasBurnedInCaptions: z4.boolean(),
|
|
2937
|
+
confidence: z4.number(),
|
|
2938
|
+
detectedLanguage: z4.string().max(100).nullable()
|
|
2765
2939
|
});
|
|
2766
2940
|
var SYSTEM_PROMPT2 = promptDedent`
|
|
2767
2941
|
<role>
|
|
@@ -2871,7 +3045,7 @@ async function analyzeStoryboard({
|
|
|
2871
3045
|
}) {
|
|
2872
3046
|
"use step";
|
|
2873
3047
|
const model = await createLanguageModelFromConfig(provider, modelId, credentials);
|
|
2874
|
-
const response = await generateText2({
|
|
3048
|
+
const response = await withContentPolicyErrorHandling(() => generateText2({
|
|
2875
3049
|
model,
|
|
2876
3050
|
output: Output2.object({ schema: burnedInCaptionsSchema }),
|
|
2877
3051
|
experimental_telemetry: { isEnabled: true },
|
|
@@ -2888,22 +3062,24 @@ async function analyzeStoryboard({
|
|
|
2888
3062
|
]
|
|
2889
3063
|
}
|
|
2890
3064
|
]
|
|
2891
|
-
});
|
|
3065
|
+
}));
|
|
3066
|
+
const output = getGeneratedOutputWithContentPolicyHandling(response);
|
|
2892
3067
|
const unexpectedKeys = detectUnexpectedKeysFromRawText(
|
|
2893
3068
|
response.text,
|
|
2894
3069
|
burnedInCaptionsSchema.keyof().options
|
|
2895
3070
|
);
|
|
2896
3071
|
return {
|
|
2897
3072
|
result: {
|
|
2898
|
-
...
|
|
2899
|
-
confidence: Math.min(1, Math.max(0,
|
|
3073
|
+
...output,
|
|
3074
|
+
confidence: Math.min(1, Math.max(0, output.confidence))
|
|
2900
3075
|
},
|
|
2901
3076
|
usage: {
|
|
2902
3077
|
inputTokens: response.usage.inputTokens,
|
|
2903
3078
|
outputTokens: response.usage.outputTokens,
|
|
2904
3079
|
totalTokens: response.usage.totalTokens,
|
|
2905
3080
|
reasoningTokens: response.usage.reasoningTokens,
|
|
2906
|
-
cachedInputTokens: response.usage.cachedInputTokens
|
|
3081
|
+
cachedInputTokens: response.usage.cachedInputTokens,
|
|
3082
|
+
cacheWriteTokens: response.usage.inputTokenDetails?.cacheWriteTokens
|
|
2907
3083
|
},
|
|
2908
3084
|
unexpectedKeys
|
|
2909
3085
|
};
|
|
@@ -3001,13 +3177,13 @@ async function hasBurnedInCaptionsInternal(assetId, options, collectedUsage) {
|
|
|
3001
3177
|
// src/workflows/chapters.ts
|
|
3002
3178
|
import { generateText as generateText3, Output as Output3 } from "ai";
|
|
3003
3179
|
import dedent4 from "dedent";
|
|
3004
|
-
import { z as
|
|
3005
|
-
var chapterSchema =
|
|
3006
|
-
startTime:
|
|
3007
|
-
title:
|
|
3180
|
+
import { z as z5 } from "zod";
|
|
3181
|
+
var chapterSchema = z5.object({
|
|
3182
|
+
startTime: z5.number(),
|
|
3183
|
+
title: z5.string().max(300)
|
|
3008
3184
|
});
|
|
3009
|
-
var chaptersSchema =
|
|
3010
|
-
chapters:
|
|
3185
|
+
var chaptersSchema = z5.object({
|
|
3186
|
+
chapters: z5.array(chapterSchema)
|
|
3011
3187
|
});
|
|
3012
3188
|
async function generateChaptersWithAI({
|
|
3013
3189
|
provider,
|
|
@@ -3019,21 +3195,24 @@ async function generateChaptersWithAI({
|
|
|
3019
3195
|
"use step";
|
|
3020
3196
|
const model = await createLanguageModelFromConfig(provider, modelId, credentials);
|
|
3021
3197
|
const response = await withRetry(
|
|
3022
|
-
() =>
|
|
3023
|
-
|
|
3024
|
-
|
|
3025
|
-
|
|
3026
|
-
|
|
3027
|
-
|
|
3028
|
-
|
|
3029
|
-
|
|
3030
|
-
|
|
3031
|
-
|
|
3032
|
-
|
|
3033
|
-
|
|
3034
|
-
|
|
3035
|
-
|
|
3198
|
+
() => withContentPolicyErrorHandling(
|
|
3199
|
+
() => generateText3({
|
|
3200
|
+
model,
|
|
3201
|
+
output: Output3.object({ schema: chaptersSchema }),
|
|
3202
|
+
messages: [
|
|
3203
|
+
{
|
|
3204
|
+
role: "system",
|
|
3205
|
+
content: systemPrompt
|
|
3206
|
+
},
|
|
3207
|
+
{
|
|
3208
|
+
role: "user",
|
|
3209
|
+
content: userPrompt
|
|
3210
|
+
}
|
|
3211
|
+
]
|
|
3212
|
+
})
|
|
3213
|
+
)
|
|
3036
3214
|
);
|
|
3215
|
+
const output = getGeneratedOutputWithContentPolicyHandling(response);
|
|
3037
3216
|
const unexpectedRootKeys = detectUnexpectedKeysFromRawText(
|
|
3038
3217
|
response.text,
|
|
3039
3218
|
chaptersSchema.keyof().options
|
|
@@ -3049,13 +3228,14 @@ async function generateChaptersWithAI({
|
|
|
3049
3228
|
} catch {
|
|
3050
3229
|
}
|
|
3051
3230
|
return {
|
|
3052
|
-
chapters:
|
|
3231
|
+
chapters: output,
|
|
3053
3232
|
usage: {
|
|
3054
3233
|
inputTokens: response.usage.inputTokens,
|
|
3055
3234
|
outputTokens: response.usage.outputTokens,
|
|
3056
3235
|
totalTokens: response.usage.totalTokens,
|
|
3057
3236
|
reasoningTokens: response.usage.reasoningTokens,
|
|
3058
|
-
cachedInputTokens: response.usage.cachedInputTokens
|
|
3237
|
+
cachedInputTokens: response.usage.cachedInputTokens,
|
|
3238
|
+
cacheWriteTokens: response.usage.inputTokenDetails?.cacheWriteTokens
|
|
3059
3239
|
},
|
|
3060
3240
|
unexpectedRootKeys,
|
|
3061
3241
|
unexpectedChapterKeys
|
|
@@ -3311,7 +3491,7 @@ async function generateChaptersInternal(assetId, options, collectedUsage) {
|
|
|
3311
3491
|
|
|
3312
3492
|
// src/workflows/edit-captions.ts
|
|
3313
3493
|
import { generateText as generateText4, Output as Output4 } from "ai";
|
|
3314
|
-
import { z as
|
|
3494
|
+
import { z as z6 } from "zod";
|
|
3315
3495
|
|
|
3316
3496
|
// src/lib/mux-tracks.ts
|
|
3317
3497
|
async function fetchVttFromMux(vttUrl) {
|
|
@@ -3594,8 +3774,8 @@ async function createPresignedGetUrlWithStorageAdapter(input, adapter) {
|
|
|
3594
3774
|
}
|
|
3595
3775
|
|
|
3596
3776
|
// src/workflows/edit-captions.ts
|
|
3597
|
-
var profanityDetectionSchema =
|
|
3598
|
-
profanity:
|
|
3777
|
+
var profanityDetectionSchema = z6.object({
|
|
3778
|
+
profanity: z6.array(z6.string().max(100)).describe(
|
|
3599
3779
|
"Unique profane words or short phrases exactly as they appear in the transcript text. Include each distinct form only once (e.g., if 'fuck' and 'fucking' both appear, list both)."
|
|
3600
3780
|
)
|
|
3601
3781
|
});
|
|
@@ -3772,7 +3952,7 @@ async function identifyProfanityWithAI({
|
|
|
3772
3952
|
tag: "transcript",
|
|
3773
3953
|
content: plainText
|
|
3774
3954
|
});
|
|
3775
|
-
const response = await generateText4({
|
|
3955
|
+
const response = await withContentPolicyErrorHandling(() => generateText4({
|
|
3776
3956
|
model,
|
|
3777
3957
|
output: Output4.object({ schema: profanityDetectionSchema }),
|
|
3778
3958
|
messages: [
|
|
@@ -3787,19 +3967,21 @@ async function identifyProfanityWithAI({
|
|
|
3787
3967
|
${transcriptSection}`
|
|
3788
3968
|
}
|
|
3789
3969
|
]
|
|
3790
|
-
});
|
|
3970
|
+
}));
|
|
3971
|
+
const output = getGeneratedOutputWithContentPolicyHandling(response);
|
|
3791
3972
|
const unexpectedKeys = detectUnexpectedKeysFromRawText(
|
|
3792
3973
|
response.text,
|
|
3793
3974
|
profanityDetectionSchema.keyof().options
|
|
3794
3975
|
);
|
|
3795
3976
|
return {
|
|
3796
|
-
profanity:
|
|
3977
|
+
profanity: output.profanity,
|
|
3797
3978
|
usage: {
|
|
3798
3979
|
inputTokens: response.usage.inputTokens,
|
|
3799
3980
|
outputTokens: response.usage.outputTokens,
|
|
3800
3981
|
totalTokens: response.usage.totalTokens,
|
|
3801
3982
|
reasoningTokens: response.usage.reasoningTokens,
|
|
3802
|
-
cachedInputTokens: response.usage.cachedInputTokens
|
|
3983
|
+
cachedInputTokens: response.usage.cachedInputTokens,
|
|
3984
|
+
cacheWriteTokens: response.usage.inputTokenDetails?.cacheWriteTokens
|
|
3803
3985
|
},
|
|
3804
3986
|
unexpectedKeys
|
|
3805
3987
|
};
|
|
@@ -4442,7 +4624,7 @@ async function generateVideoEmbeddings(assetId, options = {}) {
|
|
|
4442
4624
|
// src/workflows/engagement-insights.ts
|
|
4443
4625
|
import { generateText as generateText5, Output as Output5 } from "ai";
|
|
4444
4626
|
import dedent5 from "dedent";
|
|
4445
|
-
import { z as
|
|
4627
|
+
import { z as z7 } from "zod";
|
|
4446
4628
|
|
|
4447
4629
|
// src/primitives/heatmap.ts
|
|
4448
4630
|
async function getHeatmapForAsset(assetId, options = {}) {
|
|
@@ -4589,20 +4771,20 @@ async function getShotsForAsset(assetId, options = {}) {
|
|
|
4589
4771
|
}
|
|
4590
4772
|
|
|
4591
4773
|
// src/workflows/engagement-insights.ts
|
|
4592
|
-
var aiMomentInsightSchema =
|
|
4593
|
-
hotspotIndex:
|
|
4594
|
-
startMs:
|
|
4595
|
-
endMs:
|
|
4596
|
-
timestamp:
|
|
4597
|
-
engagementScore:
|
|
4598
|
-
insight:
|
|
4774
|
+
var aiMomentInsightSchema = z7.object({
|
|
4775
|
+
hotspotIndex: z7.number(),
|
|
4776
|
+
startMs: z7.number(),
|
|
4777
|
+
endMs: z7.number(),
|
|
4778
|
+
timestamp: z7.string(),
|
|
4779
|
+
engagementScore: z7.number(),
|
|
4780
|
+
insight: z7.string().max(1e3)
|
|
4599
4781
|
});
|
|
4600
|
-
var aiOverallInsightSchema =
|
|
4601
|
-
summary:
|
|
4602
|
-
trends:
|
|
4782
|
+
var aiOverallInsightSchema = z7.object({
|
|
4783
|
+
summary: z7.string().max(2e3),
|
|
4784
|
+
trends: z7.array(z7.string().max(500))
|
|
4603
4785
|
});
|
|
4604
|
-
var engagementInsightsSchema =
|
|
4605
|
-
momentInsights:
|
|
4786
|
+
var engagementInsightsSchema = z7.object({
|
|
4787
|
+
momentInsights: z7.array(aiMomentInsightSchema),
|
|
4606
4788
|
overallInsight: aiOverallInsightSchema
|
|
4607
4789
|
});
|
|
4608
4790
|
var SYSTEM_PROMPT4 = promptDedent`
|
|
@@ -4864,26 +5046,29 @@ async function generateInsightsWithAI(provider, modelId, systemPrompt, userPromp
|
|
|
4864
5046
|
"use step";
|
|
4865
5047
|
const model = await createLanguageModelFromConfig(provider, modelId, credentials);
|
|
4866
5048
|
const response = await withRetry(
|
|
4867
|
-
() =>
|
|
4868
|
-
|
|
4869
|
-
|
|
4870
|
-
|
|
4871
|
-
|
|
4872
|
-
|
|
4873
|
-
|
|
4874
|
-
|
|
4875
|
-
|
|
4876
|
-
|
|
4877
|
-
|
|
4878
|
-
|
|
4879
|
-
|
|
4880
|
-
|
|
4881
|
-
|
|
4882
|
-
|
|
4883
|
-
|
|
4884
|
-
|
|
5049
|
+
() => withContentPolicyErrorHandling(
|
|
5050
|
+
() => generateText5({
|
|
5051
|
+
model,
|
|
5052
|
+
output: Output5.object({ schema: engagementInsightsSchema }),
|
|
5053
|
+
experimental_telemetry: { isEnabled: true },
|
|
5054
|
+
messages: [
|
|
5055
|
+
{
|
|
5056
|
+
role: "system",
|
|
5057
|
+
content: systemPrompt
|
|
5058
|
+
},
|
|
5059
|
+
{
|
|
5060
|
+
role: "user",
|
|
5061
|
+
content: [
|
|
5062
|
+
{ type: "text", text: userPrompt },
|
|
5063
|
+
...imageUrls.map((url) => ({ type: "image", image: url }))
|
|
5064
|
+
]
|
|
5065
|
+
}
|
|
5066
|
+
]
|
|
5067
|
+
})
|
|
5068
|
+
)
|
|
4885
5069
|
);
|
|
4886
|
-
|
|
5070
|
+
const output = getGeneratedOutputWithContentPolicyHandling(response);
|
|
5071
|
+
if (!output) {
|
|
4887
5072
|
throw new Error("AI returned empty or unparseable response");
|
|
4888
5073
|
}
|
|
4889
5074
|
const unexpectedRootKeys = detectUnexpectedKeysFromRawText(
|
|
@@ -4906,13 +5091,14 @@ async function generateInsightsWithAI(provider, modelId, systemPrompt, userPromp
|
|
|
4906
5091
|
} catch {
|
|
4907
5092
|
}
|
|
4908
5093
|
return {
|
|
4909
|
-
result:
|
|
5094
|
+
result: output,
|
|
4910
5095
|
usage: {
|
|
4911
5096
|
inputTokens: response.usage.inputTokens,
|
|
4912
5097
|
outputTokens: response.usage.outputTokens,
|
|
4913
5098
|
totalTokens: response.usage.totalTokens,
|
|
4914
5099
|
reasoningTokens: response.usage.reasoningTokens,
|
|
4915
|
-
cachedInputTokens: response.usage.cachedInputTokens
|
|
5100
|
+
cachedInputTokens: response.usage.cachedInputTokens,
|
|
5101
|
+
cacheWriteTokens: response.usage.inputTokenDetails?.cacheWriteTokens
|
|
4916
5102
|
},
|
|
4917
5103
|
unexpectedRootKeys,
|
|
4918
5104
|
unexpectedMomentKeys,
|
|
@@ -5866,7 +6052,7 @@ async function getModerationScores(assetId, options = {}) {
|
|
|
5866
6052
|
// src/workflows/summarization.ts
|
|
5867
6053
|
import { generateText as generateText6, Output as Output6 } from "ai";
|
|
5868
6054
|
import dedent6 from "dedent";
|
|
5869
|
-
import { z as
|
|
6055
|
+
import { z as z8 } from "zod";
|
|
5870
6056
|
|
|
5871
6057
|
// src/lib/markdown-normalization.ts
|
|
5872
6058
|
var ESCAPED_LINE_BREAK_REGEX = /\\r\\n|\\n|\\r/g;
|
|
@@ -5879,17 +6065,17 @@ function normalizeMarkdownDescription(description) {
|
|
|
5879
6065
|
var DEFAULT_SUMMARY_KEYWORD_LIMIT = 10;
|
|
5880
6066
|
var DEFAULT_TITLE_LENGTH = 10;
|
|
5881
6067
|
var DEFAULT_DESCRIPTION_LENGTH = 50;
|
|
5882
|
-
var summarySchema =
|
|
5883
|
-
keywords:
|
|
5884
|
-
title:
|
|
5885
|
-
description:
|
|
6068
|
+
var summarySchema = z8.object({
|
|
6069
|
+
keywords: z8.array(z8.string().max(200)),
|
|
6070
|
+
title: z8.string().max(500),
|
|
6071
|
+
description: z8.string().max(1e4)
|
|
5886
6072
|
});
|
|
5887
6073
|
function buildSummarySchema(descriptionLength) {
|
|
5888
6074
|
const descriptionMaxChars = Math.max(2e3, descriptionLength * 15);
|
|
5889
|
-
return
|
|
5890
|
-
keywords:
|
|
5891
|
-
title:
|
|
5892
|
-
description:
|
|
6075
|
+
return z8.object({
|
|
6076
|
+
keywords: z8.array(z8.string().max(200)),
|
|
6077
|
+
title: z8.string().max(500),
|
|
6078
|
+
description: z8.string().max(descriptionMaxChars)
|
|
5893
6079
|
});
|
|
5894
6080
|
}
|
|
5895
6081
|
var VALID_TONES = ["neutral", "playful", "professional"];
|
|
@@ -6165,7 +6351,7 @@ async function analyzeStoryboard2(imageDataUrl, provider, modelId, userPrompt, s
|
|
|
6165
6351
|
"use step";
|
|
6166
6352
|
const model = await createLanguageModelFromConfig(provider, modelId, credentials);
|
|
6167
6353
|
const schema = buildSummarySchema(descriptionLength);
|
|
6168
|
-
const response = await generateText6({
|
|
6354
|
+
const response = await withContentPolicyErrorHandling(() => generateText6({
|
|
6169
6355
|
model,
|
|
6170
6356
|
output: Output6.object({
|
|
6171
6357
|
name: "summary_metadata",
|
|
@@ -6185,11 +6371,12 @@ async function analyzeStoryboard2(imageDataUrl, provider, modelId, userPrompt, s
|
|
|
6185
6371
|
]
|
|
6186
6372
|
}
|
|
6187
6373
|
]
|
|
6188
|
-
});
|
|
6189
|
-
|
|
6374
|
+
}));
|
|
6375
|
+
const output = getGeneratedOutputWithContentPolicyHandling(response);
|
|
6376
|
+
if (!output) {
|
|
6190
6377
|
throw new Error("Summarization output missing");
|
|
6191
6378
|
}
|
|
6192
|
-
const parsed = schema.parse(
|
|
6379
|
+
const parsed = schema.parse(output);
|
|
6193
6380
|
const unexpectedKeys = detectUnexpectedKeysFromRawText(
|
|
6194
6381
|
response.text,
|
|
6195
6382
|
schema.keyof().options
|
|
@@ -6201,7 +6388,8 @@ async function analyzeStoryboard2(imageDataUrl, provider, modelId, userPrompt, s
|
|
|
6201
6388
|
outputTokens: response.usage.outputTokens,
|
|
6202
6389
|
totalTokens: response.usage.totalTokens,
|
|
6203
6390
|
reasoningTokens: response.usage.reasoningTokens,
|
|
6204
|
-
cachedInputTokens: response.usage.cachedInputTokens
|
|
6391
|
+
cachedInputTokens: response.usage.cachedInputTokens,
|
|
6392
|
+
cacheWriteTokens: response.usage.inputTokenDetails?.cacheWriteTokens
|
|
6205
6393
|
},
|
|
6206
6394
|
unexpectedKeys
|
|
6207
6395
|
};
|
|
@@ -6210,7 +6398,7 @@ async function analyzeAudioOnly(provider, modelId, userPrompt, systemPrompt, des
|
|
|
6210
6398
|
"use step";
|
|
6211
6399
|
const model = await createLanguageModelFromConfig(provider, modelId, credentials);
|
|
6212
6400
|
const schema = buildSummarySchema(descriptionLength);
|
|
6213
|
-
const response = await generateText6({
|
|
6401
|
+
const response = await withContentPolicyErrorHandling(() => generateText6({
|
|
6214
6402
|
model,
|
|
6215
6403
|
output: Output6.object({
|
|
6216
6404
|
name: "summary_metadata",
|
|
@@ -6227,11 +6415,12 @@ async function analyzeAudioOnly(provider, modelId, userPrompt, systemPrompt, des
|
|
|
6227
6415
|
content: userPrompt
|
|
6228
6416
|
}
|
|
6229
6417
|
]
|
|
6230
|
-
});
|
|
6231
|
-
|
|
6418
|
+
}));
|
|
6419
|
+
const output = getGeneratedOutputWithContentPolicyHandling(response);
|
|
6420
|
+
if (!output) {
|
|
6232
6421
|
throw new Error("Summarization output missing");
|
|
6233
6422
|
}
|
|
6234
|
-
const parsed = schema.parse(
|
|
6423
|
+
const parsed = schema.parse(output);
|
|
6235
6424
|
const unexpectedKeys = detectUnexpectedKeysFromRawText(
|
|
6236
6425
|
response.text,
|
|
6237
6426
|
schema.keyof().options
|
|
@@ -6243,7 +6432,8 @@ async function analyzeAudioOnly(provider, modelId, userPrompt, systemPrompt, des
|
|
|
6243
6432
|
outputTokens: response.usage.outputTokens,
|
|
6244
6433
|
totalTokens: response.usage.totalTokens,
|
|
6245
6434
|
reasoningTokens: response.usage.reasoningTokens,
|
|
6246
|
-
cachedInputTokens: response.usage.cachedInputTokens
|
|
6435
|
+
cachedInputTokens: response.usage.cachedInputTokens,
|
|
6436
|
+
cacheWriteTokens: response.usage.inputTokenDetails?.cacheWriteTokens
|
|
6247
6437
|
},
|
|
6248
6438
|
unexpectedKeys
|
|
6249
6439
|
};
|
|
@@ -6814,16 +7004,16 @@ async function translateAudio(assetId, toLanguageCode, options = {}) {
|
|
|
6814
7004
|
|
|
6815
7005
|
// src/workflows/translate-captions.ts
|
|
6816
7006
|
import {
|
|
6817
|
-
APICallError,
|
|
7007
|
+
APICallError as APICallError2,
|
|
6818
7008
|
generateText as generateText7,
|
|
6819
|
-
NoObjectGeneratedError,
|
|
7009
|
+
NoObjectGeneratedError as NoObjectGeneratedError2,
|
|
6820
7010
|
Output as Output7,
|
|
6821
|
-
RetryError,
|
|
7011
|
+
RetryError as RetryError2,
|
|
6822
7012
|
TypeValidationError
|
|
6823
7013
|
} from "ai";
|
|
6824
|
-
import { z as
|
|
6825
|
-
var translationSchema =
|
|
6826
|
-
translation:
|
|
7014
|
+
import { z as z9 } from "zod";
|
|
7015
|
+
var translationSchema = z9.object({
|
|
7016
|
+
translation: z9.string()
|
|
6827
7017
|
});
|
|
6828
7018
|
var SYSTEM_PROMPT6 = promptDedent`
|
|
6829
7019
|
You are a subtitle translation expert. Translate VTT subtitle files to the target language specified by the user.
|
|
@@ -6890,10 +7080,10 @@ function isProviderServiceError(error) {
|
|
|
6890
7080
|
if (!error) {
|
|
6891
7081
|
return false;
|
|
6892
7082
|
}
|
|
6893
|
-
if (
|
|
7083
|
+
if (RetryError2.isInstance(error)) {
|
|
6894
7084
|
return isProviderServiceError(error.lastError);
|
|
6895
7085
|
}
|
|
6896
|
-
if (
|
|
7086
|
+
if (APICallError2.isInstance(error)) {
|
|
6897
7087
|
return true;
|
|
6898
7088
|
}
|
|
6899
7089
|
if (error instanceof Error && "cause" in error) {
|
|
@@ -6905,7 +7095,7 @@ function shouldSplitChunkTranslationError(error) {
|
|
|
6905
7095
|
if (isProviderServiceError(error)) {
|
|
6906
7096
|
return false;
|
|
6907
7097
|
}
|
|
6908
|
-
return
|
|
7098
|
+
return NoObjectGeneratedError2.isInstance(error) || TypeValidationError.isInstance(error) || isTranslationChunkValidationError(error);
|
|
6909
7099
|
}
|
|
6910
7100
|
function resolveTranslationChunkingOptions(options) {
|
|
6911
7101
|
const targetChunkDurationSeconds = Math.max(
|
|
@@ -7066,7 +7256,7 @@ async function translateVttWithAI({
|
|
|
7066
7256
|
"use step";
|
|
7067
7257
|
const model = await createLanguageModelFromConfig(provider, modelId, credentials);
|
|
7068
7258
|
const sanitisedVttContent = stripVttMetadataBlocks(vttContent);
|
|
7069
|
-
const response = await generateText7({
|
|
7259
|
+
const response = await withContentPolicyErrorHandling(() => generateText7({
|
|
7070
7260
|
model,
|
|
7071
7261
|
output: Output7.object({ schema: translationSchema }),
|
|
7072
7262
|
messages: [
|
|
@@ -7081,8 +7271,9 @@ async function translateVttWithAI({
|
|
|
7081
7271
|
${sanitisedVttContent}`
|
|
7082
7272
|
}
|
|
7083
7273
|
]
|
|
7084
|
-
});
|
|
7085
|
-
const
|
|
7274
|
+
}));
|
|
7275
|
+
const output = getGeneratedOutputWithContentPolicyHandling(response);
|
|
7276
|
+
const translated = normalizeTranslatedVtt(output.translation);
|
|
7086
7277
|
const leakReason = detectLeakReason(translated);
|
|
7087
7278
|
const safeTranslated = leakReason !== null ? vttContent : translated;
|
|
7088
7279
|
if (leakReason !== null) {
|
|
@@ -7107,7 +7298,8 @@ ${sanitisedVttContent}`
|
|
|
7107
7298
|
outputTokens: response.usage.outputTokens,
|
|
7108
7299
|
totalTokens: response.usage.totalTokens,
|
|
7109
7300
|
reasoningTokens: response.usage.reasoningTokens,
|
|
7110
|
-
cachedInputTokens: response.usage.cachedInputTokens
|
|
7301
|
+
cachedInputTokens: response.usage.cachedInputTokens,
|
|
7302
|
+
cacheWriteTokens: response.usage.inputTokenDetails?.cacheWriteTokens
|
|
7111
7303
|
}
|
|
7112
7304
|
};
|
|
7113
7305
|
}
|
|
@@ -7121,8 +7313,8 @@ async function translateCueChunkWithAI({
|
|
|
7121
7313
|
}) {
|
|
7122
7314
|
"use step";
|
|
7123
7315
|
const model = await createLanguageModelFromConfig(provider, modelId, credentials);
|
|
7124
|
-
const schema =
|
|
7125
|
-
translations:
|
|
7316
|
+
const schema = z9.object({
|
|
7317
|
+
translations: z9.array(z9.string().min(1).max(2e3)).length(cues.length)
|
|
7126
7318
|
});
|
|
7127
7319
|
const cuePayload = cues.map((cue, index) => ({
|
|
7128
7320
|
index,
|
|
@@ -7130,7 +7322,7 @@ async function translateCueChunkWithAI({
|
|
|
7130
7322
|
endTime: cue.endTime,
|
|
7131
7323
|
text: cue.text
|
|
7132
7324
|
}));
|
|
7133
|
-
const response = await generateText7({
|
|
7325
|
+
const response = await withContentPolicyErrorHandling(() => generateText7({
|
|
7134
7326
|
model,
|
|
7135
7327
|
output: Output7.object({ schema }),
|
|
7136
7328
|
messages: [
|
|
@@ -7146,7 +7338,8 @@ Return exactly ${cues.length} translated cues in the same order as the input.
|
|
|
7146
7338
|
${JSON.stringify(cuePayload, null, 2)}`
|
|
7147
7339
|
}
|
|
7148
7340
|
]
|
|
7149
|
-
});
|
|
7341
|
+
}));
|
|
7342
|
+
const output = getGeneratedOutputWithContentPolicyHandling(response);
|
|
7150
7343
|
const unexpectedKeys = detectUnexpectedKeysFromRawText(
|
|
7151
7344
|
response.text,
|
|
7152
7345
|
schema.keyof().options
|
|
@@ -7157,13 +7350,14 @@ ${JSON.stringify(cuePayload, null, 2)}`
|
|
|
7157
7350
|
);
|
|
7158
7351
|
}
|
|
7159
7352
|
return {
|
|
7160
|
-
translations:
|
|
7353
|
+
translations: output.translations,
|
|
7161
7354
|
usage: {
|
|
7162
7355
|
inputTokens: response.usage.inputTokens,
|
|
7163
7356
|
outputTokens: response.usage.outputTokens,
|
|
7164
7357
|
totalTokens: response.usage.totalTokens,
|
|
7165
7358
|
reasoningTokens: response.usage.reasoningTokens,
|
|
7166
|
-
cachedInputTokens: response.usage.cachedInputTokens
|
|
7359
|
+
cachedInputTokens: response.usage.cachedInputTokens,
|
|
7360
|
+
cacheWriteTokens: response.usage.inputTokenDetails?.cacheWriteTokens
|
|
7167
7361
|
},
|
|
7168
7362
|
unexpectedKeyCount: unexpectedKeys.length
|
|
7169
7363
|
};
|
|
@@ -7526,6 +7720,9 @@ async function translateCaptionsInternal(assetId, trackId, toLanguageCode, optio
|
|
|
7526
7720
|
};
|
|
7527
7721
|
}
|
|
7528
7722
|
export {
|
|
7723
|
+
ASK_QUESTIONS_DEFAULT_MAX_ANSWER_OPTION_LENGTH,
|
|
7724
|
+
ASK_QUESTIONS_DEFAULT_MAX_FREE_FORM_ANSWER_LENGTH,
|
|
7725
|
+
ASK_QUESTIONS_MAX_QUESTION_LENGTH,
|
|
7529
7726
|
DEFAULT_DESCRIPTION_LENGTH,
|
|
7530
7727
|
DEFAULT_SUMMARY_KEYWORD_LIMIT,
|
|
7531
7728
|
DEFAULT_TITLE_LENGTH,
|