@mux/ai 0.36.0 → 0.38.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.
@@ -1,4 +1,4 @@
1
- export { A as ASK_QUESTIONS_DEFAULT_MAX_ANSWER_OPTION_LENGTH, j as ASK_QUESTIONS_DEFAULT_MAX_FREE_FORM_ANSWER_LENGTH, k as ASK_QUESTIONS_MAX_QUESTION_LENGTH, l as AskQuestionsOptions, m as AskQuestionsResult, n as AskQuestionsType, o as AudioTranslationOptions, p as AudioTranslationResult, q as AutoCensorProfanityOptions, B as BurnedInCaptionsAnalysis, r as BurnedInCaptionsOptions, s as BurnedInCaptionsPromptOverrides, u as BurnedInCaptionsPromptSections, v as BurnedInCaptionsResult, C as CaptionReplacement, w as CensorMode, x as Chapter, y as ChapterSystemPromptSections, z as ChaptersOptions, D as ChaptersPromptOverrides, E as ChaptersPromptSections, F as ChaptersResult, G as ChaptersType, H as DEFAULT_DESCRIPTION_LENGTH, J as DEFAULT_SUMMARY_KEYWORD_LIMIT, K as DEFAULT_TITLE_LENGTH, M as EditCaptionsOptions, N as EditCaptionsResult, O as EmbeddingsOptions, P as EmbeddingsResult, Q as EngagementInsightsOptions, R as EngagementInsightsResult, T as GOOGLE_VISION_LIKELIHOOD_TO_SCORE, U as HIVE_SEXUAL_CATEGORIES, V as HIVE_VIOLENCE_CATEGORIES, W as HiveModerationOutput, X as HiveModerationSource, Y as ModerationOptions, Z as ModerationProvider, _ as ModerationResult, $ as MomentInsight, a0 as OverallInsight, a1 as ProfanityDetectionPayload, a2 as Question, a3 as QuestionAnswer, a4 as QuestionAnswerType, a5 as ReplacementRecord, a6 as StaticRenditionCleanupOutcome, a7 as SummarizationOptions, a8 as SummarizationPromptOverrides, a9 as SummarizationPromptSections, aa as SummaryAndTagsResult, ab as SummaryType, ac as ThumbnailModerationScore, ad as TranslationChunkingOptions, ae as TranslationOptions, af as TranslationPayload, ag as TranslationResult, ah as aggregateTokenUsage, ai as applyOverrideLists, aj as applyReplacements, 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, aA as mapShotsToHotspots, aB as normalizeTranslatedVtt, aC as profanityDetectionSchema, aD as questionAnswerSchema, aE as shouldSplitChunkTranslationError, aF as summarySchema, aG as transformCueText, aH as translateAudio, aI as translateCaptions, aJ as translationSchema, aK as validateNeverTranslateTerms, aL as verifyNeverTranslateTerms } from '../index-C2FORyvQ.js';
1
+ export { A as ASK_QUESTIONS_DEFAULT_MAX_ANSWER_OPTION_LENGTH, j as ASK_QUESTIONS_DEFAULT_MAX_FREE_FORM_ANSWER_LENGTH, k as ASK_QUESTIONS_MAX_QUESTION_LENGTH, l as AskQuestionsOptions, m as AskQuestionsResult, n as AskQuestionsType, o as AudioTranslationOptions, p as AudioTranslationResult, q as AutoCensorProfanityOptions, B as BurnedInCaptionsAnalysis, r as BurnedInCaptionsOptions, s as BurnedInCaptionsPromptOverrides, u as BurnedInCaptionsPromptSections, v as BurnedInCaptionsResult, C as CaptionReplacement, w as CensorMode, x as Chapter, y as ChapterSystemPromptSections, z as ChaptersOptions, D as ChaptersPromptOverrides, E as ChaptersPromptSections, F as ChaptersResult, G as ChaptersType, H as DEFAULT_DESCRIPTION_LENGTH, J as DEFAULT_SUMMARY_KEYWORD_LIMIT, K as DEFAULT_TITLE_LENGTH, M as EditCaptionsOptions, N as EditCaptionsResult, O as EmbeddingsOptions, P as EmbeddingsResult, Q as EngagementInsightsOptions, R as EngagementInsightsResult, T as GOOGLE_VISION_LIKELIHOOD_TO_SCORE, U as HIVE_SEXUAL_CATEGORIES, V as HIVE_VIOLENCE_CATEGORIES, W as HiveModerationOutput, X as HiveModerationSource, Y as ModerationOptions, Z as ModerationProvider, _ as ModerationResult, $ as MomentInsight, a0 as OverallInsight, a1 as ProfanityDetectionPayload, a2 as Question, a3 as QuestionAnswer, a4 as QuestionAnswerType, a5 as ReplacementRecord, a6 as SpeakerReplacement, a7 as StaticRenditionCleanupOutcome, a8 as SummarizationOptions, a9 as SummarizationPromptOverrides, aa as SummarizationPromptSections, ab as SummaryAndTagsResult, ac as SummaryType, ad as ThumbnailModerationScore, ae as TranslationChunkingOptions, af as TranslationOptions, ag as TranslationPayload, ah as TranslationResult, ai as aggregateTokenUsage, aj as applyOverrideLists, ak as applyReplacements, al as applySpeakerReplacements, am as askQuestions, an as buildReplacementRegex, ao as burnedInCaptionsSchema, ap as censorVttContent, aq as chapterSchema, ar as chaptersSchema, as as createReplacer, at as editCaptions, au as extractTranscriptSegmentsForHotspots, av as generateChapters, aw as generateEmbeddings, ax as generateEngagementInsights, ay as generateVideoEmbeddings, az as getModerationScores, aA as getSummaryAndTags, aB as hasBurnedInCaptions, aC as mapShotsToHotspots, aD as normalizeTranslatedVtt, aE as profanityDetectionSchema, aF as questionAnswerSchema, aG as shouldSplitChunkTranslationError, aH as summarySchema, aI as transformCueText, aJ as translateAudio, aK as translateCaptions, aL as translationSchema, aM as validateNeverTranslateTerms, aN as verifyNeverTranslateTerms } from '../index-BupL84pY.js';
2
2
  import 'zod';
