@mux/ai 0.41.0 → 0.42.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-C3PW73qH.d.ts → index-NkSOF92U.d.ts} +11 -72
- package/dist/index.d.ts +3 -2
- package/dist/index.js +231 -82
- package/dist/index.js.map +1 -1
- package/dist/primitives/index.js +1 -0
- package/dist/primitives/index.js.map +1 -1
- package/dist/prompts/index.d.ts +335 -0
- package/dist/prompts/index.js +490 -0
- package/dist/prompts/index.js.map +1 -0
- package/dist/workflows/index.d.ts +2 -1
- package/dist/workflows/index.js +230 -81
- package/dist/workflows/index.js.map +1 -1
- package/package.json +7 -1
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
export { E as ASK_QUESTIONS_DEFAULT_MAX_ANSWER_OPTION_LENGTH, F as ASK_QUESTIONS_DEFAULT_MAX_FREE_FORM_ANSWER_LENGTH, G as ASK_QUESTIONS_MAX_QUESTIONS_PER_CALL, H as ASK_QUESTIONS_MAX_QUESTION_LENGTH, J as AskQuestionsOptions, K as AskQuestionsResult, N as AskQuestionsType, O as AudioTranslationOptions, P as AudioTranslationResult, Q as AutoCensorProfanityOptions, U as BurnedInCaptionsAnalysis, V as BurnedInCaptionsOptions, W as BurnedInCaptionsPromptOverrides, X as BurnedInCaptionsPromptSections, Y as BurnedInCaptionsResult, Z as CaptionReplacement, _ as CensorMode, $ as Chapter, a0 as ChapterSystemPromptSections, a1 as ChaptersOptions, a2 as ChaptersPromptOverrides, a3 as ChaptersPromptSections, a4 as ChaptersResult, a5 as ChaptersType, a6 as DEFAULT_DESCRIPTION_LENGTH, a7 as DEFAULT_SUMMARY_KEYWORD_LIMIT, a8 as DEFAULT_TITLE_LENGTH, a9 as EditCaptionsOptions, aa as EditCaptionsResult, ab as EmbeddingsOptions, ac as EmbeddingsResult, ad as EngagementInsightsOptions, ae as EngagementInsightsResult, af as GOOGLE_VISION_LIKELIHOOD_TO_SCORE, ag as HIVE_SEXUAL_CATEGORIES, ah as HIVE_VIOLENCE_CATEGORIES, ai as HiveModerationOutput, aj as HiveModerationSource, ak as ModerationOptions, al as ModerationProvider, am as ModerationResult, an as MomentInsight, ao as OverallInsight, ap as ProfanityDetectionPayload, aq as Question, ar as QuestionAnswer, as as QuestionAnswerType, at as ReplacementRecord, au as SpeakerReplacement, av as StaticRenditionCleanupOutcome, aw as SummarizationOptions, ax as SummarizationPromptOverrides, ay as SummarizationPromptSections, az as SummaryAndTagsResult, aA as SummaryType, aB as ThumbnailModerationScore, aC as TranslationChunkingOptions, aD as TranslationOptions, aE as TranslationPayload, aF as TranslationResult, aG as aggregateTokenUsage, aH as applyOverrideLists, aI as applyReplacements, aJ as applySpeakerReplacements, aK as askQuestions, aL as buildReplacementRegex, aM as burnedInCaptionsSchema, aN as censorVttContent, aO as chapterSchema, aP as chaptersSchema, aQ as createReplacer, aR as editCaptions, aS as extractTranscriptSegmentsForHotspots, aT as generateChapters, aU as generateEmbeddings, aV as generateEngagementInsights, aW as generateVideoEmbeddings, aX as getModerationScores, aY as getSummaryAndTags, aZ as hasBurnedInCaptions, a_ as mapShotsToHotspots, a$ as normalizeTranslatedVtt, b0 as profanityDetectionSchema, b1 as questionAnswerSchema, b2 as shouldSplitChunkTranslationError, b3 as summarySchema, b4 as transformCueText, b5 as translateAudio, b6 as translateCaptions, b7 as translationSchema, b8 as validateNeverTranslateTerms, b9 as verifyNeverTranslateTerms } from '../index-
|
|
1
|
+
export { E as ASK_QUESTIONS_DEFAULT_MAX_ANSWER_OPTION_LENGTH, F as ASK_QUESTIONS_DEFAULT_MAX_FREE_FORM_ANSWER_LENGTH, G as ASK_QUESTIONS_MAX_QUESTIONS_PER_CALL, H as ASK_QUESTIONS_MAX_QUESTION_LENGTH, J as AskQuestionsOptions, K as AskQuestionsResult, N as AskQuestionsType, O as AudioTranslationOptions, P as AudioTranslationResult, Q as AutoCensorProfanityOptions, U as BurnedInCaptionsAnalysis, V as BurnedInCaptionsOptions, W as BurnedInCaptionsPromptOverrides, X as BurnedInCaptionsPromptSections, Y as BurnedInCaptionsResult, Z as CaptionReplacement, _ as CensorMode, $ as Chapter, a0 as ChapterSystemPromptSections, a1 as ChaptersOptions, a2 as ChaptersPromptOverrides, a3 as ChaptersPromptSections, a4 as ChaptersResult, a5 as ChaptersType, a6 as DEFAULT_DESCRIPTION_LENGTH, a7 as DEFAULT_SUMMARY_KEYWORD_LIMIT, a8 as DEFAULT_TITLE_LENGTH, a9 as EditCaptionsOptions, aa as EditCaptionsResult, ab as EmbeddingsOptions, ac as EmbeddingsResult, ad as EngagementInsightsOptions, ae as EngagementInsightsResult, af as GOOGLE_VISION_LIKELIHOOD_TO_SCORE, ag as HIVE_SEXUAL_CATEGORIES, ah as HIVE_VIOLENCE_CATEGORIES, ai as HiveModerationOutput, aj as HiveModerationSource, ak as ModerationOptions, al as ModerationProvider, am as ModerationResult, an as MomentInsight, ao as OverallInsight, ap as ProfanityDetectionPayload, aq as Question, ar as QuestionAnswer, as as QuestionAnswerType, at as ReplacementRecord, au as SpeakerReplacement, av as StaticRenditionCleanupOutcome, aw as SummarizationOptions, ax as SummarizationPromptOverrides, ay as SummarizationPromptSections, az as SummaryAndTagsResult, aA as SummaryType, aB as ThumbnailModerationScore, aC as TranslationChunkingOptions, aD as TranslationOptions, aE as TranslationPayload, aF as TranslationResult, aG as aggregateTokenUsage, aH as applyOverrideLists, aI as applyReplacements, aJ as applySpeakerReplacements, aK as askQuestions, aL as buildReplacementRegex, aM as burnedInCaptionsSchema, aN as censorVttContent, aO as chapterSchema, aP as chaptersSchema, aQ as createReplacer, aR as editCaptions, aS as extractTranscriptSegmentsForHotspots, aT as generateChapters, aU as generateEmbeddings, aV as generateEngagementInsights, aW as generateVideoEmbeddings, aX as getModerationScores, aY as getSummaryAndTags, aZ as hasBurnedInCaptions, a_ as mapShotsToHotspots, a$ as normalizeTranslatedVtt, b0 as profanityDetectionSchema, b1 as questionAnswerSchema, b2 as shouldSplitChunkTranslationError, b3 as summarySchema, b4 as transformCueText, b5 as translateAudio, b6 as translateCaptions, b7 as translationSchema, b8 as validateNeverTranslateTerms, b9 as verifyNeverTranslateTerms } from '../index-NkSOF92U.js';
|
|
2
2
|
import 'zod';
|
|
3
|
+
import '../prompts/index.js';
|
|
3
4
|
import '@ai-sdk/anthropic';
|
|
4
5
|
import '@ai-sdk/google';
|
|
5
6
|
import '@ai-sdk/openai';
|
package/dist/workflows/index.js
CHANGED
|
@@ -275,6 +275,7 @@ var PROMPT_TAG_NAMES = [
|
|
|
275
275
|
"answer_guidelines",
|
|
276
276
|
"relevance_filtering",
|
|
277
277
|
"transcript_guidance",
|
|
278
|
+
"scene_guidance",
|
|
278
279
|
"capabilities",
|
|
279
280
|
"language_guidelines",
|
|
280
281
|
"tone_guidance",
|
|
@@ -3571,6 +3572,17 @@ var AUDIO_ONLY_SYSTEM_PROMPT_OVERRIDES = {
|
|
|
3571
3572
|
You receive a timestamped transcript from audio-only content with lines in the form "[12s] Transcript text".
|
|
3572
3573
|
Use those timestamps as anchors to determine chapter start times in seconds.`
|
|
3573
3574
|
};
|
|
3575
|
+
var SCENE_CONTEXT_SYSTEM_PROMPT_OVERRIDES = {
|
|
3576
|
+
context: dedent4`
|
|
3577
|
+
You receive a timestamped transcript with lines in the form "[12s] Caption text" and an ordered scene context.
|
|
3578
|
+
Transcript timestamps are in seconds. Scene start and end timestamps are in milliseconds.
|
|
3579
|
+
Use the scene progression to choose logical chapter groupings and scene starts as chapter boundaries.`,
|
|
3580
|
+
constraints: dedent4`
|
|
3581
|
+
- Only use information present in the transcript and scene context
|
|
3582
|
+
- Return structured data that matches the requested JSON schema
|
|
3583
|
+
- Do not add commentary or extra text outside the JSON
|
|
3584
|
+
- When a <language> section is provided, all chapter titles MUST be written in that language`
|
|
3585
|
+
};
|
|
3574
3586
|
var chaptersPromptBuilder = createPromptBuilder({
|
|
3575
3587
|
template: {
|
|
3576
3588
|
task: {
|
|
@@ -3608,6 +3620,7 @@ var chaptersPromptBuilder = createPromptBuilder({
|
|
|
3608
3620
|
});
|
|
3609
3621
|
function buildUserPrompt3({
|
|
3610
3622
|
timestampedTranscript,
|
|
3623
|
+
sceneContext,
|
|
3611
3624
|
promptOverrides,
|
|
3612
3625
|
minChaptersPerHour = 3,
|
|
3613
3626
|
maxChaptersPerHour = 8,
|
|
@@ -3621,14 +3634,31 @@ function buildUserPrompt3({
|
|
|
3621
3634
|
attributes: { format: "seconds" }
|
|
3622
3635
|
}
|
|
3623
3636
|
];
|
|
3637
|
+
if (sceneContext?.length) {
|
|
3638
|
+
contextSections.push({
|
|
3639
|
+
tag: "scene_context",
|
|
3640
|
+
content: JSON.stringify(sceneContext, null, 2),
|
|
3641
|
+
attributes: { timestamps: "milliseconds", trust: "untrusted_evidence" }
|
|
3642
|
+
});
|
|
3643
|
+
}
|
|
3624
3644
|
if (languageName) {
|
|
3625
3645
|
contextSections.push(createLanguageSection(languageName));
|
|
3626
3646
|
}
|
|
3647
|
+
const sceneBoundarySeconds = getSceneBoundarySeconds(sceneContext).filter(
|
|
3648
|
+
(boundary) => scope ? boundary >= scope.startTime && boundary < scope.endTime : true
|
|
3649
|
+
);
|
|
3650
|
+
let firstChapterGuideline = "";
|
|
3651
|
+
if (sceneBoundarySeconds.length > 0) {
|
|
3652
|
+
firstChapterGuideline = `- The first chapter must start at ${sceneBoundarySeconds[0]}s, the first scene boundary inside the analyzed range`;
|
|
3653
|
+
} else if (scope) {
|
|
3654
|
+
firstChapterGuideline = `- The first chapter must start at ${scope.startTime}s`;
|
|
3655
|
+
}
|
|
3627
3656
|
const dynamicChapterGuidelines = dedent4`
|
|
3628
3657
|
- Create at least ${minChaptersPerHour} and at most ${maxChaptersPerHour} chapters per hour of content
|
|
3629
3658
|
- Use start times in seconds (not HH:MM:SS)
|
|
3630
|
-
${scope ? `- Analyze only the range from ${scope.startTime}s (inclusive) to ${scope.endTime}s (exclusive)
|
|
3631
|
-
|
|
3659
|
+
${scope ? `- Analyze only the range from ${scope.startTime}s (inclusive) to ${scope.endTime}s (exclusive)` : ""}
|
|
3660
|
+
${firstChapterGuideline}
|
|
3661
|
+
${sceneContext?.length ? "- Use the ordered scene context to identify chapter-sized runs of consecutive scenes\n- Every chapter startTime must exactly equal a scene start_ms divided by 1000; never split a scene" : ""}
|
|
3632
3662
|
- Chapter start times should be non-decreasing
|
|
3633
3663
|
- Do not include text before or after the JSON`;
|
|
3634
3664
|
const mergedOverrides = {
|
|
@@ -3637,6 +3667,32 @@ function buildUserPrompt3({
|
|
|
3637
3667
|
};
|
|
3638
3668
|
return chaptersPromptBuilder.buildWithContext(mergedOverrides, contextSections);
|
|
3639
3669
|
}
|
|
3670
|
+
function getSceneBoundarySeconds(sceneContext) {
|
|
3671
|
+
if (!sceneContext?.length) {
|
|
3672
|
+
return [];
|
|
3673
|
+
}
|
|
3674
|
+
return [...new Set(sceneContext.map((scene) => scene.start_ms / 1e3))].sort((a, b) => a - b);
|
|
3675
|
+
}
|
|
3676
|
+
function alignChaptersToSceneBoundaries(chapters, sceneBoundarySeconds) {
|
|
3677
|
+
if (sceneBoundarySeconds.length === 0) {
|
|
3678
|
+
return chapters;
|
|
3679
|
+
}
|
|
3680
|
+
const chapterByBoundary = /* @__PURE__ */ new Map();
|
|
3681
|
+
for (const chapter of chapters) {
|
|
3682
|
+
const startTime = sceneBoundarySeconds.reduce(
|
|
3683
|
+
(closest, boundary) => Math.abs(boundary - chapter.startTime) < Math.abs(closest - chapter.startTime) ? boundary : closest
|
|
3684
|
+
);
|
|
3685
|
+
const distance = Math.abs(startTime - chapter.startTime);
|
|
3686
|
+
const existing = chapterByBoundary.get(startTime);
|
|
3687
|
+
if (!existing || distance === 0 && existing.distance !== 0) {
|
|
3688
|
+
chapterByBoundary.set(startTime, {
|
|
3689
|
+
chapter: { ...chapter, startTime },
|
|
3690
|
+
distance
|
|
3691
|
+
});
|
|
3692
|
+
}
|
|
3693
|
+
}
|
|
3694
|
+
return [...chapterByBoundary.values()].map(({ chapter }) => chapter).sort((a, b) => a.startTime - b.startTime);
|
|
3695
|
+
}
|
|
3640
3696
|
async function generateChapters(assetId, options = {}) {
|
|
3641
3697
|
"use workflow";
|
|
3642
3698
|
const collectedUsage = [];
|
|
@@ -3656,7 +3712,8 @@ async function generateChaptersInternal(assetId, options, collectedUsage) {
|
|
|
3656
3712
|
maxChaptersPerHour,
|
|
3657
3713
|
credentials,
|
|
3658
3714
|
outputLanguageCode,
|
|
3659
|
-
scope
|
|
3715
|
+
scope,
|
|
3716
|
+
sceneContext
|
|
3660
3717
|
} = options;
|
|
3661
3718
|
const modelConfig = resolveLanguageModelConfig({
|
|
3662
3719
|
...options,
|
|
@@ -3698,8 +3755,18 @@ async function generateChaptersInternal(assetId, options, collectedUsage) {
|
|
|
3698
3755
|
}
|
|
3699
3756
|
const resolvedLanguageCode = outputLanguageCode && outputLanguageCode !== "auto" ? outputLanguageCode : getReliableLanguageCode(transcriptResult.track) ?? languageCode;
|
|
3700
3757
|
const languageName = resolvedLanguageCode ? getLanguageName(resolvedLanguageCode) : void 0;
|
|
3758
|
+
const sceneBoundarySeconds = getSceneBoundarySeconds(sceneContext).filter(
|
|
3759
|
+
(boundary) => resolvedScope ? boundary >= resolvedScope.startTime && boundary < resolvedScope.endTime : true
|
|
3760
|
+
);
|
|
3761
|
+
if (sceneContext?.length && sceneBoundarySeconds.length === 0) {
|
|
3762
|
+
throw new MuxAiError(
|
|
3763
|
+
"Scene context has no chapter boundaries within the requested scope.",
|
|
3764
|
+
{ type: "validation_error" }
|
|
3765
|
+
);
|
|
3766
|
+
}
|
|
3701
3767
|
const userPrompt = buildUserPrompt3({
|
|
3702
3768
|
timestampedTranscript,
|
|
3769
|
+
sceneContext,
|
|
3703
3770
|
promptOverrides,
|
|
3704
3771
|
minChaptersPerHour,
|
|
3705
3772
|
maxChaptersPerHour,
|
|
@@ -3708,7 +3775,11 @@ async function generateChaptersInternal(assetId, options, collectedUsage) {
|
|
|
3708
3775
|
});
|
|
3709
3776
|
let chaptersData = null;
|
|
3710
3777
|
try {
|
|
3711
|
-
const
|
|
3778
|
+
const systemPromptOverrides = {
|
|
3779
|
+
...isAudioOnly ? AUDIO_ONLY_SYSTEM_PROMPT_OVERRIDES : {},
|
|
3780
|
+
...sceneContext?.length ? SCENE_CONTEXT_SYSTEM_PROMPT_OVERRIDES : {}
|
|
3781
|
+
};
|
|
3782
|
+
const systemPrompt = chapterSystemPromptBuilder.build(systemPromptOverrides);
|
|
3712
3783
|
chaptersData = await generateChaptersWithAI({
|
|
3713
3784
|
provider: modelConfig.provider,
|
|
3714
3785
|
modelId: modelConfig.modelId,
|
|
@@ -3726,9 +3797,13 @@ async function generateChaptersInternal(assetId, options, collectedUsage) {
|
|
|
3726
3797
|
throw new MuxAiError(`Failed to generate chapters for asset ${assetId}.`);
|
|
3727
3798
|
}
|
|
3728
3799
|
const { chapters: chaptersPayload, usage } = chaptersData;
|
|
3729
|
-
const
|
|
3800
|
+
const scopedChapters = chaptersPayload.chapters.filter((chapter) => typeof chapter.startTime === "number" && typeof chapter.title === "string").filter(
|
|
3730
3801
|
(chapter) => resolvedScope ? chapter.startTime >= resolvedScope.startTime && chapter.startTime < resolvedScope.endTime : true
|
|
3731
3802
|
).sort((a, b) => a.startTime - b.startTime);
|
|
3803
|
+
const validChapters = alignChaptersToSceneBoundaries(
|
|
3804
|
+
scopedChapters,
|
|
3805
|
+
sceneBoundarySeconds
|
|
3806
|
+
);
|
|
3732
3807
|
if (validChapters.length === 0) {
|
|
3733
3808
|
throw new MuxAiError(`Failed to generate valid chapters for asset ${assetId}.`);
|
|
3734
3809
|
}
|
|
@@ -3754,9 +3829,11 @@ async function generateChaptersInternal(assetId, options, collectedUsage) {
|
|
|
3754
3829
|
if (scrubbedChapters.length === 0) {
|
|
3755
3830
|
throw new MuxAiError(`Failed to generate valid chapters for asset ${assetId}.`);
|
|
3756
3831
|
}
|
|
3757
|
-
|
|
3758
|
-
|
|
3759
|
-
scrubbedChapters[0].startTime
|
|
3832
|
+
if (sceneBoundarySeconds.length === 0) {
|
|
3833
|
+
const firstChapterStartTime = resolvedScope?.startTime ?? 0;
|
|
3834
|
+
if (scrubbedChapters[0].startTime !== firstChapterStartTime) {
|
|
3835
|
+
scrubbedChapters[0].startTime = firstChapterStartTime;
|
|
3836
|
+
}
|
|
3760
3837
|
}
|
|
3761
3838
|
const usageWithMetadata = {
|
|
3762
3839
|
...usage,
|
|
@@ -6730,14 +6807,44 @@ function buildDescriptionGuidance(wordCount, contentType) {
|
|
|
6730
6807
|
Write in present tense. Be specific about what is discussed or presented rather than making assumptions.
|
|
6731
6808
|
Focus on the spoken content and any key insights, dialogue, or narrative elements.`;
|
|
6732
6809
|
}
|
|
6733
|
-
function
|
|
6810
|
+
function buildSceneAwareDescriptionGuidance(wordCount) {
|
|
6811
|
+
if (wordCount < DESCRIPTION_LENGTH_THRESHOLD_SMALL) {
|
|
6812
|
+
return dedent6`A brief summary of the video in no more than ${wordCount} words. Shorter is fine.
|
|
6813
|
+
Focus on the single most important subject or action across the ordered scenes.
|
|
6814
|
+
Use the storyboard as direct visual evidence and the scene context to preserve progression.
|
|
6815
|
+
Write in present tense.`;
|
|
6816
|
+
}
|
|
6817
|
+
if (wordCount > DESCRIPTION_LENGTH_THRESHOLD_LARGE) {
|
|
6818
|
+
return dedent6`A detailed summary that describes what happens across the ordered scenes.
|
|
6819
|
+
Never exceed ${wordCount} words, but shorter is perfectly fine. You may use multiple sentences.
|
|
6820
|
+
Be thorough: cover subjects, actions, setting, progression, and notable details supported across scenes.
|
|
6821
|
+
Use the storyboard as direct visual evidence and the scene context to preserve progression.
|
|
6822
|
+
Write in present tense. If the transcript provides dialogue or narration, incorporate key points while preserving scene progression.`;
|
|
6823
|
+
}
|
|
6824
|
+
return dedent6`A summary that describes what happens across the ordered scenes.
|
|
6825
|
+
Never exceed ${wordCount} words, but shorter is perfectly fine. You may use multiple sentences.
|
|
6826
|
+
Cover the main subjects, actions, setting, and notable progression supported across scenes.
|
|
6827
|
+
Use the storyboard as direct visual evidence and the scene context to preserve progression.
|
|
6828
|
+
Write in present tense. If the transcript provides dialogue or narration, incorporate key points while preserving scene progression.`;
|
|
6829
|
+
}
|
|
6830
|
+
function createSummarizationBuilder({ titleLength, descriptionLength, tagCount } = {}, { hasSceneContext = false } = {}) {
|
|
6734
6831
|
const titleLimit = titleLength ?? DEFAULT_TITLE_LENGTH;
|
|
6735
6832
|
const keywordLimit = tagCount ?? DEFAULT_SUMMARY_KEYWORD_LIMIT;
|
|
6833
|
+
const task = hasSceneContext ? "Analyze the storyboard frames and ordered scene context together, then generate metadata that captures the full progression of the video content." : "Analyze the storyboard frames and generate metadata that captures the essence of the video content.";
|
|
6834
|
+
const qualityGuidelines = hasSceneContext ? dedent6`
|
|
6835
|
+
- Follow scene_index order to understand what begins, develops, and concludes
|
|
6836
|
+
- Use scene narratives and concepts as grounded evidence, preserving consistent terminology across the output
|
|
6837
|
+
- Use the storyboard as direct visual evidence for whole-asset details
|
|
6838
|
+
- Balance brevity with informativeness` : dedent6`
|
|
6839
|
+
- Examine all frames to understand the full context and progression
|
|
6840
|
+
- Be precise: "golden retriever" is better than "dog" when identifiable
|
|
6841
|
+
- Capture the narrative: what begins, develops, and concludes
|
|
6842
|
+
- Balance brevity with informativeness`;
|
|
6736
6843
|
return createPromptBuilder({
|
|
6737
6844
|
template: {
|
|
6738
6845
|
task: {
|
|
6739
6846
|
tag: "task",
|
|
6740
|
-
content:
|
|
6847
|
+
content: task
|
|
6741
6848
|
},
|
|
6742
6849
|
title: {
|
|
6743
6850
|
tag: "title_requirements",
|
|
@@ -6750,7 +6857,7 @@ function createSummarizationBuilder({ titleLength, descriptionLength, tagCount }
|
|
|
6750
6857
|
},
|
|
6751
6858
|
description: {
|
|
6752
6859
|
tag: "description_requirements",
|
|
6753
|
-
content: buildDescriptionGuidance(descriptionLength ?? DEFAULT_DESCRIPTION_LENGTH, "video")
|
|
6860
|
+
content: hasSceneContext ? buildSceneAwareDescriptionGuidance(descriptionLength ?? DEFAULT_DESCRIPTION_LENGTH) : buildDescriptionGuidance(descriptionLength ?? DEFAULT_DESCRIPTION_LENGTH, "video")
|
|
6754
6861
|
},
|
|
6755
6862
|
keywords: {
|
|
6756
6863
|
tag: "keywords_requirements",
|
|
@@ -6766,11 +6873,7 @@ function createSummarizationBuilder({ titleLength, descriptionLength, tagCount }
|
|
|
6766
6873
|
},
|
|
6767
6874
|
qualityGuidelines: {
|
|
6768
6875
|
tag: "quality_guidelines",
|
|
6769
|
-
content:
|
|
6770
|
-
- Examine all frames to understand the full context and progression
|
|
6771
|
-
- Be precise: "golden retriever" is better than "dog" when identifiable
|
|
6772
|
-
- Capture the narrative: what begins, develops, and concludes
|
|
6773
|
-
- Balance brevity with informativeness`
|
|
6876
|
+
content: qualityGuidelines
|
|
6774
6877
|
}
|
|
6775
6878
|
},
|
|
6776
6879
|
sectionOrder: ["task", "title", "description", "keywords", "qualityGuidelines"]
|
|
@@ -6822,59 +6925,83 @@ function createAudioOnlyBuilder({ titleLength, descriptionLength, tagCount } = {
|
|
|
6822
6925
|
sectionOrder: ["task", "title", "description", "keywords", "qualityGuidelines"]
|
|
6823
6926
|
});
|
|
6824
6927
|
}
|
|
6825
|
-
|
|
6826
|
-
|
|
6827
|
-
|
|
6828
|
-
|
|
6829
|
-
|
|
6830
|
-
|
|
6831
|
-
|
|
6832
|
-
|
|
6833
|
-
|
|
6834
|
-
|
|
6835
|
-
|
|
6836
|
-
|
|
6837
|
-
|
|
6838
|
-
|
|
6839
|
-
|
|
6840
|
-
|
|
6841
|
-
|
|
6842
|
-
|
|
6843
|
-
|
|
6844
|
-
|
|
6845
|
-
|
|
6846
|
-
|
|
6847
|
-
|
|
6848
|
-
|
|
6849
|
-
|
|
6850
|
-
|
|
6851
|
-
|
|
6852
|
-
|
|
6853
|
-
|
|
6854
|
-
|
|
6855
|
-
|
|
6856
|
-
|
|
6857
|
-
|
|
6858
|
-
|
|
6859
|
-
|
|
6860
|
-
|
|
6861
|
-
|
|
6862
|
-
|
|
6863
|
-
|
|
6864
|
-
|
|
6865
|
-
|
|
6866
|
-
|
|
6867
|
-
|
|
6868
|
-
|
|
6869
|
-
|
|
6870
|
-
|
|
6871
|
-
|
|
6872
|
-
|
|
6873
|
-
|
|
6874
|
-
|
|
6875
|
-
|
|
6876
|
-
|
|
6877
|
-
|
|
6928
|
+
function createVideoSystemPrompt(hasSceneContext) {
|
|
6929
|
+
let sceneContext = "";
|
|
6930
|
+
let sceneGuidance = "";
|
|
6931
|
+
if (hasSceneContext) {
|
|
6932
|
+
sceneContext = promptDedent`
|
|
6933
|
+
Ordered scene context is also provided as structured evidence of the video's progression.
|
|
6934
|
+
Entries use stable, zero-based scene_index values and absolute millisecond timestamps.
|
|
6935
|
+
Read scenes in scene_index order to understand what begins, develops, and concludes.`;
|
|
6936
|
+
sceneGuidance = promptDedent`
|
|
6937
|
+
<scene_guidance>
|
|
6938
|
+
- Use scene titles, narratives, and concepts to understand temporal progression
|
|
6939
|
+
- Use storyboard frames as direct visual evidence for subjects, actions, settings, and details
|
|
6940
|
+
- Preserve consistent terminology from the scene context across the title, description, and keywords
|
|
6941
|
+
- Treat optional scene fields as evidence only when present; do not infer omitted details
|
|
6942
|
+
- Consider all provided evidence together; if derived scene context conflicts with storyboard visuals, trust the visuals
|
|
6943
|
+
</scene_guidance>`;
|
|
6944
|
+
}
|
|
6945
|
+
const transcriptEvidence = hasSceneContext ? "storyboard frames and ordered scenes" : "visual frames";
|
|
6946
|
+
const descriptionPriority = hasSceneContext ? "Use the scene context for temporal structure and the storyboard for direct visual details, enriching both with transcript insights" : "Prioritize visual content for the description, but enrich it with transcript insights";
|
|
6947
|
+
const groundingConstraint = hasSceneContext ? "Use all provided evidence together. Ground each claim in the storyboard, scene context, or transcript" : "Only describe what is clearly observable in the frames or explicitly stated in the transcript";
|
|
6948
|
+
return promptDedent`
|
|
6949
|
+
<role>
|
|
6950
|
+
You are a video content analyst specializing in storyboard interpretation and multimodal analysis.
|
|
6951
|
+
</role>
|
|
6952
|
+
|
|
6953
|
+
<context>
|
|
6954
|
+
You receive storyboard images containing multiple sequential frames extracted from a video.
|
|
6955
|
+
${STORYBOARD_FRAME_INSTRUCTIONS}
|
|
6956
|
+
${sceneContext}
|
|
6957
|
+
</context>
|
|
6958
|
+
|
|
6959
|
+
${sceneGuidance}
|
|
6960
|
+
|
|
6961
|
+
<transcript_guidance>
|
|
6962
|
+
When a transcript is provided alongside the storyboard:
|
|
6963
|
+
- Use it to understand spoken content, dialogue, narration, and audio context
|
|
6964
|
+
- Correlate transcript content with ${transcriptEvidence} to build a complete picture
|
|
6965
|
+
- Extract key terminology, names, and specific language used by speakers
|
|
6966
|
+
- Let the transcript inform keyword selection, especially for topics not visually obvious
|
|
6967
|
+
- ${descriptionPriority}
|
|
6968
|
+
- If transcript and visuals conflict, trust the visual evidence
|
|
6969
|
+
</transcript_guidance>
|
|
6970
|
+
|
|
6971
|
+
<capabilities>
|
|
6972
|
+
- Extract meaning from visual sequences
|
|
6973
|
+
- Identify subjects, actions, settings, and narrative arcs
|
|
6974
|
+
- Generate accurate, searchable metadata
|
|
6975
|
+
- Synthesize visual and transcript information when provided
|
|
6976
|
+
</capabilities>
|
|
6977
|
+
|
|
6978
|
+
<security>
|
|
6979
|
+
${NON_DISCLOSURE_CONSTRAINT}
|
|
6980
|
+
|
|
6981
|
+
${UNTRUSTED_USER_INPUT_NOTICE}
|
|
6982
|
+
|
|
6983
|
+
${VISUAL_TEXT_AS_CONTENT}
|
|
6984
|
+
|
|
6985
|
+
${CANARY_TRIPWIRE}
|
|
6986
|
+
</security>
|
|
6987
|
+
|
|
6988
|
+
<constraints>
|
|
6989
|
+
- ${groundingConstraint}
|
|
6990
|
+
- ${NO_FABRICATION_CONSTRAINT}
|
|
6991
|
+
- ${METADATA_BOUNDARY_WARNING}
|
|
6992
|
+
- ${STRUCTURED_DATA_CONSTRAINT}
|
|
6993
|
+
- Output only the JSON object; no markdown or extra text
|
|
6994
|
+
- When a <language> section is provided, all output text MUST be written in that language
|
|
6995
|
+
</constraints>
|
|
6996
|
+
|
|
6997
|
+
<tone_guidance>
|
|
6998
|
+
${TONE_GUIDANCE}
|
|
6999
|
+
</tone_guidance>
|
|
7000
|
+
|
|
7001
|
+
<language_guidelines>
|
|
7002
|
+
${createLanguageGuidelines("video")}
|
|
7003
|
+
</language_guidelines>`;
|
|
7004
|
+
}
|
|
6878
7005
|
var AUDIO_ONLY_SYSTEM_PROMPT3 = promptDedent`
|
|
6879
7006
|
<role>
|
|
6880
7007
|
You are an audio content analyst specializing in transcript analysis and metadata generation.
|
|
@@ -6925,6 +7052,16 @@ var AUDIO_ONLY_SYSTEM_PROMPT3 = promptDedent`
|
|
|
6925
7052
|
<language_guidelines>
|
|
6926
7053
|
${createLanguageGuidelines("audio")}
|
|
6927
7054
|
</language_guidelines>`;
|
|
7055
|
+
function createSceneContextSection(sceneContext) {
|
|
7056
|
+
return {
|
|
7057
|
+
tag: "scene_context",
|
|
7058
|
+
content: JSON.stringify(sceneContext),
|
|
7059
|
+
attributes: {
|
|
7060
|
+
format: "json",
|
|
7061
|
+
order: "scene_index_ascending"
|
|
7062
|
+
}
|
|
7063
|
+
};
|
|
7064
|
+
}
|
|
6928
7065
|
function buildUserPrompt5({
|
|
6929
7066
|
tone,
|
|
6930
7067
|
transcriptText,
|
|
@@ -6934,7 +7071,8 @@ function buildUserPrompt5({
|
|
|
6934
7071
|
titleLength,
|
|
6935
7072
|
descriptionLength,
|
|
6936
7073
|
tagCount,
|
|
6937
|
-
languageName
|
|
7074
|
+
languageName,
|
|
7075
|
+
sceneContext
|
|
6938
7076
|
}) {
|
|
6939
7077
|
const contextSections = [createToneSection(TONE_INSTRUCTIONS[tone])];
|
|
6940
7078
|
if (languageName) {
|
|
@@ -6949,11 +7087,15 @@ function buildUserPrompt5({
|
|
|
6949
7087
|
const format = isCleanTranscript ? "plain text" : "WebVTT";
|
|
6950
7088
|
contextSections.push(createTranscriptSection(transcriptText, format));
|
|
6951
7089
|
}
|
|
7090
|
+
const hasSceneContext = !isAudioOnly && Boolean(sceneContext?.length);
|
|
7091
|
+
if (hasSceneContext && sceneContext) {
|
|
7092
|
+
contextSections.push(createSceneContextSection(sceneContext));
|
|
7093
|
+
}
|
|
6952
7094
|
const constraints = { titleLength, descriptionLength, tagCount };
|
|
6953
|
-
const promptBuilder = isAudioOnly ? createAudioOnlyBuilder(constraints) : createSummarizationBuilder(constraints);
|
|
7095
|
+
const promptBuilder = isAudioOnly ? createAudioOnlyBuilder(constraints) : createSummarizationBuilder(constraints, { hasSceneContext });
|
|
6954
7096
|
return promptBuilder.buildWithContext(promptOverrides, contextSections);
|
|
6955
7097
|
}
|
|
6956
|
-
async function
|
|
7098
|
+
async function analyzeVideo(imageDataUrl, provider, modelId, userPrompt, systemPrompt, descriptionLength, credentials) {
|
|
6957
7099
|
"use step";
|
|
6958
7100
|
const model = await createLanguageModelFromConfig(provider, modelId, credentials);
|
|
6959
7101
|
const schema = buildSummarySchema(descriptionLength);
|
|
@@ -6969,10 +7111,10 @@ async function analyzeStoryboard2(imageDataUrl, provider, modelId, userPrompt, s
|
|
|
6969
7111
|
messages: [
|
|
6970
7112
|
{
|
|
6971
7113
|
role: "user",
|
|
6972
|
-
content: [
|
|
7114
|
+
content: imageDataUrl ? [
|
|
6973
7115
|
{ type: "text", text: userPrompt },
|
|
6974
7116
|
{ type: "image", image: imageDataUrl }
|
|
6975
|
-
]
|
|
7117
|
+
] : userPrompt
|
|
6976
7118
|
}
|
|
6977
7119
|
]
|
|
6978
7120
|
}));
|
|
@@ -7082,6 +7224,7 @@ async function getSummaryAndTagsInternal(assetId, options, collectedUsage) {
|
|
|
7082
7224
|
cleanTranscript = true,
|
|
7083
7225
|
imageSubmissionMode = "url",
|
|
7084
7226
|
imageDownloadOptions,
|
|
7227
|
+
sceneContext,
|
|
7085
7228
|
promptOverrides,
|
|
7086
7229
|
credentials,
|
|
7087
7230
|
titleLength,
|
|
@@ -7105,6 +7248,7 @@ async function getSummaryAndTagsInternal(assetId, options, collectedUsage) {
|
|
|
7105
7248
|
const { asset: assetData, playbackId, policy } = await getPlaybackIdForAsset(assetId, workflowCredentials, options?.assetSnapshot);
|
|
7106
7249
|
const assetDurationSeconds = getAssetDurationSecondsFromAsset(assetData);
|
|
7107
7250
|
const isAudioOnly = isAudioOnlyAsset(assetData);
|
|
7251
|
+
const hasSceneContext = !isAudioOnly && Boolean(sceneContext?.length);
|
|
7108
7252
|
const effectiveScope = hasWorkflowScopeBoundaries(scope) ? scope : void 0;
|
|
7109
7253
|
const storyboardScope = isAudioOnly ? void 0 : resolveRenderableVideoScope(
|
|
7110
7254
|
effectiveScope,
|
|
@@ -7121,7 +7265,8 @@ async function getSummaryAndTagsInternal(assetId, options, collectedUsage) {
|
|
|
7121
7265
|
);
|
|
7122
7266
|
}
|
|
7123
7267
|
const signingContext = await resolveMuxSigningContext(workflowCredentials);
|
|
7124
|
-
|
|
7268
|
+
const requiresSignedPlaybackAccess = includeTranscript || !isAudioOnly;
|
|
7269
|
+
if (policy === "signed" && requiresSignedPlaybackAccess && !signingContext) {
|
|
7125
7270
|
throw new MuxAiError(
|
|
7126
7271
|
"Signed playback ID requires signing credentials. Set MUX_SIGNING_KEY and MUX_PRIVATE_KEY environment variables.",
|
|
7127
7272
|
{ type: "validation_error" }
|
|
@@ -7147,11 +7292,15 @@ async function getSummaryAndTagsInternal(assetId, options, collectedUsage) {
|
|
|
7147
7292
|
titleLength,
|
|
7148
7293
|
descriptionLength,
|
|
7149
7294
|
tagCount,
|
|
7150
|
-
languageName
|
|
7295
|
+
languageName,
|
|
7296
|
+
sceneContext: hasSceneContext ? sceneContext : void 0
|
|
7151
7297
|
});
|
|
7152
7298
|
let analysisResponse;
|
|
7153
7299
|
let imageUrl;
|
|
7154
|
-
|
|
7300
|
+
let systemPrompt = AUDIO_ONLY_SYSTEM_PROMPT3;
|
|
7301
|
+
if (!isAudioOnly) {
|
|
7302
|
+
systemPrompt = createVideoSystemPrompt(hasSceneContext);
|
|
7303
|
+
}
|
|
7155
7304
|
const effectiveDescriptionLength = descriptionLength ?? DEFAULT_DESCRIPTION_LENGTH;
|
|
7156
7305
|
try {
|
|
7157
7306
|
if (isAudioOnly) {
|
|
@@ -7174,7 +7323,7 @@ async function getSummaryAndTagsInternal(assetId, options, collectedUsage) {
|
|
|
7174
7323
|
imageUrl = storyboardUrl;
|
|
7175
7324
|
if (imageSubmissionMode === "base64") {
|
|
7176
7325
|
const downloadResult = await downloadImageAsBase64(storyboardUrl, imageDownloadOptions);
|
|
7177
|
-
analysisResponse = await
|
|
7326
|
+
analysisResponse = await analyzeVideo(
|
|
7178
7327
|
downloadResult.base64Data,
|
|
7179
7328
|
modelConfig.provider,
|
|
7180
7329
|
modelConfig.modelId,
|
|
@@ -7184,7 +7333,7 @@ async function getSummaryAndTagsInternal(assetId, options, collectedUsage) {
|
|
|
7184
7333
|
workflowCredentials
|
|
7185
7334
|
);
|
|
7186
7335
|
} else {
|
|
7187
|
-
analysisResponse = await
|
|
7336
|
+
analysisResponse = await analyzeVideo(
|
|
7188
7337
|
storyboardUrl,
|
|
7189
7338
|
modelConfig.provider,
|
|
7190
7339
|
modelConfig.modelId,
|
|
@@ -7769,7 +7918,7 @@ import { z as z9 } from "zod";
|
|
|
7769
7918
|
var translationSchema = z9.object({
|
|
7770
7919
|
translation: z9.string()
|
|
7771
7920
|
});
|
|
7772
|
-
var
|
|
7921
|
+
var SYSTEM_PROMPT5 = promptDedent`
|
|
7773
7922
|
You are a subtitle translation expert. Translate VTT subtitle files to the target language specified by the user.
|
|
7774
7923
|
You may receive either a full VTT file or a chunk from a larger VTT.
|
|
7775
7924
|
Preserve all timestamps, cue ordering, and VTT formatting exactly as they appear.
|
|
@@ -8094,7 +8243,7 @@ async function translateVttWithAI({
|
|
|
8094
8243
|
model,
|
|
8095
8244
|
maxRetries: 0,
|
|
8096
8245
|
output: Output7.object({ schema: translationSchema }),
|
|
8097
|
-
system:
|
|
8246
|
+
system: SYSTEM_PROMPT5,
|
|
8098
8247
|
messages: [
|
|
8099
8248
|
{
|
|
8100
8249
|
role: "user",
|