@mux/ai 0.7.6 → 0.8.1
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-B0U9upb4.d.ts → index-DP02N3iR.d.ts} +6 -0
- package/dist/index.d.ts +2 -2
- package/dist/index.js +181 -118
- package/dist/index.js.map +1 -1
- package/dist/primitives/index.js +38 -1
- package/dist/primitives/index.js.map +1 -1
- package/dist/workflows/index.d.ts +1 -1
- package/dist/workflows/index.js +180 -117
- package/dist/workflows/index.js.map +1 -1
- package/package.json +1 -1
|
@@ -523,6 +523,12 @@ interface SummarizationOptions extends MuxAIOptions {
|
|
|
523
523
|
* Useful for customizing the AI's output for specific use cases (SEO, social media, etc.)
|
|
524
524
|
*/
|
|
525
525
|
promptOverrides?: SummarizationPromptOverrides;
|
|
526
|
+
/** Desired title length in characters. */
|
|
527
|
+
titleLength?: number;
|
|
528
|
+
/** Desired description length in characters. */
|
|
529
|
+
descriptionLength?: number;
|
|
530
|
+
/** Desired number of tags. */
|
|
531
|
+
tagCount?: number;
|
|
526
532
|
}
|
|
527
533
|
declare function getSummaryAndTags(assetId: string, options?: SummarizationOptions): Promise<SummaryAndTagsResult>;
|
|
528
534
|
|
package/dist/index.d.ts
CHANGED
|
@@ -2,14 +2,14 @@ import { W as WorkflowCredentials, S as StoragePutObjectInput, a as StoragePresi
|
|
|
2
2
|
export { A as AssetTextTrack, C as ChunkEmbedding, b as ChunkingStrategy, E as Encrypted, c as EncryptedPayload, I as ImageSubmissionMode, M as MuxAIOptions, d as MuxAsset, P as PlaybackAsset, e as PlaybackPolicy, f as StorageAdapter, T as TextChunk, g as TokenChunkingConfig, h as TokenUsage, i as ToneType, U as UsageMetadata, V as VTTChunkingConfig, j as VideoEmbeddingsResult, k as WorkflowCredentialsInput, l as WorkflowMuxClient, m as decryptFromWorkflow, n as encryptForWorkflow } from './types-BRbaGW3t.js';
|
|
3
3
|
import { WORKFLOW_SERIALIZE, WORKFLOW_DESERIALIZE } from '@workflow/serde';
|
|
4
4
|
export { i as primitives } from './index-Nxf6BaBO.js';
|
|
5
|
-
export { i as workflows } from './index-
|
|
5
|
+
export { i as workflows } from './index-DP02N3iR.js';
|
|
6
6
|
import '@mux/mux-node';
|
|
7
7
|
import 'zod';
|
|
8
8
|
import '@ai-sdk/anthropic';
|
|
9
9
|
import '@ai-sdk/google';
|
|
10
10
|
import '@ai-sdk/openai';
|
|
11
11
|
|
|
12
|
-
var version = "0.
|
|
12
|
+
var version = "0.8.1";
|
|
13
13
|
|
|
14
14
|
/**
|
|
15
15
|
* A function that returns workflow credentials, either synchronously or asynchronously.
|
package/dist/index.js
CHANGED
|
@@ -5,7 +5,7 @@ var __export = (target, all) => {
|
|
|
5
5
|
};
|
|
6
6
|
|
|
7
7
|
// package.json
|
|
8
|
-
var version = "0.
|
|
8
|
+
var version = "0.8.1";
|
|
9
9
|
|
|
10
10
|
// src/env.ts
|
|
11
11
|
import { z } from "zod";
|
|
@@ -821,8 +821,44 @@ var DEFAULT_EMBEDDING_MODELS = {
|
|
|
821
821
|
var LANGUAGE_MODELS = {
|
|
822
822
|
openai: ["gpt-5.1", "gpt-5-mini"],
|
|
823
823
|
anthropic: ["claude-sonnet-4-5"],
|
|
824
|
-
google: ["gemini-3-flash-preview", "gemini-2.5-flash"]
|
|
824
|
+
google: ["gemini-3-flash-preview", "gemini-3.1-flash-lite-preview", "gemini-2.5-flash"]
|
|
825
825
|
};
|
|
826
|
+
var LANGUAGE_MODEL_DEPRECATIONS = [
|
|
827
|
+
{
|
|
828
|
+
provider: "google",
|
|
829
|
+
modelId: "gemini-2.5-flash",
|
|
830
|
+
replacementModelId: "gemini-3.1-flash-lite-preview",
|
|
831
|
+
phase: "warn",
|
|
832
|
+
deprecatedOn: "2026-03-03",
|
|
833
|
+
sunsetOn: "2026-06-30",
|
|
834
|
+
reason: "Gemini 3.1 Flash-Lite Preview offers better quality/latency/cost balance in current evals."
|
|
835
|
+
}
|
|
836
|
+
];
|
|
837
|
+
var warnedDeprecatedLanguageModels = /* @__PURE__ */ new Set();
|
|
838
|
+
function getLanguageModelDeprecation(provider, modelId) {
|
|
839
|
+
return LANGUAGE_MODEL_DEPRECATIONS.find(
|
|
840
|
+
(deprecation) => deprecation.provider === provider && deprecation.modelId === modelId
|
|
841
|
+
);
|
|
842
|
+
}
|
|
843
|
+
function maybeWarnOrThrowForDeprecatedLanguageModel(provider, modelId) {
|
|
844
|
+
const deprecation = getLanguageModelDeprecation(provider, modelId);
|
|
845
|
+
if (!deprecation) {
|
|
846
|
+
return;
|
|
847
|
+
}
|
|
848
|
+
const replacementText = deprecation.replacementModelId ? ` Use replacement provider="${provider}" model="${deprecation.replacementModelId}" instead.` : "";
|
|
849
|
+
const sunsetText = deprecation.sunsetOn ? ` Planned removal date: ${deprecation.sunsetOn}.` : "";
|
|
850
|
+
const reasonText = deprecation.reason ? ` Reason: ${deprecation.reason}` : "";
|
|
851
|
+
const message = deprecation.phase === "blocked" ? `Language model is no longer supported for provider="${provider}" model="${modelId}".${replacementText}${reasonText}` : `Language model is deprecated and in a grace period for provider="${provider}" model="${modelId}".${replacementText}${sunsetText}${reasonText}`;
|
|
852
|
+
if (deprecation.phase === "blocked") {
|
|
853
|
+
throw new Error(message);
|
|
854
|
+
}
|
|
855
|
+
const warningKey = `${provider}:${modelId}`;
|
|
856
|
+
if (warnedDeprecatedLanguageModels.has(warningKey)) {
|
|
857
|
+
return;
|
|
858
|
+
}
|
|
859
|
+
warnedDeprecatedLanguageModels.add(warningKey);
|
|
860
|
+
console.warn(message);
|
|
861
|
+
}
|
|
826
862
|
function getDefaultEvalModelConfigs() {
|
|
827
863
|
return Object.entries(DEFAULT_LANGUAGE_MODELS).map(([provider, modelId]) => ({ provider, modelId }));
|
|
828
864
|
}
|
|
@@ -853,6 +889,7 @@ function parseEvalModelPair(value) {
|
|
|
853
889
|
`Unsupported eval model "${modelId}" for provider "${provider}". Supported models: ${supportedModels.join(", ")}.`
|
|
854
890
|
);
|
|
855
891
|
}
|
|
892
|
+
maybeWarnOrThrowForDeprecatedLanguageModel(provider, modelId);
|
|
856
893
|
return {
|
|
857
894
|
provider,
|
|
858
895
|
modelId
|
|
@@ -896,6 +933,7 @@ var EVAL_MODEL_CONFIGS = resolveEvalModelConfigsFromEnv();
|
|
|
896
933
|
function resolveLanguageModelConfig(options = {}) {
|
|
897
934
|
const provider = options.provider || "openai";
|
|
898
935
|
const modelId = options.model || DEFAULT_LANGUAGE_MODELS[provider];
|
|
936
|
+
maybeWarnOrThrowForDeprecatedLanguageModel(provider, modelId);
|
|
899
937
|
return { provider, modelId };
|
|
900
938
|
}
|
|
901
939
|
function resolveEmbeddingModelConfig(options = {}) {
|
|
@@ -904,6 +942,7 @@ function resolveEmbeddingModelConfig(options = {}) {
|
|
|
904
942
|
return { provider, modelId };
|
|
905
943
|
}
|
|
906
944
|
async function createLanguageModelFromConfig(provider, modelId, credentials) {
|
|
945
|
+
maybeWarnOrThrowForDeprecatedLanguageModel(provider, modelId);
|
|
907
946
|
switch (provider) {
|
|
908
947
|
case "openai": {
|
|
909
948
|
const apiKey = await resolveProviderApiKey("openai", credentials);
|
|
@@ -1798,16 +1837,6 @@ var SYSTEM_PROMPT = dedent`
|
|
|
1798
1837
|
- GOOD: "A person runs through a park"
|
|
1799
1838
|
- Be specific and evidence-based
|
|
1800
1839
|
</language_guidelines>`;
|
|
1801
|
-
function buildSystemPrompt(allowedAnswers) {
|
|
1802
|
-
const answerList = allowedAnswers.map((answer) => `"${answer}"`).join(", ");
|
|
1803
|
-
return `${SYSTEM_PROMPT}
|
|
1804
|
-
|
|
1805
|
-
${dedent`
|
|
1806
|
-
<response_options>
|
|
1807
|
-
Allowed answers: ${answerList}
|
|
1808
|
-
</response_options>
|
|
1809
|
-
`}`;
|
|
1810
|
-
}
|
|
1811
1840
|
var askQuestionsPromptBuilder = createPromptBuilder({
|
|
1812
1841
|
template: {
|
|
1813
1842
|
questions: {
|
|
@@ -1817,21 +1846,30 @@ var askQuestionsPromptBuilder = createPromptBuilder({
|
|
|
1817
1846
|
},
|
|
1818
1847
|
sectionOrder: ["questions"]
|
|
1819
1848
|
});
|
|
1820
|
-
function buildUserPrompt(questions, transcriptText, isCleanTranscript = true) {
|
|
1849
|
+
function buildUserPrompt(questions, allowedAnswers, transcriptText, isCleanTranscript = true) {
|
|
1821
1850
|
const questionsList = questions.map((q, idx) => `${idx + 1}. ${q.question}`).join("\n");
|
|
1822
1851
|
const questionsContent = dedent`
|
|
1823
1852
|
Please answer the following yes/no questions about this video:
|
|
1824
1853
|
|
|
1825
1854
|
${questionsList}`;
|
|
1855
|
+
const answerList = allowedAnswers.map((answer) => `"${answer}"`).join(", ");
|
|
1856
|
+
const responseOptions = dedent`
|
|
1857
|
+
<response_options>
|
|
1858
|
+
Allowed answers: ${answerList}
|
|
1859
|
+
</response_options>`;
|
|
1860
|
+
const questionsSection = askQuestionsPromptBuilder.build({ questions: questionsContent });
|
|
1826
1861
|
if (!transcriptText) {
|
|
1827
|
-
return
|
|
1862
|
+
return `${questionsSection}
|
|
1863
|
+
|
|
1864
|
+
${responseOptions}`;
|
|
1828
1865
|
}
|
|
1829
1866
|
const format = isCleanTranscript ? "plain text" : "WebVTT";
|
|
1830
|
-
const transcriptSection = createTranscriptSection(transcriptText, format);
|
|
1831
|
-
return
|
|
1832
|
-
|
|
1833
|
-
|
|
1834
|
-
|
|
1867
|
+
const transcriptSection = renderSection(createTranscriptSection(transcriptText, format));
|
|
1868
|
+
return `${transcriptSection}
|
|
1869
|
+
|
|
1870
|
+
${questionsSection}
|
|
1871
|
+
|
|
1872
|
+
${responseOptions}`;
|
|
1835
1873
|
}
|
|
1836
1874
|
async function fetchImageAsBase64(imageUrl, imageDownloadOptions) {
|
|
1837
1875
|
"use step";
|
|
@@ -1927,8 +1965,8 @@ async function askQuestions(assetId, questions, options) {
|
|
|
1927
1965
|
cleanTranscript,
|
|
1928
1966
|
shouldSign: policy === "signed"
|
|
1929
1967
|
})).transcriptText : "";
|
|
1930
|
-
const userPrompt = buildUserPrompt(questions, transcriptText, cleanTranscript);
|
|
1931
|
-
const systemPrompt =
|
|
1968
|
+
const userPrompt = buildUserPrompt(questions, allowedAnswers, transcriptText, cleanTranscript);
|
|
1969
|
+
const systemPrompt = SYSTEM_PROMPT;
|
|
1932
1970
|
const imageUrl = await getStoryboardUrl(
|
|
1933
1971
|
playbackId,
|
|
1934
1972
|
storyboardWidth,
|
|
@@ -2643,7 +2681,7 @@ function planSamplingTimestamps(options) {
|
|
|
2643
2681
|
|
|
2644
2682
|
// src/workflows/moderation.ts
|
|
2645
2683
|
var DEFAULT_THRESHOLDS = {
|
|
2646
|
-
sexual: 0.
|
|
2684
|
+
sexual: 0.8,
|
|
2647
2685
|
violence: 0.8
|
|
2648
2686
|
};
|
|
2649
2687
|
var DEFAULT_PROVIDER2 = "openai";
|
|
@@ -3085,96 +3123,106 @@ var TONE_INSTRUCTIONS = {
|
|
|
3085
3123
|
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!",
|
|
3086
3124
|
professional: "Provide a professional, executive-level analysis suitable for business reporting."
|
|
3087
3125
|
};
|
|
3088
|
-
|
|
3089
|
-
|
|
3090
|
-
|
|
3091
|
-
|
|
3092
|
-
|
|
3093
|
-
|
|
3094
|
-
|
|
3095
|
-
|
|
3096
|
-
|
|
3097
|
-
|
|
3098
|
-
|
|
3099
|
-
|
|
3100
|
-
|
|
3101
|
-
|
|
3102
|
-
|
|
3103
|
-
|
|
3104
|
-
|
|
3105
|
-
|
|
3106
|
-
|
|
3107
|
-
|
|
3108
|
-
|
|
3109
|
-
|
|
3110
|
-
|
|
3111
|
-
|
|
3112
|
-
|
|
3113
|
-
|
|
3114
|
-
|
|
3115
|
-
|
|
3116
|
-
|
|
3117
|
-
|
|
3118
|
-
|
|
3119
|
-
|
|
3120
|
-
|
|
3121
|
-
|
|
3122
|
-
|
|
3123
|
-
|
|
3124
|
-
|
|
3125
|
-
|
|
3126
|
-
|
|
3127
|
-
|
|
3128
|
-
|
|
3129
|
-
|
|
3130
|
-
|
|
3131
|
-
|
|
3132
|
-
|
|
3133
|
-
|
|
3134
|
-
template: {
|
|
3135
|
-
task: {
|
|
3136
|
-
tag: "task",
|
|
3137
|
-
content: "Analyze the transcript and generate metadata that captures the essence of the audio content."
|
|
3138
|
-
},
|
|
3139
|
-
title: {
|
|
3140
|
-
tag: "title_requirements",
|
|
3141
|
-
content: dedent4`
|
|
3142
|
-
A short, compelling headline that immediately communicates the subject or topic.
|
|
3143
|
-
Aim for brevity - typically under 10 words. Think of how a podcast title or audio description would read.
|
|
3144
|
-
Start with the primary subject, action, or topic - never begin with "An audio of" or similar phrasing.
|
|
3145
|
-
Use active, specific language.`
|
|
3146
|
-
},
|
|
3147
|
-
description: {
|
|
3148
|
-
tag: "description_requirements",
|
|
3149
|
-
content: dedent4`
|
|
3150
|
-
A concise summary (2-4 sentences) that describes the audio content.
|
|
3151
|
-
Cover the main topics, speakers, themes, and any notable progression in the discussion or narration.
|
|
3152
|
-
Write in present tense. Be specific about what is discussed or presented rather than making assumptions.
|
|
3153
|
-
Focus on the spoken content and any key insights, dialogue, or narrative elements.`
|
|
3126
|
+
function createSummarizationBuilder({ titleLength, descriptionLength, tagCount } = {}) {
|
|
3127
|
+
const titleBrevity = titleLength != null ? `Aim for approximately ${titleLength} characters.` : "Aim for brevity - typically under 10 words.";
|
|
3128
|
+
const descConstraint = descriptionLength != null ? `approximately ${descriptionLength} characters` : "2-4 sentences";
|
|
3129
|
+
const keywordLimit = tagCount ?? SUMMARY_KEYWORD_LIMIT;
|
|
3130
|
+
return createPromptBuilder({
|
|
3131
|
+
template: {
|
|
3132
|
+
task: {
|
|
3133
|
+
tag: "task",
|
|
3134
|
+
content: "Analyze the storyboard frames and generate metadata that captures the essence of the video content."
|
|
3135
|
+
},
|
|
3136
|
+
title: {
|
|
3137
|
+
tag: "title_requirements",
|
|
3138
|
+
content: dedent4`
|
|
3139
|
+
A short, compelling headline that immediately communicates the subject or action.
|
|
3140
|
+
${titleBrevity} Think of how a news headline or video card title would read.
|
|
3141
|
+
Start with the primary subject, action, or topic - never begin with "A video of" or similar phrasing.
|
|
3142
|
+
Use active, specific language.`
|
|
3143
|
+
},
|
|
3144
|
+
description: {
|
|
3145
|
+
tag: "description_requirements",
|
|
3146
|
+
content: dedent4`
|
|
3147
|
+
A concise summary (${descConstraint}) that describes what happens across the video.
|
|
3148
|
+
Cover the main subjects, actions, setting, and any notable progression visible across frames.
|
|
3149
|
+
Write in present tense. Be specific about observable details rather than making assumptions.
|
|
3150
|
+
If the transcript provides dialogue or narration, incorporate key points but prioritize visual content.`
|
|
3151
|
+
},
|
|
3152
|
+
keywords: {
|
|
3153
|
+
tag: "keywords_requirements",
|
|
3154
|
+
content: dedent4`
|
|
3155
|
+
Specific, searchable terms (up to ${keywordLimit}) that capture:
|
|
3156
|
+
- Primary subjects (people, animals, objects)
|
|
3157
|
+
- Actions and activities being performed
|
|
3158
|
+
- Setting and environment
|
|
3159
|
+
- Notable objects or tools
|
|
3160
|
+
- Style or genre (if applicable)
|
|
3161
|
+
Prefer concrete nouns and action verbs over abstract concepts.
|
|
3162
|
+
Use lowercase. Avoid redundant or overly generic terms like "video" or "content".`
|
|
3163
|
+
},
|
|
3164
|
+
qualityGuidelines: {
|
|
3165
|
+
tag: "quality_guidelines",
|
|
3166
|
+
content: dedent4`
|
|
3167
|
+
- Examine all frames to understand the full context and progression
|
|
3168
|
+
- Be precise: "golden retriever" is better than "dog" when identifiable
|
|
3169
|
+
- Capture the narrative: what begins, develops, and concludes
|
|
3170
|
+
- Balance brevity with informativeness`
|
|
3171
|
+
}
|
|
3154
3172
|
},
|
|
3155
|
-
|
|
3156
|
-
|
|
3157
|
-
|
|
3158
|
-
|
|
3159
|
-
|
|
3160
|
-
|
|
3161
|
-
|
|
3162
|
-
|
|
3163
|
-
|
|
3164
|
-
|
|
3165
|
-
|
|
3173
|
+
sectionOrder: ["task", "title", "description", "keywords", "qualityGuidelines"]
|
|
3174
|
+
});
|
|
3175
|
+
}
|
|
3176
|
+
function createAudioOnlyBuilder({ titleLength, descriptionLength, tagCount } = {}) {
|
|
3177
|
+
const titleBrevity = titleLength != null ? `Aim for approximately ${titleLength} characters.` : "Aim for brevity - typically under 10 words.";
|
|
3178
|
+
const descConstraint = descriptionLength != null ? `approximately ${descriptionLength} characters` : "2-4 sentences";
|
|
3179
|
+
const keywordLimit = tagCount ?? SUMMARY_KEYWORD_LIMIT;
|
|
3180
|
+
return createPromptBuilder({
|
|
3181
|
+
template: {
|
|
3182
|
+
task: {
|
|
3183
|
+
tag: "task",
|
|
3184
|
+
content: "Analyze the transcript and generate metadata that captures the essence of the audio content."
|
|
3185
|
+
},
|
|
3186
|
+
title: {
|
|
3187
|
+
tag: "title_requirements",
|
|
3188
|
+
content: dedent4`
|
|
3189
|
+
A short, compelling headline that immediately communicates the subject or topic.
|
|
3190
|
+
${titleBrevity} Think of how a podcast title or audio description would read.
|
|
3191
|
+
Start with the primary subject, action, or topic - never begin with "An audio of" or similar phrasing.
|
|
3192
|
+
Use active, specific language.`
|
|
3193
|
+
},
|
|
3194
|
+
description: {
|
|
3195
|
+
tag: "description_requirements",
|
|
3196
|
+
content: dedent4`
|
|
3197
|
+
A concise summary (${descConstraint}) that describes the audio content.
|
|
3198
|
+
Cover the main topics, speakers, themes, and any notable progression in the discussion or narration.
|
|
3199
|
+
Write in present tense. Be specific about what is discussed or presented rather than making assumptions.
|
|
3200
|
+
Focus on the spoken content and any key insights, dialogue, or narrative elements.`
|
|
3201
|
+
},
|
|
3202
|
+
keywords: {
|
|
3203
|
+
tag: "keywords_requirements",
|
|
3204
|
+
content: dedent4`
|
|
3205
|
+
Specific, searchable terms (up to ${keywordLimit}) that capture:
|
|
3206
|
+
- Primary topics and themes
|
|
3207
|
+
- Speakers or presenters (if named)
|
|
3208
|
+
- Key concepts and terminology
|
|
3209
|
+
- Content type (interview, lecture, music, etc.)
|
|
3210
|
+
- Genre or style (if applicable)
|
|
3211
|
+
Prefer concrete nouns and relevant terms over abstract concepts.
|
|
3212
|
+
Use lowercase. Avoid redundant or overly generic terms like "audio" or "content".`
|
|
3213
|
+
},
|
|
3214
|
+
qualityGuidelines: {
|
|
3215
|
+
tag: "quality_guidelines",
|
|
3216
|
+
content: dedent4`
|
|
3217
|
+
- Analyze the full transcript to understand context and themes
|
|
3218
|
+
- Be precise: use specific terminology when mentioned
|
|
3219
|
+
- Capture the narrative: what is introduced, discussed, and concluded
|
|
3220
|
+
- Balance brevity with informativeness`
|
|
3221
|
+
}
|
|
3166
3222
|
},
|
|
3167
|
-
|
|
3168
|
-
|
|
3169
|
-
|
|
3170
|
-
- Analyze the full transcript to understand context and themes
|
|
3171
|
-
- Be precise: use specific terminology when mentioned
|
|
3172
|
-
- Capture the narrative: what is introduced, discussed, and concluded
|
|
3173
|
-
- Balance brevity with informativeness`
|
|
3174
|
-
}
|
|
3175
|
-
},
|
|
3176
|
-
sectionOrder: ["task", "title", "description", "keywords", "qualityGuidelines"]
|
|
3177
|
-
});
|
|
3223
|
+
sectionOrder: ["task", "title", "description", "keywords", "qualityGuidelines"]
|
|
3224
|
+
});
|
|
3225
|
+
}
|
|
3178
3226
|
var SYSTEM_PROMPT3 = dedent4`
|
|
3179
3227
|
<role>
|
|
3180
3228
|
You are a video content analyst specializing in storyboard interpretation and multimodal analysis.
|
|
@@ -3289,14 +3337,18 @@ function buildUserPrompt4({
|
|
|
3289
3337
|
transcriptText,
|
|
3290
3338
|
isCleanTranscript = true,
|
|
3291
3339
|
promptOverrides,
|
|
3292
|
-
isAudioOnly = false
|
|
3340
|
+
isAudioOnly = false,
|
|
3341
|
+
titleLength,
|
|
3342
|
+
descriptionLength,
|
|
3343
|
+
tagCount
|
|
3293
3344
|
}) {
|
|
3294
3345
|
const contextSections = [createToneSection(TONE_INSTRUCTIONS[tone])];
|
|
3295
3346
|
if (transcriptText) {
|
|
3296
3347
|
const format = isCleanTranscript ? "plain text" : "WebVTT";
|
|
3297
3348
|
contextSections.push(createTranscriptSection(transcriptText, format));
|
|
3298
3349
|
}
|
|
3299
|
-
const
|
|
3350
|
+
const constraints = { titleLength, descriptionLength, tagCount };
|
|
3351
|
+
const promptBuilder = isAudioOnly ? createAudioOnlyBuilder(constraints) : createSummarizationBuilder(constraints);
|
|
3300
3352
|
return promptBuilder.buildWithContext(promptOverrides, contextSections);
|
|
3301
3353
|
}
|
|
3302
3354
|
async function analyzeStoryboard2(imageDataUrl, provider, modelId, userPrompt, systemPrompt, credentials) {
|
|
@@ -3366,7 +3418,7 @@ async function analyzeAudioOnly(provider, modelId, userPrompt, systemPrompt, cre
|
|
|
3366
3418
|
}
|
|
3367
3419
|
};
|
|
3368
3420
|
}
|
|
3369
|
-
function normalizeKeywords(keywords) {
|
|
3421
|
+
function normalizeKeywords(keywords, limit = SUMMARY_KEYWORD_LIMIT) {
|
|
3370
3422
|
if (!Array.isArray(keywords) || keywords.length === 0) {
|
|
3371
3423
|
return [];
|
|
3372
3424
|
}
|
|
@@ -3383,7 +3435,7 @@ function normalizeKeywords(keywords) {
|
|
|
3383
3435
|
}
|
|
3384
3436
|
uniqueLowercase.add(lower);
|
|
3385
3437
|
normalized.push(trimmed);
|
|
3386
|
-
if (normalized.length ===
|
|
3438
|
+
if (normalized.length === limit) {
|
|
3387
3439
|
break;
|
|
3388
3440
|
}
|
|
3389
3441
|
}
|
|
@@ -3400,7 +3452,10 @@ async function getSummaryAndTags(assetId, options) {
|
|
|
3400
3452
|
imageSubmissionMode = "url",
|
|
3401
3453
|
imageDownloadOptions,
|
|
3402
3454
|
promptOverrides,
|
|
3403
|
-
credentials
|
|
3455
|
+
credentials,
|
|
3456
|
+
titleLength,
|
|
3457
|
+
descriptionLength,
|
|
3458
|
+
tagCount
|
|
3404
3459
|
} = options ?? {};
|
|
3405
3460
|
if (!VALID_TONES.includes(tone)) {
|
|
3406
3461
|
throw new Error(
|
|
@@ -3438,7 +3493,10 @@ async function getSummaryAndTags(assetId, options) {
|
|
|
3438
3493
|
transcriptText,
|
|
3439
3494
|
isCleanTranscript: cleanTranscript,
|
|
3440
3495
|
promptOverrides,
|
|
3441
|
-
isAudioOnly
|
|
3496
|
+
isAudioOnly,
|
|
3497
|
+
titleLength,
|
|
3498
|
+
descriptionLength,
|
|
3499
|
+
tagCount
|
|
3442
3500
|
});
|
|
3443
3501
|
let analysisResponse;
|
|
3444
3502
|
let imageUrl;
|
|
@@ -3495,7 +3553,7 @@ async function getSummaryAndTags(assetId, options) {
|
|
|
3495
3553
|
assetId,
|
|
3496
3554
|
title: analysisResponse.result.title,
|
|
3497
3555
|
description: analysisResponse.result.description,
|
|
3498
|
-
tags: normalizeKeywords(analysisResponse.result.keywords),
|
|
3556
|
+
tags: normalizeKeywords(analysisResponse.result.keywords, tagCount ?? SUMMARY_KEYWORD_LIMIT),
|
|
3499
3557
|
storyboardUrl: imageUrl,
|
|
3500
3558
|
// undefined for audio-only assets
|
|
3501
3559
|
usage: {
|
|
@@ -4115,6 +4173,7 @@ import { z as z6 } from "zod";
|
|
|
4115
4173
|
var translationSchema = z6.object({
|
|
4116
4174
|
translation: z6.string()
|
|
4117
4175
|
});
|
|
4176
|
+
var SYSTEM_PROMPT4 = 'You are a subtitle translation expert. Translate VTT subtitle files to the target language specified by the user. Preserve all timestamps and VTT formatting exactly as they appear. Return JSON with a single key "translation" containing the translated VTT content.';
|
|
4118
4177
|
async function fetchVttFromMux(vttUrl) {
|
|
4119
4178
|
"use step";
|
|
4120
4179
|
const vttResponse = await fetch(vttUrl);
|
|
@@ -4137,9 +4196,13 @@ async function translateVttWithAI({
|
|
|
4137
4196
|
model,
|
|
4138
4197
|
output: Output5.object({ schema: translationSchema }),
|
|
4139
4198
|
messages: [
|
|
4199
|
+
{
|
|
4200
|
+
role: "system",
|
|
4201
|
+
content: SYSTEM_PROMPT4
|
|
4202
|
+
},
|
|
4140
4203
|
{
|
|
4141
4204
|
role: "user",
|
|
4142
|
-
content: `Translate
|
|
4205
|
+
content: `Translate from ${fromLanguageCode} to ${toLanguageCode}:
|
|
4143
4206
|
|
|
4144
4207
|
${vttContent}`
|
|
4145
4208
|
}
|