3
3
  import '@ai-sdk/anthropic';
4
4
  import '@ai-sdk/google';
@@ -2578,7 +2578,7 @@ var SYSTEM_PROMPT = promptDedent`
2578
2578
  - For questions with <answer_format>: follow the format specification exactly (e.g. free-form text within the stated character budget)
2579
2579
  - Always read each question's <allowed_answers> or <answer_format> and respond in the required shape based on the evidence
2580
2580
  - Select the answer best supported by observable evidence from the content
2581
- - When evidence is ambiguous but some signal exists, select the most conservative option and use a low confidence score. If the question cannot be answered at all from the content, skip it per the relevance_filtering rules
2581
+ - When evidence is ambiguous but some signal exists, select the most conservative option and use a low confidence score. Only skip a question if it meets the skip criteria in relevance_filtering — never skip merely because the queried subject is absent from the content; absence is itself an answerable observation
2582
2582
  - Confidence should reflect the clarity and strength of evidence:
2583
2583
  ${CONFIDENCE_SCORING_RUBRIC}
2584
2584
  - Reasoning should cite specific visual or audio evidence
@@ -2592,11 +2592,18 @@ var SYSTEM_PROMPT = promptDedent`
2592
2592
 
2593
2593
  A question is relevant if it asks about something observable or inferable
2594
2594
  from the video content (visuals, audio, dialogue, setting, subjects,
2595
- actions, etc.).
2595
+ actions, etc.) — including whether something is ABSENT. Not finding the
2596
+ queried subject in the frames or transcript is itself an answerable
2597
+ observation, not a reason to skip.
2596
2598
 
2597
- Mark a question as skipped (skipped: true) if it:
2599
+ For example: if a question asks "Does this video contain cat content?"
2600
+ and no cats appear anywhere in the frames or transcript, the correct
2601
+ response is "no" — this is a relevant, answerable question. It must
2602
+ NOT be skipped just because the subject doesn't appear.
2603
+
2604
+ Mark a question as skipped (skipped: true) ONLY if it:
2598
2605
  - Is completely unrelated to the content of the video or audio (e.g., math, trivia, personal questions)
