@mux/ai 0.53.0 → 0.55.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
- import { l as WorkflowCredentialsInput, n as WorkflowScope, T as TextChunk, b as ChunkingStrategy, q as VTTCue, r as CompletedShotsResult, D as DeletedShotsResult, s as ErroredShotsResult, H as Hotspot, t as HotspotOptions, u as HotspotResponse, L as LOW_CONFIDENCE_THRESHOLD, v as MAX_CUE_TEXT_CHARS, w as ParseVTTCuesOptions, x as PendingShotsResult, y as Shot, z as ShotRequestOptions, B as ShotsResult, F as SkippedShotsResult, G as TranscriptEmptyReason, J as TranscriptFetchOptions, K as TranscriptFetchStepResult, N as TranscriptResult, O as WaitForShotsOptions, Q as buildTranscriptUrl, R as buildVttFromCueBlocks, X as buildVttFromTranslatedCueBlocks, Y as concatenateVttSegments, Z as extractTextFromVTT, _ as extractTimestampedTranscript, $ as fetchTranscriptDataForUrl, a0 as fetchTranscriptForAsset, a1 as filterVttByScope, a2 as findCaptionTrack, a3 as getHotspotsForAsset, a4 as getHotspotsForPlaybackId, a5 as getHotspotsForVideo, a6 as getReadyTextTracks, a7 as getReliableLanguageCode, a8 as getShotsForAsset, a9 as parseVTTCues, aa as replaceCueText, ab as requestShotsForAsset, ac as sanitizeUntrustedText, ad as secondsToTimestamp, ae as splitVttPreambleAndCueBlocks, af as stripVttMetadataBlocks, ag as vttTimestampToSeconds, ah as waitForShotsForAsset } from './transcripts-BCtzC8hZ.js';
1
+ import { l as WorkflowCredentialsInput, n as WorkflowScope, h as TextChunk, b as ChunkingStrategy, q as VTTCue, r as CompletedShotsResult, D as DeletedShotsResult, s as ErroredShotsResult, H as Hotspot, t as HotspotOptions, u as HotspotResponse, L as LOW_CONFIDENCE_THRESHOLD, v as MAX_CUE_TEXT_CHARS, w as ParseVTTCuesOptions, x as PendingShotsResult, y as Shot, z as ShotRequestOptions, B as ShotsResult, F as SkippedShotsResult, G as TranscriptEmptyReason, J as TranscriptFetchOptions, K as TranscriptFetchStepResult, N as TranscriptResult, O as WaitForShotsOptions, Q as buildTranscriptUrl, R as buildVttFromCueBlocks, X as buildVttFromTranslatedCueBlocks, Y as concatenateVttSegments, Z as extractTextFromVTT, _ as extractTimestampedTranscript, $ as fetchTranscriptDataForUrl, a0 as fetchTranscriptForAsset, a1 as filterVttByScope, a2 as findCaptionTrack, a3 as getHotspotsForAsset, a4 as getHotspotsForPlaybackId, a5 as getHotspotsForVideo, a6 as getReadyTextTracks, a7 as getReliableLanguageCode, a8 as getShotsForAsset, a9 as parseVTTCues, aa as replaceCueText, ab as requestShotsForAsset, ac as sanitizeUntrustedText, ad as secondsToTimestamp, ae as splitVttPreambleAndCueBlocks, af as stripVttMetadataBlocks, ag as vttTimestampToSeconds, ah as waitForShotsForAsset } from './transcripts-DxSHvTk9.js';
2
2
 
