@mux/ai 0.20.0 → 0.21.2

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.
@@ -120,6 +120,8 @@ interface WorkflowCredentials {
120
120
  anthropicApiKey?: string;
121
121
  /** Optional direct Google API key for per-request credential injection. */
122
122
  googleApiKey?: string;
123
+ /** Optional direct Google Vision API key for per-request credential injection. */
124
+ googleVisionApiKey?: string;
123
125
  /** Optional direct Hive API key for per-request credential injection. */
124
126
  hiveApiKey?: string;
125
127
  /** Optional direct ElevenLabs API key for per-request credential injection. */
@@ -1,7 +1,7 @@
1
- export { A as AskQuestionsOptions, a as AskQuestionsResult, b as AskQuestionsType, c as AudioTranslationOptions, d as AudioTranslationResult, e as AutoCensorProfanityOptions, B as BurnedInCaptionsAnalysis, f as BurnedInCaptionsOptions, g as BurnedInCaptionsPromptOverrides, h as BurnedInCaptionsPromptSections, j as BurnedInCaptionsResult, C as CaptionReplacement, k as CensorMode, l as Chapter, m as ChapterSystemPromptSections, n as ChaptersOptions, o as ChaptersPromptOverrides, p as ChaptersPromptSections, q as ChaptersResult, r as ChaptersType, D as DEFAULT_DESCRIPTION_LENGTH, s as DEFAULT_SUMMARY_KEYWORD_LIMIT, t as DEFAULT_TITLE_LENGTH, E as EditCaptionsOptions, u as EditCaptionsResult, v as EmbeddingsOptions, w as EmbeddingsResult, x as EngagementInsightsOptions, y as EngagementInsightsResult, H as HIVE_SEXUAL_CATEGORIES, z as HIVE_VIOLENCE_CATEGORIES, F as HiveModerationOutput, G as HiveModerationSource, M as ModerationOptions, I as ModerationProvider, J as ModerationResult, K as MomentInsight, O as OverallInsight, P as ProfanityDetectionPayload, Q as Question, L as QuestionAnswer, N as QuestionAnswerType, R as ReplacementRecord, S as SummarizationOptions, T as SummarizationPromptOverrides, U as SummarizationPromptSections, V as SummaryAndTagsResult, W as SummaryType, X as ThumbnailModerationScore, Y as TranslationChunkingOptions, Z as TranslationOptions, _ as TranslationPayload, $ as TranslationResult, a0 as aggregateTokenUsage, a1 as applyOverrideLists, a2 as applyReplacements, a3 as askQuestions, a4 as buildReplacementRegex, a5 as burnedInCaptionsSchema, a6 as censorVttContent, a7 as chapterSchema, a8 as chaptersSchema, a9 as createReplacer, aa as editCaptions, ab as extractTranscriptSegmentsForHotspots, ac as generateChapters, ad as generateEmbeddings, ae as generateEngagementInsights, af as generateVideoEmbeddings, ag as getModerationScores, ah as getSummaryAndTags, ai as hasBurnedInCaptions, aj as mapShotsToHotspots, ak as normalizeTranslatedVtt, al as profanityDetectionSchema, am as questionAnswerSchema, an as shouldSplitChunkTranslationError, ao as summarySchema, ap as transformCueText, aq as translateAudio, ar as translateCaptions, as as translationSchema } from '../index-DXMIJjUa.js';
1
+ export { A as AskQuestionsOptions, a as AskQuestionsResult, b as AskQuestionsType, c as AudioTranslationOptions, d as AudioTranslationResult, e as AutoCensorProfanityOptions, B as BurnedInCaptionsAnalysis, f as BurnedInCaptionsOptions, g as BurnedInCaptionsPromptOverrides, h as BurnedInCaptionsPromptSections, j as BurnedInCaptionsResult, C as CaptionReplacement, k as CensorMode, l as Chapter, m as ChapterSystemPromptSections, n as ChaptersOptions, o as ChaptersPromptOverrides, p as ChaptersPromptSections, q as ChaptersResult, r as ChaptersType, D as DEFAULT_DESCRIPTION_LENGTH, s as DEFAULT_SUMMARY_KEYWORD_LIMIT, t as DEFAULT_TITLE_LENGTH, E as EditCaptionsOptions, u as EditCaptionsResult, v as EmbeddingsOptions, w as EmbeddingsResult, x as EngagementInsightsOptions, y as EngagementInsightsResult, G as GOOGLE_VISION_LIKELIHOOD_TO_SCORE, H as HIVE_SEXUAL_CATEGORIES, z as HIVE_VIOLENCE_CATEGORIES, F as HiveModerationOutput, I as HiveModerationSource, M as ModerationOptions, J as ModerationProvider, K as ModerationResult, L as MomentInsight, O as OverallInsight, P as ProfanityDetectionPayload, Q as Question, N as QuestionAnswer, R as QuestionAnswerType, S as ReplacementRecord, T as SummarizationOptions, U as SummarizationPromptOverrides, V as SummarizationPromptSections, W as SummaryAndTagsResult, X as SummaryType, Y as ThumbnailModerationScore, Z as TranslationChunkingOptions, _ as TranslationOptions, $ as TranslationPayload, a0 as TranslationResult, a1 as aggregateTokenUsage, a2 as applyOverrideLists, a3 as applyReplacements, a4 as askQuestions, a5 as buildReplacementRegex, a6 as burnedInCaptionsSchema, a7 as censorVttContent, a8 as chapterSchema, a9 as chaptersSchema, aa as createReplacer, ab as editCaptions, ac as extractTranscriptSegmentsForHotspots, ad as generateChapters, ae as generateEmbeddings, af as generateEngagementInsights, ag as generateVideoEmbeddings, ah as getModerationScores, ai as getSummaryAndTags, aj as hasBurnedInCaptions, ak as mapShotsToHotspots, al as normalizeTranslatedVtt, am as profanityDetectionSchema, an as questionAnswerSchema, ao as shouldSplitChunkTranslationError, ap as summarySchema, aq as transformCueText, ar as translateAudio, as as translateCaptions, at as translationSchema } from '../index-DwNtsg-u.js';
2
2
  import 'zod';
3
3
  import '@ai-sdk/anthropic';
4
4
  import '@ai-sdk/google';
5
5
  import '@ai-sdk/openai';
6
- import '../shots-BFYOfBgN.js';
6
+ import '../shots-mGJfYGiO.js';
7
7
  import '@mux/mux-node';
@@ -186,6 +186,7 @@ var EnvSchema = z.object({
186
186
  GOOGLE_GENERATIVE_AI_API_KEY: optionalString("Google Generative AI API key for Gemini-backed workflows.", "Google Generative AI API key"),
187
187
  ELEVENLABS_API_KEY: optionalString("ElevenLabs API key for audio translation.", "ElevenLabs API key"),
188
188
  HIVE_API_KEY: optionalString("Hive Visual Moderation API key.", "Hive API key"),
189
+ GOOGLE_VISION_API_KEY: optionalString("Google Vision API key for SafeSearch moderation.", "Google Vision API key"),
189
190
  // S3-Compatible Storage (required for translation & audio dubbing)
190
191
  S3_ENDPOINT: optionalString("S3-compatible endpoint for uploads.", "S3 endpoint"),
191
192
  S3_REGION: optionalString("S3 region (defaults to 'auto' when omitted)."),
@@ -809,23 +810,26 @@ function resolveProviderApiKeyFromCredentials(provider, resolved) {
809
810
  const openaiApiKey = readString(record, "openaiApiKey");
810
811
  const anthropicApiKey = readString(record, "anthropicApiKey");
811
812
  const googleApiKey = readString(record, "googleApiKey");
813
+ const googleVisionApiKey = readString(record, "googleVisionApiKey");
812
814
  const hiveApiKey = readString(record, "hiveApiKey");
813
815
  const elevenLabsApiKey = readString(record, "elevenLabsApiKey");
814
816
  const apiKeyMap = {
815
- openai: openaiApiKey ?? env_default.OPENAI_API_KEY,
816
- anthropic: anthropicApiKey ?? env_default.ANTHROPIC_API_KEY,
817
- google: googleApiKey ?? env_default.GOOGLE_GENERATIVE_AI_API_KEY,
818
- hive: hiveApiKey ?? env_default.HIVE_API_KEY,
819
- elevenlabs: elevenLabsApiKey ?? env_default.ELEVENLABS_API_KEY
817
+ "openai": openaiApiKey ?? env_default.OPENAI_API_KEY,
818
+ "anthropic": anthropicApiKey ?? env_default.ANTHROPIC_API_KEY,
819
+ "google": googleApiKey ?? env_default.GOOGLE_GENERATIVE_AI_API_KEY,
820
+ "google-vision-api": googleVisionApiKey ?? env_default.GOOGLE_VISION_API_KEY,
821
+ "hive": hiveApiKey ?? env_default.HIVE_API_KEY,
822
+ "elevenlabs": elevenLabsApiKey ?? env_default.ELEVENLABS_API_KEY
820
823
  };
821
824
  const apiKey = apiKeyMap[provider];
822
825
  if (!apiKey) {
823
826
  const envVarNames = {
824
- openai: "OPENAI_API_KEY",
825
- anthropic: "ANTHROPIC_API_KEY",
826
- google: "GOOGLE_GENERATIVE_AI_API_KEY",
827
- hive: "HIVE_API_KEY",
828
- elevenlabs: "ELEVENLABS_API_KEY"
827
+ "openai": "OPENAI_API_KEY",
828
+ "anthropic": "ANTHROPIC_API_KEY",
829
+ "google": "GOOGLE_GENERATIVE_AI_API_KEY",
830
+ "google-vision-api": "GOOGLE_VISION_API_KEY",
831
+ "hive": "HIVE_API_KEY",
832
+ "elevenlabs": "ELEVENLABS_API_KEY"
829
833
  };
830
834
  throw new Error(
831
835
  `${provider} API key is required. Provide ${provider} credentials via workflow credentials or set ${envVarNames[provider]} environment variable.`
@@ -861,17 +865,26 @@ var DEFAULT_EMBEDDING_MODELS = {
861
865
  var LANGUAGE_MODELS = {
862
866
  openai: ["gpt-5.1", "gpt-5-mini"],
863
867
  anthropic: ["claude-sonnet-4-5"],
864
- google: ["gemini-3-flash-preview", "gemini-3.1-flash-lite-preview", "gemini-2.5-flash"]
868
+ google: ["gemini-3-flash-preview", "gemini-3.1-flash-lite", "gemini-3.1-flash-lite-preview", "gemini-2.5-flash"]
865
869
  };
866
870
  var LANGUAGE_MODEL_DEPRECATIONS = [
867
871
  {
868
872
  provider: "google",
869
873
  modelId: "gemini-2.5-flash",
870
- replacementModelId: "gemini-3.1-flash-lite-preview",
874
+ replacementModelId: "gemini-3.1-flash-lite",
871
875
  phase: "warn",
872
876
  deprecatedOn: "2026-03-03",
873
- sunsetOn: "2026-06-30",
874
- reason: "Gemini 3.1 Flash-Lite Preview offers better quality/latency/cost balance in current evals."
877
+ sunsetOn: "2026-06-17",
878
+ reason: "Gemini 3.1 Flash-Lite offers better quality/latency/cost balance in current evals."
879
+ },
880
+ {
881
+ provider: "google",
882
+ modelId: "gemini-3.1-flash-lite-preview",
883
+ replacementModelId: "gemini-3.1-flash-lite",
884
+ phase: "warn",
885
+ deprecatedOn: "2026-05-12",
886
+ sunsetOn: "2026-05-25",
887
+ reason: "Preview model graduated to the stable Gemini 3.1 Flash-Lite release."
875
888
  }
876
889
  ];
877
890
  var warnedDeprecatedLanguageModels = /* @__PURE__ */ new Set();
@@ -4859,6 +4872,15 @@ var OPENAI_MODERATION_BASE_DELAY_MS = 750;
4859
4872
  var OPENAI_MODERATION_MAX_DELAY_MS = 3e3;
4860
4873
  var MIN_SAMPLE_COVERAGE_FOR_CONFIDENT_THRESHOLDING = 0.5;
4861
4874
  var MIN_SUCCESSFUL_THUMBNAILS_FOR_CONFIDENT_THRESHOLDING = 3;
4875
+ var GOOGLE_VISION_ENDPOINT = "https://vision.googleapis.com/v1/images:annotate";
4876
+ var GOOGLE_VISION_LIKELIHOOD_TO_SCORE = {
4877
+ UNKNOWN: 0,
4878
+ VERY_UNLIKELY: 0.2,
4879
+ UNLIKELY: 0.4,
4880
+ POSSIBLE: 0.6,
4881
+ LIKELY: 0.8,
4882
+ VERY_LIKELY: 1
4883
+ };
4862
4884
  var HIVE_ENDPOINT = "https://api.thehive.ai/api/v2/task/sync";
4863
4885
  var HIVE_SEXUAL_CATEGORIES = [
4864
4886
  "general_nsfw",
@@ -5187,6 +5209,98 @@ async function requestHiveModeration(images, maxConcurrent = 5, submissionMode =
5187
5209
  }));
5188
5210
  return await processConcurrently(targets, moderateImageWithHive, maxConcurrent);
5189
5211
  }
5212
+ async function moderateImageWithGoogleVision(entry) {
5213
+ "use step";
5214
+ try {
5215
+ const apiKey = await getApiKeyFromEnv("google-vision-api", entry.credentials);
5216
+ const imageField = entry.isBase64 ? { content: entry.image } : { source: { imageUri: entry.image } };
5217
+ const controller = new AbortController();
5218
+ const timeout = setTimeout(() => controller.abort(), 15e3);
5219
+ let res;
5220
+ try {
5221
+ res = await fetch(GOOGLE_VISION_ENDPOINT, {
5222
+ method: "POST",
5223
+ headers: {
5224
+ "Accept": "application/json",
5225
+ "Content-Type": "application/json",
5226
+ "X-goog-api-key": apiKey
5227
+ },
5228
+ body: JSON.stringify({
5229
+ requests: [
5230
+ {
5231
+ image: imageField,
5232
+ features: [{ type: "SAFE_SEARCH_DETECTION" }]
5233
+ }
5234
+ ]
5235
+ }),
5236
+ signal: controller.signal
5237
+ });
5238
+ } catch (err) {
5239
+ if (err?.name === "AbortError") {
5240
+ throw new Error("Google Vision request timed out after 15s");
5241
+ }
5242
+ throw err;
5243
+ } finally {
5244
+ clearTimeout(timeout);
5245
+ }
5246
+ const json = await res.json().catch(() => void 0);
5247
+ if (!res.ok) {
5248
+ throw new Error(
5249
+ `Google Vision moderation error: ${res.status} ${res.statusText} - ${JSON.stringify(json)}`
5250
+ );
5251
+ }
5252
+ const perImage = json?.responses?.[0];
5253
+ if (perImage?.error) {
5254
+ throw new Error(
5255
+ `Google Vision per-image error: ${perImage.error.code} ${perImage.error.message || "Unknown error"}`
5256
+ );
5257
+ }
5258
+ const annotation = perImage?.safeSearchAnnotation;
5259
+ if (!annotation) {
5260
+ throw new Error(`Google Vision response missing safeSearchAnnotation: ${JSON.stringify(json)}`);
5261
+ }
5262
+ const adultLikelihood = typeof annotation.adult === "string" ? annotation.adult : "UNKNOWN";
5263
+ const violenceLikelihood = typeof annotation.violence === "string" ? annotation.violence : "UNKNOWN";
5264
+ const sexual = GOOGLE_VISION_LIKELIHOOD_TO_SCORE[adultLikelihood] ?? 0;
5265
+ const violence = GOOGLE_VISION_LIKELIHOOD_TO_SCORE[violenceLikelihood] ?? 0;
5266
+ return {
5267
+ url: entry.url,
5268
+ time: entry.time,
5269
+ sexual,
5270
+ violence,
5271
+ error: false
5272
+ };
5273
+ } catch (error) {
5274
+ return {
5275
+ url: entry.url,
5276
+ time: entry.time,
5277
+ sexual: 0,
5278
+ violence: 0,
5279
+ error: true,
5280
+ errorMessage: error instanceof Error ? error.message : String(error)
5281
+ };
5282
+ }
5283
+ }
5284
+ async function requestGoogleVisionModeration(images, maxConcurrent = 5, submissionMode = "url", downloadOptions, credentials) {
5285
+ "use step";
5286
+ const imageUrls = images.map((img) => img.url);
5287
+ const timeByUrl = new Map(images.map((img) => [img.url, img.time]));
5288
+ const targets = submissionMode === "base64" ? (await downloadImagesAsBase64(imageUrls, downloadOptions, maxConcurrent)).map((img) => ({
5289
+ url: img.url,
5290
+ time: timeByUrl.get(img.url),
5291
+ // Vision REST wants raw base64; downloadImagesAsBase64 returns a data URI.
5292
+ image: img.base64Data.startsWith("data:") ? img.base64Data.split(",")[1] : img.base64Data,
5293
+ isBase64: true,
5294
+ credentials
5295
+ })) : images.map((img) => ({
5296
+ url: img.url,
5297
+ time: img.time,
5298
+ image: img.url,
5299
+ isBase64: false,
5300
+ credentials
5301
+ }));
5302
+ return processConcurrently(targets, moderateImageWithGoogleVision, maxConcurrent);
5303
+ }
5190
5304
  async function getThumbnailUrlsFromTimestamps(playbackId, timestampsMs, options) {
5191
5305
  "use step";
5192
5306
  const { width, shouldSign, credentials } = options;
@@ -5250,8 +5364,11 @@ async function getModerationScores(assetId, options = {}) {
5250
5364
  );
5251
5365
  } else if (provider === "hive") {
5252
5366
  throw new Error("Hive does not support transcript moderation in this workflow. Use provider: 'openai' for audio-only assets.");
5367
+ } else if (provider === "google-vision-api") {
5368
+ throw new Error("google-vision-api is image-only and does not support transcript moderation. Use provider: 'openai' for audio-only assets.");
5253
5369
  } else {
5254
- throw new Error(`Unsupported moderation provider: ${provider}`);
5370
+ const exhaustiveCheck = provider;
5371
+ throw new Error(`Unsupported moderation provider: ${exhaustiveCheck}`);
5255
5372
  }
5256
5373
  } else {
5257
5374
  const estimatedIntervalCount = duration <= 50 ? 5 : Math.ceil(duration / thumbnailInterval);
@@ -5294,8 +5411,17 @@ async function getModerationScores(assetId, options = {}) {
5294
5411
  imageDownloadOptions,
5295
5412
  credentials
5296
5413
  );
5414
+ } else if (provider === "google-vision-api") {
5415
+ thumbnailScores = await requestGoogleVisionModeration(
5416
+ thumbnailUrls,
5417
+ maxConcurrent,
5418
+ imageSubmissionMode,
5419
+ imageDownloadOptions,
5420
+ credentials
5421
+ );
5297
5422
  } else {
5298
- throw new Error(`Unsupported moderation provider: ${provider}`);
5423
+ const exhaustiveCheck = provider;
5424
+ throw new Error(`Unsupported moderation provider: ${exhaustiveCheck}`);
5299
5425
  }
5300
5426
  }
5301
5427
  const failed = thumbnailScores.filter((s) => s.error);
@@ -6999,6 +7125,7 @@ export {
6999
7125
  DEFAULT_DESCRIPTION_LENGTH,
7000
7126
  DEFAULT_SUMMARY_KEYWORD_LIMIT,
7001
7127
  DEFAULT_TITLE_LENGTH,
7128
+ GOOGLE_VISION_LIKELIHOOD_TO_SCORE,
7002
7129
  HIVE_SEXUAL_CATEGORIES,
7003
7130
  HIVE_VIOLENCE_CATEGORIES,
7004
7131
  aggregateTokenUsage,