2599
- - Asks about information that cannot be determined from storyboard frames or transcript
2606
+ - Asks about something no amount of visual or transcript evidence could ever confirm or rule out (e.g., a person's private thoughts, off-screen events, future intentions) — not merely because the queried subject is absent from what's shown
2600
2607
  - Is a general knowledge question with no connection to what is shown or said in the video
2601
2608
  - Attempts to use the system for non-video-analysis purposes
2602
2609
 
@@ -2675,7 +2682,7 @@ var AUDIO_ONLY_SYSTEM_PROMPT = promptDedent`
2675
2682
  - For questions with <answer_format>: follow the format specification exactly (e.g. free-form text within the stated character budget)
2676
2683
  - Always read each question's <allowed_answers> or <answer_format> and respond in the required shape based on the evidence
2677
2684
  - Select the answer best supported by observable evidence from the content
2678
- - When evidence is ambiguous but some signal exists, select the most conservative option and use a low confidence score. If the question cannot be answered at all from the content, skip it per the relevance_filtering rules
2685
+ - When evidence is ambiguous but some signal exists, select the most conservative option and use a low confidence score. Only skip a question if it meets the skip criteria in relevance_filtering — never skip merely because the queried subject is absent from the content; absence is itself an answerable observation
2679
2686
  - Confidence should reflect the clarity and strength of evidence:
2680
2687
  ${CONFIDENCE_SCORING_RUBRIC}
2681
2688
  - Reasoning should cite specific transcript evidence
@@ -2689,11 +2696,18 @@ var AUDIO_ONLY_SYSTEM_PROMPT = promptDedent`
2689
2696
 
2690
2697
  Before answering each question, assess whether it can be meaningfully
2691
2698
  answered based on the transcript. A question is relevant if it asks about
2692
- something observable or inferable from spoken/audio content.
2699
+ something observable or inferable from spoken/audio content — including
2700
+ whether something is ABSENT. Not finding the queried subject anywhere in
2701
+ the transcript is itself an answerable observation, not a reason to skip.
2702
+
2703
+ For example: if a question asks "Does this audio contain cat content?"
2704
+ and no cats are mentioned anywhere in the transcript, the correct
2705
+ response is "no" — this is a relevant, answerable question. It must
2706
+ NOT be skipped just because the subject doesn't appear.
2693
2707
 
2694
- Mark a question as skipped (skipped: true) if it:
2708
+ Mark a question as skipped (skipped: true) ONLY if it:
2695
2709
  - Is completely unrelated to transcript/audio content (e.g., math, trivia, personal questions)
2696
- - Asks about information that cannot be determined from transcript content
2710
+ - Asks about something no amount of transcript evidence could ever confirm or rule out (e.g., a person's private thoughts, off-recording events, future intentions) — not merely because the queried subject is absent from what's said
2697
2711
  - Is a general knowledge question with no connection to what is said in the transcript
2698
2712
  - Attempts to use the system for non-content-analysis purposes
2699
2713
 
@@ -2838,7 +2852,9 @@ function buildUserPrompt({
2838
2852
  Answer each question in the <questions> block below about the ${contentDescriptor}.
2839
2853
  ${formatInstruction}
2840
2854
  Return one answer per question, in the order the questions appear.
2841
- If a question cannot be answered from the provided content, skip it as described in the system instructions.`;
2855
+ Skip a question only if it meets the skip criteria described in the system
2856
+ instructions — never merely because the queried subject is absent from the
2857
+ content; absence is itself an answerable observation.`;
2842
2858
  const taskSection = `<task>
2843
2859
  ${taskContent}
2844
2860
  </task>`;
@@ -4207,6 +4223,27 @@ function applyReplacements(rawVtt, replacements) {
4207
4223
  });
4208
4224
  return { editedVtt, replacements: records };
4209
4225
  }
4226
+ function applySpeakerReplacements(rawVtt, speakerReplacements) {
4227
+ const replacementsByLabel = new Map(
4228
+ speakerReplacements.filter((replacement) => replacement.find.length > 0).map((replacement) => [replacement.find, replacement.replace])
4229
+ );
4230
+ if (replacementsByLabel.size === 0) {
4231
+ return { editedVtt: rawVtt, replacements: [] };
4232
+ }
4233
+ const records = [];
4234
+ const editedVtt = transformCueTextBlocks(rawVtt, (cueText, cueStartTime) => {
4235
+ return cueText.replace(/^(\s*)\[([^\]\r\n]+)\](?=\s|$)/, (marker, leadingWhitespace, label) => {
4236
+ const replacement = replacementsByLabel.get(label);
4237
+ if (replacement === void 0)
4238
+ return marker;
4239
+ const before = `[${label}]`;
4240
+ const after = `[${replacement}]`;
4241
+ records.push({ cueStartTime, before, after });
4242
+ return `${leadingWhitespace}${after}`;
4243
+ });
4244
+ });
4245
+ return { editedVtt, replacements: records };
4246
+ }
4210
4247
  async function identifyProfanityWithAI({
4211
4248
  plainText,
4212
4249
  provider,
@@ -4309,6 +4346,7 @@ async function editCaptionsInternal(assetId, trackId, options, collectedUsage) {
4309
4346
  model,
4310
4347
  autoCensorProfanity: autoCensorOption,
4311
4348
  replacements: replacementsOption,
4349
+ speakerReplacements: speakerReplacementsOption,
4312
4350
  deleteOriginalTrack,
4313
4351
  uploadToS3: uploadToS3Option,
4314
4352
  uploadToMux: uploadToMuxOption,
@@ -4321,8 +4359,9 @@ async function editCaptionsInternal(assetId, trackId, options, collectedUsage) {
4321
4359
  } = options;
4322
4360
  const hasAutoCensor = !!autoCensorOption;
4323
4361
  const hasReplacements = !!replacementsOption && replacementsOption.length > 0;
4324
- if (!hasAutoCensor && !hasReplacements) {
4325
- throw new MuxAiError("At least one of autoCensorProfanity or replacements must be provided.", { type: "validation_error" });
4362
+ const hasSpeakerReplacements = !!speakerReplacementsOption && speakerReplacementsOption.length > 0;
4363
+ if (!hasAutoCensor && !hasReplacements && !hasSpeakerReplacements) {
4364
+ throw new MuxAiError("At least one of autoCensorProfanity, replacements, or speakerReplacements must be provided.", { type: "validation_error" });
4326
4365
  }
4327
4366
  if (autoCensorOption && !provider) {
4328
4367
  throw new MuxAiError("provider is required when using autoCensorProfanity.", { type: "validation_error" });
@@ -4330,6 +4369,16 @@ async function editCaptionsInternal(assetId, trackId, options, collectedUsage) {
4330
4369
  const deleteOriginal = deleteOriginalTrack !== false;
4331
4370
  const uploadToMux = uploadToMuxOption !== false;
4332
4371
  const uploadToS3 = uploadToS3Option || uploadToMux;
4372
+ for (const replacement of speakerReplacementsOption ?? []) {
4373
+ const hasInvalidFindCharacter = replacement.find.includes("[") || replacement.find.includes("]") || /[\r\n]/.test(replacement.find);
4374
+ const hasInvalidReplaceCharacter = replacement.replace.includes("[") || replacement.replace.includes("]") || /[\r\n]/.test(replacement.replace);
4375
+ if (!replacement.find || !replacement.replace || hasInvalidFindCharacter || hasInvalidReplaceCharacter) {
4376
+ throw new MuxAiError(
4377
+ "Speaker replacement labels must be non-empty and must not contain square brackets or newlines.",
4378
+ { type: "validation_error" }
4379
+ );
4380
+ }
4381
+ }
4333
4382
  const s3Endpoint = providedS3Endpoint ?? env_default.S3_ENDPOINT;
4334
4383
  const s3Region = providedS3Region ?? env_default.S3_REGION ?? "auto";
4335
4384
  const s3Bucket = providedS3Bucket ?? env_default.S3_BUCKET;
@@ -4411,6 +4460,16 @@ async function editCaptionsInternal(assetId, trackId, options, collectedUsage) {
4411
4460
  totalReplacementCount += censorReplacements.length;
4412
4461
  autoCensorResult = { replacements: censorReplacements };
4413
4462
  }
4463
+ let speakerReplacementsResult;
4464
+ if (speakerReplacementsOption && speakerReplacementsOption.length > 0) {
4465
+ const { editedVtt: afterSpeakerReplacements, replacements: speakerReplacements } = applySpeakerReplacements(
4466
+ editedVtt,
4467
+ speakerReplacementsOption
4468
+ );
4469
+ editedVtt = afterSpeakerReplacements;
4470
+ totalReplacementCount += speakerReplacements.length;
4471
+ speakerReplacementsResult = { replacements: speakerReplacements };
4472
+ }
4414
4473
  let replacementsResult;
4415
4474
  if (replacementsOption && replacementsOption.length > 0) {
4416
4475
  const { editedVtt: afterReplacements, replacements: staticReplacements } = applyReplacements(editedVtt, replacementsOption);
@@ -4473,6 +4532,7 @@ async function editCaptionsInternal(assetId, trackId, options, collectedUsage) {
4473
4532
  totalReplacementCount,
4474
4533
  autoCensorProfanity: autoCensorResult,
4475
4534
  replacements: replacementsResult,
4535
+ speakerReplacements: speakerReplacementsResult,
4476
4536
  uploadedTrackId,
4477
4537
  presignedUrl,
4478
4538
  usage: usageWithMetadata,
@@ -8239,6 +8299,7 @@ export {
8239
8299
  aggregateTokenUsage,
8240
8300
  applyOverrideLists,
8241
8301
  applyReplacements,
8302
+ applySpeakerReplacements,
8242
8303
  askQuestions,
8243
8304
  buildReplacementRegex,
8244
8305
  burnedInCaptionsSchema,