3
3
  interface HeatmapOptions {
4
4
  /** Time window for results, e.g., '7:days' (default: '7:days') */
@@ -1,5 +1,5 @@
1
1
  import { LanguageModel, generateText as generateText$1 } from 'ai';
2
- import { l as WorkflowCredentialsInput, M as MuxAsset, A as AssetTextTrack, i as TokenUsage, f as ScopedMuxAIOptions, I as ImageSubmissionMode, d as MuxAIOptions, b as ChunkingStrategy, k as VideoEmbeddingsResult, H as Hotspot, y as Shot, q as VTTCue, j as ToneType, g as StorageAdapter } from './transcripts-BCtzC8hZ.js';
2
+ import { l as WorkflowCredentialsInput, M as MuxAsset, A as AssetTextTrack, T as TokenUsage, f as ScopedMuxAIOptions, I as ImageSubmissionMode, d as MuxAIOptions, b as ChunkingStrategy, k as VideoEmbeddingsResult, H as Hotspot, y as Shot, q as VTTCue, j as ToneType, g as StorageAdapter } from './transcripts-DxSHvTk9.js';
3
3
  import { z } from 'zod';
4
4
  import { S as SafetyReport, e as PromptOverrides, h as SceneContextItemV1, D as DEFAULT_DESCRIPTION_LENGTH, a as DEFAULT_SUMMARY_KEYWORD_LIMIT, b as DEFAULT_TITLE_LENGTH } from './summarization-BFmhXeAu.js';
5
5
  import { createAnthropic } from '@ai-sdk/anthropic';
@@ -1488,8 +1488,9 @@ type ThumbnailModerationSkipReason = "no_video_track" | "no_video_in_scope";
1488
1488
  * body was empty, had no parseable cues, or no cue fell inside `scope`.
1489
1489
  * Treated as valid-but-empty, not as an error.
1490
1490
  * - `"unsupported_provider"` — the selected provider is image-only.
1491
+ * - `"disabled"` — the caller passed `includeTranscript: false` for a video asset.
1491
1492
  */
1492
- type TranscriptModerationSkipReason = "no_ready_text_track" | "no_cues" | "unsupported_provider";
1493
+ type TranscriptModerationSkipReason = "no_ready_text_track" | "no_cues" | "unsupported_provider" | "disabled";
1493
1494
  type ThumbnailModerationStatus = ModerationSurfaceStatus<ThumbnailModerationSkipReason>;
1494
1495
  type TranscriptModerationStatus = ModerationSurfaceStatus<TranscriptModerationSkipReason>;
1495
1496
  /** Aggregated moderation payload returned from `getModerationScores`. */
@@ -1575,6 +1576,12 @@ interface ModerationOptions extends ScopedMuxAIOptions {
1575
1576
  * subtitles track is preferred and otherwise the first ready text track is used.
1576
1577
  */
1577
1578
  languageCode?: string;
1579
+ /**
1580
+ * Moderate the caption transcript alongside thumbnails (defaults to true). When false,
1581
+ * video assets are moderated on thumbnails only. Ignored for audio-only assets, where
1582
+ * the transcript is the only surface that can be moderated.
1583
+ */
1584
+ includeTranscript?: boolean;
1578
1585
  /** Override the default sexual/violence thresholds (0-1). */
1579
1586
  thresholds?: {
1580
1587
  sexual?: number;
@@ -1654,7 +1661,8 @@ declare function buildTranscriptWindows(cues: VTTCue[], duration: number): Trans
1654
1661
  * Moderate a Mux asset. By default both surfaces are moderated where available:
1655
1662
  * - Thumbnails: sampled storyboard frames (image moderation). Skipped for audio-only assets.
1656
1663
  * - Transcript: caption text in time windows (text moderation). Skipped when no
1657
- * usable caption track exists or the provider is image-only.
1664
+ * usable caption track exists, the provider is image-only, or `includeTranscript`
1665
+ * is false for a video asset.
1658
1666
  *
1659
1667
  * Each surface reports its outcome in `thumbnailModeration` / `transcriptModeration`.
1660
1668
  * Throws when neither surface can be moderated, since a result with nothing
package/dist/index.d.ts CHANGED
@@ -1,8 +1,8 @@
1
- export { I as ISO639_1, a as ISO639_3, L as LanguageCodePair, M as MUX_TRACK_PASSTHROUGH_MAX_CHARS, R as ReplaceAndCreateTextTrackInput, b as ReplaceAndCreateTextTrackResult, c as ReplaceAndCreateTrackInput, d as ReplaceExistingTracksPolicy, e as ReplaceSourceTrackInput, f as ReplaceableTrackType, S as SourceTrackReplacementPolicy, g as SupportedISO639_1, h as SupportedISO639_3, T as TextTrackReplacementPlan, i as TextTrackSummary, j as TextTrackTarget, k as TrackSummary, l as TrackTarget, m as buildMuxAiTrackPassthrough, n as findNameCollisionTextTracks, o as findSameLanguageTextTracks, p as getLanguageCodePair, q as getLanguageName, r as isDuplicateTrackNameError, s as isMuxGeneratedTextTrack, t as isUndeterminedLanguageCode, u as isValidISO639_1, v as isValidISO639_3, w as normalizeTrackLanguageCode, x as normalizeTrackName, y as planSourceTrackReplacement, z as planTextTrackReplacement, A as replaceAndCreateTextTrack, B as replaceAndCreateTrack, C as replaceSourceTrack, D as summarizeTextTrack, E as toISO639_1, F as toISO639_3, G as validateTrackPassthrough, H as workflows } from './index-BbxJ0X25.js';
2
- import { M as MuxAsset, W as WorkflowCredentials, S as StoragePutObjectInput, a as StoragePresignGetObjectInput } from './transcripts-BCtzC8hZ.js';
3
- export { A as AssetTextTrack, C as ChunkEmbedding, b as ChunkingStrategy, E as Encrypted, c as EncryptedPayload, I as ImageSubmissionMode, d as MuxAIOptions, P as PlaybackAsset, e as PlaybackPolicy, f as ScopedMuxAIOptions, g as StorageAdapter, T as TextChunk, h as TokenChunkingConfig, i as TokenUsage, j as ToneType, U as UsageMetadata, V as VTTChunkingConfig, k as VideoEmbeddingsResult, l as WorkflowCredentialsInput, m as WorkflowMuxClient, n as WorkflowScope, o as decryptFromWorkflow, p as encryptForWorkflow } from './transcripts-BCtzC8hZ.js';
1
+ export { I as ISO639_1, a as ISO639_3, L as LanguageCodePair, M as MUX_TRACK_PASSTHROUGH_MAX_CHARS, R as ReplaceAndCreateTextTrackInput, b as ReplaceAndCreateTextTrackResult, c as ReplaceAndCreateTrackInput, d as ReplaceExistingTracksPolicy, e as ReplaceSourceTrackInput, f as ReplaceableTrackType, S as SourceTrackReplacementPolicy, g as SupportedISO639_1, h as SupportedISO639_3, T as TextTrackReplacementPlan, i as TextTrackSummary, j as TextTrackTarget, k as TrackSummary, l as TrackTarget, m as buildMuxAiTrackPassthrough, n as findNameCollisionTextTracks, o as findSameLanguageTextTracks, p as getLanguageCodePair, q as getLanguageName, r as isDuplicateTrackNameError, s as isMuxGeneratedTextTrack, t as isUndeterminedLanguageCode, u as isValidISO639_1, v as isValidISO639_3, w as normalizeTrackLanguageCode, x as normalizeTrackName, y as planSourceTrackReplacement, z as planTextTrackReplacement, A as replaceAndCreateTextTrack, B as replaceAndCreateTrack, C as replaceSourceTrack, D as summarizeTextTrack, E as toISO639_1, F as toISO639_3, G as validateTrackPassthrough, H as workflows } from './index-BBk0AcY1.js';
4
2
  import { WORKFLOW_SERIALIZE, WORKFLOW_DESERIALIZE } from '@workflow/serde';
5
- export { i as primitives } from './index-cbaPe-aE.js';
3
+ import { T as TokenUsage, M as MuxAsset, W as WorkflowCredentials, S as StoragePutObjectInput, a as StoragePresignGetObjectInput } from './transcripts-DxSHvTk9.js';
4
+ export { A as AssetTextTrack, C as ChunkEmbedding, b as ChunkingStrategy, E as Encrypted, c as EncryptedPayload, I as ImageSubmissionMode, d as MuxAIOptions, P as PlaybackAsset, e as PlaybackPolicy, f as ScopedMuxAIOptions, g as StorageAdapter, h as TextChunk, i as TokenChunkingConfig, j as ToneType, U as UsageMetadata, V as VTTChunkingConfig, k as VideoEmbeddingsResult, l as WorkflowCredentialsInput, m as WorkflowMuxClient, n as WorkflowScope, o as decryptFromWorkflow, p as encryptForWorkflow } from './transcripts-DxSHvTk9.js';
5
+ export { i as primitives } from './index-B-rLy1do.js';
6
6
  import 'ai';
7
7
  import 'zod';
8
8
  import './summarization-BFmhXeAu.js';
@@ -11,9 +11,25 @@ import '@ai-sdk/google';
11
11
  import '@ai-sdk/openai';
12
12
  import '@mux/ts';
13
13
 
14
- var version = "0.53.0";
14
+ var version = "0.55.0";
15
15
 
16
16
  type MuxAiErrorType = "validation_error" | "processing_error" | "timeout_error" | "content_policy_error";
17
+ interface ModelCallDiagnostics {
18
+ phase: "brief" | "draft" | "format_repair" | "length_repair";
19
+ provider: string;
20
+ modelId: string;
21
+ maxOutputTokens?: number;
22
+ }
23
+ interface SerializedMuxAiError {
24
+ message: string;
25
+ publicType: MuxAiErrorType;
26
+ retryable: boolean;
27
+ stack?: string;
28
+ usage?: TokenUsage;
29
+ finishReason?: string;
30
+ rawFinishReason?: string;
31
+ diagnostics?: ModelCallDiagnostics;
32
+ }
17
33
  /**
18
34
  * An error whose message is safe to surface verbatim to the customer.
19
35
  *
@@ -23,9 +39,8 @@ type MuxAiErrorType = "validation_error" | "processing_error" | "timeout_error"
23
39
  * this brand (unexpected SDK failures, upstream provider errors, etc.)
24
40
  * should be treated as internal and obfuscated.
25
41
  *
26
- * Own properties (`publicMessage`, `publicType`, `retryable`) survive
27
- * JSON serialization across workflow step boundaries, unlike inherited
28
- * `Error.message`.
42
+ * Custom Workflow serializers preserve safe metadata across step boundaries.
43
+ * The runtime's built-in Error serializer drops arbitrary own properties.
29
44
  */
30
45
  declare class MuxAiError extends Error {
31
46
  /** Structural brand for duck-type identification. */
@@ -36,6 +51,8 @@ declare class MuxAiError extends Error {
36
51
  readonly publicMessage: string;
37
52
  /** Whether the customer should retry the job. */
38
53
  readonly retryable: boolean;
54
+ usage?: TokenUsage;
55
+ diagnostics?: ModelCallDiagnostics;
39
56
  /** Internal diagnostic failure; non-enumerable and excluded from JSON. */
40
57
  readonly cause?: unknown;
41
58
  constructor(message: string, opts?: {
@@ -44,6 +61,8 @@ declare class MuxAiError extends Error {
44
61
  cause?: unknown;
45
62
  });
46
63
  static is(value: unknown): value is MuxAiError;
64
+ static [WORKFLOW_SERIALIZE](instance: MuxAiError): SerializedMuxAiError;
65
+ static [WORKFLOW_DESERIALIZE](value: SerializedMuxAiError): MuxAiError;
47
66
  }
48
67
  declare function wrapError(error: unknown, message: string): never;
49
68
 
@@ -131,4 +150,4 @@ declare class WorkflowStorageClient {
131
150
  }
132
151
  declare function createWorkflowStorageClient(options?: WorkflowStorageClientOptions): WorkflowStorageClient;
133
152
 
134
- export { type LegacyMp4SupportRendition, MuxAiError, type MuxAiErrorType, MuxAsset, type MuxPlaybackService, STATIC_RENDITIONS_GUIDE_URL, StoragePresignGetObjectInput, StoragePutObjectInput, WorkflowCredentials, type WorkflowCredentialsProvider, WorkflowStorageClient, createWorkflowStorageClient, getLegacyMp4SupportRenditionSizeError, getMuxPlaybackOrigin, resolveLegacyMp4SupportRendition, setWorkflowCredentialsProvider, usesLegacyMp4Support, version, wrapError };
153
+ export { type LegacyMp4SupportRendition, MuxAiError, type MuxAiErrorType, MuxAsset, type MuxPlaybackService, STATIC_RENDITIONS_GUIDE_URL, StoragePresignGetObjectInput, StoragePutObjectInput, TokenUsage, WorkflowCredentials, type WorkflowCredentialsProvider, WorkflowStorageClient, createWorkflowStorageClient, getLegacyMp4SupportRenditionSizeError, getMuxPlaybackOrigin, resolveLegacyMp4SupportRendition, setWorkflowCredentialsProvider, usesLegacyMp4Support, version, wrapError };
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.53.0";
8
+ var version = "0.55.0";
9
9
 
10
10
  // src/lib/language-codes.ts
11
11
  var ISO639_1_TO_3 = {
@@ -178,6 +178,10 @@ function getLanguageName(code) {
178
178
  }
179
179
  }
180
180
 
181
+ // node_modules/@workflow/serde/dist/index.js
182
+ var WORKFLOW_SERIALIZE = /* @__PURE__ */ Symbol.for("workflow-serialize");
183
+ var WORKFLOW_DESERIALIZE = /* @__PURE__ */ Symbol.for("workflow-deserialize");
184
+
181
185
  // src/lib/prompt-fragments.ts
182
186
  import dedent from "dedent";
183
187
 
@@ -699,7 +703,7 @@ function rethrowWithTokenUsage(error, collectedUsage) {
699
703
  }
700
704
 
701
705
  // src/lib/mux-ai-error.ts
702
- var MuxAiError = class extends Error {
706
+ var MuxAiError = class _MuxAiError extends Error {
703
707
  constructor(message, opts) {
704
708
  super(message);
705
709
  /** Structural brand for duck-type identification. */
@@ -715,6 +719,32 @@ var MuxAiError = class extends Error {
715
719
  static is(value) {
716
720
  return typeof value === "object" && value !== null && "__robots_error" in value && value.__robots_error === true;
717
721
  }
722
+ static [WORKFLOW_SERIALIZE](instance) {
723
+ const incomplete = instance;
724
+ return {
725
+ message: instance.publicMessage,
726
+ publicType: instance.publicType,
727
+ retryable: instance.retryable,
728
+ stack: instance.stack,
729
+ usage: getErrorTokenUsage(instance),
730
+ finishReason: incomplete.finishReason,
731
+ rawFinishReason: incomplete.rawFinishReason,
732
+ diagnostics: instance.diagnostics
733
+ };
734
+ }
735
+ static [WORKFLOW_DESERIALIZE](value) {
736
+ const error = new _MuxAiError(value.message, { type: value.publicType, retryable: value.retryable });
737
+ Object.assign(error, {
738
+ usage: value.usage,
739
+ diagnostics: value.diagnostics,
740
+ ...value.finishReason !== void 0 && { finishReason: value.finishReason },
741
+ ...value.rawFinishReason !== void 0 && { rawFinishReason: value.rawFinishReason }
742
+ });
743
+ if (value.stack !== void 0) {
744
+ error.stack = value.stack;
745
+ }
746
+ return error;
747
+ }
718
748
  };
719
749
  function extractErrorDetail(error) {
720
750
  if (error instanceof Error && error.message) {
@@ -1528,10 +1558,6 @@ async function getMuxThumbnailBaseUrl(playbackId, credentials) {
1528
1558
  return getMuxImageBaseUrl(playbackId, "thumbnail", credentials);
1529
1559
  }
1530
1560
 
1531
- // node_modules/@workflow/serde/dist/index.js
1532
- var WORKFLOW_SERIALIZE = /* @__PURE__ */ Symbol.for("workflow-serialize");
1533
- var WORKFLOW_DESERIALIZE = /* @__PURE__ */ Symbol.for("workflow-deserialize");
1534
-
1535
1561
  // src/lib/s3-sigv4.ts
1536
1562
  var AWS4_ALGORITHM = "AWS4-HMAC-SHA256";
1537
1563
  var AWS4_REQUEST_TERMINATOR = "aws4_request";
@@ -3487,7 +3513,8 @@ async function withRetry(fn, {
3487
3513
  maxRetries = DEFAULT_RETRY_OPTIONS.maxRetries,
3488
3514
  baseDelay = DEFAULT_RETRY_OPTIONS.baseDelay,
3489
3515
  maxDelay = DEFAULT_RETRY_OPTIONS.maxDelay,
3490
- shouldRetry = defaultShouldRetry
3516
+ shouldRetry = defaultShouldRetry,
3517
+ onRetry
3491
3518
  } = {}) {
3492
3519
  let lastError;
3493
3520
  for (let attempt = 0; attempt <= maxRetries; attempt++) {
@@ -3500,6 +3527,7 @@ async function withRetry(fn, {
3500
3527
  if (isLastAttempt || !shouldRetry(retryError, attempt + 1)) {
3501
3528
  throw error;
3502
3529
  }
3530
+ onRetry?.(error, attempt + 1);
3503
3531
  const delay = calculateDelay(attempt + 1, baseDelay, maxDelay);
3504
3532
  console.warn(
3505
3533
  `Attempt ${attempt + 1} failed: ${retryError.message}. Retrying in ${Math.round(delay)}ms...`
@@ -3593,10 +3621,54 @@ async function withContentPolicyErrorHandling(operation) {
3593
3621
  }
3594
3622
  }
3595
3623
  async function withContentPolicyAwareRetry(operation, retryOptions) {
3596
- return withRetry(
3597
- () => withContentPolicyErrorHandling(operation),
3598
- retryOptions
3599
- );
3624
+ const retriedUsage = [];
3625
+ try {
3626
+ const response = await withRetry(
3627
+ () => withContentPolicyErrorHandling(operation),
3628
+ {
3629
+ ...retryOptions,
3630
+ onRetry: (error, attempt) => {
3631
+ const usage = getErrorTokenUsage(error);
3632
+ if (usage) {
3633
+ retriedUsage.push(usage);
3634
+ }
3635
+ retryOptions?.onRetry?.(error, attempt);
3636
+ }
3637
+ }
3638
+ );
3639
+ return includeRetriedUsage(response, retriedUsage);
3640
+ } catch (error) {
3641
+ rethrowWithTokenUsage(error, retriedUsage);
3642
+ }
3643
+ }
3644
+ function includeRetriedUsage(response, retriedUsage) {
3645
+ if (!retriedUsage.length || typeof response !== "object" || response === null) {
3646
+ return response;
3647
+ }
3648
+ const result = response;
3649
+ const merge = (usage) => {
3650
+ const combined = aggregateTokenUsage([...retriedUsage, getErrorTokenUsage({ usage }) ?? {}]);
3651
+ return {
3652
+ ...usage,
3653
+ ...combined,
3654
+ ...combined.cachedInputTokens !== void 0 || combined.cacheWriteTokens !== void 0 ? {
3655
+ inputTokenDetails: {
3656
+ ...usage?.inputTokenDetails,
3657
+ ...combined.cachedInputTokens !== void 0 && { cacheReadTokens: combined.cachedInputTokens },
3658
+ ...combined.cacheWriteTokens !== void 0 && { cacheWriteTokens: combined.cacheWriteTokens }
3659
+ }
3660
+ } : {},
3661
+ ...combined.reasoningTokens !== void 0 ? {
3662
+ outputTokenDetails: { ...usage?.outputTokenDetails, reasoningTokens: combined.reasoningTokens }
3663
+ } : {}
3664
+ };
3665
+ };
3666
+ const descriptors = Object.getOwnPropertyDescriptors(response);
3667
+ descriptors.usage = { value: merge(result.usage), enumerable: true, configurable: true };
3668
+ if ("totalUsage" in result) {
3669
+ descriptors.totalUsage = { value: merge(result.totalUsage), enumerable: true, configurable: true };
3670
+ }
3671
+ return Object.create(Object.getPrototypeOf(response), descriptors);
3600
3672
  }
3601
3673
  function rethrowContentPolicyError(error) {
3602
3674
  const block = extractContentPolicyBlock(error);
@@ -3732,6 +3804,35 @@ function findBlockedCategory(ratings) {
3732
3804
  return ratings?.find((rating) => rating.blocked === true)?.category ?? void 0;
3733
3805
  }
3734
3806
 
3807
+ // src/lib/model-call-diagnostics.ts
3808
+ import { APICallError as APICallError3 } from "ai";
3809
+ function recordModelCallFailure(error, diagnostics, observedUsage = getErrorTokenUsage(error)) {
3810
+ if (typeof error !== "object" || error === null) {
3811
+ return;
3812
+ }
3813
+ const failure = error;
3814
+ if (failure.diagnostics) {
3815
+ return;
3816
+ }
3817
+ failure.diagnostics = { ...diagnostics };
3818
+ console.warn("[@mux/ai] Model call failed.", {
3819
+ ...diagnostics,
3820
+ finishReason: failure.finishReason,
3821
+ statusCode: failure.statusCode,
3822
+ errorName: error instanceof Error ? error.name : void 0,
3823
+ requestId: APICallError3.isInstance(error) ? error.responseHeaders?.["x-request-id"] ?? error.responseHeaders?.["request-id"] : void 0,
3824
+ usage: observedUsage
3825
+ });
3826
+ }
3827
+ async function withModelCallDiagnostics(diagnostics, operation) {
3828
+ try {
3829
+ return await operation();
3830
+ } catch (error) {
3831
+ recordModelCallFailure(error, diagnostics);
3832
+ throw error;
3833
+ }
3834
+ }
3835
+
3735
3836
  // src/lib/text-repair-capabilities.ts
3736
3837
  var OPENAI_REPAIR_CAPABILITIES = {
3737
3838
  "gpt-5.1": { supportsReasoningNone: true },
@@ -3893,13 +3994,14 @@ function createTextRepairGenerator(options) {
3893
3994
  return async (plan) => {
3894
3995
  let usage;
3895
3996
  const started = Date.now();
3997
+ const maxOutputTokens = options.maxOutputTokens ?? Math.min(32768, Math.max(1024, plan.spans.reduce((total, span) => total + Math.min(...span.budgets.map((limit) => limit.value * (limit.unit === "words" ? 4 : 2))), 0) + 128));
3896
3998
  try {
3897
3999
  const response = await withContentPolicyErrorHandling(() => generateText({
3898
4000
  model: options.model,
3899
4001
  providerOptions: options.providerOptions ?? getTextRepairProviderOptions(options.model),
3900
4002
  maxRetries: 0,
3901
4003
  abortSignal: AbortSignal.timeout(options.timeoutMs ?? 3e4),
3902
- maxOutputTokens: options.maxOutputTokens ?? Math.min(32768, Math.max(1024, plan.spans.reduce((total, span) => total + Math.min(...span.budgets.map((limit) => limit.value * (limit.unit === "words" ? 4 : 2))), 0) + 128)),
4004
+ maxOutputTokens,
3903
4005
  output: Output.object({ schema: jsonSchema(repairJsonSchema(plan)), name: "paragraph_replacements" }),
3904
4006
  system: promptDedent`
3905
4007
  ${NON_DISCLOSURE_CONSTRAINT}
@@ -3927,6 +4029,12 @@ function createTextRepairGenerator(options) {
3927
4029
  return { replacements: output, usage, elapsedMs: Date.now() - started };
3928
4030
  } catch (error) {
3929
4031
  const cause = NoObjectGeneratedError2.isInstance(error) && error.finishReason && error.finishReason !== "stop" ? new IncompleteGenerationError(error.finishReason, void 0, getErrorTokenUsage(error)) : error;
4032
+ recordModelCallFailure(cause, {
4033
+ phase: "length_repair",
4034
+ provider: typeof options.model === "object" ? options.model.provider : "gateway",
4035
+ modelId: typeof options.model === "object" ? options.model.modelId : options.model,
4036
+ maxOutputTokens
4037
+ }, usage ?? getErrorTokenUsage(cause));
3930
4038
  return { usage: usage ?? getErrorTokenUsage(cause), elapsedMs: Date.now() - started, error: cause instanceof Error ? cause.message : String(cause), errorCause: cause };
3931
4039
  }
3932
4040
  };
@@ -6789,7 +6897,7 @@ async function generateEngagementInsightsInternal(assetId, options, collectedUsa
6789
6897
  }
6790
6898
 
6791
6899
  // src/workflows/generate-text.ts
6792
- import { APICallError as APICallError3, generateText as generateTextWithModel, Output as Output7 } from "ai";
6900
+ import { APICallError as APICallError4, generateText as generateTextWithModel, Output as Output7 } from "ai";
6793
6901
  import { z as z8 } from "zod";
6794
6902
  var GENERATE_TEXT_MAX_VARIANTS = 5;
6795
6903
  var GENERATE_TEXT_MAX_ARTIFACTS = 5;
@@ -6912,7 +7020,7 @@ function resolveGenerateTextOptions(options) {
6912
7020
  }
6913
7021
  return { ...options, variants };
6914
7022
  }
6915
- var LENGTH_TARGET_RATIO = 0.9;
7023
+ var LENGTH_TARGET_RATIO = 0.85;
6916
7024
  function resolveLengthTarget(limit) {
6917
7025
  return Math.max(1, Math.floor(limit.value * LENGTH_TARGET_RATIO));
6918
7026
  }
@@ -7201,122 +7309,124 @@ function readUsage(response) {
7201
7309
  }
7202
7310
  async function extractBriefWithModel(args) {
7203
7311
  "use step";
7204
- const model = await createLanguageModelFromConfig(args.provider, args.modelId, args.credentials);
7205
- const response = await withContentPolicyAwareRetry(() => generateTextWithModel({
7206
- model,
7207
- maxRetries: 0,
7208
- output: Output7.object({
7209
- name: "editorial_brief",
7210
- description: "Source-grounded editorial brief used to write every requested artifact.",
7211
- schema: briefSchema
7212
- }),
7213
- system: args.systemPrompt,
7214
- messages: [
7215
- {
7216
- role: "user",
7217
- content: [
7218
- { type: "text", text: args.userPrompt },
7219
- ...args.imageUrls.map((url) => ({ type: "image", image: url }))
7220
- ]
7221
- }
7222
- ]
7223
- }));
7224
- const output = getGeneratedOutputWithContentPolicyHandling(response);
7225
- if (!output) {
7226
- throw new Error("Editorial brief output missing");
7227
- }
7228
- return {
7229
- brief: clampBriefArrays(briefSchema.parse(output)),
7230
- usage: readUsage(response),
7231
- unexpectedKeys: detectUnexpectedKeysFromRawText(response.text, briefSchema.keyof().options)
7232
- };
7233
- }
7234
- var artifactSchema = z8.object({ content: z8.string() });
7235
- async function generateArtifactWithModel(args) {
7236
- "use step";
7237
- const model = await createLanguageModelFromConfig(args.provider, args.modelId, args.credentials);
7238
- const attempt = async (messages) => {
7312
+ return withModelCallDiagnostics({ phase: "brief", provider: args.provider, modelId: args.modelId }, async () => {
7313
+ const model = await createLanguageModelFromConfig(args.provider, args.modelId, args.credentials);
7239
7314
  const response = await withContentPolicyAwareRetry(() => generateTextWithModel({
7240
7315
  model,
7241
7316
  maxRetries: 0,
7242
7317
  output: Output7.object({
7243
- name: "generated_text",
7244
- description: "One finished artifact written from the editorial brief.",
7245
- schema: artifactSchema
7318
+ name: "editorial_brief",
7319
+ description: "Source-grounded editorial brief used to write every requested artifact.",
7320
+ schema: briefSchema
7246
7321
  }),
7247
7322
  system: args.systemPrompt,
7248
- messages
7323
+ messages: [
7324
+ {
7325
+ role: "user",
7326
+ content: [
7327
+ { type: "text", text: args.userPrompt },
7328
+ ...args.imageUrls.map((url) => ({ type: "image", image: url }))
7329
+ ]
7330
+ }
7331
+ ]
7249
7332
  }));
7250
7333
  const output = getGeneratedOutputWithContentPolicyHandling(response);
7251
7334
  if (!output) {
7252
- throw new Error("Generated text output missing");
7335
+ throw new Error("Editorial brief output missing");
7253
7336
  }
7254
7337
  return {
7255
- content: artifactSchema.parse(output).content.trim(),
7338
+ brief: clampBriefArrays(briefSchema.parse(output)),
7256
7339
  usage: readUsage(response),
7257
- unexpectedKeys: detectUnexpectedKeysFromRawText(response.text, artifactSchema.keyof().options)
7340
+ unexpectedKeys: detectUnexpectedKeysFromRawText(response.text, briefSchema.keyof().options)
7258
7341
  };
7259
- };
7260
- const judge = (content) => content ? findGenerateTextLengthViolation(content, args.limits) : "empty";
7261
- const initialMessages = [{ role: "user", content: args.userPrompt }];
7262
- const first = await attempt(initialMessages);
7263
- const firstViolation = judge(first.content);
7264
- const firstEscaped = hasEscapedLineBreaks(first.content);
7265
- if (!firstViolation && !firstEscaped) {
7266
- return { content: first.content, usages: [first.usage], unexpectedKeys: first.unexpectedKeys };
7267
- }
7268
- let candidate = first;
7269
- const usages = [first.usage];
7270
- const unexpectedKeys = [...first.unexpectedKeys];
7271
- if (firstViolation === "empty" || firstEscaped) {
7272
- const feedback = [
7273
- firstViolation && firstViolation !== "empty" ? describeLengthRevision(firstViolation) : void 0,
7274
- firstEscaped ? ESCAPED_TEXT_REVISION : void 0
7275
- ].filter((line) => Boolean(line)).join("\n");
7276
- const revisionMessages = firstViolation === "empty" ? [{ role: "user", content: `${args.userPrompt}
7342
+ });
7343
+ }
7344
+ var artifactSchema = z8.object({ content: z8.string() });
7345
+ async function generateArtifactWithModel(args) {
7346
+ "use step";
7347
+ const diagnostics = { phase: "draft", provider: args.provider, modelId: args.modelId };
7348
+ return withModelCallDiagnostics(diagnostics, async () => {
7349
+ const model = await createLanguageModelFromConfig(args.provider, args.modelId, args.credentials);
7350
+ const attempt = async (messages) => {
7351
+ const response = await withContentPolicyAwareRetry(() => generateTextWithModel({
7352
+ model,
7353
+ maxRetries: 0,
7354
+ output: Output7.object({
7355
+ name: "generated_text",
7356
+ description: "One finished artifact written from the editorial brief.",
7357
+ schema: artifactSchema
7358
+ }),
7359
+ system: args.systemPrompt,
7360
+ messages
7361
+ }));
7362
+ const output = getGeneratedOutputWithContentPolicyHandling(response);
7363
+ if (!output) {
7364
+ throw new Error("Generated text output missing");
7365
+ }
7366
+ return {
7367
+ content: artifactSchema.parse(output).content.trim(),
7368
+ usage: readUsage(response),
7369
+ unexpectedKeys: detectUnexpectedKeysFromRawText(response.text, artifactSchema.keyof().options)
7370
+ };
7371
+ };
7372
+ const judge = (content) => content ? findGenerateTextLengthViolation(content, args.limits) : "empty";
7373
+ const initialMessages = [{ role: "user", content: args.userPrompt }];
7374
+ const first = await attempt(initialMessages);
7375
+ const firstViolation = judge(first.content);
7376
+ const firstEscaped = hasEscapedLineBreaks(first.content);
7377
+ if (!firstViolation && !firstEscaped) {
7378
+ return { content: first.content, usages: [first.usage], unexpectedKeys: first.unexpectedKeys };
7379
+ }
7380
+ let candidate = first;
7381
+ const usages = [first.usage];
7382
+ const unexpectedKeys = [...first.unexpectedKeys];
7383
+ if (firstViolation === "empty" || firstEscaped) {
7384
+ const feedback = [
7385
+ firstViolation && firstViolation !== "empty" ? describeLengthRevision(firstViolation) : void 0,
7386
+ firstEscaped ? ESCAPED_TEXT_REVISION : void 0
7387
+ ].filter((line) => Boolean(line)).join("\n");
7388
+ const revisionMessages = firstViolation === "empty" ? [{ role: "user", content: `${args.userPrompt}
7277
7389
 
7278
7390
  ${renderSection({ tag: "revision_request", content: "The previous draft was empty. Write the complete artifact." })}` }] : [...initialMessages, { role: "assistant", content: JSON.stringify({ content: first.content }) }, { role: "user", content: renderSection({ tag: "revision_request", content: feedback }) }];
7279
- candidate = await attempt(revisionMessages).catch((error) => rethrowWithTokenUsage(error, usages));
7280
- usages.push(candidate.usage);
7281
- unexpectedKeys.push(...candidate.unexpectedKeys);
7282
- }
7283
- const violation = judge(candidate.content);
7284
- if (!violation || violation === "empty") {
7285
- return { content: candidate.content, usages, unexpectedKeys, violation };
7286
- }
7287
- const repaired = await repairText({
7288
- content: candidate.content,
7289
- limits: args.limits,
7290
- protectedTerms: args.protectedTerms,
7291
- model
7292
- });
7293
- usages.push(...repaired.attempts.flatMap((item) => item.call.usage ? [item.call.usage] : []));
7294
- if (repaired.status !== "valid") {
7295
- const cause = repaired.attempts.at(-1)?.call.errorCause;
7296
- let failure;
7297
- if (isIncompleteGenerationError(cause)) {
7298
- failure = new IncompleteGenerationError(cause.finishReason, cause.rawFinishReason);
7299
- } else if (MuxAiError.is(cause)) {
7300
- failure = new MuxAiError(cause.publicMessage, { type: cause.publicType, retryable: cause.retryable });
7301
- } else {
7302
- const apiError = APICallError3.isInstance(cause) ? cause : void 0;
7303
- console.warn("[@mux/ai] Bounded text repair failed.", {
7304
- provider: args.provider,
7305
- modelId: args.modelId,
7306
- reason: repaired.reason,
7307
- errorName: cause instanceof Error ? cause.name : void 0,
7308
- statusCode: apiError?.statusCode,
7309
- requestId: apiError?.responseHeaders?.["x-request-id"] ?? apiError?.responseHeaders?.["request-id"]
7310
- });
7311
- failure = new MuxAiError("Unable to complete text generation.", {
7312
- type: "processing_error",
7313
- retryable: apiError?.isRetryable ?? true,
7314
- cause
7315
- });
7391
+ diagnostics.phase = "format_repair";
7392
+ candidate = await attempt(revisionMessages).catch((error) => rethrowWithTokenUsage(error, usages));
7393
+ usages.push(candidate.usage);
7394
+ unexpectedKeys.push(...candidate.unexpectedKeys);
7395
+ }
7396
+ const violation = judge(candidate.content);
7397
+ if (!violation || violation === "empty") {
7398
+ return { content: candidate.content, usages, unexpectedKeys, violation };
7399
+ }
7400
+ diagnostics.phase = "length_repair";
7401
+ const repaired = await repairText({
7402
+ content: candidate.content,
7403
+ limits: args.limits,
7404
+ protectedTerms: args.protectedTerms,
7405
+ model
7406
+ });
7407
+ usages.push(...repaired.attempts.flatMap((item) => item.call.usage ? [item.call.usage] : []));
7408
+ if (repaired.status !== "valid") {
7409
+ const cause = repaired.attempts.at(-1)?.call.errorCause;
7410
+ let failure;
7411
+ if (isIncompleteGenerationError(cause)) {
7412
+ failure = new IncompleteGenerationError(cause.finishReason, cause.rawFinishReason);
7413
+ } else if (MuxAiError.is(cause)) {
7414
+ failure = new MuxAiError(cause.publicMessage, { type: cause.publicType, retryable: cause.retryable });
7415
+ } else {
7416
+ const apiError = APICallError4.isInstance(cause) ? cause : void 0;
7417
+ failure = new MuxAiError("Unable to complete text generation.", {
7418
+ type: "processing_error",
7419
+ retryable: apiError?.isRetryable ?? true,
7420
+ cause
7421
+ });
7422
+ }
7423
+ if (typeof cause === "object" && cause !== null && "diagnostics" in cause) {
7424
+ failure.diagnostics = cause.diagnostics;
7425
+ }
7426
+ rethrowWithTokenUsage(failure, usages);
7316
7427
  }
7317
- rethrowWithTokenUsage(failure, usages);
7318
- }
7319
- return { content: repaired.content, usages, unexpectedKeys };
7428
+ return { content: repaired.content, usages, unexpectedKeys };
7429
+ });
7320
7430
  }
7321
7431
  async function generateText7(assetId, options) {
7322
7432
  "use workflow";
@@ -8199,6 +8309,7 @@ async function getModerationScores(assetId, options = {}) {
8199
8309
  provider = DEFAULT_PROVIDER2,
8200
8310
  model = provider === "openai" ? "omni-moderation-latest" : void 0,
8201
8311
  languageCode,
8312
+ includeTranscript = true,
8202
8313
  thresholds = DEFAULT_THRESHOLDS,
8203
8314
  thumbnailInterval = 10,
8204
8315
  thumbnailWidth = 640,
@@ -8279,8 +8390,15 @@ async function getModerationScores(assetId, options = {}) {
8279
8390
  let transcriptModeration = { status: "completed" };
8280
8391
  let transcriptCues = [];
8281
8392
  let shouldRunTranscript = false;
8282
- const captionTrack = provider === "openai" ? findCaptionTrack(asset, languageCode) : void 0;
8283
- if (provider !== "openai") {
8393
+ const transcriptEnabled = includeTranscript || isAudioOnly;
8394
+ const captionTrack = transcriptEnabled && provider === "openai" ? findCaptionTrack(asset, languageCode) : void 0;
8395
+ if (!transcriptEnabled) {
8396
+ transcriptModeration = {
8397
+ status: "skipped",
8398
+ skipReason: "disabled",
8399
+ skipMessage: "Transcript moderation was disabled with includeTranscript: false."
8400
+ };
8401
+ } else if (provider !== "openai") {
8284
8402
  transcriptModeration = {
8285
8403
  status: "skipped",
8286
8404
  skipReason: "unsupported_provider",
@@ -9694,7 +9812,7 @@ async function translateAudio(assetId, toLanguageCode, options = {}) {
9694
9812
 
9695
9813
  // src/workflows/translate-captions.ts
9696
9814
  import {
9697
- APICallError as APICallError4,
9815
+ APICallError as APICallError5,
9698
9816
  generateText as generateText9,
9699
9817
  NoObjectGeneratedError as NoObjectGeneratedError3,
9700
9818
  NoOutputGeneratedError as NoOutputGeneratedError2,
@@ -9849,7 +9967,7 @@ function isProviderServiceError(error) {
9849
9967
  if (RetryError2.isInstance(error)) {
9850
9968
  return isProviderServiceError(error.lastError);
9851
9969
  }
9852
- if (APICallError4.isInstance(error)) {
9970
+ if (APICallError5.isInstance(error)) {
9853
9971
  return true;
9854
9972
  }
9855
9973
  if (error instanceof Error && "cause" in error) {