@mux/ai 0.7.3 → 0.7.5

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.
@@ -156,6 +156,9 @@ var EnvSchema = z.object({
156
156
  ),
157
157
  MUX_TEST_ASSET_ID_AUDIO_ONLY: optionalString("Mux test asset ID for audio-only assets.", "Mux test asset id for audio-only assets for testing"),
158
158
  MUX_TEST_ASSET_ID_VIOLENT_AUDIO_ONLY: optionalString("Mux test asset ID for audio-only assets with violent content.", "Mux test asset id for audio-only assets with violent content for testing"),
159
+ // Eval config
160
+ MUX_AI_EVAL_MODEL_SET: optionalString("Eval model selection mode.", "Choose between 'default' (provider defaults only) or 'all' (all configured models)"),
161
+ MUX_AI_EVAL_MODELS: optionalString("Comma-separated eval model pairs.", "Comma-separated provider:model pairs (e.g. 'openai:gpt-5.1,anthropic:claude-sonnet-4-5,google:gemini-3-flash-preview')"),
159
162
  // AI Providers
160
163
  OPENAI_API_KEY: optionalString("OpenAI API key for OpenAI-backed workflows.", "OpenAI API key"),
161
164
  ANTHROPIC_API_KEY: optionalString("Anthropic API key for Claude-backed workflows.", "Anthropic API key"),
@@ -421,12 +424,13 @@ function readString(record, key) {
421
424
  function resolveDirectMuxCredentials(record) {
422
425
  const tokenId = readString(record, "muxTokenId");
423
426
  const tokenSecret = readString(record, "muxTokenSecret");
427
+ const authorizationToken = readString(record, "muxAuthorizationToken");
424
428
  const signingKey = readString(record, "muxSigningKey");
425
429
  const privateKey = readString(record, "muxPrivateKey");
426
- if (!tokenId && !tokenSecret && !signingKey && !privateKey) {
430
+ if (!tokenId && !tokenSecret && !authorizationToken && !signingKey && !privateKey) {
427
431
  return void 0;
428
432
  }
429
- if (!tokenId || !tokenSecret) {
433
+ if ((!tokenId || !tokenSecret) && !authorizationToken) {
430
434
  throw new Error(
431
435
  "Both muxTokenId and muxTokenSecret are required when passing direct Mux workflow credentials."
432
436
  );
@@ -434,6 +438,7 @@ function resolveDirectMuxCredentials(record) {
434
438
  return {
435
439
  tokenId,
436
440
  tokenSecret,
441
+ authorizationToken,
437
442
  signingKey,
438
443
  privateKey
439
444
  };
@@ -444,7 +449,8 @@ function createWorkflowMuxClient(options) {
444
449
  const { default: MuxClient } = await import("@mux/mux-node");
445
450
  return new MuxClient({
446
451
  tokenId: options.tokenId,
447
- tokenSecret: options.tokenSecret
452
+ tokenSecret: options.tokenSecret,
453
+ authorizationToken: options.authorizationToken
448
454
  });
449
455
  },
450
456
  getSigningKey() {
@@ -530,6 +536,81 @@ var DEFAULT_EMBEDDING_MODELS = {
530
536
  openai: "text-embedding-3-small",
531
537
  google: "gemini-embedding-001"
532
538
  };
539
+ var LANGUAGE_MODELS = {
540
+ openai: ["gpt-5.1", "gpt-5-mini"],
541
+ anthropic: ["claude-sonnet-4-5"],
542
+ google: ["gemini-3-flash-preview", "gemini-2.5-flash"]
543
+ };
544
+ function getDefaultEvalModelConfigs() {
545
+ return Object.entries(DEFAULT_LANGUAGE_MODELS).map(([provider, modelId]) => ({ provider, modelId }));
546
+ }
547
+ function getAllEvalModelConfigs() {
548
+ return Object.entries(LANGUAGE_MODELS).flatMap(([provider, models]) => models.map((modelId) => ({ provider, modelId })));
549
+ }
550
+ function isSupportedProvider(value) {
551
+ return value === "openai" || value === "anthropic" || value === "google";
552
+ }
553
+ function parseEvalModelPair(value) {
554
+ const trimmed = value.trim();
555
+ const [providerRaw, modelIdRaw] = trimmed.split(":", 2);
556
+ const provider = providerRaw?.trim();
557
+ const modelId = modelIdRaw?.trim();
558
+ if (!provider || !modelId) {
559
+ throw new Error(
560
+ `Invalid eval model pair "${value}". Use "provider:model" (example: "openai:gpt-5.1").`
561
+ );
562
+ }
563
+ if (!isSupportedProvider(provider)) {
564
+ throw new Error(
565
+ `Unsupported eval provider "${provider}" in "${value}". Supported providers: ${Object.keys(LANGUAGE_MODELS).join(", ")}.`
566
+ );
567
+ }
568
+ const supportedModels = LANGUAGE_MODELS[provider];
569
+ if (!supportedModels.includes(modelId)) {
570
+ throw new Error(
571
+ `Unsupported eval model "${modelId}" for provider "${provider}". Supported models: ${supportedModels.join(", ")}.`
572
+ );
573
+ }
574
+ return {
575
+ provider,
576
+ modelId
577
+ };
578
+ }
579
+ function resolveEvalModelConfigs(options = {}) {
580
+ const explicitPairs = options.modelPairs?.map((value) => value.trim()).filter(Boolean) ?? [];
581
+ if (explicitPairs.length > 0) {
582
+ const dedupedPairs = Array.from(new Set(explicitPairs));
583
+ return dedupedPairs.map(parseEvalModelPair);
584
+ }
585
+ const selection = options.selection ?? "default";
586
+ if (selection === "all") {
587
+ return getAllEvalModelConfigs();
588
+ }
589
+ return getDefaultEvalModelConfigs();
590
+ }
591
+ function resolveEvalModelConfigsFromEnv(environment = env_default) {
592
+ const rawSelection = environment.MUX_AI_EVAL_MODEL_SET?.trim();
593
+ const rawModelPairs = environment.MUX_AI_EVAL_MODELS?.trim();
594
+ let selection;
595
+ if (!rawSelection || rawSelection === "default") {
596
+ selection = "default";
597
+ } else if (rawSelection === "all") {
598
+ selection = "all";
599
+ } else {
600
+ throw new Error(
601
+ `Invalid MUX_AI_EVAL_MODEL_SET="${rawSelection}". Expected "default" or "all".`
602
+ );
603
+ }
604
+ let modelPairs;
605
+ if (rawModelPairs) {
606
+ modelPairs = rawModelPairs.split(",").map((value) => value.trim()).filter(Boolean);
607
+ }
608
+ return resolveEvalModelConfigs({
609
+ selection,
610
+ modelPairs
611
+ });
612
+ }
613
+ var EVAL_MODEL_CONFIGS = resolveEvalModelConfigsFromEnv();
533
614
  function resolveLanguageModelConfig(options = {}) {
534
615
  const provider = options.provider || "openai";
535
616
  const modelId = options.model || DEFAULT_LANGUAGE_MODELS[provider];
@@ -778,9 +859,9 @@ async function signPlaybackId(playbackId, context, type = "video", params) {
778
859
  params: stringParams
779
860
  });
780
861
  }
781
- async function signUrl(url, playbackId, context, type = "video", params, credentials) {
862
+ async function signUrl(url, playbackId, type = "video", params, credentials) {
782
863
  "use step";
783
- const resolvedContext = context ?? await resolveMuxSigningContext(credentials);
864
+ const resolvedContext = await resolveMuxSigningContext(credentials);
784
865
  if (!resolvedContext) {
785
866
  throw new Error(
786
867
  "Signed playback ID requires signing credentials. Provide muxSigningKey and muxPrivateKey via workflow credentials or set MUX_SIGNING_KEY and MUX_PRIVATE_KEY environment variables."
@@ -797,7 +878,7 @@ async function getStoryboardUrl(playbackId, width = DEFAULT_STORYBOARD_WIDTH, sh
797
878
  "use step";
798
879
  const baseUrl = `https://image.mux.com/${playbackId}/storyboard.png`;
799
880
  if (shouldSign) {
800
- return signUrl(baseUrl, playbackId, void 0, "storyboard", { width }, credentials);
881
+ return signUrl(baseUrl, playbackId, "storyboard", { width }, credentials);
801
882
  }
802
883
  return `${baseUrl}?width=${width}`;
803
884
  }
@@ -914,7 +995,7 @@ async function buildTranscriptUrl(playbackId, trackId, shouldSign = false, crede
914
995
  "use step";
915
996
  const baseUrl = `https://stream.mux.com/${playbackId}/text/${trackId}.vtt`;
916
997
  if (shouldSign) {
917
- return signUrl(baseUrl, playbackId, void 0, "video", void 0, credentials);
998
+ return signUrl(baseUrl, playbackId, "video", void 0, credentials);
918
999
  }
919
1000
  return baseUrl;
920
1001
  }
@@ -2007,7 +2088,7 @@ async function getThumbnailUrls(playbackId, duration, options = {}) {
2007
2088
  const baseUrl = `https://image.mux.com/${playbackId}/thumbnail.png`;
2008
2089
  const urlPromises = timestamps.map(async (time) => {
2009
2090
  if (shouldSign) {
2010
- return signUrl(baseUrl, playbackId, void 0, "thumbnail", { time, width }, credentials);
2091
+ return signUrl(baseUrl, playbackId, "thumbnail", { time, width }, credentials);
2011
2092
  }
2012
2093
  return `${baseUrl}?time=${time}&width=${width}`;
2013
2094
  });
@@ -2023,11 +2104,10 @@ var DEFAULT_PROVIDER2 = "openai";
2023
2104
  var HIVE_ENDPOINT = "https://api.thehive.ai/api/v2/task/sync";
2024
2105
  var HIVE_SEXUAL_CATEGORIES = [
2025
2106
  "general_nsfw",
2026
- "general_suggestive",
2027
2107
  "yes_sexual_activity",
2028
- "sex_toys",
2029
- "nudity_female",
2030
- "nudity_male"
2108
+ "yes_sex_toy",
2109
+ "yes_female_nudity",
2110
+ "yes_male_nudity"
2031
2111
  ];
2032
2112
  var HIVE_VIOLENCE_CATEGORIES = [
2033
2113
  "gun_in_hand",
@@ -2038,10 +2118,8 @@ var HIVE_VIOLENCE_CATEGORIES = [
2038
2118
  "hanging",
2039
2119
  "noose",
2040
2120
  "human_corpse",
2041
- "emaciated_body",
2042
- "self_harm",
2043
- "animal_abuse",
2044
- "fights",
2121
+ "yes_emaciated_body",
2122
+ "yes_self_harm",
2045
2123
  "garm_death_injury_or_military_conflict"
2046
2124
  ];
2047
2125
  async function processConcurrently(items, processor, maxConcurrent = 5) {
@@ -2185,6 +2263,12 @@ function getHiveCategoryScores(classes, categoryNames) {
2185
2263
  const scoreMap = Object.fromEntries(
2186
2264
  classes.map((c) => [c.class, c.score])
2187
2265
  );
2266
+ const missingCategories = categoryNames.filter((category) => !(category in scoreMap));
2267
+ if (missingCategories.length > 0) {
2268
+ console.warn(
2269
+ `Hive response missing expected categories: ${missingCategories.join(", ")}`
2270
+ );
2271
+ }
2188
2272
  const scores = categoryNames.map((category) => scoreMap[category] || 0);
2189
2273
  return Math.max(...scores, 0);
2190
2274
  }
@@ -2282,7 +2366,7 @@ async function getThumbnailUrlsFromTimestamps(playbackId, timestampsMs, options)
2282
2366
  const urlPromises = timestampsMs.map(async (tsMs) => {
2283
2367
  const time = Number((tsMs / 1e3).toFixed(2));
2284
2368
  if (shouldSign) {
2285
- return signUrl(baseUrl, playbackId, void 0, "thumbnail", { time, width }, credentials);
2369
+ return signUrl(baseUrl, playbackId, "thumbnail", { time, width }, credentials);
2286
2370
  }
2287
2371
  return `${baseUrl}?time=${time}&width=${width}`;
2288
2372
  });
@@ -3307,6 +3391,18 @@ function getReadyAudioStaticRendition(asset) {
3307
3391
  );
3308
3392
  }
3309
3393
  var hasReadyAudioStaticRendition = (asset) => Boolean(getReadyAudioStaticRendition(asset));
3394
+ function getAudioStaticRenditionStatus(asset) {
3395
+ const files = asset.static_renditions?.files;
3396
+ const audioRendition = files?.find((rendition) => rendition.name === "audio.m4a");
3397
+ if (typeof audioRendition?.status === "string" && audioRendition.status.length > 0) {
3398
+ return audioRendition.status;
3399
+ }
3400
+ const aggregateStatus = asset.static_renditions?.status;
3401
+ if (typeof aggregateStatus === "string" && aggregateStatus.length > 0) {
3402
+ return aggregateStatus;
3403
+ }
3404
+ return asset.static_renditions ? "requested" : "not_requested";
3405
+ }
3310
3406
  async function requestStaticRenditionCreation(assetId, credentials) {
3311
3407
  "use step";
3312
3408
  const muxClient = await resolveMuxClient(credentials);
@@ -3352,7 +3448,7 @@ async function waitForAudioStaticRendition({
3352
3448
  if (hasReadyAudioStaticRendition(currentAsset)) {
3353
3449
  return currentAsset;
3354
3450
  }
3355
- const currentStatus = currentAsset.static_renditions?.status || "unknown";
3451
+ const currentStatus = getAudioStaticRenditionStatus(currentAsset);
3356
3452
  console.warn(
3357
3453
  `\u231B Waiting for static rendition (attempt ${attempt}/${STATIC_RENDITION_MAX_ATTEMPTS}) \u2192 ${currentStatus}`
3358
3454
  );
@@ -3378,6 +3474,7 @@ async function createElevenLabsDubbingJob({
3378
3474
  audioBuffer,
3379
3475
  assetId,
3380
3476
  elevenLabsLangCode,
3477
+ elevenLabsSourceLangCode,
3381
3478
  numSpeakers,
3382
3479
  credentials
3383
3480
  }) {
@@ -3387,8 +3484,14 @@ async function createElevenLabsDubbingJob({
3387
3484
  const formData = new FormData();
3388
3485
  formData.append("file", audioBlob);
3389
3486
  formData.append("target_lang", elevenLabsLangCode);
3487
+ if (elevenLabsSourceLangCode) {
3488
+ formData.append("source_lang", elevenLabsSourceLangCode);
3489
+ }
3390
3490
  formData.append("num_speakers", numSpeakers.toString());
3391
- formData.append("name", `Mux Asset ${assetId} - auto to ${elevenLabsLangCode}`);
3491
+ formData.append(
3492
+ "name",
3493
+ `Mux Asset ${assetId} - ${elevenLabsSourceLangCode ?? "auto"} to ${elevenLabsLangCode}`
3494
+ );
3392
3495
  const dubbingResponse = await fetch("https://api.elevenlabs.io/v1/dubbing", {
3393
3496
  method: "POST",
3394
3497
  headers: {
@@ -3497,6 +3600,7 @@ async function translateAudio(assetId, toLanguageCode, options = {}) {
3497
3600
  "use workflow";
3498
3601
  const {
3499
3602
  provider = "elevenlabs",
3603
+ fromLanguageCode,
3500
3604
  numSpeakers = 0,
3501
3605
  // 0 = auto-detect
3502
3606
  uploadToMux = true,
@@ -3535,7 +3639,7 @@ async function translateAudio(assetId, toLanguageCode, options = {}) {
3535
3639
  }
3536
3640
  let audioUrl = `https://stream.mux.com/${playbackId}/audio.m4a`;
3537
3641
  if (policy === "signed") {
3538
- audioUrl = await signUrl(audioUrl, playbackId, void 0, "video", void 0, credentials);
3642
+ audioUrl = await signUrl(audioUrl, playbackId, "video", void 0, credentials);
3539
3643
  }
3540
3644
  console.warn("\u{1F399}\uFE0F Fetching audio from Mux...");
3541
3645
  let audioBuffer;
@@ -3546,13 +3650,18 @@ async function translateAudio(assetId, toLanguageCode, options = {}) {
3546
3650
  }
3547
3651
  console.warn("\u{1F399}\uFE0F Creating dubbing job in ElevenLabs...");
3548
3652
  const elevenLabsLangCode = toISO639_3(toLanguageCode);
3549
- console.warn(`\u{1F50D} Creating dubbing job for asset ${assetId} with language code: ${elevenLabsLangCode}`);
3653
+ const normalizedFromLanguageCode = fromLanguageCode?.trim();
3654
+ const elevenLabsSourceLangCode = normalizedFromLanguageCode ? toISO639_3(normalizedFromLanguageCode) : void 0;
3655
+ console.warn(
3656
+ `\u{1F50D} Creating dubbing job for asset ${assetId}: ${elevenLabsSourceLangCode ?? "auto"} -> ${elevenLabsLangCode}`
3657
+ );
3550
3658
  let dubbingId;
3551
3659
  try {
3552
3660
  dubbingId = await createElevenLabsDubbingJob({
3553
3661
  audioBuffer,
3554
3662
  assetId,
3555
3663
  elevenLabsLangCode,
3664
+ elevenLabsSourceLangCode,
3556
3665
  numSpeakers,
3557
3666
  credentials
3558
3667
  });
@@ -3910,6 +4019,8 @@ async function translateCaptions(assetId, fromLanguageCode, toLanguageCode, opti
3910
4019
  };
3911
4020
  }
3912
4021
  export {
4022
+ HIVE_SEXUAL_CATEGORIES,
4023
+ HIVE_VIOLENCE_CATEGORIES,
3913
4024
  SUMMARY_KEYWORD_LIMIT,
3914
4025
  askQuestions,
3915
4026
  burnedInCaptionsSchema,