@mux/ai 0.43.0 → 0.44.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-BCyjRRuI.d.ts → index-DigKByh7.d.ts} +2 -5
- package/dist/index.d.ts +3 -3
- package/dist/index.js +144 -130
- package/dist/index.js.map +1 -1
- package/dist/prompts/index.d.ts +77 -305
- package/dist/prompts/index.js +201 -0
- package/dist/prompts/index.js.map +1 -1
- package/dist/summarization-BFmhXeAu.d.ts +351 -0
- package/dist/workflows/index.d.ts +2 -2
- package/dist/workflows/index.js +143 -129
- package/dist/workflows/index.js.map +1 -1
- package/package.json +1 -1
package/dist/workflows/index.js
CHANGED
|
@@ -3480,8 +3480,32 @@ async function hasBurnedInCaptionsInternal(assetId, options, collectedUsage) {
|
|
|
3480
3480
|
|
|
3481
3481
|
// src/workflows/chapters.ts
|
|
3482
3482
|
import { generateText as generateText3, Output as Output3 } from "ai";
|
|
3483
|
-
import
|
|
3483
|
+
import dedent5 from "dedent";
|
|
3484
3484
|
import { z as z5 } from "zod";
|
|
3485
|
+
|
|
3486
|
+
// src/prompts/chapters.ts
|
|
3487
|
+
import dedent4 from "dedent";
|
|
3488
|
+
function createChapterGuidance({
|
|
3489
|
+
terminologySource = "transcript"
|
|
3490
|
+
} = {}) {
|
|
3491
|
+
return {
|
|
3492
|
+
qualityGuidelines: {
|
|
3493
|
+
tag: "quality_guidelines",
|
|
3494
|
+
content: dedent4`
|
|
3495
|
+
- Create chapters at topic shifts or clear transitions
|
|
3496
|
+
- Keep chapter titles concise and descriptive`
|
|
3497
|
+
},
|
|
3498
|
+
titleGuidelines: {
|
|
3499
|
+
tag: "title_guidelines",
|
|
3500
|
+
content: dedent4`
|
|
3501
|
+
- Keep titles concise and descriptive
|
|
3502
|
+
- Avoid filler or generic labels like "Chapter 1"
|
|
3503
|
+
${terminologySource === "transcript" ? "- Use the transcript's terminology" : "- Use terminology from the provided evidence"}`
|
|
3504
|
+
}
|
|
3505
|
+
};
|
|
3506
|
+
}
|
|
3507
|
+
|
|
3508
|
+
// src/workflows/chapters.ts
|
|
3485
3509
|
var chapterSchema = z5.object({
|
|
3486
3510
|
startTime: z5.number(),
|
|
3487
3511
|
title: z5.string().max(300)
|
|
@@ -3539,6 +3563,7 @@ async function generateChaptersWithAI({
|
|
|
3539
3563
|
unexpectedChapterKeys
|
|
3540
3564
|
};
|
|
3541
3565
|
}
|
|
3566
|
+
var chapterGuidance = createChapterGuidance();
|
|
3542
3567
|
var chapterSystemPromptBuilder = createPromptBuilder({
|
|
3543
3568
|
template: {
|
|
3544
3569
|
role: {
|
|
@@ -3547,13 +3572,13 @@ var chapterSystemPromptBuilder = createPromptBuilder({
|
|
|
3547
3572
|
},
|
|
3548
3573
|
context: {
|
|
3549
3574
|
tag: "context",
|
|
3550
|
-
content:
|
|
3575
|
+
content: dedent5`
|
|
3551
3576
|
You receive a timestamped transcript with lines in the form "[12s] Caption text".
|
|
3552
3577
|
Use those timestamps as anchors to determine chapter start times in seconds.`
|
|
3553
3578
|
},
|
|
3554
3579
|
security: {
|
|
3555
3580
|
tag: "security",
|
|
3556
|
-
content:
|
|
3581
|
+
content: dedent5`
|
|
3557
3582
|
${NON_DISCLOSURE_CONSTRAINT}
|
|
3558
3583
|
|
|
3559
3584
|
${UNTRUSTED_USER_INPUT_NOTICE}
|
|
@@ -3562,34 +3587,32 @@ var chapterSystemPromptBuilder = createPromptBuilder({
|
|
|
3562
3587
|
},
|
|
3563
3588
|
constraints: {
|
|
3564
3589
|
tag: "constraints",
|
|
3565
|
-
content:
|
|
3590
|
+
content: dedent5`
|
|
3566
3591
|
- Only use information present in the transcript
|
|
3567
3592
|
- Return structured data that matches the requested JSON schema
|
|
3568
3593
|
- Do not add commentary or extra text outside the JSON
|
|
3569
3594
|
- When a <language> section is provided, all chapter titles MUST be written in that language`
|
|
3570
3595
|
},
|
|
3571
3596
|
qualityGuidelines: {
|
|
3572
|
-
|
|
3573
|
-
content:
|
|
3574
|
-
|
|
3575
|
-
- Keep chapter titles concise and descriptive
|
|
3576
|
-
- Ensure the first chapter starts at 0 seconds`
|
|
3597
|
+
...chapterGuidance.qualityGuidelines,
|
|
3598
|
+
content: `${chapterGuidance.qualityGuidelines.content}
|
|
3599
|
+
- Ensure the first chapter starts at 0 seconds`
|
|
3577
3600
|
}
|
|
3578
3601
|
},
|
|
3579
3602
|
sectionOrder: ["role", "context", "security", "constraints", "qualityGuidelines"]
|
|
3580
3603
|
});
|
|
3581
3604
|
var AUDIO_ONLY_SYSTEM_PROMPT_OVERRIDES = {
|
|
3582
3605
|
role: "You are an audio editor and transcript analyst specializing in segmenting content into logical chapters.",
|
|
3583
|
-
context:
|
|
3606
|
+
context: dedent5`
|
|
3584
3607
|
You receive a timestamped transcript from audio-only content with lines in the form "[12s] Transcript text".
|
|
3585
3608
|
Use those timestamps as anchors to determine chapter start times in seconds.`
|
|
3586
3609
|
};
|
|
3587
3610
|
var SCENE_CONTEXT_SYSTEM_PROMPT_OVERRIDES = {
|
|
3588
|
-
context:
|
|
3611
|
+
context: dedent5`
|
|
3589
3612
|
You receive a timestamped transcript with lines in the form "[12s] Caption text" and an ordered scene context.
|
|
3590
3613
|
Transcript timestamps are in seconds. Scene start and end timestamps are in milliseconds.
|
|
3591
3614
|
Use the scene progression to choose logical chapter groupings and scene starts as chapter boundaries.`,
|
|
3592
|
-
constraints:
|
|
3615
|
+
constraints: dedent5`
|
|
3593
3616
|
- Only use information present in the transcript and scene context
|
|
3594
3617
|
- Return structured data that matches the requested JSON schema
|
|
3595
3618
|
- Do not add commentary or extra text outside the JSON
|
|
@@ -3603,7 +3626,7 @@ var chaptersPromptBuilder = createPromptBuilder({
|
|
|
3603
3626
|
},
|
|
3604
3627
|
outputFormat: {
|
|
3605
3628
|
tag: "output_format",
|
|
3606
|
-
content:
|
|
3629
|
+
content: dedent5`
|
|
3607
3630
|
Return valid JSON in this exact shape:
|
|
3608
3631
|
{
|
|
3609
3632
|
"chapters": [
|
|
@@ -3620,13 +3643,7 @@ var chaptersPromptBuilder = createPromptBuilder({
|
|
|
3620
3643
|
content: ""
|
|
3621
3644
|
// Placeholder - always overridden with dynamic content
|
|
3622
3645
|
},
|
|
3623
|
-
titleGuidelines:
|
|
3624
|
-
tag: "title_guidelines",
|
|
3625
|
-
content: dedent4`
|
|
3626
|
-
- Keep titles concise and descriptive
|
|
3627
|
-
- Avoid filler or generic labels like "Chapter 1"
|
|
3628
|
-
- Use the transcript's terminology`
|
|
3629
|
-
}
|
|
3646
|
+
titleGuidelines: chapterGuidance.titleGuidelines
|
|
3630
3647
|
},
|
|
3631
3648
|
sectionOrder: ["task", "outputFormat", "chapterGuidelines", "titleGuidelines"]
|
|
3632
3649
|
});
|
|
@@ -3665,7 +3682,7 @@ function buildUserPrompt3({
|
|
|
3665
3682
|
} else if (scope) {
|
|
3666
3683
|
firstChapterGuideline = `- The first chapter must start at ${scope.startTime}s`;
|
|
3667
3684
|
}
|
|
3668
|
-
const dynamicChapterGuidelines =
|
|
3685
|
+
const dynamicChapterGuidelines = dedent5`
|
|
3669
3686
|
- Create at least ${minChaptersPerHour} and at most ${maxChaptersPerHour} chapters per hour of content
|
|
3670
3687
|
- Use start times in seconds (not HH:MM:SS)
|
|
3671
3688
|
${scope ? `- Analyze only the range from ${scope.startTime}s (inclusive) to ${scope.endTime}s (exclusive)` : ""}
|
|
@@ -5323,7 +5340,7 @@ async function generateVideoEmbeddings(assetId, options = {}) {
|
|
|
5323
5340
|
|
|
5324
5341
|
// src/workflows/engagement-insights.ts
|
|
5325
5342
|
import { generateText as generateText5, Output as Output5 } from "ai";
|
|
5326
|
-
import
|
|
5343
|
+
import dedent6 from "dedent";
|
|
5327
5344
|
import { z as z7 } from "zod";
|
|
5328
5345
|
|
|
5329
5346
|
// src/primitives/heatmap.ts
|
|
@@ -5592,7 +5609,7 @@ var AUDIO_ONLY_SYSTEM_PROMPT2 = promptDedent`
|
|
|
5592
5609
|
- Connect engagement data to content features
|
|
5593
5610
|
</quality_guidelines>
|
|
5594
5611
|
`;
|
|
5595
|
-
var INSIGHT_GUIDELINES =
|
|
5612
|
+
var INSIGHT_GUIDELINES = dedent6`
|
|
5596
5613
|
Provide explanatory insights that describe WHY moments are engaging.
|
|
5597
5614
|
Focus on observable patterns and viewer behavior correlation.
|
|
5598
5615
|
Example: "The cooking demonstration at 2:15 has 3x average engagement
|
|
@@ -5610,7 +5627,7 @@ var engagementInsightsPromptBuilder = createPromptBuilder({
|
|
|
5610
5627
|
},
|
|
5611
5628
|
outputFormat: {
|
|
5612
5629
|
tag: "output_format",
|
|
5613
|
-
content:
|
|
5630
|
+
content: dedent6`
|
|
5614
5631
|
Return valid JSON matching the provided schema.
|
|
5615
5632
|
Include insights for each hotspot and overall engagement trends.
|
|
5616
5633
|
Each momentInsight must include the hotspotIndex matching the input.
|
|
@@ -5618,7 +5635,7 @@ var engagementInsightsPromptBuilder = createPromptBuilder({
|
|
|
5618
5635
|
},
|
|
5619
5636
|
visualContext: {
|
|
5620
5637
|
tag: "visual_context",
|
|
5621
|
-
content:
|
|
5638
|
+
content: dedent6`
|
|
5622
5639
|
Images are provided for each hotspot timestamp.
|
|
5623
5640
|
A storyboard overview image may also be included showing the full video timeline.
|
|
5624
5641
|
Use visual evidence from these images to support your engagement analysis.
|
|
@@ -5632,14 +5649,14 @@ function buildUserPrompt4(hotspots, transcriptSegments, heatmap, isAudioOnly) {
|
|
|
5632
5649
|
const transcript = transcriptSegments[idx]?.text || "(no transcript)";
|
|
5633
5650
|
const startTimestamp = secondsToTimestamp(h.startMs / 1e3);
|
|
5634
5651
|
const endTimestamp = secondsToTimestamp(h.endMs / 1e3);
|
|
5635
|
-
return
|
|
5652
|
+
return dedent6`
|
|
5636
5653
|
Hotspot ${idx} (hotspotIndex: ${idx}):
|
|
5637
5654
|
- Time Range: ${startTimestamp} - ${endTimestamp}
|
|
5638
5655
|
- Engagement Score: ${h.score.toFixed(2)} (0=low, 1=high)
|
|
5639
5656
|
- Transcript: ${JSON.stringify(transcript)}
|
|
5640
5657
|
`;
|
|
5641
5658
|
}).join("\n\n");
|
|
5642
|
-
const heatmapData =
|
|
5659
|
+
const heatmapData = dedent6`
|
|
5643
5660
|
Engagement Heatmap (${heatmap.length} values, each representing 1/${heatmap.length}th of the video, higher = more re-watches):
|
|
5644
5661
|
[${heatmap.map((v) => v.toFixed(2)).join(", ")}]
|
|
5645
5662
|
`;
|
|
@@ -7458,7 +7475,6 @@ async function getModerationScores(assetId, options = {}) {
|
|
|
7458
7475
|
|
|
7459
7476
|
// src/workflows/summarization.ts
|
|
7460
7477
|
import { generateText as generateText7, Output as Output7 } from "ai";
|
|
7461
|
-
import dedent6 from "dedent";
|
|
7462
7478
|
import { z as z9 } from "zod";
|
|
7463
7479
|
|
|
7464
7480
|
// src/lib/markdown-normalization.ts
|
|
@@ -7468,124 +7484,128 @@ function normalizeMarkdownDescription(description) {
|
|
|
7468
7484
|
return description.replace(ESCAPED_LINE_BREAK_REGEX, "\n").replace(/([^\n]) {2,}([*-]) (?=\S)/g, "$1\n$2 ").replace(/([.!?:;]) ([*-]) (?=\S)/g, "$1\n$2 ").replace(MULTIPLE_BLANK_LINES_REGEX, "\n\n");
|
|
7469
7485
|
}
|
|
7470
7486
|
|
|
7471
|
-
// src/
|
|
7487
|
+
// src/prompts/summarization.ts
|
|
7488
|
+
import dedent7 from "dedent";
|
|
7472
7489
|
var DEFAULT_SUMMARY_KEYWORD_LIMIT = 10;
|
|
7473
7490
|
var DEFAULT_TITLE_LENGTH = 10;
|
|
7474
7491
|
var DEFAULT_DESCRIPTION_LENGTH = 50;
|
|
7475
|
-
var summarySchema = z9.object({
|
|
7476
|
-
keywords: z9.array(z9.string().max(200)),
|
|
7477
|
-
title: z9.string().max(500),
|
|
7478
|
-
description: z9.string().max(1e4)
|
|
7479
|
-
});
|
|
7480
|
-
function buildSummarySchema(descriptionLength) {
|
|
7481
|
-
const descriptionMaxChars = Math.max(2e3, descriptionLength * 15);
|
|
7482
|
-
return z9.object({
|
|
7483
|
-
keywords: z9.array(z9.string().max(200)),
|
|
7484
|
-
title: z9.string().max(500),
|
|
7485
|
-
description: z9.string().max(descriptionMaxChars)
|
|
7486
|
-
});
|
|
7487
|
-
}
|
|
7488
|
-
var VALID_TONES = ["neutral", "playful", "professional"];
|
|
7489
|
-
var TONE_INSTRUCTIONS = {
|
|
7490
|
-
neutral: "Provide a clear, straightforward analysis.",
|
|
7491
|
-
playful: "Channel your inner diva! Answer with maximum sass, wit, and playful attitude. Don't hold back - be cheeky, clever, and delightfully snarky. Make it pop!",
|
|
7492
|
-
professional: "Provide a professional, executive-level analysis suitable for business reporting."
|
|
7493
|
-
};
|
|
7494
7492
|
var DESCRIPTION_LENGTH_THRESHOLD_SMALL = 25;
|
|
7495
7493
|
var DESCRIPTION_LENGTH_THRESHOLD_LARGE = 100;
|
|
7496
7494
|
function buildDescriptionGuidance(wordCount, contentType) {
|
|
7497
7495
|
if (wordCount < DESCRIPTION_LENGTH_THRESHOLD_SMALL) {
|
|
7498
7496
|
if (contentType === "video") {
|
|
7499
|
-
return
|
|
7497
|
+
return dedent7`A brief summary of the video in no more than ${wordCount} words. Shorter is fine.
|
|
7500
7498
|
Focus on the single most important subject or action.
|
|
7501
7499
|
Write in present tense.`;
|
|
7502
7500
|
}
|
|
7503
|
-
return
|
|
7501
|
+
return dedent7`A brief summary of the audio content in no more than ${wordCount} words. Shorter is fine.
|
|
7504
7502
|
Focus on the single most important topic or theme.
|
|
7505
7503
|
Write in present tense.`;
|
|
7506
7504
|
}
|
|
7507
7505
|
if (wordCount > DESCRIPTION_LENGTH_THRESHOLD_LARGE) {
|
|
7508
7506
|
if (contentType === "video") {
|
|
7509
|
-
return
|
|
7507
|
+
return dedent7`A detailed summary that describes what happens across the video.
|
|
7510
7508
|
Never exceed ${wordCount} words, but shorter is perfectly fine. You may use multiple sentences.
|
|
7511
7509
|
Be thorough: cover subjects, actions, setting, progression, and any notable details visible across frames.
|
|
7512
7510
|
Write in present tense. Be specific about observable details rather than making assumptions.
|
|
7513
7511
|
If the transcript provides dialogue or narration, incorporate key points but prioritize visual content.`;
|
|
7514
7512
|
}
|
|
7515
|
-
return
|
|
7513
|
+
return dedent7`A detailed summary that describes the audio content.
|
|
7516
7514
|
Never exceed ${wordCount} words, but shorter is perfectly fine. You may use multiple sentences.
|
|
7517
7515
|
Be thorough: cover topics, speakers, themes, progression, and any notable insights.
|
|
7518
7516
|
Write in present tense. Be specific about what is discussed or presented rather than making assumptions.
|
|
7519
7517
|
Focus on the spoken content and any key insights, dialogue, or narrative elements.`;
|
|
7520
7518
|
}
|
|
7521
7519
|
if (contentType === "video") {
|
|
7522
|
-
return
|
|
7520
|
+
return dedent7`A summary that describes what happens across the video.
|
|
7523
7521
|
Never exceed ${wordCount} words, but shorter is perfectly fine. You may use multiple sentences.
|
|
7524
7522
|
Cover the main subjects, actions, setting, and any notable progression visible across frames.
|
|
7525
7523
|
Write in present tense. Be specific about observable details rather than making assumptions.
|
|
7526
7524
|
If the transcript provides dialogue or narration, incorporate key points but prioritize visual content.`;
|
|
7527
7525
|
}
|
|
7528
|
-
return
|
|
7526
|
+
return dedent7`A summary that describes the audio content.
|
|
7529
7527
|
Never exceed ${wordCount} words, but shorter is perfectly fine. You may use multiple sentences.
|
|
7530
7528
|
Cover the main topics, speakers, themes, and any notable progression in the discussion or narration.
|
|
7531
7529
|
Write in present tense. Be specific about what is discussed or presented rather than making assumptions.
|
|
7532
7530
|
Focus on the spoken content and any key insights, dialogue, or narrative elements.`;
|
|
7533
7531
|
}
|
|
7534
|
-
function buildSceneAwareDescriptionGuidance(wordCount) {
|
|
7532
|
+
function buildSceneAwareDescriptionGuidance(wordCount, hasStoryboard) {
|
|
7533
|
+
const storyboardGuidance = hasStoryboard ? "Use the storyboard as direct visual evidence and the scene context to preserve progression." : "Use the ordered scene context as grounded evidence of the content and its progression.";
|
|
7535
7534
|
if (wordCount < DESCRIPTION_LENGTH_THRESHOLD_SMALL) {
|
|
7536
|
-
return
|
|
7535
|
+
return dedent7`A brief summary of the video in no more than ${wordCount} words. Shorter is fine.
|
|
7537
7536
|
Focus on the single most important subject or action across the ordered scenes.
|
|
7538
|
-
|
|
7537
|
+
${storyboardGuidance}
|
|
7539
7538
|
Write in present tense.`;
|
|
7540
7539
|
}
|
|
7541
7540
|
if (wordCount > DESCRIPTION_LENGTH_THRESHOLD_LARGE) {
|
|
7542
|
-
return
|
|
7541
|
+
return dedent7`A detailed summary that describes what happens across the ordered scenes.
|
|
7543
7542
|
Never exceed ${wordCount} words, but shorter is perfectly fine. You may use multiple sentences.
|
|
7544
7543
|
Be thorough: cover subjects, actions, setting, progression, and notable details supported across scenes.
|
|
7545
|
-
|
|
7544
|
+
${storyboardGuidance}
|
|
7546
7545
|
Write in present tense. If the transcript provides dialogue or narration, incorporate key points while preserving scene progression.`;
|
|
7547
7546
|
}
|
|
7548
|
-
return
|
|
7547
|
+
return dedent7`A summary that describes what happens across the ordered scenes.
|
|
7549
7548
|
Never exceed ${wordCount} words, but shorter is perfectly fine. You may use multiple sentences.
|
|
7550
7549
|
Cover the main subjects, actions, setting, and notable progression supported across scenes.
|
|
7551
|
-
|
|
7550
|
+
${storyboardGuidance}
|
|
7552
7551
|
Write in present tense. If the transcript provides dialogue or narration, incorporate key points while preserving scene progression.`;
|
|
7553
7552
|
}
|
|
7554
|
-
function
|
|
7553
|
+
function createSummarizationGuidance({
|
|
7554
|
+
mediaType = "video",
|
|
7555
|
+
titleLength,
|
|
7556
|
+
descriptionLength,
|
|
7557
|
+
tagCount,
|
|
7558
|
+
hasSceneContext = false,
|
|
7559
|
+
hasStoryboard = true
|
|
7560
|
+
} = {}) {
|
|
7555
7561
|
const titleLimit = titleLength ?? DEFAULT_TITLE_LENGTH;
|
|
7562
|
+
const descriptionLimit = descriptionLength ?? DEFAULT_DESCRIPTION_LENGTH;
|
|
7556
7563
|
const keywordLimit = tagCount ?? DEFAULT_SUMMARY_KEYWORD_LIMIT;
|
|
7557
|
-
const
|
|
7558
|
-
const
|
|
7559
|
-
|
|
7560
|
-
-
|
|
7561
|
-
-
|
|
7562
|
-
-
|
|
7563
|
-
-
|
|
7564
|
-
|
|
7565
|
-
|
|
7566
|
-
|
|
7567
|
-
|
|
7568
|
-
|
|
7569
|
-
|
|
7570
|
-
|
|
7571
|
-
|
|
7572
|
-
|
|
7573
|
-
|
|
7574
|
-
|
|
7575
|
-
|
|
7564
|
+
const isAudioOnly = mediaType === "audio";
|
|
7565
|
+
const sceneAware = !isAudioOnly && hasSceneContext;
|
|
7566
|
+
const qualityGuidelines = isAudioOnly ? dedent7`
|
|
7567
|
+
- Analyze the full transcript to understand context and themes
|
|
7568
|
+
- Be precise: use specific terminology when mentioned
|
|
7569
|
+
- Capture the narrative: what is introduced, discussed, and concluded
|
|
7570
|
+
- Balance brevity with informativeness` : sceneAware ? dedent7`
|
|
7571
|
+
- Follow scene_index order to understand what begins, develops, and concludes
|
|
7572
|
+
- Use scene narratives and concepts as grounded evidence, preserving consistent terminology across the output
|
|
7573
|
+
${hasStoryboard ? "- Use the storyboard as direct visual evidence for whole-asset details" : "- Ground whole-asset details in the provided scene context and transcript when available"}
|
|
7574
|
+
- Balance brevity with informativeness` : dedent7`
|
|
7575
|
+
- Examine all frames to understand the full context and progression
|
|
7576
|
+
- Be precise: "golden retriever" is better than "dog" when identifiable
|
|
7577
|
+
- Capture the narrative: what begins, develops, and concludes
|
|
7578
|
+
- Balance brevity with informativeness`;
|
|
7579
|
+
return {
|
|
7580
|
+
title: {
|
|
7581
|
+
tag: "title_requirements",
|
|
7582
|
+
content: isAudioOnly ? dedent7`
|
|
7583
|
+
A concise, label-style title — not a sentence or description.
|
|
7584
|
+
Never exceed ${titleLimit} words, but shorter is better.
|
|
7585
|
+
Think of how a podcast episode title or playlist entry would read — e.g. "Weekly News Roundup" or "Interview with Dr. Smith".
|
|
7586
|
+
Start with the primary subject or topic. Never begin with "An audio of" or similar phrasing.
|
|
7587
|
+
Use specific nouns over lengthy descriptions. Avoid clauses, conjunctions, or narrative structure.` : dedent7`
|
|
7576
7588
|
A concise, label-style title — not a sentence or description.
|
|
7577
7589
|
Never exceed ${titleLimit} words, but shorter is better.
|
|
7578
7590
|
Think of how a video card title, playlist entry, or file name would read — e.g. "Predator: Badlands Trailer" or "Chef Prepares Holiday Feast".
|
|
7579
7591
|
Start with the primary subject or topic. Never begin with "A video of" or similar phrasing.
|
|
7580
7592
|
Use specific nouns over lengthy descriptions. Avoid clauses, conjunctions, or narrative structure.`
|
|
7581
|
-
|
|
7582
|
-
|
|
7583
|
-
|
|
7584
|
-
|
|
7585
|
-
|
|
7586
|
-
|
|
7587
|
-
|
|
7588
|
-
|
|
7593
|
+
},
|
|
7594
|
+
description: {
|
|
7595
|
+
tag: "description_requirements",
|
|
7596
|
+
content: sceneAware ? buildSceneAwareDescriptionGuidance(descriptionLimit, hasStoryboard) : buildDescriptionGuidance(descriptionLimit, mediaType)
|
|
7597
|
+
},
|
|
7598
|
+
keywords: {
|
|
7599
|
+
tag: "keywords_requirements",
|
|
7600
|
+
content: isAudioOnly ? dedent7`
|
|
7601
|
+
Specific, searchable terms (up to ${keywordLimit}) that capture:
|
|
7602
|
+
- Primary topics and themes
|
|
7603
|
+
- Speakers or presenters (if named)
|
|
7604
|
+
- Key concepts and terminology
|
|
7605
|
+
- Content type (interview, lecture, music, etc.)
|
|
7606
|
+
- Genre or style (if applicable)
|
|
7607
|
+
Prefer concrete nouns and relevant terms over abstract concepts.
|
|
7608
|
+
Use lowercase. Avoid redundant or overly generic terms like "audio" or "content".` : dedent7`
|
|
7589
7609
|
Specific, searchable terms (up to ${keywordLimit}) that capture:
|
|
7590
7610
|
- Primary subjects (people, animals, objects)
|
|
7591
7611
|
- Actions and activities being performed
|
|
@@ -7594,57 +7614,51 @@ function createSummarizationBuilder({ titleLength, descriptionLength, tagCount }
|
|
|
7594
7614
|
- Style or genre (if applicable)
|
|
7595
7615
|
Prefer concrete nouns and action verbs over abstract concepts.
|
|
7596
7616
|
Use lowercase. Avoid redundant or overly generic terms like "video" or "content".`
|
|
7617
|
+
},
|
|
7618
|
+
qualityGuidelines: { tag: "quality_guidelines", content: qualityGuidelines }
|
|
7619
|
+
};
|
|
7620
|
+
}
|
|
7621
|
+
|
|
7622
|
+
// src/workflows/summarization.ts
|
|
7623
|
+
var summarySchema = z9.object({
|
|
7624
|
+
keywords: z9.array(z9.string().max(200)),
|
|
7625
|
+
title: z9.string().max(500),
|
|
7626
|
+
description: z9.string().max(1e4)
|
|
7627
|
+
});
|
|
7628
|
+
function buildSummarySchema(descriptionLength) {
|
|
7629
|
+
const descriptionMaxChars = Math.max(2e3, descriptionLength * 15);
|
|
7630
|
+
return z9.object({
|
|
7631
|
+
keywords: z9.array(z9.string().max(200)),
|
|
7632
|
+
title: z9.string().max(500),
|
|
7633
|
+
description: z9.string().max(descriptionMaxChars)
|
|
7634
|
+
});
|
|
7635
|
+
}
|
|
7636
|
+
var VALID_TONES = ["neutral", "playful", "professional"];
|
|
7637
|
+
var TONE_INSTRUCTIONS = {
|
|
7638
|
+
neutral: "Provide a clear, straightforward analysis.",
|
|
7639
|
+
playful: "Channel your inner diva! Answer with maximum sass, wit, and playful attitude. Don't hold back - be cheeky, clever, and delightfully snarky. Make it pop!",
|
|
7640
|
+
professional: "Provide a professional, executive-level analysis suitable for business reporting."
|
|
7641
|
+
};
|
|
7642
|
+
function createSummarizationBuilder(constraints = {}, { hasSceneContext = false } = {}) {
|
|
7643
|
+
return createPromptBuilder({
|
|
7644
|
+
template: {
|
|
7645
|
+
task: {
|
|
7646
|
+
tag: "task",
|
|
7647
|
+
content: 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."
|
|
7597
7648
|
},
|
|
7598
|
-
|
|
7599
|
-
tag: "quality_guidelines",
|
|
7600
|
-
content: qualityGuidelines
|
|
7601
|
-
}
|
|
7649
|
+
...createSummarizationGuidance({ ...constraints, hasSceneContext })
|
|
7602
7650
|
},
|
|
7603
7651
|
sectionOrder: ["task", "title", "description", "keywords", "qualityGuidelines"]
|
|
7604
7652
|
});
|
|
7605
7653
|
}
|
|
7606
|
-
function createAudioOnlyBuilder(
|
|
7607
|
-
const titleLimit = titleLength ?? DEFAULT_TITLE_LENGTH;
|
|
7608
|
-
const keywordLimit = tagCount ?? DEFAULT_SUMMARY_KEYWORD_LIMIT;
|
|
7654
|
+
function createAudioOnlyBuilder(constraints = {}) {
|
|
7609
7655
|
return createPromptBuilder({
|
|
7610
7656
|
template: {
|
|
7611
7657
|
task: {
|
|
7612
7658
|
tag: "task",
|
|
7613
7659
|
content: "Analyze the transcript and generate metadata that captures the essence of the audio content."
|
|
7614
7660
|
},
|
|
7615
|
-
|
|
7616
|
-
tag: "title_requirements",
|
|
7617
|
-
content: dedent6`
|
|
7618
|
-
A concise, label-style title — not a sentence or description.
|
|
7619
|
-
Never exceed ${titleLimit} words, but shorter is better.
|
|
7620
|
-
Think of how a podcast episode title or playlist entry would read — e.g. "Weekly News Roundup" or "Interview with Dr. Smith".
|
|
7621
|
-
Start with the primary subject or topic. Never begin with "An audio of" or similar phrasing.
|
|
7622
|
-
Use specific nouns over lengthy descriptions. Avoid clauses, conjunctions, or narrative structure.`
|
|
7623
|
-
},
|
|
7624
|
-
description: {
|
|
7625
|
-
tag: "description_requirements",
|
|
7626
|
-
content: buildDescriptionGuidance(descriptionLength ?? DEFAULT_DESCRIPTION_LENGTH, "audio")
|
|
7627
|
-
},
|
|
7628
|
-
keywords: {
|
|
7629
|
-
tag: "keywords_requirements",
|
|
7630
|
-
content: dedent6`
|
|
7631
|
-
Specific, searchable terms (up to ${keywordLimit}) that capture:
|
|
7632
|
-
- Primary topics and themes
|
|
7633
|
-
- Speakers or presenters (if named)
|
|
7634
|
-
- Key concepts and terminology
|
|
7635
|
-
- Content type (interview, lecture, music, etc.)
|
|
7636
|
-
- Genre or style (if applicable)
|
|
7637
|
-
Prefer concrete nouns and relevant terms over abstract concepts.
|
|
7638
|
-
Use lowercase. Avoid redundant or overly generic terms like "audio" or "content".`
|
|
7639
|
-
},
|
|
7640
|
-
qualityGuidelines: {
|
|
7641
|
-
tag: "quality_guidelines",
|
|
7642
|
-
content: dedent6`
|
|
7643
|
-
- Analyze the full transcript to understand context and themes
|
|
7644
|
-
- Be precise: use specific terminology when mentioned
|
|
7645
|
-
- Capture the narrative: what is introduced, discussed, and concluded
|
|
7646
|
-
- Balance brevity with informativeness`
|
|
7647
|
-
}
|
|
7661
|
+
...createSummarizationGuidance({ ...constraints, mediaType: "audio" })
|
|
7648
7662
|
},
|
|
7649
7663
|
sectionOrder: ["task", "title", "description", "keywords", "qualityGuidelines"]
|
|
7650
7664
|
});
|