@mux/ai 0.7.2 → 0.7.3

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.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../package.json","../src/env.ts","../src/lib/workflow-crypto.ts","../src/lib/workflow-credentials.ts","../node_modules/@workflow/serde/src/index.ts","../src/lib/s3-sigv4.ts","../src/lib/workflow-serialization.ts","../src/lib/workflow-storage-client.ts","../src/primitives/index.ts","../src/lib/providers.ts","../src/lib/client-factory.ts","../src/primitives/heatmap.ts","../src/primitives/hotspots.ts","../src/lib/url-signing.ts","../src/primitives/storyboards.ts","../src/primitives/text-chunking.ts","../src/primitives/thumbnails.ts","../src/primitives/transcripts.ts","../src/workflows/index.ts","../src/workflows/ask-questions.ts","../src/lib/image-download.ts","../src/lib/mux-assets.ts","../src/lib/prompt-builder.ts","../src/lib/retry.ts","../src/workflows/burned-in-captions.ts","../src/workflows/chapters.ts","../src/workflows/embeddings.ts","../src/lib/sampling-plan.ts","../src/workflows/moderation.ts","../src/workflows/summarization.ts","../src/lib/language-codes.ts","../src/lib/storage-adapter.ts","../src/workflows/translate-audio.ts","../src/workflows/translate-captions.ts"],"sourcesContent":["{\n \"name\": \"@mux/ai\",\n \"type\": \"module\",\n \"version\": \"0.7.2\",\n \"description\": \"AI library for Mux\",\n \"author\": \"Mux\",\n \"license\": \"Apache-2.0\",\n \"homepage\": \"https://github.com/muxinc/ai#readme\",\n \"repository\": {\n \"type\": \"git\",\n \"url\": \"git+https://github.com/muxinc/ai.git\"\n },\n \"bugs\": {\n \"url\": \"https://github.com/muxinc/ai/issues\"\n },\n \"publishConfig\": {\n \"access\": \"public\",\n \"registry\": \"https://registry.npmjs.org/\"\n },\n \"keywords\": [\n \"mux\",\n \"video\",\n \"ai\",\n \"llm\",\n \"openai\",\n \"anthropic\",\n \"google\",\n \"gemini\",\n \"multimodal\",\n \"video-analysis\",\n \"summarization\",\n \"moderation\",\n \"captions\",\n \"translation\",\n \"dubbing\",\n \"chapters\",\n \"embeddings\",\n \"typescript\"\n ],\n \"exports\": {\n \".\": {\n \"types\": {\n \"import\": \"./dist/index.d.ts\"\n },\n \"import\": \"./dist/index.js\"\n },\n \"./primitives\": {\n \"types\": {\n \"import\": \"./dist/primitives/index.d.ts\"\n },\n \"import\": \"./dist/primitives/index.js\"\n },\n \"./workflows\": {\n \"types\": {\n \"import\": \"./dist/workflows/index.d.ts\"\n },\n \"import\": \"./dist/workflows/index.js\"\n }\n },\n \"main\": \"./dist/index.js\",\n \"module\": \"./dist/index.js\",\n \"types\": \"./dist/index.d.ts\",\n \"files\": [\n \"dist\"\n ],\n \"engines\": {\n \"node\": \">=21.0.0\"\n },\n \"scripts\": {\n \"build\": \"tsup\",\n \"dev\": \"tsup --watch\",\n \"test\": \"DOTENV_CONFIG_PATH=.env.test vitest run\",\n \"test:watch\": \"DOTENV_CONFIG_PATH=.env.test vitest\",\n \"test:ui\": \"DOTENV_CONFIG_PATH=.env.test vitest --ui\",\n \"test:integration\": \"DOTENV_CONFIG_PATH=.env.test vitest run tests/integration\",\n \"test:integration-workflowdevkit\": \"DOTENV_CONFIG_PATH=.env.test vitest run --config vitest.workflowdevkit.config.ts\",\n \"test:unit\": \"DOTENV_CONFIG_PATH=.env.test vitest run tests/unit\",\n \"test:eval\": \"DOTENV_CONFIG_PATH=.env.test evalite serve tests/eval\",\n \"lint\": \"eslint .\",\n \"lint:fix\": \"eslint . --fix\",\n \"typecheck\": \"tsc --noEmit\",\n \"example:ask-questions\": \"npx tsx examples/ask-questions/basic-example.ts\",\n \"example:ask-questions:multiple\": \"npx tsx examples/ask-questions/multiple-questions-example.ts\",\n \"example:chapters\": \"npx tsx examples/chapters/basic-example.ts\",\n \"example:chapters:compare\": \"npx tsx examples/chapters/provider-comparison.ts\",\n \"example:burned-in\": \"npx tsx examples/burned-in-captions/basic-example.ts\",\n \"example:burned-in:compare\": \"npx tsx examples/burned-in-captions/provider-comparison.ts\",\n \"example:summarization\": \"npx tsx examples/summarization/basic-example.ts\",\n \"example:summarization:audio-only\": \"npx tsx examples/summarization/audio-only-example.ts\",\n \"example:summarization:compare\": \"npx tsx examples/summarization/provider-comparison.ts\",\n \"example:signed-playback\": \"npx tsx examples/signed-playback/signed-playback.ts\",\n \"example:signed-playback:summarization\": \"npx tsx examples/signed-playback/signed-summarization.ts\",\n \"example:moderation\": \"npx tsx examples/moderation/basic-example.ts\",\n \"example:moderation:compare\": \"npx tsx examples/moderation/provider-comparison.ts\",\n \"example:embeddings\": \"npx tsx examples/embeddings/basic-example.ts\",\n \"example:translate-captions\": \"npx tsx examples/translate-captions/basic-example.ts\",\n \"example:translate-captions:aws-sdk\": \"npx tsx examples/translate-captions/aws-sdk-storage-adapter-example.ts\",\n \"example:translate-audio\": \"npx tsx examples/translate-audio/basic-example.ts\",\n \"evalite:post-results:dev\": \"npx tsx scripts/export-evalite-results.ts && npx tsx scripts/post-evalite-results.ts -d -k\",\n \"evalite:post-results:production\": \"npx tsx scripts/export-evalite-results.ts && npx tsx scripts/post-evalite-results.ts\",\n \"prepare\": \"husky\"\n },\n \"dependencies\": {\n \"@ai-sdk/anthropic\": \"^3.0.16\",\n \"@ai-sdk/google\": \"^3.0.10\",\n \"@ai-sdk/openai\": \"^3.0.12\",\n \"@mux/mux-node\": \"^12.5.0\",\n \"@noble/ciphers\": \"^2.1.1\",\n \"ai\": \"^6.0.42\",\n \"dedent\": \"^1.7.0\",\n \"dotenv\": \"^17.2.2\",\n \"dotenv-expand\": \"^12.0.3\",\n \"p-retry\": \"^7.0.0\",\n \"zod\": \"^3.25.76\"\n },\n \"devDependencies\": {\n \"@antfu/eslint-config\": \"^6.2.0\",\n \"@types/node\": \"^20.0.0\",\n \"@vitest/ui\": \"^4.0.13\",\n \"commander\": \"^14.0.2\",\n \"eslint\": \"^9.39.1\",\n \"eslint-plugin-format\": \"^1.0.2\",\n \"evalite\": \"1.0.0-beta.2\",\n \"husky\": \"^9.1.7\",\n \"nitro\": \"^3.0.1-alpha.1\",\n \"ts-node\": \"^10.9.2\",\n \"tsc-alias\": \"^1.8.16\",\n \"tsconfig-paths\": \"^4.2.0\",\n \"tsup\": \"^8.5.0\",\n \"typescript\": \"^5.0.0\",\n \"vitest\": \"^4.0.13\",\n \"workflow\": \"^4.1.0-beta.55\"\n }\n}\n","/* eslint-disable node/no-process-env */\n\nimport { z } from \"zod\";\n\nimport \"dotenv/config\";\n\nfunction optionalString(description: string, message?: string) {\n return z.preprocess(\n value => typeof value === \"string\" && value.trim().length === 0 ? undefined : value,\n z.string().trim().min(1, message).optional(),\n ).describe(description);\n}\n\n// eslint-disable-next-line unused-imports/no-unused-vars\nfunction requiredString(description: string, message?: string) {\n return z.preprocess(\n value => typeof value === \"string\" ? value.trim().length > 0 ? value.trim() : undefined : value,\n z.string().trim().min(1, message),\n ).describe(description);\n}\n\nconst EnvSchema = z.object({\n NODE_ENV: z.string().default(\"development\").describe(\"Runtime environment.\"),\n\n MUX_TOKEN_ID: optionalString(\"Mux access token ID.\", \"Required to access Mux APIs\"),\n MUX_TOKEN_SECRET: optionalString(\"Mux access token secret.\", \"Required to access Mux APIs\"),\n MUX_AI_WORKFLOW_SECRET_KEY: optionalString(\n \"Base64-encoded 32-byte key for workflow encryption/decryption.\",\n \"Workflow secret key\",\n ),\n EVALITE_INGEST_SECRET: optionalString(\n \"Shared secret for posting Evalite results.\",\n \"Evalite ingest secret\",\n ),\n\n MUX_SIGNING_KEY: optionalString(\"Mux signing key ID for signed playback URLs.\", \"Used to sign playback URLs\"),\n MUX_PRIVATE_KEY: optionalString(\"Mux signing private key for signed playback URLs.\", \"Used to sign playback URLs\"),\n\n // Test-only helpers (used by this repo's integration tests)\n MUX_TEST_ASSET_ID: optionalString(\"Mux asset ID used by integration tests.\", \"Mux test asset id\"),\n MUX_TEST_ASSET_ID_CHAPTERS: optionalString(\"Mux asset ID used by integration tests for chapters.\", \"Mux test asset id for chapters\"),\n MUX_TEST_ASSET_ID_VIOLENT: optionalString(\"Mux violent asset ID used by integration tests.\", \"Mux violent test asset id\"),\n MUX_TEST_ASSET_ID_BURNED_IN_CAPTIONS: optionalString(\n \"Mux burned-in captions asset ID used by integration tests.\",\n \"Mux burned-in captions test asset id\",\n ),\n MUX_TEST_ASSET_ID_BURNED_IN_CAPTIONS_2: optionalString(\n \"Mux burned-in captions asset ID 2 (a different asset) used by integration tests.\",\n \"Mux burned-in captions test asset id 2 (a different asset)\",\n ),\n MUX_TEST_ASSET_ID_WITHOUT_BURNED_IN_CAPTIONS: optionalString(\n \"Mux without burned-in captions asset ID used by integration tests.\",\n \"Mux without burned-in captions test asset id\",\n ),\n 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\"),\n 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\"),\n\n // AI Providers\n OPENAI_API_KEY: optionalString(\"OpenAI API key for OpenAI-backed workflows.\", \"OpenAI API key\"),\n ANTHROPIC_API_KEY: optionalString(\"Anthropic API key for Claude-backed workflows.\", \"Anthropic API key\"),\n GOOGLE_GENERATIVE_AI_API_KEY: optionalString(\"Google Generative AI API key for Gemini-backed workflows.\", \"Google Generative AI API key\"),\n\n ELEVENLABS_API_KEY: optionalString(\"ElevenLabs API key for audio translation.\", \"ElevenLabs API key\"),\n HIVE_API_KEY: optionalString(\"Hive Visual Moderation API key.\", \"Hive API key\"),\n\n // S3-Compatible Storage (required for translation & audio dubbing)\n S3_ENDPOINT: optionalString(\"S3-compatible endpoint for uploads.\", \"S3 endpoint\"),\n S3_REGION: optionalString(\"S3 region (defaults to 'auto' when omitted).\"),\n S3_BUCKET: optionalString(\"Bucket used for caption and audio uploads.\", \"S3 bucket\"),\n S3_ACCESS_KEY_ID: optionalString(\"Access key ID for S3-compatible uploads.\", \"S3 access key id\"),\n S3_SECRET_ACCESS_KEY: optionalString(\"Secret access key for S3-compatible uploads.\", \"S3 secret access key\"),\n S3_ALLOWED_ENDPOINT_HOSTS: optionalString(\n \"Comma-separated S3 endpoint allowlist (supports exact hosts and *.suffix patterns).\",\n ),\n\n EVALITE_RESULTS_ENDPOINT: optionalString(\n \"Full URL for posting Evalite results (e.g., https://example.com/api/evalite-results).\",\n \"Evalite results endpoint\",\n ),\n}).refine(\n (env) => {\n const hasMuxCredentials = Boolean(env.MUX_TOKEN_ID && env.MUX_TOKEN_SECRET);\n const hasWorkflowKey = Boolean(env.MUX_AI_WORKFLOW_SECRET_KEY);\n return hasMuxCredentials || hasWorkflowKey;\n },\n {\n message: \"Either MUX_TOKEN_ID + MUX_TOKEN_SECRET or MUX_AI_WORKFLOW_SECRET_KEY must be set.\",\n },\n);\n\nexport type Env = z.infer<typeof EnvSchema>;\n\nfunction parseEnv(): Env {\n const parsedEnv = EnvSchema.safeParse(process.env);\n\n if (!parsedEnv.success) {\n console.error(\"❌ Invalid env:\");\n console.error(JSON.stringify(parsedEnv.error.flatten().fieldErrors, null, 2));\n process.exit(1);\n }\n\n return parsedEnv.data;\n}\n\nconst env: Env = parseEnv();\n\nexport function reloadEnv(): Env {\n const parsed = parseEnv();\n Object.assign(env, parsed);\n return env;\n}\n\nexport { env };\nexport default env;\n","/**\n * Workflow Crypto\n *\n * Provides AES-256-GCM encryption for securely passing credentials to workflows.\n * Encrypted payloads are JSON-serializable and include version/algorithm metadata\n * for forward compatibility.\n */\n\nimport { gcm } from \"@noble/ciphers/aes.js\";\n\nconst BASE64_CHUNK_SIZE = 0x8000;\nconst BASE64_ALPHABET = \"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/\";\nconst BASE64_LOOKUP = (() => {\n const table = new Uint8Array(256);\n table.fill(255);\n for (let i = 0; i < BASE64_ALPHABET.length; i++) {\n table[BASE64_ALPHABET.charCodeAt(i)] = i;\n }\n return table;\n})();\n\n// Encryption parameters (AES-256-GCM with standard IV/tag sizes)\nconst WORKFLOW_ENCRYPTION_VERSION = 1;\nconst WORKFLOW_ENCRYPTION_ALGORITHM = \"aes-256-gcm\" as const;\nconst IV_LENGTH_BYTES = 12; // 96-bit IV per NIST recommendation for GCM\nconst AUTH_TAG_LENGTH_BYTES = 16; // 128-bit authentication tag\n\n/**\n * Structure of an encrypted payload (all fields are base64-encoded where applicable).\n *\n * The optional `kid` (key ID) field supports key rotation scenarios:\n * - Include a `kid` when encrypting to identify which key was used\n * - On decryption, read `payload.kid` to look up the correct key\n * - Keys should be invalidated/deleted after rotation, not kept indefinitely\n *\n * Security notes:\n * - `kid` is stored in plaintext (not encrypted) - don't put sensitive data in it\n * - Tampering with `kid` doesn't weaken security - wrong key = decryption fails\n */\nexport interface EncryptedPayload {\n v: typeof WORKFLOW_ENCRYPTION_VERSION;\n alg: typeof WORKFLOW_ENCRYPTION_ALGORITHM;\n kid?: string;\n iv: string;\n tag: string;\n ciphertext: string;\n}\n\n/** Branded type that preserves the original type information for decryption */\nexport type Encrypted<T> = EncryptedPayload & { __type?: T };\n\nfunction getWebCrypto(): NonNullable<typeof globalThis.crypto> {\n const webCrypto = globalThis.crypto;\n if (!webCrypto || typeof webCrypto.getRandomValues !== \"function\") {\n throw new Error(\"Web Crypto API is required in workflow functions.\");\n }\n return webCrypto;\n}\n\nfunction bytesToBase64(bytes: Uint8Array): string {\n if (bytes.length === 0) {\n return \"\";\n }\n const btoaImpl = typeof globalThis.btoa === \"function\" ? globalThis.btoa.bind(globalThis) : undefined;\n if (btoaImpl) {\n let binary = \"\";\n for (let i = 0; i < bytes.length; i += BASE64_CHUNK_SIZE) {\n const chunk = bytes.subarray(i, i + BASE64_CHUNK_SIZE);\n binary += String.fromCharCode(...chunk);\n }\n return btoaImpl(binary);\n }\n\n let output = \"\";\n for (let i = 0; i < bytes.length; i += 3) {\n const b0 = bytes[i] ?? 0;\n const b1 = bytes[i + 1] ?? 0;\n const b2 = bytes[i + 2] ?? 0;\n const triple = (b0 << 16) | (b1 << 8) | b2;\n output += BASE64_ALPHABET[(triple >> 18) & 63];\n output += BASE64_ALPHABET[(triple >> 12) & 63];\n output += i + 1 < bytes.length ? BASE64_ALPHABET[(triple >> 6) & 63] : \"=\";\n output += i + 2 < bytes.length ? BASE64_ALPHABET[triple & 63] : \"=\";\n }\n return output;\n}\n\nfunction normalizeBase64Input(value: string): string {\n const cleaned = value\n .replace(/\\s+/g, \"\")\n .replace(/-/g, \"+\")\n .replace(/_/g, \"/\");\n\n // Pad with '=' to make the length a multiple of 4 (required by standard base64).\n // When cleaned is empty, length is 0 and 0 % 4 === 0, so it passes through unchanged.\n return cleaned?.length % 4 === 0 ? cleaned : cleaned + \"=\".repeat(4 - (cleaned.length % 4));\n}\n\nfunction base64ToBytes(value: string, label: string): Uint8Array {\n if (!value) {\n throw new Error(`${label} is missing`);\n }\n const normalized = normalizeBase64Input(value);\n const atobImpl = typeof globalThis.atob === \"function\" ? globalThis.atob.bind(globalThis) : undefined;\n if (atobImpl) {\n let binary: string;\n try {\n binary = atobImpl(normalized);\n } catch {\n throw new Error(`${label} is not valid base64`);\n }\n if (!binary) {\n throw new Error(`${label} decoded to empty value`);\n }\n const bytes = new Uint8Array(binary.length);\n for (let i = 0; i < binary.length; i++) {\n bytes[i] = binary.charCodeAt(i);\n }\n return bytes;\n }\n\n if (normalized.length % 4 !== 0) {\n throw new Error(`${label} is not valid base64`);\n }\n\n const padding = normalized.endsWith(\"==\") ? 2 : normalized.endsWith(\"=\") ? 1 : 0;\n const outputLength = (normalized.length / 4) * 3 - padding;\n const bytes = new Uint8Array(outputLength);\n let offset = 0;\n\n for (let i = 0; i < normalized.length; i += 4) {\n const c0 = normalized.charCodeAt(i);\n const c1 = normalized.charCodeAt(i + 1);\n const c2 = normalized.charCodeAt(i + 2);\n const c3 = normalized.charCodeAt(i + 3);\n\n if (c0 === 61 || c1 === 61) {\n throw new Error(`${label} is not valid base64`);\n }\n\n const n0 = BASE64_LOOKUP[c0] ?? 255;\n const n1 = BASE64_LOOKUP[c1] ?? 255;\n const n2 = c2 === 61 ? 0 : (BASE64_LOOKUP[c2] ?? 255);\n const n3 = c3 === 61 ? 0 : (BASE64_LOOKUP[c3] ?? 255);\n\n if (n0 === 255 || n1 === 255 || n2 === 255 || n3 === 255) {\n throw new Error(`${label} is not valid base64`);\n }\n\n const triple = (n0 << 18) | (n1 << 12) | (n2 << 6) | n3;\n bytes[offset++] = (triple >> 16) & 0xFF;\n if (c2 !== 61) {\n bytes[offset++] = (triple >> 8) & 0xFF;\n }\n if (c3 !== 61) {\n bytes[offset++] = triple & 0xFF;\n }\n }\n\n if (!bytes.length) {\n throw new Error(`${label} decoded to empty value`);\n }\n\n return bytes;\n}\n\n/** Wraps base64ToBytes for encrypted payload fields, adding context to errors */\nfunction payloadBase64ToBytes(value: string, field: string): Uint8Array {\n try {\n return base64ToBytes(value, field);\n } catch (error) {\n const detail = error instanceof Error ? error.message : `${field} is invalid`;\n throw new Error(`Invalid encrypted payload: ${detail}`);\n }\n}\n\n/** Converts key to bytes and validates it's exactly 32 bytes (256 bits) */\nfunction normalizeKey(key: Uint8Array | string): Uint8Array {\n let keyBytes: Uint8Array;\n if (typeof key === \"string\") {\n try {\n keyBytes = base64ToBytes(key, \"value\");\n } catch (error) {\n const detail = error instanceof Error ? error.message : \"value is not valid base64\";\n throw new Error(`Invalid workflow secret key: ${detail}. Expected 32-byte base64 value.`);\n }\n } else {\n keyBytes = new Uint8Array(key);\n }\n\n if (keyBytes.length !== 32) {\n throw new Error(`Invalid workflow secret key: expected 32 bytes, got ${keyBytes.length}.`);\n }\n\n return keyBytes;\n}\n\n/** Type guard to check if a value is a valid encrypted payload structure */\nexport function isEncryptedPayload(value: unknown): value is EncryptedPayload {\n if (!value || typeof value !== \"object\") {\n return false;\n }\n\n const payload = value as EncryptedPayload;\n return (\n payload.v === WORKFLOW_ENCRYPTION_VERSION &&\n payload.alg === WORKFLOW_ENCRYPTION_ALGORITHM &&\n typeof payload.iv === \"string\" &&\n typeof payload.tag === \"string\" &&\n typeof payload.ciphertext === \"string\"\n );\n}\n\n/** Validates payload structure and cryptographic parameters before decryption */\nfunction assertEncryptedPayload(payload: EncryptedPayload): void {\n if (payload.v !== WORKFLOW_ENCRYPTION_VERSION) {\n throw new Error(\"Invalid encrypted payload: unsupported version.\");\n }\n\n if (payload.alg !== WORKFLOW_ENCRYPTION_ALGORITHM) {\n throw new Error(\"Invalid encrypted payload: unsupported algorithm.\");\n }\n\n const iv = payloadBase64ToBytes(payload.iv, \"iv\");\n const tag = payloadBase64ToBytes(payload.tag, \"tag\");\n\n if (iv.length !== IV_LENGTH_BYTES) {\n throw new Error(\"Invalid encrypted payload: iv length mismatch.\");\n }\n\n if (tag.length !== AUTH_TAG_LENGTH_BYTES) {\n throw new Error(\"Invalid encrypted payload: tag length mismatch.\");\n }\n\n payloadBase64ToBytes(payload.ciphertext, \"ciphertext\");\n}\n\n/**\n * Encrypts a value for secure transport to a workflow.\n *\n * @param value - Any JSON-serializable value (typically WorkflowCredentials)\n * @param key - 32-byte secret key (base64 string or Uint8Array)\n * @param keyId - Optional key identifier for rotation support (stored in plaintext)\n * @returns Encrypted payload with metadata, safe to pass through untrusted channels\n */\nexport async function encryptForWorkflow<T>(\n value: T,\n key: Uint8Array | string,\n keyId?: string,\n): Promise<Encrypted<T>> {\n const keyBytes = normalizeKey(key);\n const webCrypto = getWebCrypto();\n const iv = new Uint8Array(IV_LENGTH_BYTES);\n webCrypto.getRandomValues(iv); // Fresh IV for each encryption\n\n let serialized: string;\n try {\n serialized = JSON.stringify(value);\n } catch {\n throw new Error(\"Failed to serialize value for encryption.\");\n }\n\n const encoder = new TextEncoder();\n const plaintext = encoder.encode(serialized);\n const encryptedBytes = gcm(keyBytes, iv).encrypt(plaintext);\n const tag = encryptedBytes.slice(encryptedBytes.length - AUTH_TAG_LENGTH_BYTES);\n const ciphertext = encryptedBytes.slice(0, encryptedBytes.length - AUTH_TAG_LENGTH_BYTES);\n\n return {\n v: WORKFLOW_ENCRYPTION_VERSION,\n alg: WORKFLOW_ENCRYPTION_ALGORITHM,\n ...(keyId !== undefined && { kid: keyId }),\n iv: bytesToBase64(iv),\n tag: bytesToBase64(tag),\n ciphertext: bytesToBase64(ciphertext),\n };\n}\n\n/**\n * Decrypts and deserializes a workflow payload.\n *\n * @param payload - Encrypted payload from `encryptForWorkflow`\n * @param key - Same 32-byte secret key used for encryption\n * @returns The original decrypted value\n * @throws If payload is invalid, tampered with, or key is wrong\n */\nexport async function decryptFromWorkflow<T>(\n payload: EncryptedPayload,\n key: Uint8Array | string,\n): Promise<T> {\n if (!isEncryptedPayload(payload)) {\n throw new Error(\"Invalid encrypted payload.\");\n }\n\n assertEncryptedPayload(payload);\n\n const keyBytes = normalizeKey(key);\n const iv = payloadBase64ToBytes(payload.iv, \"iv\");\n const tag = payloadBase64ToBytes(payload.tag, \"tag\");\n const ciphertext = payloadBase64ToBytes(payload.ciphertext, \"ciphertext\");\n\n const combined = new Uint8Array(ciphertext.length + tag.length);\n combined.set(ciphertext);\n combined.set(tag, ciphertext.length);\n\n let plaintext: Uint8Array;\n try {\n plaintext = gcm(keyBytes, iv).decrypt(combined);\n } catch (error) {\n const message = (error as { message?: string } | undefined)?.message;\n throw new Error(`Failed to decrypt workflow payload. ${message ?? String(error)}`);\n }\n\n try {\n const decoder = new TextDecoder();\n return JSON.parse(decoder.decode(plaintext)) as T;\n } catch {\n throw new Error(\"Failed to parse decrypted payload.\");\n }\n}\n","/**\n * Workflow Credentials Management\n *\n * This module provides a unified way to resolve credentials from multiple sources:\n * 1. A custom credentials provider (set via `setWorkflowCredentialsProvider`)\n * 2. Encrypted credentials passed directly to workflow functions\n * 3. Environment variables as fallback\n *\n * Credentials are merged in order of precedence: direct input > provider > environment.\n */\nimport env from \"@mux/ai/env\";\nimport type { Env } from \"@mux/ai/env\";\nimport type { SigningContext } from \"@mux/ai/lib/url-signing\";\nimport { decryptFromWorkflow, isEncryptedPayload } from \"@mux/ai/lib/workflow-crypto\";\nimport type {\n WorkflowCredentials,\n WorkflowCredentialsInput,\n WorkflowMuxClient,\n} from \"@mux/ai/types\";\n\n/**\n * A function that returns workflow credentials, either synchronously or asynchronously.\n * Used to inject credentials from external sources (e.g., a secrets manager).\n */\nexport type WorkflowCredentialsProvider =\n () => Promise<WorkflowCredentials | undefined> | WorkflowCredentials | undefined;\n\n/** Module-level credentials provider, set via `setWorkflowCredentialsProvider` */\nlet workflowCredentialsProvider: WorkflowCredentialsProvider | undefined;\n\n/**\n * Registers a custom credentials provider for the module.\n * The provider will be called whenever credentials need to be resolved.\n */\nexport function setWorkflowCredentialsProvider(provider?: WorkflowCredentialsProvider): void {\n workflowCredentialsProvider = provider;\n}\n\n/**\n * Detects whether code is running inside a Workflow Dev Kit runtime.\n * getWorkflowMetadata throws when invoked outside a workflow.\n */\nasync function isWorkflowRuntime(): Promise<boolean> {\n try {\n const workflowModule = await import(\"workflow\");\n if (typeof workflowModule.getWorkflowMetadata !== \"function\") {\n return false;\n }\n workflowModule.getWorkflowMetadata();\n return true;\n } catch {\n return false;\n }\n}\n\n/**\n * Determines if we should enforce encrypted credentials.\n * This triggers in workflow runtimes or when the workflow secret key is set.\n */\nasync function shouldEnforceEncryptedCredentials(): Promise<boolean> {\n return Boolean(env.MUX_AI_WORKFLOW_SECRET_KEY) || await isWorkflowRuntime();\n}\n\n/**\n * Retrieves the workflow secret key from environment variables.\n * This key is used to decrypt encrypted credential payloads.\n */\nfunction getWorkflowSecretKeyFromEnv(): string {\n const key = env.MUX_AI_WORKFLOW_SECRET_KEY;\n if (!key) {\n throw new Error(\n \"Workflow secret key is required. Set MUX_AI_WORKFLOW_SECRET_KEY environment variable.\",\n );\n }\n return key;\n}\n\n/**\n * Invokes the registered credentials provider (if any) and validates the result.\n */\nasync function resolveProviderCredentials(): Promise<WorkflowCredentials | undefined> {\n if (!workflowCredentialsProvider) {\n return undefined;\n }\n\n const provided = await workflowCredentialsProvider();\n if (!provided) {\n return undefined;\n }\n\n if (typeof provided !== \"object\") {\n throw new TypeError(\"Workflow credentials provider must return an object.\");\n }\n\n return provided;\n}\n\n/**\n * Resolves workflow credentials by merging from multiple sources.\n *\n * Resolution order (later sources override earlier):\n * 1. Credentials from the registered provider\n * 2. Decrypted credentials (if input is an encrypted payload)\n * OR plain credentials object (if input is already decrypted)\n *\n * @param credentials - Optional credentials input\n * @returns Merged credentials object\n */\nexport async function resolveWorkflowCredentials(\n credentials?: WorkflowCredentialsInput,\n): Promise<WorkflowCredentials> {\n // Start with provider credentials as the base\n const providerCredentials = await resolveProviderCredentials();\n const resolved: WorkflowCredentials = providerCredentials ? { ...providerCredentials } : {};\n\n if (!credentials) {\n return resolved;\n }\n\n // Handle encrypted payloads by decrypting them first.\n if (isEncryptedPayload(credentials)) {\n try {\n const decrypted = await decryptFromWorkflow<WorkflowCredentials>(\n credentials,\n getWorkflowSecretKeyFromEnv(),\n );\n return { ...resolved, ...decrypted };\n } catch (error) {\n const detail = error instanceof Error ? error.message : \"Unknown error.\";\n throw new Error(`Failed to decrypt workflow credentials. ${detail}`);\n }\n }\n\n if (await shouldEnforceEncryptedCredentials()) {\n throw new Error(\n \"Plaintext workflow credentials are not allowed when using Workflow Dev Kit.\" +\n \" Pass encrypted credentials (encryptForWorkflow) or resolve secrets via environment variables.\",\n );\n }\n\n // Plain credentials object - merge directly.\n return { ...resolved, ...credentials };\n}\n\ninterface DirectMuxCredentials {\n tokenId: string;\n tokenSecret: string;\n signingKey?: string;\n privateKey?: string;\n}\n\nfunction readString(record: Record<string, unknown> | undefined, key: string): string | undefined {\n const value = record?.[key];\n return typeof value === \"string\" && value.length > 0 ? value : undefined;\n}\n\nfunction resolveDirectMuxCredentials(record: Record<string, unknown> | undefined): DirectMuxCredentials | undefined {\n const tokenId = readString(record, \"muxTokenId\");\n const tokenSecret = readString(record, \"muxTokenSecret\");\n const signingKey = readString(record, \"muxSigningKey\");\n const privateKey = readString(record, \"muxPrivateKey\");\n\n if (!tokenId && !tokenSecret && !signingKey && !privateKey) {\n return undefined;\n }\n\n if (!tokenId || !tokenSecret) {\n throw new Error(\n \"Both muxTokenId and muxTokenSecret are required when passing direct Mux workflow credentials.\",\n );\n }\n\n return {\n tokenId,\n tokenSecret,\n signingKey,\n privateKey,\n };\n}\n\nfunction createWorkflowMuxClient(options: DirectMuxCredentials): WorkflowMuxClient {\n return {\n async createClient() {\n // Dynamic import to avoid pulling mux-node into workflow VM bundles.\n const { default: MuxClient } = await import(\"@mux/mux-node\");\n return new MuxClient({\n tokenId: options.tokenId,\n tokenSecret: options.tokenSecret,\n });\n },\n getSigningKey() {\n return options.signingKey;\n },\n getPrivateKey() {\n return options.privateKey;\n },\n };\n}\n\n/**\n * Resolves a WorkflowMuxClient from workflow credentials or environment variables.\n *\n * Checks direct workflow credentials first (muxTokenId/muxTokenSecret),\n * then provider credentials, then falls back to MUX_TOKEN_ID / MUX_TOKEN_SECRET\n * (and optional MUX_SIGNING_KEY / MUX_PRIVATE_KEY) environment variables.\n *\n * @param credentials - Optional workflow credentials input\n * @returns A WorkflowMuxClient instance\n * @throws Error if Mux credentials are not available\n */\nexport async function resolveMuxClient(\n credentials?: WorkflowCredentialsInput,\n): Promise<WorkflowMuxClient> {\n const resolved = await resolveWorkflowCredentials(credentials);\n const resolvedRecord = resolved as Record<string, unknown>;\n const resolvedMuxCredentials = resolveDirectMuxCredentials(resolvedRecord);\n if (resolvedMuxCredentials) {\n return createWorkflowMuxClient(resolvedMuxCredentials);\n }\n\n // Fall back to environment variables\n const muxTokenId = env.MUX_TOKEN_ID;\n const muxTokenSecret = env.MUX_TOKEN_SECRET;\n\n if (!muxTokenId || !muxTokenSecret) {\n throw new Error(\n \"Mux credentials are required. Provide muxTokenId/muxTokenSecret via workflow credentials, or set MUX_TOKEN_ID and MUX_TOKEN_SECRET environment variables.\",\n );\n }\n\n return createWorkflowMuxClient({\n tokenId: muxTokenId,\n tokenSecret: muxTokenSecret,\n signingKey: env.MUX_SIGNING_KEY,\n privateKey: env.MUX_PRIVATE_KEY,\n });\n}\n\n/** Supported AI/ML provider identifiers for API key resolution. */\nexport type ApiKeyProvider = \"openai\" | \"anthropic\" | \"google\" | \"hive\" | \"elevenlabs\";\n\nfunction resolveProviderApiKeyFromCredentials(\n provider: ApiKeyProvider,\n resolved: WorkflowCredentials,\n): string {\n const record = resolved as Record<string, unknown>;\n const openaiApiKey = readString(record, \"openaiApiKey\");\n const anthropicApiKey = readString(record, \"anthropicApiKey\");\n const googleApiKey = readString(record, \"googleApiKey\");\n const hiveApiKey = readString(record, \"hiveApiKey\");\n const elevenLabsApiKey = readString(record, \"elevenLabsApiKey\");\n\n // Map each provider to its credential source and env var fallback\n const apiKeyMap: Record<ApiKeyProvider, string | undefined> = {\n openai: openaiApiKey ?? env.OPENAI_API_KEY,\n anthropic: anthropicApiKey ?? env.ANTHROPIC_API_KEY,\n google: googleApiKey ?? env.GOOGLE_GENERATIVE_AI_API_KEY,\n hive: hiveApiKey ?? env.HIVE_API_KEY,\n elevenlabs: elevenLabsApiKey ?? env.ELEVENLABS_API_KEY,\n };\n\n const apiKey = apiKeyMap[provider];\n if (!apiKey) {\n // Provide helpful error message with the correct env var name.\n // Using `satisfies` ensures these stay in sync with the Env schema.\n const envVarNames = {\n openai: \"OPENAI_API_KEY\",\n anthropic: \"ANTHROPIC_API_KEY\",\n google: \"GOOGLE_GENERATIVE_AI_API_KEY\",\n hive: \"HIVE_API_KEY\",\n elevenlabs: \"ELEVENLABS_API_KEY\",\n } as const satisfies Record<ApiKeyProvider, keyof Env>;\n\n throw new Error(\n `${provider} API key is required. Provide ${provider} credentials via workflow credentials or set ${envVarNames[provider]} environment variable.`,\n );\n }\n\n return apiKey;\n}\n/**\n * Resolves an API key for a specific AI/ML provider.\n *\n * Checks resolved workflow credentials first, then falls back to the\n * provider-specific environment variable.\n *\n * @param provider - The provider identifier (e.g., \"openai\", \"anthropic\")\n * @param credentials - Optional workflow credentials input\n * @returns The resolved API key string\n * @throws Error if no API key is available for the specified provider\n */\nexport async function resolveProviderApiKey(\n provider: ApiKeyProvider,\n credentials?: WorkflowCredentialsInput,\n): Promise<string> {\n const resolved = await resolveWorkflowCredentials(credentials);\n return resolveProviderApiKeyFromCredentials(provider, resolved);\n}\n\n/**\n * Resolves Mux URL signing context for generating signed playback URLs.\n *\n * Unlike other resolve functions, this returns undefined if signing keys\n * are not configured (signing is optional for public assets).\n *\n * @param credentials - Optional workflow credentials input\n * @returns SigningContext if keys are available, undefined otherwise\n */\nexport async function resolveMuxSigningContext(\n credentials?: WorkflowCredentialsInput,\n): Promise<SigningContext | undefined> {\n const resolved = await resolveWorkflowCredentials(credentials);\n const resolvedRecord = resolved as Record<string, unknown>;\n\n // Try direct credentials first, then fall back to environment variables.\n const keyId = readString(resolvedRecord, \"muxSigningKey\") ?? env.MUX_SIGNING_KEY;\n const keySecret =\n readString(resolvedRecord, \"muxPrivateKey\") ?? env.MUX_PRIVATE_KEY;\n\n if (!keyId || !keySecret) {\n return undefined;\n }\n\n return { keyId, keySecret };\n}\n",null,"import env from \"@mux/ai/env\";\n\nconst AWS4_ALGORITHM = \"AWS4-HMAC-SHA256\";\nconst AWS4_REQUEST_TERMINATOR = \"aws4_request\";\nconst AWS4_SERVICE = \"s3\";\n\n// Env flags for endpoint hardening.\n// - S3_ALLOWED_ENDPOINT_HOSTS=\"s3.amazonaws.com,*.r2.cloudflarestorage.com\"\n// restricts requests to explicit hostnames / wildcard suffixes.\nconst S3_ALLOWED_ENDPOINT_PATTERNS = parseEndpointAllowlist(\n env.S3_ALLOWED_ENDPOINT_HOSTS,\n);\n\ninterface S3Credentials {\n accessKeyId: string;\n secretAccessKey: string;\n}\n\ninterface S3Target {\n endpoint: string;\n region: string;\n bucket: string;\n key: string;\n}\n\nexport interface PutObjectOptions extends S3Target, S3Credentials {\n body: string | Uint8Array;\n contentType?: string;\n}\n\nexport interface PresignGetObjectOptions extends S3Target, S3Credentials {\n expiresInSeconds?: number;\n}\n\nfunction getCrypto() {\n const webCrypto = globalThis.crypto as any;\n if (!webCrypto?.subtle) {\n throw new Error(\"Web Crypto API is required for S3 signing.\");\n }\n\n return webCrypto;\n}\n\nconst textEncoder = new TextEncoder();\n\nfunction toBytes(value: string | Uint8Array): Uint8Array {\n return typeof value === \"string\" ? textEncoder.encode(value) : value;\n}\n\nfunction bytesToHex(bytes: Uint8Array): string {\n return Array.from(bytes)\n .map(byte => byte.toString(16).padStart(2, \"0\"))\n .join(\"\");\n}\n\nasync function sha256Hex(value: string | Uint8Array): Promise<string> {\n const digest = await getCrypto().subtle.digest(\"SHA-256\", toBytes(value));\n return bytesToHex(new Uint8Array(digest));\n}\n\nasync function hmacSha256Raw(key: Uint8Array, value: string): Promise<Uint8Array> {\n const cryptoKey = await getCrypto().subtle.importKey(\n \"raw\",\n key,\n { name: \"HMAC\", hash: \"SHA-256\" },\n false,\n [\"sign\"],\n );\n const signature = await getCrypto().subtle.sign(\"HMAC\", cryptoKey, textEncoder.encode(value));\n return new Uint8Array(signature);\n}\n\nasync function deriveSigningKey(\n secretAccessKey: string,\n shortDate: string,\n region: string,\n): Promise<Uint8Array> {\n const kDate = await hmacSha256Raw(textEncoder.encode(`AWS4${secretAccessKey}`), shortDate);\n const kRegion = await hmacSha256Raw(kDate, region);\n const kService = await hmacSha256Raw(kRegion, AWS4_SERVICE);\n return hmacSha256Raw(kService, AWS4_REQUEST_TERMINATOR);\n}\n\nfunction formatAmzDate(date = new Date()): { amzDate: string; shortDate: string } {\n const iso = date.toISOString(); // always UTC\n const shortDate = iso.slice(0, 10).replace(/-/g, \"\");\n const amzDate = `${iso.slice(0, 19).replace(/[-:]/g, \"\")}Z`;\n return { amzDate, shortDate };\n}\n\nfunction encodeRFC3986(value: string): string {\n return encodeURIComponent(value).replace(/[!'()*]/g, (char: string) =>\n `%${char.charCodeAt(0).toString(16).toUpperCase()}`);\n}\n\nfunction encodePath(path: string): string {\n return path.split(\"/\").map(segment => encodeRFC3986(segment)).join(\"/\");\n}\n\nfunction normalizeEndpoint(endpoint: string): URL {\n let url: URL;\n try {\n url = new URL(endpoint);\n } catch {\n throw new Error(`Invalid S3 endpoint: ${endpoint}`);\n }\n\n if (url.search || url.hash) {\n throw new Error(\"S3 endpoint must not include query params or hash fragments.\");\n }\n\n enforceEndpointPolicy(url);\n\n return url;\n}\n\nfunction parseEndpointAllowlist(allowlist: string | undefined): string[] {\n if (!allowlist) {\n return [];\n }\n\n return allowlist\n .split(\",\")\n .map(value => value.trim().toLowerCase())\n .filter(Boolean);\n}\n\nfunction hostnameMatchesPattern(hostname: string, pattern: string): boolean {\n if (pattern.startsWith(\"*.\")) {\n const suffix = pattern.slice(1); // \".example.com\"\n return hostname.endsWith(suffix) && hostname.length > suffix.length;\n }\n\n return hostname === pattern;\n}\n\nfunction enforceEndpointPolicy(url: URL): void {\n const hostname = url.hostname.toLowerCase();\n\n // Enforce secure transport for all S3 uploads/signing flows.\n if (url.protocol !== \"https:\") {\n throw new Error(\n `Insecure S3 endpoint protocol \"${url.protocol}\" is not allowed. Use HTTPS.`,\n );\n }\n\n // Optional allowlist enforcement to prevent exfiltration/SSRF-like endpoint misuse.\n // When unset, behavior remains backward compatible (any host allowed).\n if (\n S3_ALLOWED_ENDPOINT_PATTERNS.length > 0 &&\n !S3_ALLOWED_ENDPOINT_PATTERNS.some(pattern => hostnameMatchesPattern(hostname, pattern))\n ) {\n throw new Error(\n `S3 endpoint host \"${hostname}\" is not in S3_ALLOWED_ENDPOINT_HOSTS.`,\n );\n }\n}\n\nfunction buildCanonicalUri(endpoint: URL, bucket: string, key: string): string {\n const endpointPath =\n endpoint.pathname === \"/\" ? \"\" : encodePath(endpoint.pathname.replace(/\\/+$/, \"\"));\n const encodedBucket = encodeRFC3986(bucket);\n const encodedKey = encodePath(key);\n\n return `${endpointPath}/${encodedBucket}/${encodedKey}`;\n}\n\nfunction buildCanonicalQuery(params: Record<string, string>): string {\n return Object.entries(params)\n .sort(([a], [b]) => a.localeCompare(b))\n .map(([key, value]) => `${encodeRFC3986(key)}=${encodeRFC3986(value)}`)\n .join(\"&\");\n}\n\nasync function signString(\n secretAccessKey: string,\n shortDate: string,\n region: string,\n value: string,\n): Promise<string> {\n const signingKey = await deriveSigningKey(secretAccessKey, shortDate, region);\n const signatureBytes = await hmacSha256Raw(signingKey, value);\n return bytesToHex(signatureBytes);\n}\n\nfunction buildCredentialScope(shortDate: string, region: string): string {\n return `${shortDate}/${region}/${AWS4_SERVICE}/${AWS4_REQUEST_TERMINATOR}`;\n}\n\nexport async function putObjectToS3({\n accessKeyId,\n secretAccessKey,\n endpoint,\n region,\n bucket,\n key,\n body,\n contentType,\n}: PutObjectOptions): Promise<void> {\n const resolvedEndpoint = normalizeEndpoint(endpoint);\n const canonicalUri = buildCanonicalUri(resolvedEndpoint, bucket, key);\n const host = resolvedEndpoint.host;\n const normalizedContentType = contentType?.trim();\n const { amzDate, shortDate } = formatAmzDate();\n const payloadHash = await sha256Hex(body);\n\n const signingHeaders = [\n [\"host\", host],\n [\"x-amz-content-sha256\", payloadHash],\n [\"x-amz-date\", amzDate],\n ...(normalizedContentType ? [[\"content-type\", normalizedContentType] as const] : []),\n ].sort(([a], [b]) => a.localeCompare(b));\n const canonicalHeaders = signingHeaders.map(([name, value]) => `${name}:${value}`).join(\"\\n\");\n const signedHeaders = signingHeaders.map(([name]) => name).join(\";\");\n const canonicalRequest = [\n \"PUT\",\n canonicalUri,\n \"\",\n `${canonicalHeaders}\\n`,\n signedHeaders,\n payloadHash,\n ].join(\"\\n\");\n const credentialScope = buildCredentialScope(shortDate, region);\n const stringToSign = [\n AWS4_ALGORITHM,\n amzDate,\n credentialScope,\n await sha256Hex(canonicalRequest),\n ].join(\"\\n\");\n const signature = await signString(secretAccessKey, shortDate, region, stringToSign);\n const authorization = `${AWS4_ALGORITHM} Credential=${accessKeyId}/${credentialScope}, SignedHeaders=${signedHeaders}, Signature=${signature}`;\n const requestUrl = `${resolvedEndpoint.origin}${canonicalUri}`;\n\n const response = await fetch(requestUrl, {\n method: \"PUT\",\n headers: {\n \"Authorization\": authorization,\n \"x-amz-content-sha256\": payloadHash,\n \"x-amz-date\": amzDate,\n ...(normalizedContentType ? { \"content-type\": normalizedContentType } : {}),\n },\n body,\n });\n\n if (!response.ok) {\n const errorBody = await response.text().catch(() => \"\");\n const detail = errorBody ? ` ${errorBody}` : \"\";\n throw new Error(`S3 PUT failed (${response.status} ${response.statusText}).${detail}`);\n }\n}\n\nexport async function createPresignedGetUrl({\n accessKeyId,\n secretAccessKey,\n endpoint,\n region,\n bucket,\n key,\n expiresInSeconds = 3600,\n}: PresignGetObjectOptions): Promise<string> {\n const resolvedEndpoint = normalizeEndpoint(endpoint);\n const canonicalUri = buildCanonicalUri(resolvedEndpoint, bucket, key);\n const host = resolvedEndpoint.host;\n const { amzDate, shortDate } = formatAmzDate();\n const credentialScope = buildCredentialScope(shortDate, region);\n const signedHeaders = \"host\";\n const queryParams = {\n \"X-Amz-Algorithm\": AWS4_ALGORITHM,\n \"X-Amz-Credential\": `${accessKeyId}/${credentialScope}`,\n \"X-Amz-Date\": amzDate,\n \"X-Amz-Expires\": `${expiresInSeconds}`,\n \"X-Amz-SignedHeaders\": signedHeaders,\n };\n const canonicalQuery = buildCanonicalQuery(queryParams);\n const canonicalRequest = [\n \"GET\",\n canonicalUri,\n canonicalQuery,\n `host:${host}\\n`,\n signedHeaders,\n \"UNSIGNED-PAYLOAD\",\n ].join(\"\\n\");\n const stringToSign = [\n AWS4_ALGORITHM,\n amzDate,\n credentialScope,\n await sha256Hex(canonicalRequest),\n ].join(\"\\n\");\n const signature = await signString(secretAccessKey, shortDate, region, stringToSign);\n const queryWithSignature = `${canonicalQuery}&X-Amz-Signature=${signature}`;\n\n return `${resolvedEndpoint.origin}${canonicalUri}?${queryWithSignature}`;\n}\n","interface SerializableClass {\n classId?: string;\n}\n\ninterface SerializableClassConstructor {\n new (...args: never[]): unknown;\n classId?: string;\n}\n\ntype Registry = Map<string, SerializableClassConstructor>;\n\nconst WORKFLOW_CLASS_REGISTRY = Symbol.for(\"workflow-class-registry\");\n\n/**\n * Registers a class in Workflow's global serialization registry.\n *\n * This mirrors Workflow DevKit's internal `registerSerializationClass` behavior\n * without requiring consumers to import private workflow internals.\n */\nexport function registerWorkflowSerializationClass(classId: string, cls: SerializableClassConstructor): void {\n const globalObject = globalThis as typeof globalThis & {\n [WORKFLOW_CLASS_REGISTRY]?: Registry;\n };\n\n let registry = globalObject[WORKFLOW_CLASS_REGISTRY];\n if (!registry) {\n registry = new Map<string, SerializableClassConstructor>();\n globalObject[WORKFLOW_CLASS_REGISTRY] = registry;\n }\n\n registry.set(classId, cls);\n\n const serializableClass = cls as SerializableClass;\n if (serializableClass.classId !== classId) {\n Object.defineProperty(cls, \"classId\", {\n value: classId,\n writable: false,\n enumerable: false,\n configurable: false,\n });\n }\n}\n","import { WORKFLOW_DESERIALIZE, WORKFLOW_SERIALIZE } from \"@workflow/serde\";\n\nimport {\n createPresignedGetUrl,\n putObjectToS3,\n} from \"@mux/ai/lib/s3-sigv4\";\nimport { registerWorkflowSerializationClass } from \"@mux/ai/lib/workflow-serialization\";\nimport type {\n StoragePresignGetObjectInput,\n StoragePutObjectInput,\n} from \"@mux/ai/types\";\n\nexport interface WorkflowStorageClientOptions {\n accessKeyId?: string;\n secretAccessKey?: string;\n}\n\n/**\n * Serializable storage client wrapper for workflow boundaries.\n *\n * By default, this uses the internal SigV4 implementation to keep object\n * operations compatible across edge/ESM and Node runtimes.\n */\nexport class WorkflowStorageClient {\n static classId = \"WorkflowStorageClient\";\n\n private readonly accessKeyId?: string;\n private readonly secretAccessKey?: string;\n\n constructor(options: WorkflowStorageClientOptions = {}) {\n this.accessKeyId = options.accessKeyId;\n this.secretAccessKey = options.secretAccessKey;\n }\n\n private resolveCredentials(input: {\n accessKeyId?: string;\n secretAccessKey?: string;\n }): { accessKeyId: string; secretAccessKey: string } {\n const accessKeyId = input.accessKeyId ?? this.accessKeyId;\n const secretAccessKey = input.secretAccessKey ?? this.secretAccessKey;\n\n if (!accessKeyId || !secretAccessKey) {\n throw new Error(\n \"Storage credentials are required. \" +\n \"Provide accessKeyId/secretAccessKey in WorkflowStorageClient options \" +\n \"or in the storage operation input.\",\n );\n }\n\n return { accessKeyId, secretAccessKey };\n }\n\n async putObject(input: StoragePutObjectInput): Promise<void> {\n const credentials = this.resolveCredentials(input);\n await putObjectToS3({\n accessKeyId: credentials.accessKeyId,\n secretAccessKey: credentials.secretAccessKey,\n endpoint: input.endpoint,\n region: input.region,\n bucket: input.bucket,\n key: input.key,\n body: input.body,\n contentType: input.contentType,\n });\n }\n\n async createPresignedGetUrl(input: StoragePresignGetObjectInput): Promise<string> {\n const credentials = this.resolveCredentials(input);\n return createPresignedGetUrl({\n accessKeyId: credentials.accessKeyId,\n secretAccessKey: credentials.secretAccessKey,\n endpoint: input.endpoint,\n region: input.region,\n bucket: input.bucket,\n key: input.key,\n expiresInSeconds: input.expiresInSeconds,\n });\n }\n\n static [WORKFLOW_SERIALIZE](instance: WorkflowStorageClient): WorkflowStorageClientOptions {\n return {\n accessKeyId: instance.accessKeyId,\n secretAccessKey: instance.secretAccessKey,\n };\n }\n\n static [WORKFLOW_DESERIALIZE](this: typeof WorkflowStorageClient, value: WorkflowStorageClientOptions): WorkflowStorageClient {\n return new this(value);\n }\n}\n\nregisterWorkflowSerializationClass(WorkflowStorageClient.classId, WorkflowStorageClient);\n\nfunction isSerializedWorkflowStorageClient(value: unknown): value is WorkflowStorageClientOptions {\n if (!value || typeof value !== \"object\") {\n return false;\n }\n\n const candidate = value as WorkflowStorageClientOptions;\n return \"accessKeyId\" in candidate || \"secretAccessKey\" in candidate;\n}\n\nexport function normalizeWorkflowStorageClient(value: unknown): WorkflowStorageClient | undefined {\n if (value instanceof WorkflowStorageClient) {\n return value;\n }\n\n if (isSerializedWorkflowStorageClient(value)) {\n return new WorkflowStorageClient(value);\n }\n\n return undefined;\n}\n\nexport function createWorkflowStorageClient(\n options: WorkflowStorageClientOptions = {},\n): WorkflowStorageClient {\n return new WorkflowStorageClient(options);\n}\n","// primitives public surface intentionally minimal; provider plumbing lives in lib/providers\nexport * from \"./heatmap\";\nexport * from \"./hotspots\";\nexport * from \"./storyboards\";\nexport * from \"./text-chunking\";\nexport * from \"./thumbnails\";\nexport * from \"./transcripts\";\n","import { createAnthropic } from \"@ai-sdk/anthropic\";\nimport { createGoogleGenerativeAI } from \"@ai-sdk/google\";\nimport { createOpenAI } from \"@ai-sdk/openai\";\n\nimport env from \"@mux/ai/env\";\nimport { resolveProviderApiKey } from \"@mux/ai/lib/workflow-credentials\";\nimport type { MuxAIOptions, WorkflowCredentialsInput } from \"@mux/ai/types\";\n\nimport type { EmbeddingModel, LanguageModel } from \"ai\";\n\nexport type SupportedProvider = \"openai\" | \"anthropic\" | \"google\";\nexport type SupportedEmbeddingProvider = \"openai\" | \"google\";\n\n// Model ID unions inferred from ai-sdk provider call signatures\ntype OpenAIModelId = Parameters<ReturnType<typeof createOpenAI>[\"chat\"]>[0];\ntype AnthropicModelId = Parameters<ReturnType<typeof createAnthropic>[\"chat\"]>[0];\ntype GoogleModelId = Parameters<ReturnType<typeof createGoogleGenerativeAI>[\"chat\"]>[0];\n\ntype OpenAIEmbeddingModelId = Parameters<ReturnType<typeof createOpenAI>[\"embedding\"]>[0];\ntype GoogleEmbeddingModelId = Parameters<ReturnType<typeof createGoogleGenerativeAI>[\"textEmbeddingModel\"]>[0];\n\nexport interface ModelIdByProvider {\n openai: OpenAIModelId;\n anthropic: AnthropicModelId;\n google: GoogleModelId;\n}\n\nexport interface EmbeddingModelIdByProvider {\n openai: OpenAIEmbeddingModelId;\n google: GoogleEmbeddingModelId;\n}\n\nexport interface ModelRequestOptions<P extends SupportedProvider = SupportedProvider> extends MuxAIOptions {\n provider?: P;\n model?: ModelIdByProvider[P];\n}\n\nexport interface ResolvedModel<P extends SupportedProvider = SupportedProvider> {\n provider: P;\n modelId: ModelIdByProvider[P];\n model: LanguageModel;\n}\n\nexport const DEFAULT_LANGUAGE_MODELS: { [K in SupportedProvider]: ModelIdByProvider[K] } = {\n openai: \"gpt-5.1\",\n anthropic: \"claude-sonnet-4-5\",\n google: \"gemini-3-flash-preview\",\n};\n\nconst DEFAULT_EMBEDDING_MODELS: { [K in SupportedEmbeddingProvider]: EmbeddingModelIdByProvider[K] } = {\n openai: \"text-embedding-3-small\",\n google: \"gemini-embedding-001\",\n};\n\nexport function resolveLanguageModelConfig<P extends SupportedProvider = SupportedProvider>(\n options: ModelRequestOptions<P> = {},\n): { provider: P; modelId: ModelIdByProvider[P] } {\n const provider = options.provider || (\"openai\" as P);\n const modelId = (options.model || DEFAULT_LANGUAGE_MODELS[provider]) as ModelIdByProvider[P];\n\n return { provider, modelId };\n}\n\nexport function resolveEmbeddingModelConfig<P extends SupportedEmbeddingProvider = \"openai\">(\n options: MuxAIOptions & { provider?: P; model?: EmbeddingModelIdByProvider[P] } = {},\n): { provider: P; modelId: EmbeddingModelIdByProvider[P] } {\n const provider = options.provider || (\"openai\" as P);\n const modelId = (options.model || DEFAULT_EMBEDDING_MODELS[provider]) as EmbeddingModelIdByProvider[P];\n\n return { provider, modelId };\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Model Pricing\n// ─────────────────────────────────────────────────────────────────────────────\n//\n// Pricing is in USD per million tokens. These values are used for cost estimation\n// in evaluations and should be periodically verified against official sources.\n//\n// Sources (as of December 2025):\n// - OpenAI: https://openai.com/api/pricing\n// - Anthropic: https://www.anthropic.com/pricing\n// - Google: https://ai.google.dev/pricing\n// ─────────────────────────────────────────────────────────────────────────────\n\n/**\n * Pricing structure for a language model.\n * All costs are in USD per million tokens.\n */\nexport interface ModelPricing {\n /** Cost per million input tokens (USD). */\n inputPerMillion: number;\n /** Cost per million output tokens (USD). */\n outputPerMillion: number;\n /** Cost per million cached input tokens (USD), if supported. */\n cachedInputPerMillion?: number;\n /** URL to the official pricing page for verification. */\n pricingUrl: string;\n}\n\n/**\n * Pricing data for the default language models.\n * Used for cost estimation in evaluations and expense tracking.\n *\n * @remarks\n * Prices are subject to change. Verify against official sources before production use.\n */\nexport const THIRD_PARTY_MODEL_PRICING: { [K in SupportedProvider]: ModelPricing } = {\n // OpenAI GPT-5.1\n // Reference: https://openai.com/api/pricing\n openai: {\n inputPerMillion: 1.25,\n outputPerMillion: 10.00,\n cachedInputPerMillion: 0.125,\n pricingUrl: \"https://openai.com/api/pricing\",\n },\n\n // Anthropic Claude Sonnet 4.5\n // Reference: https://www.anthropic.com/pricing\n anthropic: {\n inputPerMillion: 3.00,\n outputPerMillion: 15.00,\n cachedInputPerMillion: 0.30, // Prompt caching read cost (≤200K tokens)\n pricingUrl: \"https://www.anthropic.com/pricing\",\n },\n\n // Google Gemini 3 Flash Preview\n // Reference: https://ai.google.dev/pricing\n google: {\n inputPerMillion: 0.50,\n outputPerMillion: 3.00,\n cachedInputPerMillion: 0.05, // Context caching price\n pricingUrl: \"https://ai.google.dev/pricing\",\n },\n};\n\n/**\n * Calculates the estimated cost for a request based on token usage.\n *\n * @param provider - The AI provider used\n * @param inputTokens - Number of input tokens consumed\n * @param outputTokens - Number of output tokens generated\n * @param cachedInputTokens - Number of input tokens served from cache (optional)\n * @returns Estimated cost in USD\n *\n * @example\n * ```typescript\n * const cost = calculateCost('openai', 2000, 500);\n * console.log(`Estimated cost: $${cost.toFixed(6)}`);\n * ```\n */\nexport function calculateCost(\n provider: SupportedProvider,\n inputTokens: number,\n outputTokens: number,\n cachedInputTokens: number = 0,\n): number {\n const pricing = THIRD_PARTY_MODEL_PRICING[provider];\n\n // Adjust input tokens: cached tokens are charged at cached rate, rest at full rate\n const uncachedInputTokens = Math.max(0, inputTokens - cachedInputTokens);\n\n const inputCost = (uncachedInputTokens / 1_000_000) * pricing.inputPerMillion;\n const outputCost = (outputTokens / 1_000_000) * pricing.outputPerMillion;\n let cachedCost = 0;\n if (pricing.cachedInputPerMillion) {\n cachedCost = (cachedInputTokens / 1_000_000) * pricing.cachedInputPerMillion;\n }\n\n return inputCost + outputCost + cachedCost;\n}\n\nfunction requireEnv(value: string | undefined, name: string): string {\n if (!value) {\n throw new Error(`Missing ${name}. Set ${name} in your environment or pass it in options.`);\n }\n return value;\n}\n\n/**\n * Creates a language model instance from serializable config.\n * Use this in steps to instantiate models from config passed through workflow.\n * Fetches credentials internally from environment variables to avoid exposing them in step I/O.\n */\nexport async function createLanguageModelFromConfig<P extends SupportedProvider = SupportedProvider>(\n provider: P,\n modelId: ModelIdByProvider[P],\n credentials?: WorkflowCredentialsInput,\n): Promise<LanguageModel> {\n switch (provider) {\n case \"openai\": {\n const apiKey = await resolveProviderApiKey(\"openai\", credentials);\n const openai = createOpenAI({ apiKey });\n return openai(modelId);\n }\n case \"anthropic\": {\n const apiKey = await resolveProviderApiKey(\"anthropic\", credentials);\n const anthropic = createAnthropic({ apiKey });\n return anthropic(modelId);\n }\n case \"google\": {\n const apiKey = await resolveProviderApiKey(\"google\", credentials);\n const google = createGoogleGenerativeAI({ apiKey });\n return google(modelId);\n }\n default: {\n const exhaustiveCheck: never = provider;\n throw new Error(`Unsupported provider: ${exhaustiveCheck}`);\n }\n }\n}\n\n/**\n * Creates an embedding model instance from serializable config.\n * Use this in steps to instantiate embedding models from config passed through workflow.\n * Fetches credentials internally from environment variables to avoid exposing them in step I/O.\n */\nexport async function createEmbeddingModelFromConfig<\n P extends SupportedEmbeddingProvider = SupportedEmbeddingProvider,\n>(\n provider: P,\n modelId: EmbeddingModelIdByProvider[P],\n credentials?: WorkflowCredentialsInput,\n): Promise<EmbeddingModel> {\n switch (provider) {\n case \"openai\": {\n const apiKey = await resolveProviderApiKey(\"openai\", credentials);\n const openai = createOpenAI({ apiKey });\n return openai.embedding(modelId);\n }\n case \"google\": {\n const apiKey = await resolveProviderApiKey(\"google\", credentials);\n const google = createGoogleGenerativeAI({ apiKey });\n return google.textEmbeddingModel(modelId);\n }\n default: {\n const exhaustiveCheck: never = provider;\n throw new Error(`Unsupported embedding provider: ${exhaustiveCheck}`);\n }\n }\n}\n\n/**\n * Resolves a language model from a suggested provider.\n */\nexport function resolveLanguageModel<P extends SupportedProvider = SupportedProvider>(\n options: ModelRequestOptions<P> = {},\n): ResolvedModel<P> {\n const provider = options.provider || (\"openai\" as P);\n const modelId = (options.model || DEFAULT_LANGUAGE_MODELS[provider]) as ModelIdByProvider[P];\n\n switch (provider) {\n case \"openai\": {\n const apiKey = env.OPENAI_API_KEY;\n requireEnv(apiKey, \"OPENAI_API_KEY\");\n const openai = createOpenAI({\n apiKey,\n });\n\n return {\n provider,\n modelId,\n model: openai(modelId),\n };\n }\n case \"anthropic\": {\n const apiKey = env.ANTHROPIC_API_KEY;\n requireEnv(apiKey, \"ANTHROPIC_API_KEY\");\n const anthropic = createAnthropic({\n apiKey,\n });\n\n return {\n provider,\n modelId,\n model: anthropic(modelId),\n };\n }\n case \"google\": {\n const apiKey = env.GOOGLE_GENERATIVE_AI_API_KEY;\n requireEnv(apiKey, \"GOOGLE_GENERATIVE_AI_API_KEY\");\n const google = createGoogleGenerativeAI({\n apiKey,\n });\n\n return {\n provider,\n modelId,\n model: google(modelId),\n };\n }\n default: {\n const exhaustiveCheck: never = provider;\n throw new Error(`Unsupported provider: ${exhaustiveCheck}`);\n }\n }\n}\n\n/**\n * Resolves an embedding model from a suggested provider.\n */\nexport function resolveEmbeddingModel<P extends SupportedEmbeddingProvider = \"openai\">(\n options: MuxAIOptions & { provider?: P; model?: EmbeddingModelIdByProvider[P] } = {},\n): { provider: P; modelId: EmbeddingModelIdByProvider[P]; model: EmbeddingModel } {\n const provider = options.provider || (\"openai\" as P);\n const modelId = (options.model || DEFAULT_EMBEDDING_MODELS[provider]) as EmbeddingModelIdByProvider[P];\n\n switch (provider) {\n case \"openai\": {\n const apiKey = env.OPENAI_API_KEY;\n requireEnv(apiKey, \"OPENAI_API_KEY\");\n const openai = createOpenAI({\n apiKey,\n });\n\n return {\n provider,\n modelId,\n model: openai.embedding(modelId),\n };\n }\n case \"google\": {\n const apiKey = env.GOOGLE_GENERATIVE_AI_API_KEY;\n requireEnv(apiKey, \"GOOGLE_GENERATIVE_AI_API_KEY\");\n const google = createGoogleGenerativeAI({\n apiKey,\n });\n\n return {\n provider,\n modelId,\n model: google.textEmbeddingModel(modelId),\n };\n }\n default: {\n const exhaustiveCheck: never = provider;\n throw new Error(`Unsupported embedding provider: ${exhaustiveCheck}`);\n }\n }\n}\n","import type {\n ModelIdByProvider,\n ModelRequestOptions,\n SupportedProvider,\n} from \"@mux/ai/lib/providers\";\nimport {\n resolveLanguageModel,\n} from \"@mux/ai/lib/providers\";\nimport type { ApiKeyProvider } from \"@mux/ai/lib/workflow-credentials\";\nimport { resolveMuxClient, resolveProviderApiKey } from \"@mux/ai/lib/workflow-credentials\";\nimport type { WorkflowCredentialsInput, WorkflowMuxClient } from \"@mux/ai/types\";\n\n/**\n * Gets a WorkflowMuxClient from workflow credentials or environment variables.\n * Used internally by workflow steps to avoid passing credentials through step I/O.\n * Throws if Mux credentials are not available.\n */\nexport async function getMuxClientFromEnv(\n credentials?: WorkflowCredentialsInput,\n): Promise<WorkflowMuxClient> {\n return resolveMuxClient(credentials);\n}\n\n/**\n * Gets an API key from workflow credentials or environment variables for the specified provider.\n * Used internally by workflow steps to avoid passing credentials through step I/O.\n * Throws if the API key is not available.\n */\nexport async function getApiKeyFromEnv(\n provider: ApiKeyProvider,\n credentials?: WorkflowCredentialsInput,\n): Promise<string> {\n return resolveProviderApiKey(provider, credentials);\n}\n\nexport interface WorkflowConfig<P extends SupportedProvider = SupportedProvider> {\n muxClient: WorkflowMuxClient;\n provider: P;\n modelId: ModelIdByProvider[P];\n}\n\n/**\n * Resolves Mux client and model configuration for a workflow.\n * This function is NOT a workflow step to avoid exposing credentials in step I/O.\n */\nexport async function createWorkflowConfig<P extends SupportedProvider = SupportedProvider>(\n options: ModelRequestOptions<P>,\n provider?: P,\n): Promise<WorkflowConfig<P>> {\n const providerToUse = provider || options.provider || (\"openai\" as P);\n const muxClient = await resolveMuxClient(options.credentials);\n const resolved = resolveLanguageModel({\n ...options,\n provider: providerToUse,\n });\n\n return {\n muxClient,\n provider: resolved.provider,\n modelId: resolved.modelId,\n };\n}\n","import { getMuxClientFromEnv } from \"@mux/ai/lib/client-factory\";\nimport type { WorkflowCredentialsInput } from \"@mux/ai/types\";\n\nexport interface HeatmapOptions {\n /** Time window for results, e.g., ['7:days'] (default: ['7:days']) */\n timeframe?: string;\n /** Optional workflow credentials */\n credentials?: WorkflowCredentialsInput;\n}\n\n/** Raw API response structure from Mux Data API */\n// To be removed when the Mux Node SDK is updated\ninterface HeatmapApiResponse {\n asset_id?: string;\n video_id?: string;\n playback_id?: string;\n heatmap: number[];\n timeframe: [number, number];\n}\n\nexport interface HeatmapResponse {\n assetId?: string;\n videoId?: string;\n playbackId?: string;\n /** Array of 100 values representing engagement for each 1/100th of the video */\n heatmap: number[];\n timeframe: [number, number];\n}\n\n/**\n * Fetches engagement heatmap for a Mux asset.\n * Returns a length 100 array where each value represents how many times\n * that 1/100th of the video was watched.\n *\n * @param assetId - The Mux asset ID\n * @param options - Heatmap query options\n * @returns Heatmap data with 100 engagement values\n */\nexport async function getHeatmapForAsset(\n assetId: string,\n options: HeatmapOptions = {},\n): Promise<HeatmapResponse> {\n \"use step\";\n return fetchHeatmap(\"assets\", assetId, options);\n}\n\n/**\n * Fetches engagement heatmap for a Mux video ID.\n * Returns a length 100 array where each value represents how many times\n * that 1/100th of the video was watched.\n *\n * @param videoId - The Mux video ID\n * @param options - Heatmap query options\n * @returns Heatmap data with 100 engagement values\n */\nexport async function getHeatmapForVideo(\n videoId: string,\n options: HeatmapOptions = {},\n): Promise<HeatmapResponse> {\n \"use step\";\n return fetchHeatmap(\"videos\", videoId, options);\n}\n\n/**\n * Fetches engagement heatmap for a Mux playback ID.\n * Returns a length 100 array where each value represents how many times\n * that 1/100th of the video was watched.\n *\n * @param playbackId - The Mux playback ID\n * @param options - Heatmap query options\n * @returns Heatmap data with 100 engagement values\n */\nexport async function getHeatmapForPlaybackId(\n playbackId: string,\n options: HeatmapOptions = {},\n): Promise<HeatmapResponse> {\n \"use step\";\n return fetchHeatmap(\"playback-ids\", playbackId, options);\n}\n\n/**\n * Transforms the snake_case API response to camelCase for the public interface.\n * TODO: Remove when the Mux Node SDK is updated\n */\nfunction transformHeatmapResponse(\n response: HeatmapApiResponse,\n): HeatmapResponse {\n return {\n assetId: response.asset_id,\n videoId: response.video_id,\n playbackId: response.playback_id,\n heatmap: response.heatmap,\n timeframe: response.timeframe,\n };\n}\n\n/**\n * Internal helper to fetch heatmap from the Mux Data API.\n * Uses the raw HTTP methods on the Mux client since the SDK doesn't have\n * typed methods for the engagement endpoints yet.\n */\nasync function fetchHeatmap(\n identifierType: \"assets\" | \"videos\" | \"playback-ids\",\n id: string,\n options: HeatmapOptions,\n): Promise<HeatmapResponse> {\n \"use step\";\n const { timeframe = \"[24:hours]\", credentials } = options;\n\n const muxClient = await getMuxClientFromEnv(credentials);\n const mux = await muxClient.createClient();\n\n // Build query parameters\n const queryParams = new URLSearchParams();\n queryParams.append(\"timeframe[]\", timeframe);\n\n // Use the raw HTTP method since the SDK doesn't have typed engagement methods yet\n const path = `/data/v1/engagement/${identifierType}/${id}/heatmap?${queryParams.toString()}`;\n const response = await mux.get<unknown, HeatmapApiResponse>(path);\n\n return transformHeatmapResponse(response);\n}\n","import { getMuxClientFromEnv } from \"@mux/ai/lib/client-factory\";\nimport type { WorkflowCredentialsInput } from \"@mux/ai/types\";\n\nexport interface Hotspot {\n /** Inclusive start time in milliseconds */\n startMs: number;\n /** Exclusive end time in milliseconds */\n endMs: number;\n /** Hotspot score using distribution-based normalization (0-1) */\n score: number;\n}\n\nexport interface HotspotOptions {\n /** Maximum number of hotspots to return (default: 5) */\n limit?: number;\n /** Sort order: 'asc' or 'desc' (default: 'desc') */\n orderDirection?: \"asc\" | \"desc\";\n /** Order by field (default: 'score') */\n orderBy?: \"score\";\n /** Time window for results, e.g., ['7:days'] (default: ['7:days']) */\n timeframe?: string;\n /** Optional workflow credentials */\n credentials?: WorkflowCredentialsInput;\n}\n\n/** Raw API response structure from Mux Data API */\n// To be removed when the Mux Node SDK is updated\ninterface HotspotApiResponse {\n total_row_count: number | null;\n timeframe: [number, number];\n data: {\n asset_id?: string;\n video_id?: string;\n playback_id?: string;\n hotspots: Array<{\n start_ms: number;\n end_ms: number;\n score: number;\n }>;\n };\n}\n\nexport interface HotspotResponse {\n assetId?: string;\n videoId?: string;\n playbackId?: string;\n hotspots: Hotspot[];\n}\n\n/**\n * Fetches engagement hotspots for a Mux asset.\n * Returns the top N \"hot\" time ranges based on engagement data.\n *\n * @param assetId - The Mux asset ID\n * @param options - Hotspot query options\n * @returns Array of hotspots with time ranges and scores\n */\nexport async function getHotspotsForAsset(\n assetId: string,\n options: HotspotOptions = {},\n): Promise<Hotspot[]> {\n \"use step\";\n const response = await fetchHotspots(\"assets\", assetId, options);\n return response.hotspots;\n}\n\n/**\n * Fetches engagement hotspots for a Mux video ID.\n * Returns the top N \"hot\" time ranges based on engagement data.\n *\n * @param videoId - The Mux video ID\n * @param options - Hotspot query options\n * @returns Array of hotspots with time ranges and scores\n */\nexport async function getHotspotsForVideo(\n videoId: string,\n options: HotspotOptions = {},\n): Promise<Hotspot[]> {\n \"use step\";\n const response = await fetchHotspots(\"videos\", videoId, options);\n return response.hotspots;\n}\n\n/**\n * Fetches engagement hotspots for a Mux playback ID.\n * Returns the top N \"hot\" time ranges based on engagement data.\n *\n * @param playbackId - The Mux playback ID\n * @param options - Hotspot query options\n * @returns Array of hotspots with time ranges and scores\n */\nexport async function getHotspotsForPlaybackId(\n playbackId: string,\n options: HotspotOptions = {},\n): Promise<Hotspot[]> {\n \"use step\";\n const response = await fetchHotspots(\"playback-ids\", playbackId, options);\n return response.hotspots;\n}\n\n/**\n * Transforms the snake_case API response to camelCase for the public interface.\n * TODO: Remove when the Mux Node SDK is updated\n */\nfunction transformHotspotResponse(response: HotspotApiResponse): HotspotResponse {\n return {\n assetId: response.data.asset_id,\n videoId: response.data.video_id,\n playbackId: response.data.playback_id,\n hotspots: response.data.hotspots.map(h => ({\n startMs: h.start_ms,\n endMs: h.end_ms,\n score: h.score,\n })),\n };\n}\n\n/**\n * Internal helper to fetch hotspots from the Mux Data API.\n * Uses the raw HTTP methods on the Mux client since the SDK doesn't have\n * typed methods for the engagement endpoints yet.\n */\nasync function fetchHotspots(\n identifierType: \"assets\" | \"videos\" | \"playback-ids\",\n id: string,\n options: HotspotOptions,\n): Promise<HotspotResponse> {\n \"use step\";\n const {\n limit = 5,\n orderDirection = \"desc\",\n orderBy = \"score\",\n timeframe = \"[24:hours]\",\n credentials,\n } = options;\n\n const muxClient = await getMuxClientFromEnv(credentials);\n const mux = await muxClient.createClient();\n\n // Build query parameters\n const queryParams = new URLSearchParams();\n queryParams.append(\"limit\", String(limit));\n queryParams.append(\"order_direction\", orderDirection);\n queryParams.append(\"order_by\", orderBy);\n queryParams.append(\"timeframe[]\", timeframe);\n\n // Use the raw HTTP method since the SDK doesn't have typed engagement methods yet\n const path = `/data/v1/engagement/${identifierType}/${id}/hotspots?${queryParams.toString()}`;\n const response = await mux.get<unknown, HotspotApiResponse>(path);\n\n return transformHotspotResponse(response);\n}\n","import env from \"@mux/ai/env\";\nimport { resolveMuxSigningContext } from \"@mux/ai/lib/workflow-credentials\";\nimport type { WorkflowCredentialsInput } from \"@mux/ai/types\";\n\nimport type Mux from \"@mux/mux-node\";\n\n/**\n * Context required to sign URLs for signed playback IDs.\n */\nexport interface SigningContext {\n /** The signing key ID from Mux dashboard. */\n keyId: string;\n /** The base64-encoded private key from Mux dashboard. */\n keySecret: string;\n /** Token expiration time (e.g. '1h', '1d'). Defaults to '1h'. */\n expiration?: string;\n}\n\n/**\n * Token type determines which Mux service the token is valid for.\n */\nexport type TokenType = \"video\" | \"thumbnail\" | \"storyboard\" | \"gif\";\n\n/**\n * Resolves signing context from config or environment variables.\n * Returns undefined if signing keys are not configured.\n */\nexport function getMuxSigningContextFromEnv(): SigningContext | undefined {\n const keyId = env.MUX_SIGNING_KEY;\n const keySecret = env.MUX_PRIVATE_KEY;\n\n if (!keyId || !keySecret) {\n return undefined;\n }\n\n return { keyId, keySecret };\n}\n\n/**\n * Creates a Mux client configured for JWT signing.\n * This client is used internally for signing operations.\n */\nasync function createSigningClient(context: SigningContext): Promise<Mux> {\n // Dynamic import to prevent @mux/mux-node (and its transitive dep jose)\n // from being bundled into workflow VM code where `require` is unavailable.\n const { default: MuxClient } = await import(\"@mux/mux-node\");\n return new MuxClient({\n // These are not needed for signing, but the SDK requires them\n // Using empty strings as we only need the jwt functionality\n tokenId: env.MUX_TOKEN_ID || \"\",\n tokenSecret: env.MUX_TOKEN_SECRET || \"\",\n jwtSigningKey: context.keyId,\n jwtPrivateKey: context.keySecret,\n });\n}\n\n/**\n * Generates a signed token for a playback ID using the Mux SDK.\n *\n * @param playbackId - The Mux playback ID to sign\n * @param context - Signing context with key credentials\n * @param type - Token type (video, thumbnail, storyboard, gif)\n * @param params - Additional parameters for thumbnail/storyboard tokens (values will be stringified)\n * @returns Signed JWT token\n */\nexport async function signPlaybackId(\n playbackId: string,\n context: SigningContext,\n type: TokenType = \"video\",\n params?: Record<string, string | number>,\n): Promise<string> {\n \"use step\";\n const client = await createSigningClient(context);\n\n // Convert params to Record<string, string> as required by the SDK\n const stringParams = params ?\n Object.fromEntries(\n Object.entries(params).map(([key, value]) => [key, String(value)]),\n ) :\n undefined;\n\n return client.jwt.signPlaybackId(playbackId, {\n type,\n expiration: context.expiration || \"1h\",\n params: stringParams,\n });\n}\n\n/**\n * Appends a signed token to a Mux URL.\n *\n * @param url - The base Mux URL (e.g. https://image.mux.com/{playbackId}/thumbnail.png)\n * @param playbackId - The Mux playback ID\n * @param context - Signing context with key credentials\n * @param type - Token type for the URL\n * @param params - Additional parameters for the token\n * @returns URL with token query parameter appended\n */\nexport async function signUrl(\n url: string,\n playbackId: string,\n context?: SigningContext,\n type: TokenType = \"video\",\n params?: Record<string, string | number>,\n credentials?: WorkflowCredentialsInput,\n): Promise<string> {\n \"use step\";\n const resolvedContext = context ?? await resolveMuxSigningContext(credentials);\n if (!resolvedContext) {\n throw new Error(\n \"Signed playback ID requires signing credentials. \" +\n \"Provide muxSigningKey and muxPrivateKey via workflow credentials or set MUX_SIGNING_KEY and MUX_PRIVATE_KEY environment variables.\",\n );\n }\n const token = await signPlaybackId(playbackId, resolvedContext, type, params);\n const separator = url.includes(\"?\") ? \"&\" : \"?\";\n return `${url}${separator}token=${token}`;\n}\n","import { signUrl } from \"@mux/ai/lib/url-signing\";\nimport type { WorkflowCredentialsInput } from \"@mux/ai/types\";\n\nexport const DEFAULT_STORYBOARD_WIDTH = 640;\n\n/**\n * Generates a storyboard URL for the given playback ID.\n * If shouldSign is true, the URL will be signed with a token using credentials from environment variables.\n *\n * @param playbackId - The Mux playback ID\n * @param width - Width of the storyboard in pixels (default: 640)\n * @param shouldSign - Flag for whether or not to use signed playback IDs (default: false)\n * @returns Storyboard URL (signed if shouldSign is true)\n */\nexport async function getStoryboardUrl(\n playbackId: string,\n width: number = DEFAULT_STORYBOARD_WIDTH,\n shouldSign: boolean = false,\n credentials?: WorkflowCredentialsInput,\n): Promise<string> {\n \"use step\";\n const baseUrl = `https://image.mux.com/${playbackId}/storyboard.png`;\n\n if (shouldSign) {\n return signUrl(baseUrl, playbackId, undefined, \"storyboard\", { width }, credentials);\n }\n\n return `${baseUrl}?width=${width}`;\n}\n","import type { VTTCue } from \"@mux/ai/primitives/transcripts\";\nimport type { ChunkingStrategy, TextChunk } from \"@mux/ai/types\";\n\n/**\n * Simple token counter that approximates tokens by word count.\n * For production use with OpenAI, consider using a proper tokenizer like tiktoken.\n * This approximation is generally close enough for chunking purposes (1 token ≈ 0.75 words).\n */\nexport function estimateTokenCount(text: string): number {\n const words = text.trim().split(/\\s+/).length;\n return Math.ceil(words / 0.75);\n}\n\n/**\n * Chunks text into overlapping segments based on token count.\n *\n * @param text - The text to chunk\n * @param maxTokens - Maximum tokens per chunk\n * @param overlapTokens - Number of tokens to overlap between chunks\n * @returns Array of text chunks with metadata\n */\nexport function chunkByTokens(\n text: string,\n maxTokens: number,\n overlapTokens: number = 0,\n): TextChunk[] {\n if (!text.trim()) {\n return [];\n }\n\n const chunks: TextChunk[] = [];\n const words = text.trim().split(/\\s+/);\n\n // Convert tokens to approximate word count\n const wordsPerChunk = Math.floor(maxTokens * 0.75);\n const overlapWords = Math.floor(overlapTokens * 0.75);\n\n let chunkIndex = 0;\n let currentPosition = 0;\n\n while (currentPosition < words.length) {\n const chunkWords = words.slice(\n currentPosition,\n currentPosition + wordsPerChunk,\n );\n const chunkText = chunkWords.join(\" \");\n const tokenCount = estimateTokenCount(chunkText);\n\n chunks.push({\n id: `chunk-${chunkIndex}`,\n text: chunkText,\n tokenCount,\n });\n\n // Move forward by chunk size minus overlap\n currentPosition += wordsPerChunk - overlapWords;\n chunkIndex++;\n\n // Prevent infinite loop if overlap is too large\n if (currentPosition <= (chunkIndex - 1) * (wordsPerChunk - overlapWords)) {\n break;\n }\n }\n\n return chunks;\n}\n\n/**\n * Creates a TextChunk from a group of VTT cues.\n */\nfunction createChunkFromCues(cues: VTTCue[], index: number): TextChunk {\n const text = cues.map(c => c.text).join(\" \");\n return {\n id: `chunk-${index}`,\n text,\n tokenCount: estimateTokenCount(text),\n startTime: cues[0].startTime,\n endTime: cues[cues.length - 1].endTime,\n };\n}\n\n/**\n * Chunks VTT cues into groups that respect natural cue boundaries.\n * Splits at cue boundaries rather than mid-sentence, preserving accurate timestamps.\n *\n * @param cues - Array of VTT cues to chunk\n * @param maxTokens - Maximum tokens per chunk\n * @param overlapCues - Number of cues to overlap between chunks (default: 2)\n * @returns Array of text chunks with accurate start/end times\n */\nexport function chunkVTTCues(\n cues: VTTCue[],\n maxTokens: number,\n overlapCues: number = 2,\n): TextChunk[] {\n if (cues.length === 0)\n return [];\n\n const chunks: TextChunk[] = [];\n let currentCues: VTTCue[] = [];\n let currentTokens = 0;\n let chunkIndex = 0;\n\n for (let i = 0; i < cues.length; i++) {\n const cue = cues[i];\n const cueTokens = estimateTokenCount(cue.text);\n\n // If adding this cue would exceed limit, finalize current chunk\n if (currentTokens + cueTokens > maxTokens && currentCues.length > 0) {\n chunks.push(createChunkFromCues(currentCues, chunkIndex));\n chunkIndex++;\n\n // Start new chunk with overlap from end of previous\n const overlapStart = Math.max(0, currentCues.length - overlapCues);\n currentCues = currentCues.slice(overlapStart);\n currentTokens = currentCues.reduce(\n (sum, c) => sum + estimateTokenCount(c.text),\n 0,\n );\n }\n\n currentCues.push(cue);\n currentTokens += cueTokens;\n }\n\n // Don't forget the last chunk\n if (currentCues.length > 0) {\n chunks.push(createChunkFromCues(currentCues, chunkIndex));\n }\n\n return chunks;\n}\n\n/**\n * Chunks text according to the specified strategy.\n *\n * @param text - The text to chunk\n * @param strategy - The chunking strategy to use\n * @returns Array of text chunks\n */\nexport function chunkText(text: string, strategy: ChunkingStrategy): TextChunk[] {\n switch (strategy.type) {\n case \"token\": {\n return chunkByTokens(text, strategy.maxTokens, strategy.overlap ?? 0);\n }\n default: {\n const exhaustiveCheck: never = strategy as never;\n throw new Error(`Unsupported chunking strategy: ${exhaustiveCheck}`);\n }\n }\n}\n","import { signUrl } from \"@mux/ai/lib/url-signing\";\nimport type { WorkflowCredentialsInput } from \"@mux/ai/types\";\n\nexport interface ThumbnailOptions {\n /** Interval between thumbnails in seconds (default: 10) */\n interval?: number;\n /** Width of the thumbnail in pixels (default: 640) */\n width?: number;\n /** Flag for whether or not to use signed playback IDs (default: false) */\n shouldSign?: boolean;\n /** Maximum number of thumbnails to generate. When set, samples are evenly distributed with first and last frames pinned. */\n maxSamples?: number;\n /** Workflow credentials for signing (optional). */\n credentials?: WorkflowCredentialsInput;\n}\n\n/**\n * Generates thumbnail URLs at regular intervals based on video duration.\n * If shouldSign is true, the URLs will be signed with tokens using credentials from environment variables.\n *\n * @param playbackId - The Mux playback ID\n * @param duration - Video duration in seconds\n * @param options - Thumbnail generation options\n * @returns Array of thumbnail URLs (signed if shouldSign is true)\n */\nexport async function getThumbnailUrls(\n playbackId: string,\n duration: number,\n options: ThumbnailOptions = {},\n): Promise<string[]> {\n \"use step\";\n const { interval = 10, width = 640, shouldSign = false, maxSamples, credentials } = options;\n let timestamps: number[] = [];\n\n if (duration <= 50) {\n const spacing = duration / 6;\n for (let i = 1; i <= 5; i++) {\n timestamps.push(Math.round(i * spacing));\n }\n } else {\n for (let time = 0; time < duration; time += interval) {\n timestamps.push(time);\n }\n }\n\n // Apply maxSamples cap if specified and we have more timestamps than the limit\n if (maxSamples !== undefined && timestamps.length > maxSamples) {\n const newTimestamps: number[] = [];\n\n // Always include first frame\n newTimestamps.push(0);\n\n // If maxSamples >= 2, add evenly distributed middle frames and last frame\n if (maxSamples >= 2) {\n const spacing = duration / (maxSamples - 1);\n for (let i = 1; i < maxSamples - 1; i++) {\n newTimestamps.push(spacing * i);\n }\n // Always include last frame\n newTimestamps.push(duration);\n }\n\n timestamps = newTimestamps;\n }\n\n const baseUrl = `https://image.mux.com/${playbackId}/thumbnail.png`;\n\n const urlPromises = timestamps.map(async (time) => {\n if (shouldSign) {\n return signUrl(baseUrl, playbackId, undefined, \"thumbnail\", { time, width }, credentials);\n }\n\n return `${baseUrl}?time=${time}&width=${width}`;\n });\n\n return Promise.all(urlPromises);\n}\n","import { signUrl } from \"@mux/ai/lib/url-signing\";\nimport type { AssetTextTrack, MuxAsset, WorkflowCredentialsInput } from \"@mux/ai/types\";\n\n/** A single cue from a VTT file with timing info. */\nexport interface VTTCue {\n startTime: number;\n endTime: number;\n text: string;\n}\n\nexport interface TranscriptFetchOptions {\n languageCode?: string;\n cleanTranscript?: boolean;\n /** Optional signing context for signed playback IDs */\n shouldSign?: boolean;\n credentials?: WorkflowCredentialsInput;\n /**\n * When true, throws if no usable transcript can be retrieved (no ready text track,\n * missing track id, fetch error, or empty transcript).\n *\n * Default behavior is non-fatal and returns an empty `transcriptText`.\n */\n required?: boolean;\n}\n\nexport interface TranscriptResult {\n transcriptText: string;\n transcriptUrl?: string;\n track?: AssetTextTrack;\n}\n\nexport function getReadyTextTracks(asset: MuxAsset): AssetTextTrack[] {\n return (asset.tracks || []).filter(\n track => track.type === \"text\" && track.status === \"ready\",\n );\n}\n\nexport function findCaptionTrack(asset: MuxAsset, languageCode?: string): AssetTextTrack | undefined {\n const tracks = getReadyTextTracks(asset);\n if (!tracks.length)\n return undefined;\n\n if (!languageCode) {\n return tracks[0];\n }\n\n return tracks.find(\n track =>\n track.text_type === \"subtitles\" &&\n track.language_code === languageCode,\n );\n}\n\nexport function extractTextFromVTT(vttContent: string): string {\n if (!vttContent.trim()) {\n return \"\";\n }\n\n const lines = vttContent.split(\"\\n\");\n const textLines: string[] = [];\n\n for (let i = 0; i < lines.length; i++) {\n const line = lines[i].trim();\n\n if (!line)\n continue;\n if (line === \"WEBVTT\")\n continue;\n if (line.startsWith(\"NOTE \"))\n continue;\n if (line.includes(\"-->\"))\n continue;\n if (/^[\\w-]+$/.test(line) && !line.includes(\" \"))\n continue;\n if (line.startsWith(\"STYLE\") || line.startsWith(\"REGION\"))\n continue;\n\n const cleanLine = line.replace(/<[^>]*>/g, \"\").trim();\n\n if (cleanLine) {\n textLines.push(cleanLine);\n }\n }\n\n return textLines.join(\" \").replace(/\\s+/g, \" \").trim();\n}\n\nexport function vttTimestampToSeconds(timestamp: string): number {\n const parts = timestamp.split(\":\");\n if (parts.length !== 3)\n return 0;\n\n const hours = Number.parseInt(parts[0], 10) || 0;\n const minutes = Number.parseInt(parts[1], 10) || 0;\n const seconds = Number.parseFloat(parts[2]) || 0;\n\n return hours * 3600 + minutes * 60 + seconds;\n}\n\n/**\n * Converts seconds to a human-readable timestamp.\n * Returns M:SS for durations under an hour, H:MM:SS for an hour or more.\n *\n * @param seconds - The number of seconds to convert\n * @returns A formatted timestamp string (e.g., \"2:05\" or \"01:02:05\")\n */\nexport function secondsToTimestamp(seconds: number): string {\n const rounded = Math.max(0, Math.floor(seconds));\n const hours = Math.floor(rounded / 3600);\n const minutes = Math.floor((rounded % 3600) / 60);\n const remainingSeconds = rounded % 60;\n\n if (hours > 0) {\n return `${hours.toString().padStart(2, \"0\")}:${minutes.toString().padStart(2, \"0\")}:${remainingSeconds.toString().padStart(2, \"0\")}`;\n }\n return `${minutes}:${remainingSeconds.toString().padStart(2, \"0\")}`;\n}\n\nexport function extractTimestampedTranscript(vttContent: string): string {\n if (!vttContent.trim()) {\n return \"\";\n }\n\n const lines = vttContent.split(\"\\n\");\n const segments: Array<{ time: number; text: string }> = [];\n\n for (let i = 0; i < lines.length; i++) {\n const line = lines[i].trim();\n\n if (line.includes(\"-->\")) {\n const startTime = line.split(\" --> \")[0].trim();\n const timeInSeconds = vttTimestampToSeconds(startTime);\n\n let j = i + 1;\n while (j < lines.length && !lines[j].trim()) {\n j++;\n }\n\n if (j < lines.length) {\n const text = lines[j].trim().replace(/<[^>]*>/g, \"\");\n if (text) {\n segments.push({ time: timeInSeconds, text });\n }\n }\n }\n }\n\n return segments\n .map(segment => `[${Math.floor(segment.time)}s] ${segment.text}`)\n .join(\"\\n\");\n}\n\n/**\n * Parses VTT content into structured cues with timing.\n *\n * @param vttContent - Raw VTT file content\n * @returns Array of VTT cues with start/end times and text\n */\nexport function parseVTTCues(vttContent: string): VTTCue[] {\n if (!vttContent.trim())\n return [];\n\n const lines = vttContent.split(\"\\n\");\n const cues: VTTCue[] = [];\n\n for (let i = 0; i < lines.length; i++) {\n const line = lines[i].trim();\n\n if (line.includes(\"-->\")) {\n const [startStr, endStr] = line.split(\" --> \").map(s => s.trim());\n const startTime = vttTimestampToSeconds(startStr);\n const endTime = vttTimestampToSeconds(endStr.split(\" \")[0]); // Handle cue settings\n\n // Collect text lines until empty line or next timestamp\n const textLines: string[] = [];\n let j = i + 1;\n while (j < lines.length && lines[j].trim() && !lines[j].includes(\"-->\")) {\n const cleanLine = lines[j].trim().replace(/<[^>]*>/g, \"\");\n if (cleanLine)\n textLines.push(cleanLine);\n j++;\n }\n\n if (textLines.length > 0) {\n cues.push({\n startTime,\n endTime,\n text: textLines.join(\" \"),\n });\n }\n }\n }\n\n return cues;\n}\n\n/**\n * Builds a transcript URL for the given playback ID and track ID.\n * If a signing context is provided, the URL will be signed with a token.\n *\n * @param playbackId - The Mux playback ID\n * @param trackId - The text track ID\n * @param shouldSign - Flag for whether or not to use signed playback IDs\n * @returns Transcript URL (signed if context provided)\n */\nexport async function buildTranscriptUrl(\n playbackId: string,\n trackId: string,\n shouldSign: boolean = false,\n credentials?: WorkflowCredentialsInput,\n): Promise<string> {\n \"use step\";\n const baseUrl = `https://stream.mux.com/${playbackId}/text/${trackId}.vtt`;\n\n if (shouldSign) {\n return signUrl(baseUrl, playbackId, undefined, \"video\", undefined, credentials);\n }\n\n return baseUrl;\n}\n\nexport async function fetchTranscriptForAsset(\n asset: MuxAsset,\n playbackId: string,\n options: TranscriptFetchOptions = {},\n): Promise<TranscriptResult> {\n \"use step\";\n const {\n languageCode,\n cleanTranscript = true,\n shouldSign,\n credentials,\n required = false,\n } = options;\n const track = findCaptionTrack(asset, languageCode);\n\n if (!track) {\n if (required) {\n const availableLanguages = getReadyTextTracks(asset)\n .map(t => t.language_code)\n .filter(Boolean)\n .join(\", \");\n throw new Error(\n `No transcript track found${languageCode ? ` for language '${languageCode}'` : \"\"}. Available languages: ${availableLanguages || \"none\"}`,\n );\n }\n return { transcriptText: \"\" };\n }\n\n if (!track.id) {\n if (required) {\n throw new Error(\"Transcript track is missing an id\");\n }\n return { transcriptText: \"\", track };\n }\n\n const transcriptUrl = await buildTranscriptUrl(playbackId, track.id, shouldSign, credentials);\n\n try {\n const response = await fetch(transcriptUrl);\n if (!response.ok) {\n if (required) {\n throw new Error(`Failed to fetch transcript (HTTP ${response.status})`);\n }\n return { transcriptText: \"\", transcriptUrl, track };\n }\n\n const rawVtt = await response.text();\n const transcriptText = cleanTranscript ? extractTextFromVTT(rawVtt) : rawVtt;\n\n if (required && !transcriptText.trim()) {\n throw new Error(\"Transcript is empty\");\n }\n\n return { transcriptText, transcriptUrl, track };\n } catch (error) {\n if (required) {\n throw new Error(\n `Failed to fetch transcript: ${error instanceof Error ? error.message : \"Unknown error\"}`,\n );\n }\n console.warn(\"Failed to fetch transcript:\", error);\n return { transcriptText: \"\", transcriptUrl, track };\n }\n}\n","export * from \"./ask-questions\";\nexport * from \"./burned-in-captions\";\nexport * from \"./chapters\";\nexport * from \"./embeddings\";\nexport * from \"./moderation\";\nexport * from \"./summarization\";\nexport * from \"./translate-audio\";\nexport * from \"./translate-captions\";\n","import { generateText, Output } from \"ai\";\nimport dedent from \"dedent\";\nimport { z } from \"zod\";\n\nimport type { ImageDownloadOptions } from \"@mux/ai/lib/image-download\";\nimport { downloadImageAsBase64 } from \"@mux/ai/lib/image-download\";\nimport { getAssetDurationSecondsFromAsset, getPlaybackIdForAsset } from \"@mux/ai/lib/mux-assets\";\nimport { createPromptBuilder, createTranscriptSection } from \"@mux/ai/lib/prompt-builder\";\nimport { createLanguageModelFromConfig, resolveLanguageModelConfig } from \"@mux/ai/lib/providers\";\nimport type { ModelIdByProvider, SupportedProvider } from \"@mux/ai/lib/providers\";\nimport { withRetry } from \"@mux/ai/lib/retry\";\nimport { resolveMuxSigningContext } from \"@mux/ai/lib/workflow-credentials\";\nimport { getStoryboardUrl } from \"@mux/ai/primitives/storyboards\";\nimport { fetchTranscriptForAsset } from \"@mux/ai/primitives/transcripts\";\nimport type { ImageSubmissionMode, MuxAIOptions, TokenUsage, WorkflowCredentialsInput } from \"@mux/ai/types\";\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Types\n// ─────────────────────────────────────────────────────────────────────────────\n\n/** A single yes/no question to be answered about video content. */\nexport interface Question {\n /** The question text */\n question: string;\n}\n\n/** A single answer to a question. */\nexport interface QuestionAnswer {\n /** The original question */\n question: string;\n /** Answer selected from the allowed options */\n answer: string;\n /** Confidence score between 0 and 1 */\n confidence: number;\n /** Reasoning explaining the answer based on observable evidence */\n reasoning: string;\n}\n\n/** Configuration options for askQuestions workflow. */\nexport interface AskQuestionsOptions extends MuxAIOptions {\n /** AI provider to run (defaults to 'openai'). */\n provider?: SupportedProvider;\n /** Provider-specific chat model identifier. */\n model?: ModelIdByProvider[SupportedProvider];\n /** Allowed answers for each question (defaults to [\"yes\", \"no\"]). */\n answerOptions?: string[];\n /** Fetch transcript alongside storyboard (defaults to true). */\n includeTranscript?: boolean;\n /** Strip timestamps/markup from transcripts (defaults to true). */\n cleanTranscript?: boolean;\n /** How storyboard should be delivered to the provider (defaults to 'url'). */\n imageSubmissionMode?: ImageSubmissionMode;\n /** Fine-tune storyboard downloads when imageSubmissionMode === 'base64'. */\n imageDownloadOptions?: ImageDownloadOptions;\n /** Storyboard width in pixels (defaults to 640). */\n storyboardWidth?: number;\n}\n\n/** Structured return payload for askQuestions workflow. */\nexport interface AskQuestionsResult {\n /** Asset ID passed into the workflow. */\n assetId: string;\n /** Array of answers for each question. */\n answers: QuestionAnswer[];\n /** Storyboard image URL that was analyzed. */\n storyboardUrl: string;\n /** Token usage from the AI provider (for efficiency/cost analysis). */\n usage?: TokenUsage;\n /** Raw transcript text used for analysis (when includeTranscript is true). */\n transcriptText?: string;\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Zod Schemas\n// ─────────────────────────────────────────────────────────────────────────────\n\n/** Zod schema for a single answer. */\nexport const questionAnswerSchema = z.object({\n question: z.string(),\n answer: z.string(),\n confidence: z.number(),\n reasoning: z.string(),\n});\n\nexport type QuestionAnswerType = z.infer<typeof questionAnswerSchema>;\n\nfunction createAskQuestionsSchema(allowedAnswers: [string, ...string[]]) {\n const answerSchema = z.enum(allowedAnswers);\n\n return z.object({\n answers: z.array(\n questionAnswerSchema.extend({\n answer: answerSchema,\n }),\n ),\n });\n}\n\ntype AskQuestionsSchema = ReturnType<typeof createAskQuestionsSchema>;\nexport type AskQuestionsType = z.infer<AskQuestionsSchema>;\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Prompts\n// ─────────────────────────────────────────────────────────────────────────────\n\nconst SYSTEM_PROMPT = dedent`\n <role>\n You are a video content analyst specializing in classification tasks.\n Your job is to answer questions about video content based on storyboard\n images and optional transcript data.\n </role>\n\n <context>\n You will receive:\n - A storyboard image containing multiple sequential frames from a video\n - A list of questions about the video content\n - Optionally, a transcript of the audio/dialogue\n\n The storyboard frames are arranged in a grid and represent the visual\n progression of the content over time. Read frames left-to-right,\n top-to-bottom to understand the temporal sequence.\n </context>\n\n <transcript_guidance>\n When a transcript is provided:\n - Use it to understand spoken content, dialogue, and audio context\n - Correlate transcript with visual frames for comprehensive analysis\n - Consider both visual and audio evidence when answering questions\n - If transcript and visuals conflict, trust the visual evidence\n </transcript_guidance>\n\n <task>\n For each question provided, you must:\n 1. Analyze the storyboard frames and transcript (if provided)\n 2. Answer with ONLY the allowed response options - no other values are acceptable\n 3. Provide a confidence score between 0 and 1 reflecting your certainty\n 4. Explain your reasoning based on observable evidence\n </task>\n\n <answer_guidelines>\n - Choose the affirmative option only if you have clear evidence supporting it\n - Choose the negative/contradicting option if evidence contradicts or if insufficient evidence exists\n - Confidence should reflect the clarity and strength of evidence:\n * 0.9-1.0: Clear, unambiguous evidence\n * 0.7-0.9: Strong evidence with minor ambiguity\n * 0.5-0.7: Moderate evidence or some conflicting signals\n * 0.3-0.5: Weak evidence or significant ambiguity\n * 0.0-0.3: Very uncertain, minimal relevant evidence\n - Reasoning should cite specific visual or audio evidence\n - Be precise: cite specific frames, objects, actions, or transcript quotes\n </answer_guidelines>\n\n <constraints>\n - You MUST answer every question with one of the allowed response options\n - Only describe observable evidence from frames or transcript\n - Do not fabricate details or make unsupported assumptions\n - Return structured data matching the requested schema exactly\n </constraints>\n\n <language_guidelines>\n When explaining reasoning:\n - Describe content directly, not the medium\n - BAD: \"The video shows a person running\"\n - GOOD: \"A person runs through a park\"\n - Be specific and evidence-based\n </language_guidelines>`;\n\nfunction buildSystemPrompt(allowedAnswers: string[]): string {\n const answerList = allowedAnswers.map(answer => `\"${answer}\"`).join(\", \");\n\n return `${SYSTEM_PROMPT}\\n\\n${dedent`\n <response_options>\n Allowed answers: ${answerList}\n </response_options>\n `}`;\n}\n\ntype AskQuestionsPromptSections = \"questions\";\n\nconst askQuestionsPromptBuilder = createPromptBuilder<AskQuestionsPromptSections>({\n template: {\n questions: {\n tag: \"questions\",\n content: \"Please answer the following yes/no questions about this video:\",\n },\n },\n sectionOrder: [\"questions\"],\n});\n\nfunction buildUserPrompt(\n questions: Question[],\n transcriptText?: string,\n isCleanTranscript: boolean = true,\n): string {\n const questionsList = questions\n .map((q, idx) => `${idx + 1}. ${q.question}`)\n .join(\"\\n\");\n\n const questionsContent = dedent`\n Please answer the following yes/no questions about this video:\n\n ${questionsList}`;\n\n if (!transcriptText) {\n return askQuestionsPromptBuilder.build({ questions: questionsContent });\n }\n\n const format = isCleanTranscript ? \"plain text\" : \"WebVTT\";\n const transcriptSection = createTranscriptSection(transcriptText, format);\n\n return askQuestionsPromptBuilder.buildWithContext(\n { questions: questionsContent },\n [transcriptSection],\n );\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Implementation\n// ─────────────────────────────────────────────────────────────────────────────\n\ninterface AnalysisResponse {\n result: AskQuestionsType;\n usage: TokenUsage;\n}\n\nasync function fetchImageAsBase64(\n imageUrl: string,\n imageDownloadOptions?: ImageDownloadOptions,\n): Promise<string> {\n \"use step\";\n\n const downloadResult = await downloadImageAsBase64(imageUrl, imageDownloadOptions);\n return downloadResult.base64Data;\n}\n\nasync function analyzeQuestionsWithStoryboard(\n imageDataUrl: string,\n provider: SupportedProvider,\n modelId: string,\n userPrompt: string,\n systemPrompt: string,\n allowedAnswers: [string, ...string[]],\n credentials?: WorkflowCredentialsInput,\n): Promise<AnalysisResponse> {\n \"use step\";\n const model = await createLanguageModelFromConfig(provider, modelId, credentials);\n const responseSchema = createAskQuestionsSchema(allowedAnswers);\n\n const response = await generateText({\n model,\n output: Output.object({ schema: responseSchema }),\n experimental_telemetry: { isEnabled: true },\n messages: [\n {\n role: \"system\",\n content: systemPrompt,\n },\n {\n role: \"user\",\n content: [\n { type: \"text\", text: userPrompt },\n { type: \"image\", image: imageDataUrl },\n ],\n },\n ],\n });\n\n return {\n result: {\n answers: response.output.answers.map(answer => ({\n ...answer,\n // Strip numbering prefix (e.g., \"1. \" or \"2. \") from questions\n question: answer.question.replace(/^\\d+\\.\\s*/, \"\"),\n confidence: Math.min(1, Math.max(0, answer.confidence)),\n })),\n },\n usage: {\n inputTokens: response.usage.inputTokens,\n outputTokens: response.usage.outputTokens,\n totalTokens: response.usage.totalTokens,\n reasoningTokens: response.usage.reasoningTokens,\n cachedInputTokens: response.usage.cachedInputTokens,\n },\n };\n}\n\n/**\n * Answer questions about a Mux video asset by analyzing storyboard frames and transcript.\n * Defaults to yes/no answers unless `answerOptions` are provided.\n *\n * This workflow takes a list of questions and returns structured answers with confidence\n * scores and reasoning for each question. All questions are processed in a single LLM call for\n * efficiency.\n *\n * @param assetId - The Mux asset ID to analyze\n * @param questions - Array of questions to answer (each must have a 'question' field)\n * @param options - Configuration options for the workflow\n * @returns Structured answers with confidence scores and reasoning\n *\n * @example\n * ```typescript\n * const result = await askQuestions(\"abc123\", [\n * { question: \"Does this video contain cooking?\" },\n * { question: \"Are there people visible in the video?\" },\n * ]);\n *\n * console.log(result.answers[0]);\n * // {\n * // question: \"Does this video contain cooking?\",\n * // answer: \"yes\",\n * // confidence: 0.95,\n * // reasoning: \"A chef prepares ingredients and cooks in a kitchen throughout the video.\"\n * // }\n * ```\n */\nexport async function askQuestions(\n assetId: string,\n questions: Question[],\n options?: AskQuestionsOptions,\n): Promise<AskQuestionsResult> {\n \"use workflow\";\n\n // Validate questions array is non-empty\n if (!questions || questions.length === 0) {\n throw new Error(\"At least one question must be provided\");\n }\n\n // Validate each question has valid text\n questions.forEach((q, idx) => {\n if (!q.question || typeof q.question !== \"string\" || !q.question.trim()) {\n throw new Error(\n `Question at index ${idx} is invalid: must have non-empty 'question' field`,\n );\n }\n });\n\n const {\n provider = \"openai\",\n model,\n answerOptions,\n includeTranscript = true,\n cleanTranscript = true,\n imageSubmissionMode = \"url\",\n imageDownloadOptions,\n storyboardWidth = 640,\n credentials,\n } = options ?? {};\n\n const normalizedAnswerOptions = Array.from(\n new Set(\n (answerOptions?.length ? answerOptions : [\"yes\", \"no\"])\n .map(option => option.trim())\n .filter(Boolean),\n ),\n );\n\n if (!normalizedAnswerOptions.length) {\n throw new Error(\"answerOptions must include at least one non-empty value\");\n }\n\n const allowedAnswers = normalizedAnswerOptions as [string, ...string[]];\n\n const modelConfig = resolveLanguageModelConfig({\n ...options,\n model,\n provider: provider as SupportedProvider,\n });\n // Fetch asset data and playback ID from Mux\n const { asset: assetData, playbackId, policy } = await getPlaybackIdForAsset(assetId, credentials);\n\n const assetDurationSeconds = getAssetDurationSecondsFromAsset(assetData);\n\n // Resolve signing context for signed playback IDs\n const signingContext = await resolveMuxSigningContext(credentials);\n if (policy === \"signed\" && !signingContext) {\n throw new Error(\n \"Signed playback ID requires signing credentials. \" +\n \"Set MUX_SIGNING_KEY and MUX_PRIVATE_KEY environment variables.\",\n );\n }\n\n const transcriptText =\n includeTranscript ?\n (await fetchTranscriptForAsset(assetData, playbackId, {\n cleanTranscript,\n shouldSign: policy === \"signed\",\n })).transcriptText :\n \"\";\n\n // Build the user prompt with questions and optional transcript\n const userPrompt = buildUserPrompt(questions, transcriptText, cleanTranscript);\n const systemPrompt = buildSystemPrompt(normalizedAnswerOptions);\n\n // Generate storyboard URL (signed if needed)\n const imageUrl = await getStoryboardUrl(\n playbackId,\n storyboardWidth,\n policy === \"signed\",\n credentials,\n );\n\n let analysisResponse: AnalysisResponse;\n\n try {\n if (imageSubmissionMode === \"base64\") {\n const base64Data = await fetchImageAsBase64(imageUrl, imageDownloadOptions);\n analysisResponse = await analyzeQuestionsWithStoryboard(\n base64Data,\n modelConfig.provider,\n modelConfig.modelId,\n userPrompt,\n systemPrompt,\n allowedAnswers,\n credentials,\n );\n } else {\n // URL-based submission with retry\n analysisResponse = await withRetry(() =>\n analyzeQuestionsWithStoryboard(\n imageUrl,\n modelConfig.provider,\n modelConfig.modelId,\n userPrompt,\n systemPrompt,\n allowedAnswers,\n credentials,\n ),\n );\n }\n } catch (error: unknown) {\n throw new Error(\n `Failed to analyze questions with ${provider}: ${\n error instanceof Error ? error.message : \"Unknown error\"\n }`,\n );\n }\n\n if (!analysisResponse.result?.answers) {\n throw new Error(`Failed to get answers for asset ${assetId}`);\n }\n\n // Validate we got answers for all questions\n if (analysisResponse.result.answers.length !== questions.length) {\n throw new Error(\n `Expected ${questions.length} answers but received ${analysisResponse.result.answers.length}`,\n );\n }\n\n return {\n assetId,\n answers: analysisResponse.result.answers,\n storyboardUrl: imageUrl,\n usage: {\n ...analysisResponse.usage,\n metadata: {\n assetDurationSeconds,\n },\n },\n transcriptText: transcriptText || undefined,\n };\n}\n","import pRetry, { AbortError } from \"p-retry\";\n\nconst BASE64_CHUNK_SIZE = 0x8000;\n\nfunction bytesToBase64(bytes: Uint8Array): string {\n if (bytes.length === 0) {\n return \"\";\n }\n let binary = \"\";\n for (let i = 0; i < bytes.length; i += BASE64_CHUNK_SIZE) {\n const chunk = bytes.subarray(i, i + BASE64_CHUNK_SIZE);\n binary += String.fromCharCode(...chunk);\n }\n if (typeof globalThis.btoa !== \"function\") {\n throw new TypeError(\"Base64 encoder is not available in this environment.\");\n }\n return globalThis.btoa(binary);\n}\n\nexport interface ImageDownloadOptions {\n /** Request timeout in milliseconds (default: 10000) */\n timeout?: number;\n /** Maximum number of retry attempts (default: 3) */\n retries?: number;\n /** Base delay between retries in milliseconds (default: 1000) */\n retryDelay?: number;\n /** Maximum delay between retries in milliseconds (default: 10000) */\n maxRetryDelay?: number;\n /** Whether to use exponential backoff (default: true) */\n exponentialBackoff?: boolean;\n}\n\nexport interface ImageDownloadResult {\n /** Base64 encoded image data with data URI prefix (e.g., \"data:image/png;base64,iVBORw0K...\") */\n base64Data: string;\n /** Raw image bytes for multipart/form-data uploads */\n buffer: Uint8Array;\n /** Original image URL */\n url: string;\n /** Content type of the downloaded image */\n contentType: string;\n /** Size of the downloaded image in bytes */\n sizeBytes: number;\n /** Number of retry attempts made (0 if successful on first try) */\n attempts: number;\n}\n\nexport interface AnthropicFileUploadResult {\n /** Anthropic Files API file ID */\n fileId: string;\n /** Original image URL */\n url: string;\n /** Content type of the uploaded image */\n contentType: string;\n /** Size of the uploaded image in bytes */\n sizeBytes: number;\n}\n\nconst DEFAULT_OPTIONS: Required<ImageDownloadOptions> = {\n timeout: 10000,\n retries: 3,\n retryDelay: 1000,\n maxRetryDelay: 10000,\n exponentialBackoff: true,\n};\n\n/**\n * Downloads an image from a URL and converts it to base64 with robust retry logic\n *\n * @param url - The image URL to download\n * @param options - Download configuration options\n * @returns Promise resolving to ImageDownloadResult with base64 data and metadata\n * @throws Error if download fails after all retries\n */\nexport async function downloadImageAsBase64(\n url: string,\n options: ImageDownloadOptions = {},\n): Promise<ImageDownloadResult> {\n \"use step\";\n const opts = { ...DEFAULT_OPTIONS, ...options };\n let attemptCount = 0;\n\n return pRetry(\n async () => {\n attemptCount++;\n const controller = new AbortController();\n const timeoutId = setTimeout(() => controller.abort(), opts.timeout);\n\n try {\n const response = await fetch(url, {\n signal: controller.signal,\n headers: {\n \"User-Agent\": \"@mux/ai image downloader\",\n },\n });\n\n clearTimeout(timeoutId);\n\n if (!response.ok) {\n // Don't retry 4xx errors (except 429 rate limiting)\n if (response.status >= 400 && response.status < 500 && response.status !== 429) {\n throw new AbortError(`HTTP ${response.status}: ${response.statusText}`);\n }\n throw new Error(`HTTP ${response.status}: ${response.statusText}`);\n }\n\n const contentType = response.headers.get(\"content-type\");\n if (!contentType?.startsWith(\"image/\")) {\n throw new AbortError(`Invalid content type: ${contentType}. Expected image/*`);\n }\n\n const arrayBuffer = await response.arrayBuffer();\n const buffer = new Uint8Array(arrayBuffer);\n\n if (buffer.length === 0) {\n throw new AbortError(\"Downloaded image is empty\");\n }\n\n // Convert to base64 with data URI prefix\n const base64Data = `data:${contentType};base64,${bytesToBase64(buffer)}`;\n\n return {\n base64Data,\n buffer,\n url,\n contentType,\n sizeBytes: buffer.length,\n attempts: attemptCount,\n };\n } catch (error) {\n clearTimeout(timeoutId);\n\n // If it's an AbortError (non-retryable), re-throw it\n if (error instanceof AbortError) {\n throw error;\n }\n\n // For network errors, timeout errors, etc., wrap in retryable error\n if (error instanceof Error) {\n if (error.name === \"AbortError\") {\n throw new Error(`Request timeout after ${opts.timeout}ms`);\n }\n throw new Error(`Download failed: ${error.message}`);\n }\n\n throw new Error(\"Unknown download error\");\n }\n },\n {\n retries: opts.retries,\n minTimeout: opts.retryDelay,\n maxTimeout: opts.maxRetryDelay,\n factor: opts.exponentialBackoff ? 2 : 1,\n randomize: true, // Add jitter to prevent thundering herd\n onFailedAttempt: (error) => {\n console.warn(`Image download attempt ${error.attemptNumber} failed for ${url}`);\n if (error.retriesLeft > 0) {\n console.warn(`Retrying... (${error.retriesLeft} attempts left)`);\n }\n },\n },\n );\n}\n\n/**\n * Downloads multiple images concurrently with controlled concurrency\n *\n * @param urls - Array of image URLs to download\n * @param options - Download configuration options\n * @param maxConcurrent - Maximum concurrent downloads (default: 5)\n * @returns Promise resolving to array of ImageDownloadResult (in same order as input URLs)\n */\nexport async function downloadImagesAsBase64(\n urls: string[],\n options: ImageDownloadOptions = {},\n maxConcurrent: number = 5,\n): Promise<ImageDownloadResult[]> {\n \"use step\";\n const results: ImageDownloadResult[] = [];\n\n for (let i = 0; i < urls.length; i += maxConcurrent) {\n const batch = urls.slice(i, i + maxConcurrent);\n const batchPromises = batch.map(url => downloadImageAsBase64(url, options));\n const batchResults = await Promise.all(batchPromises);\n results.push(...batchResults);\n }\n\n return results;\n}\n\n/**\n * Uploads an image to Anthropic Files API for use in messages\n *\n * @param url - The image URL to download and upload\n * @param anthropicApiKey - Anthropic API key\n * @param options - Download configuration options\n * @returns Promise resolving to AnthropicFileUploadResult with file ID and metadata\n * @throws Error if download or upload fails\n */\nexport async function uploadImageToAnthropicFiles(\n url: string,\n anthropicApiKey: string,\n options: ImageDownloadOptions = {},\n): Promise<AnthropicFileUploadResult> {\n \"use step\";\n // First download the image\n const downloadResult = await downloadImageAsBase64(url, options);\n\n // Create form data for Files API upload\n const formData = new FormData();\n\n // Create a Blob from the buffer for form data\n const imageBlob = new Blob([downloadResult.buffer], {\n type: downloadResult.contentType,\n });\n\n // Get file extension from content type\n const extension = downloadResult.contentType.split(\"/\")[1] || \"png\";\n formData.append(\"file\", imageBlob, `image.${extension}`);\n\n // Upload to Anthropic Files API\n const response = await fetch(\"https://api.anthropic.com/v1/files\", {\n method: \"POST\",\n headers: {\n \"x-api-key\": anthropicApiKey,\n \"anthropic-version\": \"2023-06-01\",\n \"anthropic-beta\": \"files-api-2025-04-14\",\n // Don't set Content-Type header - let fetch set it with boundary for multipart\n },\n body: formData,\n });\n\n if (!response.ok) {\n const errorText = await response.text();\n throw new Error(`Anthropic Files API error: ${response.status} ${response.statusText} - ${errorText}`);\n }\n\n const fileResult = await response.json() as { id: string };\n\n return {\n fileId: fileResult.id,\n url: downloadResult.url,\n contentType: downloadResult.contentType,\n sizeBytes: downloadResult.sizeBytes,\n };\n}\n","import { getMuxClientFromEnv } from \"@mux/ai/lib/client-factory\";\nimport type { MuxAsset, PlaybackAsset, PlaybackPolicy, WorkflowCredentialsInput, WorkflowMuxClient } from \"@mux/ai/types\";\n\n/**\n * Finds a usable playback ID for the given asset.\n * Prefers public playback IDs, falls back to signed if no public is available.\n * Throws an error if no public or signed playback ID is found.\n */\n// Use the asset payload we already fetched to avoid extra Mux Video API calls.\nfunction getPlaybackId(asset: MuxAsset): { id: string; policy: PlaybackPolicy } {\n const playbackIds = asset.playback_ids || [];\n\n // First, try to find a public playback ID\n const publicPlaybackId = playbackIds.find(pid => pid.policy === \"public\");\n if (publicPlaybackId?.id) {\n return { id: publicPlaybackId.id, policy: \"public\" };\n }\n\n // Fall back to signed playback ID\n const signedPlaybackId = playbackIds.find(pid => pid.policy === \"signed\");\n if (signedPlaybackId?.id) {\n return { id: signedPlaybackId.id, policy: \"signed\" };\n }\n\n throw new Error(\n \"No public or signed playback ID found for this asset. \" +\n \"A public or signed playback ID is required. DRM playback IDs are not currently supported.\",\n );\n}\n\n/**\n * Determines if an asset is audio-only by checking if it has any video tracks.\n * Returns true if the asset has at least one audio track and no video tracks.\n */\nexport function isAudioOnlyAsset(asset: MuxAsset): boolean {\n const hasAudioTrack = asset.tracks?.some(track => track.type === \"audio\") ?? false;\n const hasVideoTrack = asset.tracks?.some(track => track.type === \"video\") ?? false;\n return hasAudioTrack && !hasVideoTrack;\n}\n\nfunction toPlaybackAsset(asset: MuxAsset): PlaybackAsset {\n const { id: playbackId, policy } = getPlaybackId(asset);\n return { asset, playbackId, policy };\n}\n\nexport async function getPlaybackIdForAsset(\n assetId: string,\n credentials?: WorkflowCredentialsInput,\n): Promise<PlaybackAsset> {\n \"use step\";\n // Centralize the Mux Video API fetch so callers can reuse the same asset payload\n // for playback IDs, duration, and other derived fields without double-hitting.\n // Note: getMuxAsset still resolves Mux token ID/secret from env or provided\n // credentials to preserve multi-tenant behavior.\n const asset = await getMuxAsset(assetId, credentials);\n return toPlaybackAsset(asset);\n}\n\nexport async function getPlaybackIdForAssetWithClient(\n assetId: string,\n muxClient: WorkflowMuxClient,\n): Promise<PlaybackAsset> {\n // Do not mark this as a workflow step: muxClient contains function properties\n // (e.g. createClient) that are not serializable across step boundaries.\n const asset = await getMuxAssetWithClient(assetId, muxClient);\n return toPlaybackAsset(asset);\n}\n\n/**\n * Fetches the Mux asset once so callers can derive playback IDs, duration, tracks,\n * and other metadata from a single Video API call.\n */\nexport async function getMuxAsset(\n assetId: string,\n credentials?: WorkflowCredentialsInput,\n): Promise<MuxAsset> {\n \"use step\";\n const muxClient = await getMuxClientFromEnv(credentials);\n const mux = await muxClient.createClient();\n return mux.video.assets.retrieve(assetId);\n}\n\nexport async function getMuxAssetWithClient(\n assetId: string,\n muxClient: WorkflowMuxClient,\n): Promise<MuxAsset> {\n // Do not mark this as a workflow step for the same serialization reason as above.\n const client = await muxClient.createClient();\n return client.video.assets.retrieve(assetId);\n}\n\nexport async function getAssetDurationSeconds(\n assetId: string,\n credentials?: WorkflowCredentialsInput,\n): Promise<number | undefined> {\n \"use step\";\n // Keep this helper, but route through getMuxAsset so a caller that already\n // fetched the asset can prefer getAssetDurationSecondsFromAsset instead.\n const asset = await getMuxAsset(assetId, credentials);\n return getAssetDurationSecondsFromAsset(asset);\n}\n\n// Use this when you already have the asset to avoid another Video API round trip.\nexport function getAssetDurationSecondsFromAsset(asset: MuxAsset): number | undefined {\n const duration = asset.duration;\n return typeof duration === \"number\" && Number.isFinite(duration) ? duration : undefined;\n}\n\nexport function getVideoTrackDurationSecondsFromAsset(asset: MuxAsset): number | undefined {\n const videoTrack = asset.tracks?.find(track => track.type === \"video\");\n const duration = (videoTrack as { duration?: unknown } | undefined)?.duration;\n return typeof duration === \"number\" && Number.isFinite(duration) ? duration : undefined;\n}\n\nexport function getVideoTrackMaxFrameRateFromAsset(asset: MuxAsset): number | undefined {\n const videoTrack = asset.tracks?.find(track => track.type === \"video\");\n const maxFrameRate = (videoTrack as { max_frame_rate?: unknown } | undefined)?.max_frame_rate;\n return typeof maxFrameRate === \"number\" && Number.isFinite(maxFrameRate) && maxFrameRate > 0 ?\n maxFrameRate :\n undefined;\n}\n","// ─────────────────────────────────────────────────────────────────────────────\n// Types\n// ─────────────────────────────────────────────────────────────────────────────\n\n/**\n * A single section of a prompt, rendered as an XML-like tag.\n */\nexport interface PromptSection {\n /** The XML tag name for this section */\n tag: string;\n /** The content inside the tag */\n content: string;\n /** Optional attributes to add to the tag (e.g., { format: \"plain text\" }) */\n attributes?: Record<string, string>;\n}\n\n/**\n * Configuration for building a prompt section.\n * Can be a full PromptSection object, just a string (content only), or undefined to use default.\n */\nexport type SectionOverride = string | PromptSection | undefined;\n\n/**\n * A template defining the default sections for a prompt.\n * Keys are section identifiers, values are the default PromptSection definitions.\n */\nexport type PromptTemplate<TSections extends string> = Record<TSections, PromptSection>;\n\n/**\n * User-provided overrides for prompt sections.\n * Each key can override the corresponding section's content or full definition.\n */\nexport type PromptOverrides<TSections extends string> = Partial<Record<TSections, SectionOverride>>;\n\n/**\n * Configuration for the prompt builder.\n */\nexport interface PromptBuilderConfig<TSections extends string> {\n /** Default sections that make up the prompt template */\n template: PromptTemplate<TSections>;\n /** Order in which sections should appear in the final prompt */\n sectionOrder: TSections[];\n}\n\n/**\n * A configured prompt builder instance with methods to build prompts.\n */\nexport interface PromptBuilder<TSections extends string> {\n /** The default template sections */\n template: PromptTemplate<TSections>;\n /** Build a prompt string, optionally overriding specific sections */\n build: (overrides?: PromptOverrides<TSections>) => string;\n /** Build a prompt with additional dynamic sections appended */\n buildWithContext: (\n overrides?: PromptOverrides<TSections>,\n additionalSections?: PromptSection[],\n ) => string;\n /** Get a single section's content (useful for partial rendering) */\n getSection: (section: TSections, override?: SectionOverride) => string;\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Helpers\n// ─────────────────────────────────────────────────────────────────────────────\n\n/**\n * Renders a PromptSection as an XML-like string.\n */\nexport function renderSection(section: PromptSection): string {\n const { tag, content, attributes } = section;\n\n const XML_NAME_PATTERN = /^[A-Z_][\\w.:-]*$/i;\n\n const assertValidXmlName = (name: string, context: \"tag\" | \"attribute\"): void => {\n if (!XML_NAME_PATTERN.test(name)) {\n throw new Error(`Invalid XML ${context} name: \"${name}\"`);\n }\n };\n\n const escapeXmlText = (value: string): string =>\n value\n .replace(/&/g, \"&amp;\")\n .replace(/</g, \"&lt;\")\n .replace(/>/g, \"&gt;/\");\n\n const escapeXmlAttribute = (value: string): string =>\n escapeXmlText(value).replace(/\"/g, \"&quot;\");\n\n if (!content.trim()) {\n return \"\";\n }\n\n assertValidXmlName(tag, \"tag\");\n\n const attrString = attributes ?\n ` ${\n Object.entries(attributes)\n .map(([key, value]) => {\n assertValidXmlName(key, \"attribute\");\n return `${key}=\"${escapeXmlAttribute(value)}\"`;\n })\n .join(\" \")}` :\n \"\";\n\n const safeContent = escapeXmlText(content.trim());\n\n return `<${tag}${attrString}>\\n${safeContent}\\n</${tag}>`;\n}\n\n/**\n * Resolves a section override to a full PromptSection.\n */\nfunction resolveSection(\n defaultSection: PromptSection,\n override?: SectionOverride,\n): PromptSection {\n if (override === undefined) {\n return defaultSection;\n }\n\n if (typeof override === \"string\") {\n return { ...defaultSection, content: override };\n }\n\n return override;\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Factory\n// ─────────────────────────────────────────────────────────────────────────────\n\n/**\n * Creates a type-safe prompt builder from a template configuration.\n *\n * @example\n * ```typescript\n * const builder = createPromptBuilder({\n * template: {\n * task: { tag: 'task', content: 'Analyze the image...' },\n * tone: { tag: 'tone', content: 'Be professional.' },\n * },\n * sectionOrder: ['task', 'tone'],\n * });\n *\n * // Use defaults\n * const prompt1 = builder.build();\n *\n * // Override specific sections\n * const prompt2 = builder.build({\n * tone: 'Be casual and friendly.',\n * });\n * ```\n */\nexport function createPromptBuilder<TSections extends string>(\n config: PromptBuilderConfig<TSections>,\n): PromptBuilder<TSections> {\n const { template, sectionOrder } = config;\n\n const getSection = (section: TSections, override?: SectionOverride): string => {\n const resolved = resolveSection(template[section], override);\n return renderSection(resolved);\n };\n\n const build = (overrides?: PromptOverrides<TSections>): string => {\n const sections = sectionOrder\n .map(sectionKey => getSection(sectionKey, overrides?.[sectionKey]))\n .filter(Boolean);\n\n return sections.join(\"\\n\\n\");\n };\n\n const buildWithContext = (\n overrides?: PromptOverrides<TSections>,\n additionalSections?: PromptSection[],\n ): string => {\n const basePrompt = build(overrides);\n\n if (!additionalSections?.length) {\n return basePrompt;\n }\n\n const additional = additionalSections\n .map(renderSection)\n .filter(Boolean)\n .join(\"\\n\\n\");\n\n return additional ? `${basePrompt}\\n\\n${additional}` : basePrompt;\n };\n\n return {\n template,\n build,\n buildWithContext,\n getSection,\n };\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Common Helpers\n// ─────────────────────────────────────────────────────────────────────────────\n\n/**\n * Creates a transcript section for inclusion in prompts.\n */\nexport function createTranscriptSection(\n transcriptText: string,\n format: \"plain text\" | \"WebVTT\" = \"plain text\",\n): PromptSection {\n return {\n tag: \"transcript\",\n content: transcriptText,\n attributes: { format },\n };\n}\n\n/**\n * Creates a tone section for inclusion in prompts.\n */\nexport function createToneSection(instruction: string): PromptSection {\n return {\n tag: \"tone\",\n content: instruction,\n };\n}\n","/**\n * Retry configuration options\n */\nexport interface RetryOptions {\n maxRetries?: number;\n baseDelay?: number;\n maxDelay?: number;\n shouldRetry?: (error: Error, attempt: number) => boolean;\n}\n\nconst DEFAULT_RETRY_OPTIONS: Required<Omit<RetryOptions, \"shouldRetry\">> = {\n maxRetries: 3,\n baseDelay: 2000,\n maxDelay: 10000,\n};\n\n/**\n * Default retry condition - retries on timeout errors\n */\nfunction defaultShouldRetry(error: Error, _attempt: number): boolean {\n return Boolean(error.message && error.message.includes(\"Timeout while downloading\"));\n}\n\n/**\n * Calculates exponential backoff delay with jitter\n */\nfunction calculateDelay(attempt: number, baseDelay: number, maxDelay: number): number {\n const exponentialDelay = baseDelay * 2 ** (attempt - 1);\n const delayWithJitter = exponentialDelay * (0.5 + Math.random() * 0.5);\n return Math.min(delayWithJitter, maxDelay);\n}\n\n/**\n * Executes an async function with retry logic\n */\nexport async function withRetry<T>(\n fn: () => Promise<T>,\n {\n maxRetries = DEFAULT_RETRY_OPTIONS.maxRetries,\n baseDelay = DEFAULT_RETRY_OPTIONS.baseDelay,\n maxDelay = DEFAULT_RETRY_OPTIONS.maxDelay,\n shouldRetry = defaultShouldRetry,\n }: RetryOptions = {},\n): Promise<T> {\n let lastError: Error | undefined;\n\n for (let attempt = 0; attempt <= maxRetries; attempt++) {\n try {\n return await fn();\n } catch (error) {\n lastError = error instanceof Error ? error : new Error(String(error));\n\n const isLastAttempt = attempt === maxRetries;\n if (isLastAttempt || !shouldRetry(lastError, attempt + 1)) {\n throw lastError;\n }\n\n const delay = calculateDelay(attempt + 1, baseDelay, maxDelay);\n console.warn(\n `Attempt ${attempt + 1} failed: ${lastError.message}. Retrying in ${Math.round(delay)}ms...`,\n );\n await new Promise(resolve => setTimeout(resolve, delay));\n }\n }\n\n throw lastError || new Error(\"Retry failed with unknown error\");\n}\n","import { generateText, Output } from \"ai\";\nimport dedent from \"dedent\";\nimport { z } from \"zod\";\n\nimport type { ImageDownloadOptions } from \"@mux/ai/lib/image-download\";\nimport { downloadImageAsBase64 } from \"@mux/ai/lib/image-download\";\nimport { getAssetDurationSecondsFromAsset, getPlaybackIdForAsset } from \"@mux/ai/lib/mux-assets\";\nimport type { PromptOverrides } from \"@mux/ai/lib/prompt-builder\";\nimport { createPromptBuilder } from \"@mux/ai/lib/prompt-builder\";\nimport { createLanguageModelFromConfig, resolveLanguageModelConfig } from \"@mux/ai/lib/providers\";\nimport type { ModelIdByProvider, SupportedProvider } from \"@mux/ai/lib/providers\";\nimport { getStoryboardUrl } from \"@mux/ai/primitives/storyboards\";\nimport type {\n ImageSubmissionMode,\n MuxAIOptions,\n TokenUsage,\n WorkflowCredentialsInput,\n} from \"@mux/ai/types\";\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Types\n// ─────────────────────────────────────────────────────────────────────────────\n\n/** Structured payload returned from `hasBurnedInCaptions`. */\nexport interface BurnedInCaptionsResult {\n assetId: string;\n hasBurnedInCaptions: boolean;\n confidence: number;\n detectedLanguage: string | null;\n storyboardUrl: string;\n /** Token usage from the AI provider (for efficiency/cost analysis). */\n usage?: TokenUsage;\n}\n\n/**\n * Sections of the burned-in captions user prompt that can be overridden.\n * Use these to customize the AI's behavior for your specific use case.\n */\nexport type BurnedInCaptionsPromptSections =\n \"task\" |\n \"analysisSteps\" |\n \"positiveIndicators\" |\n \"negativeIndicators\";\n\n/**\n * Override specific sections of the burned-in captions prompt.\n * Each key corresponds to a section that can be customized.\n *\n * @example\n * ```typescript\n * const result = await hasBurnedInCaptions(assetId, {\n * promptOverrides: {\n * task: 'Detect any text overlays in the video frames.',\n * positiveIndicators: 'Classify as captions if text appears consistently.',\n * },\n * });\n * ```\n */\nexport type BurnedInCaptionsPromptOverrides = PromptOverrides<BurnedInCaptionsPromptSections>;\n\n/** Configuration accepted by `hasBurnedInCaptions`. */\nexport interface BurnedInCaptionsOptions extends MuxAIOptions {\n /** AI provider used for storyboard inspection (defaults to 'openai'). */\n provider?: SupportedProvider;\n /** Provider-specific model identifier. */\n model?: ModelIdByProvider[SupportedProvider];\n /** Transport used for storyboard submission (defaults to 'url'). */\n imageSubmissionMode?: ImageSubmissionMode;\n /** Download tuning used when `imageSubmissionMode` === 'base64'. */\n imageDownloadOptions?: ImageDownloadOptions;\n /**\n * Override specific sections of the user prompt.\n * Useful for customizing the AI's detection criteria for specific use cases.\n */\n promptOverrides?: BurnedInCaptionsPromptOverrides;\n}\n\n/** Schema used to validate burned-in captions analysis responses. */\nexport const burnedInCaptionsSchema = z.object({\n hasBurnedInCaptions: z.boolean(),\n confidence: z.number(),\n detectedLanguage: z.string().nullable(),\n});\n\n/** Inferred shape returned from the burned-in captions schema. */\nexport type BurnedInCaptionsAnalysis = z.infer<typeof burnedInCaptionsSchema>;\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Prompts\n// ─────────────────────────────────────────────────────────────────────────────\n\nconst SYSTEM_PROMPT = dedent`\n <role>\n You are an expert at analyzing video frames to detect burned-in captions (also called open captions or hardcoded subtitles).\n These are text overlays that are permanently embedded in the video image, common on TikTok, Instagram Reels, and other social media platforms.\n </role>\n\n <critical_note>\n Burned-in captions must appear consistently across MOST frames in the storyboard.\n Text appearing in only 1-2 frames at the end is typically marketing copy, taglines, or end-cards - NOT burned-in captions.\n </critical_note>\n\n <confidence_scoring>\n Use this rubric to determine your confidence score (0.0-1.0):\n\n - Score 1.0: Definitive captions - text overlays visible in most frames, consistent positioning, content changes between frames indicating dialogue/narration, clear caption-style formatting\n - Score 0.7-0.9: Strong evidence - captions visible across multiple frames with consistent placement, but minor ambiguity (e.g., some frames unclear, atypical styling)\n - Score 0.4-0.6: Moderate evidence - text present in several frames but uncertain classification (e.g., could be captions or persistent on-screen graphics, ambiguous formatting)\n - Score 0.1-0.3: Weak evidence - minimal text detected, appears in only a few frames, likely marketing copy or end-cards rather than captions\n - Score 0.0: No captions - no text overlays detected, or text is clearly not captions (logos, watermarks, scene content, single end-card)\n </confidence_scoring>\n\n <context>\n You receive storyboard images containing multiple sequential frames extracted from a video.\n These frames are arranged in a grid and represent the visual progression of the content over time.\n Read frames left-to-right, top-to-bottom to understand the temporal sequence.\n </context>\n\n <capabilities>\n - Detect and analyze text overlays in video frames\n - Distinguish between captions and other text elements (marketing, logos, UI)\n - Identify language of detected caption text\n - Assess confidence in caption detection\n </capabilities>\n\n <constraints>\n - Only classify as burned-in captions when evidence is clear across multiple frames\n - Base decisions on observable visual evidence\n - Return structured data matching the requested schema\n </constraints>`;\n\n/**\n * Prompt builder for the burned-in captions user prompt.\n * Sections can be individually overridden via `promptOverrides` in BurnedInCaptionsOptions.\n */\nconst burnedInCaptionsPromptBuilder = createPromptBuilder<BurnedInCaptionsPromptSections>({\n template: {\n task: {\n tag: \"task\",\n content: dedent`\n Analyze the provided video storyboard to detect burned-in captions (hardcoded subtitles).\n Count frames with text vs no text, note position consistency and whether text changes across frames.\n Decide if captions exist, with confidence (0.0-1.0) and detected language if any.`,\n },\n analysisSteps: {\n tag: \"analysis_steps\",\n content: dedent`\n 1. COUNT how many frames contain text overlays vs. how many don't\n 2. Check if text appears in consistent positions across multiple frames\n 3. Verify text changes content between frames (indicating dialogue/narration)\n 4. Ensure text has caption-style formatting (contrasting colors, readable fonts)\n 5. If captions are detected, identify the language of the text`,\n },\n positiveIndicators: {\n tag: \"classify_as_captions\",\n content: dedent`\n ONLY classify as burned-in captions if:\n - Text appears in multiple frames (not just 1-2 end frames)\n - Text positioning is consistent across those frames\n - Content suggests dialogue, narration, or subtitles (not marketing)\n - Formatting looks like captions (not graphics/logos)`,\n },\n negativeIndicators: {\n tag: \"not_captions\",\n content: dedent`\n DO NOT classify as burned-in captions:\n - Marketing taglines appearing only in final 1-2 frames\n - Single words or phrases that don't change between frames\n - Graphics, logos, watermarks, or UI elements\n - Text that's part of the original scene content\n - End-cards with calls-to-action or brand messaging`,\n },\n },\n sectionOrder: [\"task\", \"analysisSteps\", \"positiveIndicators\", \"negativeIndicators\"],\n});\n\nfunction buildUserPrompt(promptOverrides?: BurnedInCaptionsPromptOverrides): string {\n return burnedInCaptionsPromptBuilder.build(promptOverrides);\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Implementation\n// ─────────────────────────────────────────────────────────────────────────────\nconst DEFAULT_PROVIDER = \"openai\";\n\ninterface AnalysisResponse {\n result: BurnedInCaptionsAnalysis;\n usage: TokenUsage;\n}\n\nasync function fetchImageAsBase64(\n imageUrl: string,\n imageDownloadOptions?: ImageDownloadOptions,\n): Promise<string> {\n \"use step\";\n\n const downloadResult = await downloadImageAsBase64(imageUrl, imageDownloadOptions);\n return downloadResult.base64Data;\n}\n\nasync function analyzeStoryboard({\n imageDataUrl,\n provider,\n modelId,\n userPrompt,\n systemPrompt,\n credentials,\n}: {\n imageDataUrl: string;\n provider: SupportedProvider;\n modelId: string;\n userPrompt: string;\n systemPrompt: string;\n credentials?: WorkflowCredentialsInput;\n}): Promise<AnalysisResponse> {\n \"use step\";\n\n const model = await createLanguageModelFromConfig(provider, modelId, credentials);\n\n const response = await generateText({\n model,\n output: Output.object({ schema: burnedInCaptionsSchema }),\n experimental_telemetry: { isEnabled: true },\n messages: [\n {\n role: \"system\",\n content: systemPrompt,\n },\n {\n role: \"user\",\n content: [\n { type: \"text\", text: userPrompt },\n { type: \"image\", image: imageDataUrl },\n ],\n },\n ],\n });\n\n return {\n result: {\n ...response.output,\n confidence: Math.min(1, Math.max(0, response.output.confidence)),\n },\n usage: {\n inputTokens: response.usage.inputTokens,\n outputTokens: response.usage.outputTokens,\n totalTokens: response.usage.totalTokens,\n reasoningTokens: response.usage.reasoningTokens,\n cachedInputTokens: response.usage.cachedInputTokens,\n },\n };\n}\n\nexport async function hasBurnedInCaptions(\n assetId: string,\n options: BurnedInCaptionsOptions = {},\n): Promise<BurnedInCaptionsResult> {\n \"use workflow\";\n const {\n provider = DEFAULT_PROVIDER,\n model,\n imageSubmissionMode = \"url\",\n imageDownloadOptions,\n promptOverrides,\n credentials,\n ...config\n } = options;\n\n // Build the user prompt with any overrides\n const userPrompt = buildUserPrompt(promptOverrides);\n\n const modelConfig = resolveLanguageModelConfig({\n ...config,\n model,\n provider: provider as SupportedProvider,\n });\n const { asset: assetData, playbackId, policy } = await getPlaybackIdForAsset(assetId, credentials);\n const assetDurationSeconds = getAssetDurationSecondsFromAsset(assetData);\n\n const imageUrl = await getStoryboardUrl(playbackId, 640, policy === \"signed\", credentials);\n\n let analysisResponse: AnalysisResponse;\n\n if (imageSubmissionMode === \"base64\") {\n const base64Data = await fetchImageAsBase64(imageUrl, imageDownloadOptions);\n analysisResponse = await analyzeStoryboard({\n imageDataUrl: base64Data,\n provider: modelConfig.provider,\n modelId: modelConfig.modelId,\n userPrompt,\n systemPrompt: SYSTEM_PROMPT,\n credentials,\n });\n } else {\n analysisResponse = await analyzeStoryboard({\n imageDataUrl: imageUrl,\n provider: modelConfig.provider,\n modelId: modelConfig.modelId,\n userPrompt,\n systemPrompt: SYSTEM_PROMPT,\n credentials,\n });\n }\n\n if (!analysisResponse.result) {\n throw new Error(\"No analysis result received from AI provider\");\n }\n\n return {\n assetId,\n hasBurnedInCaptions: analysisResponse.result.hasBurnedInCaptions ?? false,\n confidence: analysisResponse.result.confidence ?? 0,\n detectedLanguage: analysisResponse.result.detectedLanguage ?? null,\n storyboardUrl: imageUrl,\n usage: {\n ...analysisResponse.usage,\n metadata: {\n assetDurationSeconds,\n },\n },\n };\n}\n","import { generateText, Output } from \"ai\";\nimport dedent from \"dedent\";\nimport { z } from \"zod\";\n\nimport {\n getAssetDurationSecondsFromAsset,\n getPlaybackIdForAsset,\n isAudioOnlyAsset,\n} from \"@mux/ai/lib/mux-assets\";\nimport type { PromptOverrides, PromptSection } from \"@mux/ai/lib/prompt-builder\";\nimport { createPromptBuilder } from \"@mux/ai/lib/prompt-builder\";\nimport { createLanguageModelFromConfig, resolveLanguageModelConfig } from \"@mux/ai/lib/providers\";\nimport type { ModelIdByProvider, SupportedProvider } from \"@mux/ai/lib/providers\";\nimport { withRetry } from \"@mux/ai/lib/retry\";\nimport { resolveMuxSigningContext } from \"@mux/ai/lib/workflow-credentials\";\nimport {\n extractTimestampedTranscript,\n fetchTranscriptForAsset,\n getReadyTextTracks,\n} from \"@mux/ai/primitives/transcripts\";\nimport type { MuxAIOptions, TokenUsage, WorkflowCredentialsInput } from \"@mux/ai/types\";\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Types\n// ─────────────────────────────────────────────────────────────────────────────\n\nexport const chapterSchema = z.object({\n startTime: z.number(),\n title: z.string(),\n});\n\nexport type Chapter = z.infer<typeof chapterSchema>;\n\nexport const chaptersSchema = z.object({\n chapters: z.array(chapterSchema),\n});\n\nexport type ChaptersType = z.infer<typeof chaptersSchema>;\n\n/** Structured return payload from `generateChapters`. */\nexport interface ChaptersResult {\n assetId: string;\n languageCode: string;\n chapters: Chapter[];\n /** Token usage from the AI provider (for efficiency/cost analysis). */\n usage?: TokenUsage;\n}\n\n/**\n * Sections of the chaptering user prompt that can be overridden.\n * Use these to customize the AI's behavior for your specific use case.\n */\nexport type ChaptersPromptSections =\n \"task\" |\n \"outputFormat\" |\n \"chapterGuidelines\" |\n \"titleGuidelines\";\n\n/**\n * Override specific sections of the chaptering prompt.\n * Each key corresponds to a section that can be customized.\n *\n * @example\n * ```typescript\n * const result = await generateChapters(assetId, \"en\", {\n * promptOverrides: {\n * titleGuidelines: \"Use short, punchy titles under 6 words.\",\n * },\n * });\n * ```\n */\nexport type ChaptersPromptOverrides = PromptOverrides<ChaptersPromptSections>;\n\n/** Configuration accepted by `generateChapters`. */\nexport interface ChaptersOptions extends MuxAIOptions {\n /** AI provider used to interpret the transcript (defaults to 'openai'). */\n provider?: SupportedProvider;\n /** Provider-specific model identifier. */\n model?: ModelIdByProvider[SupportedProvider];\n /**\n * Override specific sections of the user prompt.\n * Useful for customizing chaptering criteria for specific use cases.\n */\n promptOverrides?: ChaptersPromptOverrides;\n /**\n * Minimum number of chapters to generate per hour of content.\n * Defaults to 3.\n */\n minChaptersPerHour?: number;\n /**\n * Maximum number of chapters to generate per hour of content.\n * Defaults to 8.\n */\n maxChaptersPerHour?: number;\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Implementation\n// ─────────────────────────────────────────────────────────────────────────────\n\nasync function generateChaptersWithAI({\n provider,\n modelId,\n userPrompt,\n systemPrompt,\n credentials,\n}: {\n provider: SupportedProvider;\n modelId: string;\n userPrompt: string;\n systemPrompt: string;\n credentials?: WorkflowCredentialsInput;\n}): Promise<{ chapters: ChaptersType; usage: TokenUsage }> {\n \"use step\";\n\n const model = await createLanguageModelFromConfig(provider, modelId, credentials);\n\n const response = await withRetry(() =>\n generateText({\n model,\n output: Output.object({ schema: chaptersSchema }),\n messages: [\n {\n role: \"system\",\n content: systemPrompt,\n },\n {\n role: \"user\",\n content: userPrompt,\n },\n ],\n }),\n );\n\n return {\n chapters: response.output,\n usage: {\n inputTokens: response.usage.inputTokens,\n outputTokens: response.usage.outputTokens,\n totalTokens: response.usage.totalTokens,\n reasoningTokens: response.usage.reasoningTokens,\n cachedInputTokens: response.usage.cachedInputTokens,\n },\n };\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Prompts\n// ─────────────────────────────────────────────────────────────────────────────\n\n/**\n * Sections of the chaptering system prompt that can be overridden.\n * Use these to customize the AI's persona and constraints.\n */\nexport type ChapterSystemPromptSections = \"role\" | \"context\" | \"constraints\" | \"qualityGuidelines\";\n\n/**\n * Prompt builder for the chaptering system prompt.\n * Sections can be individually overridden for different content types.\n */\nconst chapterSystemPromptBuilder = createPromptBuilder<ChapterSystemPromptSections>({\n template: {\n role: {\n tag: \"role\",\n content: \"You are a video editor and transcript analyst specializing in segmenting content into logical chapters.\",\n },\n context: {\n tag: \"context\",\n content: dedent`\n You receive a timestamped transcript with lines in the form \"[12s] Caption text\".\n Use those timestamps as anchors to determine chapter start times in seconds.`,\n },\n constraints: {\n tag: \"constraints\",\n content: dedent`\n - Only use information present in the transcript\n - Return structured data that matches the requested JSON schema\n - Do not add commentary or extra text outside the JSON`,\n },\n qualityGuidelines: {\n tag: \"quality_guidelines\",\n content: dedent`\n - Create chapters at topic shifts or clear transitions\n - Keep chapter titles concise and descriptive\n - Ensure the first chapter starts at 0 seconds`,\n },\n },\n sectionOrder: [\"role\", \"context\", \"constraints\", \"qualityGuidelines\"],\n});\n\n/**\n * System prompt overrides for audio-only assets.\n * Adjusts the role and context to be audio-focused rather than video-focused.\n */\nconst AUDIO_ONLY_SYSTEM_PROMPT_OVERRIDES: Partial<Record<ChapterSystemPromptSections, string>> = {\n role: \"You are an audio editor and transcript analyst specializing in segmenting content into logical chapters.\",\n context: dedent`\n You receive a timestamped transcript from audio-only content with lines in the form \"[12s] Transcript text\".\n Use those timestamps as anchors to determine chapter start times in seconds.`,\n};\n\n/**\n * Prompt builder for the chaptering user prompt.\n * Sections can be individually overridden via `promptOverrides` in ChaptersOptions.\n */\nconst chaptersPromptBuilder = createPromptBuilder<ChaptersPromptSections>({\n template: {\n task: {\n tag: \"task\",\n content: \"Segment the transcript into logical chapters and provide a short title for each chapter.\",\n },\n outputFormat: {\n tag: \"output_format\",\n content: dedent`\n Return valid JSON in this exact shape:\n {\n \"chapters\": [\n {\"startTime\": 0, \"title\": \"Introduction\"},\n {\"startTime\": 45.5, \"title\": \"Main Topic Discussion\"},\n {\"startTime\": 120.0, \"title\": \"Conclusion\"}\n ]\n }`,\n },\n // Note: chapterGuidelines is dynamically generated in buildUserPrompt()\n // based on minChaptersPerHour/maxChaptersPerHour options\n chapterGuidelines: {\n tag: \"chapter_guidelines\",\n content: \"\", // Placeholder - always overridden with dynamic content\n },\n titleGuidelines: {\n tag: \"title_guidelines\",\n content: dedent`\n - Keep titles concise and descriptive\n - Avoid filler or generic labels like \"Chapter 1\"\n - Use the transcript's language and terminology`,\n },\n },\n sectionOrder: [\"task\", \"outputFormat\", \"chapterGuidelines\", \"titleGuidelines\"],\n});\n\nfunction buildUserPrompt({\n timestampedTranscript,\n promptOverrides,\n minChaptersPerHour = 3,\n maxChaptersPerHour = 8,\n}: {\n timestampedTranscript: string;\n promptOverrides?: ChaptersPromptOverrides;\n minChaptersPerHour?: number;\n maxChaptersPerHour?: number;\n}): string {\n const contextSections: PromptSection[] = [\n {\n tag: \"timestamped_transcript\",\n content: timestampedTranscript,\n attributes: { format: \"seconds\" },\n },\n ];\n\n // Build dynamic chapter guidelines with configurable min/max per hour\n const dynamicChapterGuidelines = dedent`\n - Create at least ${minChaptersPerHour} and at most ${maxChaptersPerHour} chapters per hour of content\n - Use start times in seconds (not HH:MM:SS)\n - Chapter start times should be non-decreasing\n - Do not include text before or after the JSON`;\n\n // Merge with any user-provided overrides (user overrides take precedence)\n const mergedOverrides: ChaptersPromptOverrides = {\n chapterGuidelines: dynamicChapterGuidelines,\n ...promptOverrides,\n };\n\n return chaptersPromptBuilder.buildWithContext(mergedOverrides, contextSections);\n}\n\nexport async function generateChapters(\n assetId: string,\n languageCode: string,\n options: ChaptersOptions = {},\n): Promise<ChaptersResult> {\n \"use workflow\";\n const {\n provider = \"openai\",\n model,\n promptOverrides,\n minChaptersPerHour,\n maxChaptersPerHour,\n credentials,\n } = options;\n\n const modelConfig = resolveLanguageModelConfig({\n ...options,\n model,\n provider: provider as SupportedProvider,\n });\n // Fetch asset and transcript\n const { asset: assetData, playbackId, policy } = await getPlaybackIdForAsset(assetId, credentials);\n const assetDurationSeconds = getAssetDurationSecondsFromAsset(assetData);\n const isAudioOnly = isAudioOnlyAsset(assetData);\n\n // Resolve signing context for signed playback IDs\n const signingContext = await resolveMuxSigningContext(credentials);\n if (policy === \"signed\" && !signingContext) {\n throw new Error(\n \"Signed playback ID requires signing credentials. \" +\n \"Set MUX_SIGNING_KEY and MUX_PRIVATE_KEY environment variables.\",\n );\n }\n\n const readyTextTracks = getReadyTextTracks(assetData);\n let transcriptResult = await fetchTranscriptForAsset(assetData, playbackId, {\n languageCode,\n cleanTranscript: false, // keep timestamps for chapter segmentation\n shouldSign: policy === \"signed\",\n credentials,\n });\n\n if (isAudioOnly && !transcriptResult.track && readyTextTracks.length === 1) {\n transcriptResult = await fetchTranscriptForAsset(assetData, playbackId, {\n cleanTranscript: false, // keep timestamps for chapter segmentation\n shouldSign: policy === \"signed\",\n credentials,\n required: true,\n });\n }\n\n if (!transcriptResult.track || !transcriptResult.transcriptText) {\n const availableLanguages = readyTextTracks\n .map(t => t.language_code)\n .filter(Boolean)\n .join(\", \");\n throw new Error(\n `No caption track found for language '${languageCode}'. Available languages: ${availableLanguages || \"none\"}`,\n );\n }\n\n const timestampedTranscript = extractTimestampedTranscript(transcriptResult.transcriptText);\n if (!timestampedTranscript) {\n const contentLabel = isAudioOnly ? \"transcript\" : \"caption track\";\n throw new Error(`No usable content found in ${contentLabel}`);\n }\n\n const userPrompt = buildUserPrompt({\n timestampedTranscript,\n promptOverrides,\n minChaptersPerHour,\n maxChaptersPerHour,\n });\n\n // Generate chapters using AI SDK\n let chaptersData: { chapters: ChaptersType; usage: TokenUsage } | null = null;\n\n try {\n const systemPrompt = isAudioOnly ?\n chapterSystemPromptBuilder.build(AUDIO_ONLY_SYSTEM_PROMPT_OVERRIDES) :\n chapterSystemPromptBuilder.build();\n chaptersData = await generateChaptersWithAI({\n provider: modelConfig.provider,\n modelId: modelConfig.modelId,\n userPrompt,\n systemPrompt,\n credentials,\n });\n } catch (error) {\n throw new Error(\n `Failed to generate chapters with ${provider}: ${error instanceof Error ? error.message : \"Unknown error\"}`,\n );\n }\n\n if (!chaptersData || !chaptersData.chapters) {\n throw new Error(\"No chapters generated from AI response\");\n }\n\n // Validate and sort chapters\n const { chapters: chaptersPayload, usage } = chaptersData;\n const validChapters = chaptersPayload.chapters\n .filter(chapter => typeof chapter.startTime === \"number\" && typeof chapter.title === \"string\")\n .sort((a, b) => a.startTime - b.startTime);\n\n if (validChapters.length === 0) {\n throw new Error(\"No valid chapters found in AI response\");\n }\n\n // Ensure first chapter starts at 0\n if (validChapters[0].startTime !== 0) {\n validChapters[0].startTime = 0;\n }\n\n const usageWithMetadata: TokenUsage = {\n ...usage,\n metadata: {\n ...usage?.metadata,\n assetDurationSeconds,\n },\n };\n\n return {\n assetId,\n languageCode,\n chapters: validChapters,\n usage: usageWithMetadata,\n };\n}\n","import { embed } from \"ai\";\n\nimport {\n getAssetDurationSecondsFromAsset,\n getPlaybackIdForAsset,\n isAudioOnlyAsset,\n} from \"@mux/ai/lib/mux-assets\";\nimport type { EmbeddingModelIdByProvider, SupportedEmbeddingProvider } from \"@mux/ai/lib/providers\";\nimport { createEmbeddingModelFromConfig, resolveEmbeddingModelConfig } from \"@mux/ai/lib/providers\";\nimport { withRetry } from \"@mux/ai/lib/retry\";\nimport { resolveMuxSigningContext } from \"@mux/ai/lib/workflow-credentials\";\nimport { chunkText, chunkVTTCues } from \"@mux/ai/primitives/text-chunking\";\nimport { fetchTranscriptForAsset, getReadyTextTracks, parseVTTCues } from \"@mux/ai/primitives/transcripts\";\nimport type {\n ChunkEmbedding,\n ChunkingStrategy,\n MuxAIOptions,\n TextChunk,\n VideoEmbeddingsResult,\n WorkflowCredentialsInput,\n} from \"@mux/ai/types\";\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Types\n// ─────────────────────────────────────────────────────────────────────────────\n\n/** Configuration accepted by `generateEmbeddings`. */\nexport interface EmbeddingsOptions extends MuxAIOptions {\n /** AI provider used to generate embeddings (defaults to 'openai'). */\n provider?: SupportedEmbeddingProvider;\n /** Provider-specific model identifier (defaults to text-embedding-3-small for OpenAI). */\n model?: EmbeddingModelIdByProvider[SupportedEmbeddingProvider];\n /** Language code for transcript selection (defaults to first available). */\n languageCode?: string;\n /** Chunking strategy configuration (defaults to token-based with 500 tokens, 100 overlap). */\n chunkingStrategy?: ChunkingStrategy;\n /** Maximum number of chunks to process concurrently (defaults to 5). */\n batchSize?: number;\n}\n\n/** Alias for embedding results (supports video or audio transcripts). */\nexport type EmbeddingsResult = VideoEmbeddingsResult;\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Implementation\n// ─────────────────────────────────────────────────────────────────────────────\n\n/**\n * Averages multiple embedding vectors into a single vector.\n *\n * @param embeddings - Array of embedding vectors to average\n * @returns Single averaged embedding vector\n */\nfunction averageEmbeddings(embeddings: number[][]): number[] {\n if (embeddings.length === 0) {\n return [];\n }\n\n const dimensions = embeddings[0].length;\n const averaged = Array.from({ length: dimensions }, () => 0);\n\n for (const embedding of embeddings) {\n for (let i = 0; i < dimensions; i++) {\n averaged[i] += embedding[i];\n }\n }\n\n for (let i = 0; i < dimensions; i++) {\n averaged[i] /= embeddings.length;\n }\n\n return averaged;\n}\n\n/**\n * Generates embedding for a single text chunk using the specified AI provider.\n *\n * @param options - Configuration object\n * @param options.chunk - Text chunk to embed\n * @param options.provider - AI provider for embedding generation\n * @param options.modelId - Provider-specific model identifier\n * @param options.credentials - Optional workflow credentials for API access\n * @returns Chunk embedding with metadata\n */\nasync function generateSingleChunkEmbedding({\n chunk,\n provider,\n modelId,\n credentials,\n}: {\n chunk: TextChunk;\n provider: SupportedEmbeddingProvider;\n modelId: string;\n credentials?: WorkflowCredentialsInput;\n}): Promise<ChunkEmbedding> {\n \"use step\";\n\n const model = await createEmbeddingModelFromConfig(provider, modelId, credentials);\n const response = await withRetry(() =>\n embed({\n model,\n value: chunk.text,\n }),\n );\n\n return {\n chunkId: chunk.id,\n embedding: response.embedding,\n metadata: {\n startTime: chunk.startTime,\n endTime: chunk.endTime,\n tokenCount: chunk.tokenCount,\n },\n };\n}\n\n/**\n * Generates vector embeddings for a media asset's transcript.\n *\n * This function:\n * 1. Fetches the transcript from Mux\n * 2. Chunks the transcript according to the specified strategy\n * 3. Generates embeddings for each chunk using the specified AI provider\n * 4. Returns both individual chunk embeddings and an averaged embedding\n *\n * @param assetId - Mux asset ID\n * @param options - Configuration options\n * @returns Embeddings result with chunks and averaged embedding\n *\n * @example\n * ```typescript\n * const embeddings = await generateEmbeddings(\"asset-id\", {\n * provider: \"openai\",\n * chunkingStrategy: { type: \"token\", maxTokens: 500, overlap: 100 },\n * });\n *\n * // Store in vector database\n * for (const chunk of embeddings.chunks) {\n * await db.insert({\n * assetId: embeddings.assetId,\n * chunkId: chunk.chunkId,\n * embedding: chunk.embedding,\n * metadata: chunk.metadata,\n * });\n * }\n * ```\n */\nasync function generateEmbeddingsInternal(\n assetId: string,\n options: EmbeddingsOptions = {},\n): Promise<EmbeddingsResult> {\n const {\n provider = \"openai\",\n model,\n languageCode,\n chunkingStrategy = { type: \"token\", maxTokens: 500, overlap: 100 } as ChunkingStrategy,\n batchSize = 5,\n credentials,\n } = options;\n\n const embeddingModel = resolveEmbeddingModelConfig({ ...options, provider, model });\n // Fetch asset and playback ID\n const { asset: assetData, playbackId, policy } = await getPlaybackIdForAsset(assetId, credentials);\n const assetDurationSeconds = getAssetDurationSecondsFromAsset(assetData);\n const isAudioOnly = isAudioOnlyAsset(assetData);\n\n // Resolve signing context for signed playback IDs\n const signingContext = await resolveMuxSigningContext(credentials);\n if (policy === \"signed\" && !signingContext) {\n throw new Error(\n \"Signed playback ID requires signing credentials. \" +\n \"Set MUX_SIGNING_KEY and MUX_PRIVATE_KEY environment variables.\",\n );\n }\n\n // Fetch transcript (raw VTT for VTT strategy, cleaned text otherwise)\n const readyTextTracks = getReadyTextTracks(assetData);\n const useVttChunking = chunkingStrategy.type === \"vtt\";\n let transcriptResult = await fetchTranscriptForAsset(assetData, playbackId, {\n languageCode,\n cleanTranscript: !useVttChunking,\n shouldSign: policy === \"signed\",\n credentials,\n });\n\n if (isAudioOnly && !transcriptResult.track && readyTextTracks.length === 1) {\n transcriptResult = await fetchTranscriptForAsset(assetData, playbackId, {\n cleanTranscript: !useVttChunking,\n shouldSign: policy === \"signed\",\n credentials,\n });\n }\n\n if (!transcriptResult.track || !transcriptResult.transcriptText) {\n const availableLanguages = readyTextTracks\n .map(t => t.language_code)\n .filter(Boolean)\n .join(\", \");\n if (isAudioOnly) {\n throw new Error(\n `No transcript track found${languageCode ? ` for language '${languageCode}'` : \"\"}. ` +\n `Audio-only assets require a transcript. Available languages: ${availableLanguages || \"none\"}`,\n );\n }\n throw new Error(\n `No caption track found${languageCode ? ` for language '${languageCode}'` : \"\"}. Available languages: ${availableLanguages || \"none\"}`,\n );\n }\n\n const transcriptText = transcriptResult.transcriptText;\n if (!transcriptText.trim()) {\n throw new Error(\"Transcript is empty\");\n }\n\n // Chunk the transcript\n const chunks = useVttChunking ?\n chunkVTTCues(\n parseVTTCues(transcriptText),\n chunkingStrategy.maxTokens,\n chunkingStrategy.overlapCues,\n ) :\n chunkText(transcriptText, chunkingStrategy);\n if (chunks.length === 0) {\n throw new Error(\"No chunks generated from transcript\");\n }\n\n // Generate embeddings for all chunks (process in batches)\n const chunkEmbeddings: ChunkEmbedding[] = [];\n try {\n for (let i = 0; i < chunks.length; i += batchSize) {\n const batch = chunks.slice(i, i + batchSize);\n\n const batchResults = await Promise.all(\n batch.map(chunk =>\n generateSingleChunkEmbedding({\n chunk,\n provider: embeddingModel.provider,\n modelId: embeddingModel.modelId as string,\n credentials,\n }),\n ),\n );\n\n chunkEmbeddings.push(...batchResults);\n }\n } catch (error) {\n throw new Error(\n `Failed to generate embeddings with ${provider}: ${error instanceof Error ? error.message : \"Unknown error\"}`,\n );\n }\n\n if (chunkEmbeddings.length === 0) {\n throw new Error(\"No embeddings generated\");\n }\n\n // Calculate averaged embedding\n const averagedEmbedding = averageEmbeddings(chunkEmbeddings.map(ce => ce.embedding));\n\n // Calculate total tokens\n const totalTokens = chunks.reduce((sum, chunk) => sum + chunk.tokenCount, 0);\n\n return {\n assetId,\n chunks: chunkEmbeddings,\n averagedEmbedding,\n provider,\n model: embeddingModel.modelId,\n metadata: {\n totalChunks: chunks.length,\n totalTokens,\n chunkingStrategy: JSON.stringify(chunkingStrategy),\n embeddingDimensions: chunkEmbeddings[0].embedding.length,\n generatedAt: new Date().toISOString(),\n },\n usage: {\n metadata: {\n assetDurationSeconds,\n },\n },\n };\n}\n\nexport async function generateEmbeddings(\n assetId: string,\n options: EmbeddingsOptions = {},\n): Promise<EmbeddingsResult> {\n \"use workflow\";\n return generateEmbeddingsInternal(assetId, options);\n}\n\n/**\n * @deprecated Use {@link generateEmbeddings} instead. This name will be removed in a future release.\n */\nexport async function generateVideoEmbeddings(\n assetId: string,\n options: EmbeddingsOptions = {},\n): Promise<EmbeddingsResult> {\n \"use workflow\";\n console.warn(\"generateVideoEmbeddings is deprecated. Use generateEmbeddings instead.\");\n return generateEmbeddingsInternal(assetId, options);\n}\n","export interface SamplingPlanOptionsV1 {\n duration_sec: number;\n min_candidates?: number;\n max_candidates?: number;\n trim_start_sec?: number;\n trim_end_sec?: number;\n fps?: number;\n base_cadence_hz?: number;\n anchor_percents?: number[];\n anchor_window_sec?: number;\n}\n\nconst DEFAULT_FPS = 30;\n\nexport function roundToNearestFrameMs(tsMs: number, fps: number = DEFAULT_FPS): number {\n const frameMs = 1000 / fps;\n return Math.round(Math.round(tsMs / frameMs) * frameMs * 100) / 100;\n}\n\n/**\n * Compute a bounded, duration‑aware set of candidate timestamps (ms) for thumbnail extraction.\n *\n * High-level approach:\n * - Trim the ends of the video to avoid low‑information regions like fades or buffers.\n * - Pick a sampling cadence (Hz) based on duration, unless the caller specifies one.\n * - Evenly distribute a base set of timestamps across the usable span.\n * - If we still have headroom up to `maxCandidates`, sprinkle extra timestamps inside small\n * windows around a few semantic anchors (percent positions like 20%, 50%, 80%) to\n * increase diversity and catch likely interesting frames.\n * - Snap all timestamps to the nearest frame boundary and enforce bounds + uniqueness.\n *\n * Inputs (from `SamplingPlanOptions`):\n * - durationSec: total media duration in seconds\n * - minCandidates/maxCandidates: hard lower/upper bounds for candidate count\n * - trimStartSec/trimEndSec: seconds trimmed from the start/end when sampling\n * - fps: frames per second used to quantize timestamps to frame boundaries\n * - baseCadenceHz: optional explicit sampling cadence; overrides duration‑based default\n * - anchorPercents: list of normalized positions [0..1] to seed diversity windows\n * - anchorWindowSec: window size (seconds) centered around each anchor percent\n */\nexport function planSamplingTimestamps(options: SamplingPlanOptionsV1): number[] {\n const DEFAULT_MIN_CANDIDATES = 10;\n const DEFAULT_MAX_CANDIDATES = 30;\n const {\n duration_sec,\n min_candidates = DEFAULT_MIN_CANDIDATES,\n max_candidates = DEFAULT_MAX_CANDIDATES,\n trim_start_sec = 1.0,\n trim_end_sec = 1.0,\n fps = DEFAULT_FPS,\n base_cadence_hz,\n anchor_percents = [0.2, 0.5, 0.8],\n anchor_window_sec = 1.5,\n } = options;\n\n // The span of the video we consider eligible for sampling after trimming.\n const usableSec = Math.max(0, duration_sec - (trim_start_sec + trim_end_sec));\n if (usableSec <= 0)\n return [];\n\n // Determine sampling cadence (samples per second). Shorter videos get denser sampling\n // to ensure enough candidates; longer videos are sparser to respect API caps and cost.\n const cadenceHz =\n base_cadence_hz ??\n (duration_sec < 15 ? 3 : duration_sec < 60 ? 2 : duration_sec < 180 ? 1.5 : 1);\n\n // Target number of candidates = cadence * usable duration, clamped within bounds.\n let target = Math.round(usableSec * cadenceHz);\n target = Math.max(min_candidates, Math.min(max_candidates, target));\n\n // Evenly spread the base timestamps across the usable span by dividing it into\n // `target` segments and sampling near each segment's center to avoid boundary bias.\n const stepSec = usableSec / target;\n const t0 = trim_start_sec;\n const base: number[] = [];\n for (let i = 0; i < target; i++) {\n const tsSec = t0 + (i + 0.5) * stepSec; // center of each segment\n base.push(tsSec * 1000);\n }\n\n // If we still have budget up to `maxCandidates`, allocate extra samples around a few\n // anchor percent positions to capture likely compelling frames (e.g., intro, midpoint).\n const slack = Math.max(0, max_candidates - base.length);\n const extra: number[] = [];\n if (slack > 0 && anchor_percents.length > 0) {\n // Distribute remaining budget across anchors; at least one per anchor if any slack.\n // Cap per-anchor samples to avoid blowing up when max_candidates is very large\n // relative to the base count — a 1.5 s window only holds ~45 frames at 30 fps.\n const perAnchor = Math.max(1, Math.min(5, Math.floor(slack / anchor_percents.length)));\n for (const p of anchor_percents) {\n // Compute the window center in seconds, clamped within the usable span.\n // Note: 1e-3 = 0.001 seconds (1 ms). We use a tiny epsilon to avoid exact\n // boundary positions which can cause zero-width windows or duplicate frames\n // after frame-quantization due to floating-point rounding.\n const centerSec = Math.min(\n t0 + usableSec - 1e-3, // nudge just inside the end bound\n Math.max(t0 + 1e-3, duration_sec * p), // nudge just inside the start bound\n );\n // Define a small window around the center and split it into `perAnchor` slots.\n const startSec = Math.max(t0, centerSec - anchor_window_sec / 2);\n const endSec = Math.min(t0 + usableSec, centerSec + anchor_window_sec / 2);\n if (endSec <= startSec)\n continue;\n const wStep = (endSec - startSec) / perAnchor;\n for (let i = 0; i < perAnchor; i++) {\n const tsSec = startSec + (i + 0.5) * wStep;\n extra.push(tsSec * 1000);\n }\n }\n }\n\n // Combine base and anchor‑window samples, snap to frame boundaries, and hard‑enforce\n // trimmed bounds for safety. Rounding to frames ensures stable, cache‑friendly URLs.\n const all = base.concat(extra)\n .map(ms => roundToNearestFrameMs(ms, fps))\n .filter(ms => ms >= trim_start_sec * 1000 && ms <= (duration_sec - trim_end_sec) * 1000);\n\n // Remove duplicates introduced by frame rounding, sort chronologically, and enforce\n // the global cap again just in case.\n const uniqSorted = Array.from(new Set(all)).sort((a, b) => a - b);\n return uniqSorted.slice(0, max_candidates);\n}\n","import { getApiKeyFromEnv } from \"@mux/ai/lib/client-factory\";\nimport type { ImageDownloadOptions } from \"@mux/ai/lib/image-download\";\nimport { downloadImagesAsBase64 } from \"@mux/ai/lib/image-download\";\nimport {\n getAssetDurationSecondsFromAsset,\n getPlaybackIdForAsset,\n getVideoTrackDurationSecondsFromAsset,\n getVideoTrackMaxFrameRateFromAsset,\n isAudioOnlyAsset,\n} from \"@mux/ai/lib/mux-assets\";\nimport { planSamplingTimestamps } from \"@mux/ai/lib/sampling-plan\";\nimport { signUrl } from \"@mux/ai/lib/url-signing\";\nimport { resolveMuxSigningContext } from \"@mux/ai/lib/workflow-credentials\";\nimport { getThumbnailUrls } from \"@mux/ai/primitives/thumbnails\";\nimport { fetchTranscriptForAsset, getReadyTextTracks } from \"@mux/ai/primitives/transcripts\";\nimport type {\n ImageSubmissionMode,\n MuxAIOptions,\n TokenUsage,\n WorkflowCredentialsInput,\n} from \"@mux/ai/types\";\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Types\n// ─────────────────────────────────────────────────────────────────────────────\n\n/** Per-thumbnail moderation result returned from `getModerationScores`. */\nexport interface ThumbnailModerationScore {\n url: string;\n sexual: number;\n violence: number;\n error: boolean;\n errorMessage?: string;\n}\n\n/** Aggregated moderation payload returned from `getModerationScores`. */\nexport interface ModerationResult {\n assetId: string;\n /** Whether moderation ran on thumbnails (video) or transcript text (audio-only). */\n mode: \"thumbnails\" | \"transcript\";\n /** Convenience flag so callers can understand why `thumbnailScores` may contain a transcript entry. */\n isAudioOnly: boolean;\n thumbnailScores: ThumbnailModerationScore[];\n /** Workflow usage metadata (asset duration, thumbnails, etc.). */\n usage?: TokenUsage;\n maxScores: {\n sexual: number;\n violence: number;\n };\n exceedsThreshold: boolean;\n thresholds: {\n sexual: number;\n violence: number;\n };\n}\n\n/** Provider list accepted by `getModerationScores`. */\nexport type ModerationProvider = \"openai\" | \"hive\";\n\nexport type HiveModerationSource =\n | { kind: \"url\"; value: string } |\n { kind: \"file\"; buffer: Uint8Array; contentType: string };\n\nexport interface HiveModerationOutput {\n classes?: Array<{\n class: string;\n score: number;\n }>;\n}\n\n/** Configuration accepted by `getModerationScores`. */\nexport interface ModerationOptions extends MuxAIOptions {\n /** Provider used for moderation (defaults to 'openai'). */\n provider?: ModerationProvider;\n /** OpenAI moderation model identifier (defaults to 'omni-moderation-latest'). */\n model?: string;\n /**\n * Optional transcript language code used when moderating audio-only assets.\n * If omitted, the first ready text track will be used.\n */\n languageCode?: string;\n /** Override the default sexual/violence thresholds (0-1). */\n thresholds?: {\n sexual?: number;\n violence?: number;\n };\n /** Interval between storyboard thumbnails in seconds (defaults to 10). */\n thumbnailInterval?: number;\n /** Width of storyboard thumbnails in pixels (defaults to 640). */\n thumbnailWidth?: number;\n /** Maximum number of thumbnails to sample (defaults to unlimited). When set, samples are evenly distributed with first and last frames pinned. */\n maxSamples?: number;\n /** Max concurrent moderation requests (defaults to 5). */\n maxConcurrent?: number;\n /** Transport used for thumbnails (defaults to 'url'). */\n imageSubmissionMode?: ImageSubmissionMode;\n /** Download tuning used when `imageSubmissionMode` === 'base64'. */\n imageDownloadOptions?: ImageDownloadOptions;\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Implementation\n// ─────────────────────────────────────────────────────────────────────────────\n\nconst DEFAULT_THRESHOLDS = {\n sexual: 0.7,\n violence: 0.8,\n};\n\nconst DEFAULT_PROVIDER = \"openai\";\n\nconst HIVE_ENDPOINT = \"https://api.thehive.ai/api/v2/task/sync\";\nconst HIVE_SEXUAL_CATEGORIES = [\n \"general_nsfw\",\n \"general_suggestive\",\n \"yes_sexual_activity\",\n \"sex_toys\",\n \"nudity_female\",\n \"nudity_male\",\n];\n\nconst HIVE_VIOLENCE_CATEGORIES = [\n \"gun_in_hand\",\n \"gun_not_in_hand\",\n \"knife_in_hand\",\n \"very_bloody\",\n \"other_blood\",\n \"hanging\",\n \"noose\",\n \"human_corpse\",\n \"emaciated_body\",\n \"self_harm\",\n \"animal_abuse\",\n \"fights\",\n \"garm_death_injury_or_military_conflict\",\n];\n\nasync function processConcurrently<T>(\n items: any[],\n processor: (item: any) => Promise<T>,\n maxConcurrent: number = 5,\n): Promise<T[]> {\n \"use step\";\n const results: T[] = [];\n\n for (let i = 0; i < items.length; i += maxConcurrent) {\n const batch = items.slice(i, i + maxConcurrent);\n const batchPromises = batch.map(processor);\n const batchResults = await Promise.all(batchPromises);\n results.push(...batchResults);\n }\n\n return results;\n}\n\nasync function moderateImageWithOpenAI(entry: {\n url: string;\n image: string;\n model: string;\n credentials?: WorkflowCredentialsInput;\n}): Promise<ThumbnailModerationScore> {\n \"use step\";\n const apiKey = await getApiKeyFromEnv(\"openai\", entry.credentials);\n try {\n const res = await fetch(\"https://api.openai.com/v1/moderations\", {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n \"Authorization\": `Bearer ${apiKey}`,\n },\n body: JSON.stringify({\n model: entry.model,\n input: [\n {\n type: \"image_url\",\n image_url: {\n url: entry.image,\n },\n },\n ],\n }),\n });\n\n const json: any = await res.json();\n if (!res.ok) {\n throw new Error(\n `OpenAI moderation error: ${res.status} ${res.statusText} - ${JSON.stringify(json)}`,\n );\n }\n\n const categoryScores = json.results?.[0]?.category_scores || {};\n\n return {\n url: entry.url,\n sexual: categoryScores.sexual || 0,\n violence: categoryScores.violence || 0,\n error: false,\n };\n } catch (error) {\n console.error(\"OpenAI moderation failed:\", error);\n return {\n url: entry.url,\n sexual: 0,\n violence: 0,\n error: true,\n errorMessage: error instanceof Error ? error.message : String(error),\n };\n }\n}\n\nasync function requestOpenAIModeration(\n imageUrls: string[],\n model: string,\n maxConcurrent: number = 5,\n submissionMode: \"url\" | \"base64\" = \"url\",\n downloadOptions?: ImageDownloadOptions,\n credentials?: WorkflowCredentialsInput,\n): Promise<ThumbnailModerationScore[]> {\n \"use step\";\n const targetUrls =\n submissionMode === \"base64\" ?\n (await downloadImagesAsBase64(imageUrls, downloadOptions, maxConcurrent)).map(\n img => ({ url: img.url, image: img.base64Data, model, credentials }),\n ) :\n imageUrls.map(url => ({ url, image: url, model, credentials }));\n\n return processConcurrently(targetUrls, moderateImageWithOpenAI, maxConcurrent);\n}\n\nasync function requestOpenAITextModeration(\n text: string,\n model: string,\n url: string,\n credentials?: WorkflowCredentialsInput,\n): Promise<ThumbnailModerationScore> {\n \"use step\";\n const apiKey = await getApiKeyFromEnv(\"openai\", credentials);\n try {\n const res = await fetch(\"https://api.openai.com/v1/moderations\", {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n \"Authorization\": `Bearer ${apiKey}`,\n },\n body: JSON.stringify({\n model,\n input: text,\n }),\n });\n\n const json: any = await res.json();\n if (!res.ok) {\n throw new Error(\n `OpenAI moderation error: ${res.status} ${res.statusText} - ${JSON.stringify(json)}`,\n );\n }\n\n const categoryScores = json.results?.[0]?.category_scores || {};\n\n return {\n url,\n sexual: categoryScores.sexual || 0,\n violence: categoryScores.violence || 0,\n error: false,\n };\n } catch (error) {\n console.error(\"OpenAI text moderation failed:\", error);\n return {\n url,\n sexual: 0,\n violence: 0,\n error: true,\n errorMessage: error instanceof Error ? error.message : String(error),\n };\n }\n}\n\nfunction chunkTextByUtf16CodeUnits(text: string, maxUnits: number): string[] {\n if (!text.trim()) {\n return [];\n }\n if (text.length <= maxUnits) {\n return [text];\n }\n const chunks: string[] = [];\n for (let i = 0; i < text.length; i += maxUnits) {\n const chunk = text.slice(i, i + maxUnits).trim();\n if (chunk) {\n chunks.push(chunk);\n }\n }\n return chunks;\n}\n\nasync function requestOpenAITranscriptModeration(\n transcriptText: string,\n model: string,\n maxConcurrent: number = 5,\n credentials?: WorkflowCredentialsInput,\n): Promise<ThumbnailModerationScore[]> {\n \"use step\";\n // OpenAI supports larger inputs, but chunking avoids pathological single-request sizes and\n // mirrors our \"max over segments\" behavior used for thumbnail moderation.\n const chunks = chunkTextByUtf16CodeUnits(transcriptText, 10_000);\n if (!chunks.length) {\n return [\n { url: \"transcript:0\", sexual: 0, violence: 0, error: true, errorMessage: \"No transcript chunks to moderate\" },\n ];\n }\n const targets = chunks.map((chunk, idx) => ({\n chunk,\n url: `transcript:${idx}`,\n }));\n return processConcurrently(\n targets,\n async entry => requestOpenAITextModeration(entry.chunk, model, entry.url, credentials),\n maxConcurrent,\n );\n}\n\nfunction getHiveCategoryScores(\n classes: NonNullable<HiveModerationOutput[\"classes\"]>,\n categoryNames: string[],\n): number {\n const scoreMap = Object.fromEntries(\n classes.map(c => [c.class, c.score]),\n );\n const scores = categoryNames.map(category => scoreMap[category] || 0);\n return Math.max(...scores, 0);\n}\n\nasync function moderateImageWithHive(entry: {\n url: string;\n source: HiveModerationSource;\n credentials?: WorkflowCredentialsInput;\n}): Promise<ThumbnailModerationScore> {\n \"use step\";\n const apiKey = await getApiKeyFromEnv(\"hive\", entry.credentials);\n try {\n const formData = new FormData();\n\n if (entry.source.kind === \"url\") {\n formData.append(\"url\", entry.source.value);\n } else {\n const extension = entry.source.contentType.split(\"/\")[1] || \"jpg\";\n const blob = new Blob([entry.source.buffer], {\n type: entry.source.contentType,\n });\n formData.append(\"media\", blob, `thumbnail.${extension}`);\n }\n\n const controller = new AbortController();\n const timeout = setTimeout(() => controller.abort(), 15_000);\n let res: Response;\n try {\n res = await fetch(HIVE_ENDPOINT, {\n method: \"POST\",\n headers: {\n Accept: \"application/json\",\n Authorization: `Token ${apiKey}`,\n },\n body: formData,\n signal: controller.signal,\n });\n } catch (err: any) {\n if (err?.name === \"AbortError\") {\n throw new Error(\"Hive request timed out after 15s\");\n }\n throw err;\n } finally {\n clearTimeout(timeout);\n }\n\n const json: any = await res.json().catch(() => undefined);\n\n if (!res.ok) {\n throw new Error(\n `Hive moderation error: ${res.status} ${res.statusText} - ${JSON.stringify(json)}`,\n );\n }\n\n if (json?.return_code != null && json.return_code !== 0) {\n throw new Error(\n `Hive API error (return_code ${json.return_code}): ${json.message || \"Unknown error\"}`,\n );\n }\n\n // Extract scores from Hive response\n // Hive returns scores in status[0].response.output[0].classes as array of {class, score}\n const classes = json?.status?.[0]?.response?.output?.[0]?.classes;\n if (!Array.isArray(classes)) {\n throw new TypeError(\n `Unexpected Hive response structure: ${JSON.stringify(json)}`,\n );\n }\n\n const sexual = getHiveCategoryScores(classes, HIVE_SEXUAL_CATEGORIES);\n const violence = getHiveCategoryScores(classes, HIVE_VIOLENCE_CATEGORIES);\n\n return {\n url: entry.url,\n sexual,\n violence,\n error: false,\n };\n } catch (error) {\n return {\n url: entry.url,\n sexual: 0,\n violence: 0,\n error: true,\n errorMessage: error instanceof Error ? error.message : String(error),\n };\n }\n}\n\nasync function requestHiveModeration(\n imageUrls: string[],\n maxConcurrent: number = 5,\n submissionMode: \"url\" | \"base64\" = \"url\",\n downloadOptions?: ImageDownloadOptions,\n credentials?: WorkflowCredentialsInput,\n): Promise<ThumbnailModerationScore[]> {\n \"use step\";\n\n const targets: Array<{ url: string; source: HiveModerationSource; credentials?: WorkflowCredentialsInput }> =\n submissionMode === \"base64\" ?\n (await downloadImagesAsBase64(imageUrls, downloadOptions, maxConcurrent)).map(img => ({\n url: img.url,\n source: {\n kind: \"file\",\n buffer: img.buffer,\n contentType: img.contentType,\n },\n credentials,\n })) :\n imageUrls.map(url => ({\n url,\n source: { kind: \"url\", value: url },\n credentials,\n }));\n\n return await processConcurrently(targets, moderateImageWithHive, maxConcurrent);\n}\n\nasync function getThumbnailUrlsFromTimestamps(\n playbackId: string,\n timestampsMs: number[],\n options: {\n width: number;\n shouldSign: boolean;\n credentials?: WorkflowCredentialsInput;\n },\n): Promise<string[]> {\n \"use step\";\n const { width, shouldSign, credentials } = options;\n const baseUrl = `https://image.mux.com/${playbackId}/thumbnail.png`;\n\n const urlPromises = timestampsMs.map(async (tsMs) => {\n const time = Number((tsMs / 1000).toFixed(2));\n if (shouldSign) {\n return signUrl(baseUrl, playbackId, undefined, \"thumbnail\", { time, width }, credentials);\n }\n\n return `${baseUrl}?time=${time}&width=${width}`;\n });\n\n return Promise.all(urlPromises);\n}\n\n/**\n * Moderate a Mux asset.\n * - Video assets: moderates storyboard thumbnails (image moderation)\n * - Audio-only assets: moderates transcript text (text moderation)\n *\n * Provider notes:\n * - provider 'openai' uses OpenAI's hosted moderation endpoint (requires OPENAI_API_KEY)\n * Ref: https://platform.openai.com/docs/guides/moderation\n * - provider 'hive' uses Hive's moderation API for thumbnails only (requires HIVE_API_KEY)\n */\nexport async function getModerationScores(\n assetId: string,\n options: ModerationOptions = {},\n): Promise<ModerationResult> {\n \"use workflow\";\n const {\n provider = DEFAULT_PROVIDER,\n model = provider === \"openai\" ? \"omni-moderation-latest\" : undefined,\n languageCode,\n thresholds = DEFAULT_THRESHOLDS,\n thumbnailInterval = 10,\n thumbnailWidth = 640,\n maxSamples,\n maxConcurrent = 5,\n imageSubmissionMode = \"url\",\n imageDownloadOptions,\n credentials: providedCredentials,\n } = options;\n const credentials = providedCredentials;\n // Fetch asset data and playback ID from Mux via helper\n const { asset, playbackId, policy } = await getPlaybackIdForAsset(assetId, credentials);\n const videoTrackDurationSeconds = getVideoTrackDurationSecondsFromAsset(asset);\n const videoTrackFps = getVideoTrackMaxFrameRateFromAsset(asset);\n const assetDurationSeconds = getAssetDurationSecondsFromAsset(asset);\n // Use the shorter of video-track and asset duration so thumbnail timestamps never\n // exceed the renderable range reported by the Mux thumbnail service.\n const candidateDurations = [videoTrackDurationSeconds, assetDurationSeconds].filter(\n (d): d is number => d != null,\n );\n const duration = candidateDurations.length > 0 ? Math.min(...candidateDurations) : 0;\n const isAudioOnly = isAudioOnlyAsset(asset);\n\n // Resolve signing context for signed playback IDs\n const signingContext = await resolveMuxSigningContext(credentials);\n if (policy === \"signed\" && !signingContext) {\n throw new Error(\n \"Signed playback ID requires signing credentials. \" +\n \"Set MUX_SIGNING_KEY and MUX_PRIVATE_KEY environment variables.\",\n );\n }\n\n let thumbnailScores: ThumbnailModerationScore[];\n let mode: ModerationResult[\"mode\"] = \"thumbnails\";\n let thumbnailCount: number | undefined;\n\n if (isAudioOnly) {\n mode = \"transcript\";\n const readyTextTracks = getReadyTextTracks(asset);\n let transcriptResult = await fetchTranscriptForAsset(asset, playbackId, {\n languageCode,\n cleanTranscript: true,\n shouldSign: policy === \"signed\",\n credentials,\n required: true,\n });\n\n // Audio-only assets may have a single ready text track that isn't \"subtitles\" (e.g. transcripts).\n // If a language-specific subtitle wasn't found but there's exactly one track, fall back to it.\n if (!transcriptResult.track && readyTextTracks.length === 1) {\n transcriptResult = await fetchTranscriptForAsset(asset, playbackId, {\n cleanTranscript: true,\n shouldSign: policy === \"signed\",\n credentials,\n required: true,\n });\n }\n\n if (provider === \"openai\") {\n thumbnailScores = await requestOpenAITranscriptModeration(\n transcriptResult.transcriptText,\n model || \"omni-moderation-latest\",\n maxConcurrent,\n credentials,\n );\n } else if (provider === \"hive\") {\n throw new Error(\"Hive does not support transcript moderation in this workflow. Use provider: 'openai' for audio-only assets.\");\n } else {\n throw new Error(`Unsupported moderation provider: ${provider}`);\n }\n } else {\n const thumbnailUrls = maxSamples === undefined ?\n // Generate thumbnail URLs (signed if needed) using existing interval-based logic.\n await getThumbnailUrls(playbackId, duration, {\n interval: thumbnailInterval,\n width: thumbnailWidth,\n shouldSign: policy === \"signed\",\n credentials,\n }) :\n // In maxSamples mode, sample valid timestamps over the trimmed usable span.\n // Use proportional trims (≈ duration/6, capped at 5s) to stay well inside the\n // renderable range — Mux can't always serve thumbnails at the very edges.\n await getThumbnailUrlsFromTimestamps(\n playbackId,\n planSamplingTimestamps({\n duration_sec: duration,\n max_candidates: maxSamples,\n trim_start_sec: duration > 2 ? Math.min(5, Math.max(1, duration / 6)) : 0,\n trim_end_sec: duration > 2 ? Math.min(5, Math.max(1, duration / 6)) : 0,\n fps: videoTrackFps,\n base_cadence_hz: thumbnailInterval > 0 ? 1 / thumbnailInterval : undefined,\n }),\n {\n width: thumbnailWidth,\n shouldSign: policy === \"signed\",\n credentials,\n },\n );\n thumbnailCount = thumbnailUrls.length;\n\n if (provider === \"openai\") {\n thumbnailScores = await requestOpenAIModeration(\n thumbnailUrls,\n model || \"omni-moderation-latest\",\n maxConcurrent,\n imageSubmissionMode,\n imageDownloadOptions,\n credentials,\n );\n } else if (provider === \"hive\") {\n thumbnailScores = await requestHiveModeration(\n thumbnailUrls,\n maxConcurrent,\n imageSubmissionMode,\n imageDownloadOptions,\n credentials,\n );\n } else {\n throw new Error(`Unsupported moderation provider: ${provider}`);\n }\n }\n\n const failed = thumbnailScores.filter(s => s.error);\n if (failed.length > 0) {\n const details = failed.map(s => `${s.url}: ${s.errorMessage || \"Unknown error\"}`).join(\"; \");\n throw new Error(\n `Moderation failed for ${failed.length}/${thumbnailScores.length} thumbnail(s): ${details}`,\n );\n }\n\n // Find highest scores across all thumbnails\n const maxSexual = Math.max(...thumbnailScores.map(s => s.sexual));\n const maxViolence = Math.max(...thumbnailScores.map(s => s.violence));\n\n const finalThresholds = { ...DEFAULT_THRESHOLDS, ...thresholds };\n\n return {\n assetId,\n mode,\n isAudioOnly,\n thumbnailScores,\n usage: {\n metadata: {\n assetDurationSeconds: duration,\n ...(thumbnailCount === undefined ? {} : { thumbnailCount }),\n },\n },\n maxScores: {\n sexual: maxSexual,\n violence: maxViolence,\n },\n exceedsThreshold: maxSexual > finalThresholds.sexual || maxViolence > finalThresholds.violence,\n thresholds: finalThresholds,\n };\n}\n","import { generateText, Output } from \"ai\";\nimport dedent from \"dedent\";\nimport { z } from \"zod\";\n\nimport type { ImageDownloadOptions } from \"@mux/ai/lib/image-download\";\nimport { downloadImageAsBase64 } from \"@mux/ai/lib/image-download\";\nimport {\n getAssetDurationSecondsFromAsset,\n getPlaybackIdForAsset,\n isAudioOnlyAsset,\n} from \"@mux/ai/lib/mux-assets\";\nimport type {\n PromptOverrides,\n} from \"@mux/ai/lib/prompt-builder\";\nimport {\n createPromptBuilder,\n createToneSection,\n createTranscriptSection,\n} from \"@mux/ai/lib/prompt-builder\";\nimport { createLanguageModelFromConfig, resolveLanguageModelConfig } from \"@mux/ai/lib/providers\";\nimport type { ModelIdByProvider, SupportedProvider } from \"@mux/ai/lib/providers\";\nimport { withRetry } from \"@mux/ai/lib/retry\";\nimport {\n resolveMuxSigningContext,\n} from \"@mux/ai/lib/workflow-credentials\";\nimport { getStoryboardUrl } from \"@mux/ai/primitives/storyboards\";\nimport { fetchTranscriptForAsset } from \"@mux/ai/primitives/transcripts\";\nimport type {\n ImageSubmissionMode,\n MuxAIOptions,\n TokenUsage,\n ToneType,\n WorkflowCredentialsInput,\n} from \"@mux/ai/types\";\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Types\n// ─────────────────────────────────────────────────────────────────────────────\n\nexport const SUMMARY_KEYWORD_LIMIT = 10;\n\nexport const summarySchema = z.object({\n keywords: z.array(z.string()),\n title: z.string(),\n description: z.string(),\n}).strict();\n\nexport type SummaryType = z.infer<typeof summarySchema>;\n\nconst SUMMARY_OUTPUT = Output.object({\n name: \"summary_metadata\",\n description: \"Structured summary with title, description, and keywords.\",\n schema: summarySchema,\n});\n\n/** Structured return payload for `getSummaryAndTags`. */\nexport interface SummaryAndTagsResult {\n /** Asset ID passed into the workflow. */\n assetId: string;\n /** Short headline generated from the storyboard. */\n title: string;\n /** Longer description of the detected content. */\n description: string;\n /** Up to 10 keywords extracted by the model. */\n tags: string[];\n /** Storyboard image URL that was analyzed (undefined for audio-only assets). */\n storyboardUrl?: string;\n /** Token usage from the AI provider (for efficiency/cost analysis). */\n usage?: TokenUsage;\n /** Raw transcript text used for analysis (when includeTranscript is true). */\n transcriptText?: string;\n}\n\n/**\n * Sections of the summarization user prompt that can be overridden.\n * Use these to customize the AI's behavior for your specific use case.\n */\nexport type SummarizationPromptSections =\n | \"task\" |\n \"title\" |\n \"description\" |\n \"keywords\" |\n \"qualityGuidelines\";\n\n/**\n * Override specific sections of the summarization prompt.\n * Each key corresponds to a section that can be customized.\n *\n * @example\n * ```typescript\n * const result = await getSummaryAndTags(assetId, {\n * promptOverrides: {\n * task: 'Generate SEO-optimized metadata for this product video.',\n * title: 'Create a click-worthy title under 60 characters for YouTube.',\n * },\n * });\n * ```\n */\nexport type SummarizationPromptOverrides = PromptOverrides<SummarizationPromptSections>;\n\n/** Configuration accepted by `getSummaryAndTags`. */\nexport interface SummarizationOptions extends MuxAIOptions {\n /** AI provider to run (defaults to 'openai'). */\n provider?: SupportedProvider;\n /** Provider-specific chat model identifier. */\n model?: ModelIdByProvider[SupportedProvider];\n /** Prompt tone shim applied to the system instruction (defaults to 'neutral'). */\n tone?: ToneType;\n /** Fetch the transcript and send it alongside the storyboard (defaults to true). */\n includeTranscript?: boolean;\n /** Strip timestamps/markup from transcripts before including them (defaults to true). */\n cleanTranscript?: boolean;\n /** How storyboard frames should be delivered to the provider (defaults to 'url'). */\n imageSubmissionMode?: ImageSubmissionMode;\n /** Fine-tune storyboard downloads when `imageSubmissionMode` === 'base64'. */\n imageDownloadOptions?: ImageDownloadOptions;\n /**\n * Override specific sections of the user prompt.\n * Useful for customizing the AI's output for specific use cases (SEO, social media, etc.)\n */\n promptOverrides?: SummarizationPromptOverrides;\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Prompts\n// ─────────────────────────────────────────────────────────────────────────────\n\nconst VALID_TONES = [\"neutral\", \"playful\", \"professional\"] as const;\n\nconst TONE_INSTRUCTIONS: Record<ToneType, string> = {\n neutral: \"Provide a clear, straightforward analysis.\",\n 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!\",\n professional: \"Provide a professional, executive-level analysis suitable for business reporting.\",\n};\n\n/**\n * Prompt builder for the summarization user prompt.\n * Sections can be individually overridden via `promptOverrides` in SummarizationOptions.\n */\nconst summarizationPromptBuilder = createPromptBuilder<SummarizationPromptSections>({\n template: {\n task: {\n tag: \"task\",\n content: \"Analyze the storyboard frames and generate metadata that captures the essence of the video content.\",\n },\n title: {\n tag: \"title_requirements\",\n content: dedent`\n A short, compelling headline that immediately communicates the subject or action.\n Aim for brevity - typically under 10 words. Think of how a news headline or video card title would read.\n Start with the primary subject, action, or topic - never begin with \"A video of\" or similar phrasing.\n Use active, specific language.`,\n },\n description: {\n tag: \"description_requirements\",\n content: dedent`\n A concise summary (2-4 sentences) that describes what happens across the video.\n Cover the main subjects, actions, setting, and any notable progression visible across frames.\n Write in present tense. Be specific about observable details rather than making assumptions.\n If the transcript provides dialogue or narration, incorporate key points but prioritize visual content.`,\n },\n keywords: {\n tag: \"keywords_requirements\",\n content: dedent`\n Specific, searchable terms (up to ${SUMMARY_KEYWORD_LIMIT}) that capture:\n - Primary subjects (people, animals, objects)\n - Actions and activities being performed\n - Setting and environment\n - Notable objects or tools\n - Style or genre (if applicable)\n Prefer concrete nouns and action verbs over abstract concepts.\n Use lowercase. Avoid redundant or overly generic terms like \"video\" or \"content\".`,\n },\n qualityGuidelines: {\n tag: \"quality_guidelines\",\n content: dedent`\n - Examine all frames to understand the full context and progression\n - Be precise: \"golden retriever\" is better than \"dog\" when identifiable\n - Capture the narrative: what begins, develops, and concludes\n - Balance brevity with informativeness`,\n },\n },\n sectionOrder: [\"task\", \"title\", \"description\", \"keywords\", \"qualityGuidelines\"],\n});\n\n/**\n * Prompt builder for audio-only content.\n * Focuses on transcript analysis without visual references.\n */\nconst audioOnlyPromptBuilder = createPromptBuilder<SummarizationPromptSections>({\n template: {\n task: {\n tag: \"task\",\n content: \"Analyze the transcript and generate metadata that captures the essence of the audio content.\",\n },\n title: {\n tag: \"title_requirements\",\n content: dedent`\n A short, compelling headline that immediately communicates the subject or topic.\n Aim for brevity - typically under 10 words. Think of how a podcast title or audio description would read.\n Start with the primary subject, action, or topic - never begin with \"An audio of\" or similar phrasing.\n Use active, specific language.`,\n },\n description: {\n tag: \"description_requirements\",\n content: dedent`\n A concise summary (2-4 sentences) that describes the audio content.\n Cover the main topics, speakers, themes, and any notable progression in the discussion or narration.\n Write in present tense. Be specific about what is discussed or presented rather than making assumptions.\n Focus on the spoken content and any key insights, dialogue, or narrative elements.`,\n },\n keywords: {\n tag: \"keywords_requirements\",\n content: dedent`\n Specific, searchable terms (up to ${SUMMARY_KEYWORD_LIMIT}) that capture:\n - Primary topics and themes\n - Speakers or presenters (if named)\n - Key concepts and terminology\n - Content type (interview, lecture, music, etc.)\n - Genre or style (if applicable)\n Prefer concrete nouns and relevant terms over abstract concepts.\n Use lowercase. Avoid redundant or overly generic terms like \"audio\" or \"content\".`,\n },\n qualityGuidelines: {\n tag: \"quality_guidelines\",\n content: dedent`\n - Analyze the full transcript to understand context and themes\n - Be precise: use specific terminology when mentioned\n - Capture the narrative: what is introduced, discussed, and concluded\n - Balance brevity with informativeness`,\n },\n },\n sectionOrder: [\"task\", \"title\", \"description\", \"keywords\", \"qualityGuidelines\"],\n});\n\nconst SYSTEM_PROMPT = dedent`\n <role>\n You are a video content analyst specializing in storyboard interpretation and multimodal analysis.\n </role>\n\n <context>\n You receive storyboard images containing multiple sequential frames extracted from a video.\n These frames are arranged in a grid and represent the visual progression of the content over time.\n Read frames left-to-right, top-to-bottom to understand the temporal sequence.\n </context>\n\n <transcript_guidance>\n When a transcript is provided alongside the storyboard:\n - Use it to understand spoken content, dialogue, narration, and audio context\n - Correlate transcript content with visual frames to build a complete picture\n - Extract key terminology, names, and specific language used by speakers\n - Let the transcript inform keyword selection, especially for topics not visually obvious\n - Prioritize visual content for the description, but enrich it with transcript insights\n - If transcript and visuals conflict, trust the visual evidence\n </transcript_guidance>\n\n <capabilities>\n - Extract meaning from visual sequences\n - Identify subjects, actions, settings, and narrative arcs\n - Generate accurate, searchable metadata\n - Synthesize visual and transcript information when provided\n </capabilities>\n\n <constraints>\n - Only describe what is clearly observable in the frames or explicitly stated in the transcript\n - Do not fabricate details or make unsupported assumptions\n - Return structured data matching the requested schema\n - Output only the JSON object; no markdown or extra text\n </constraints>\n\n <tone_guidance>\n Pay special attention to the <tone> section and lean heavily into those instructions.\n Adapt your entire analysis and writing style to match the specified tone - this should influence\n your word choice, personality, formality level, and overall presentation of the content.\n The tone instructions are not suggestions but core requirements for how you should express yourself.\n </tone_guidance>\n\n <language_guidelines>\n AVOID these meta-descriptive phrases that reference the medium rather than the content:\n - \"The image shows...\" / \"The storyboard shows...\"\n - \"In this video...\" / \"This video features...\"\n - \"The frames depict...\" / \"The footage shows...\"\n - \"We can see...\" / \"You can see...\"\n - \"The clip shows...\" / \"The scene shows...\"\n\n INSTEAD, describe the content directly:\n - BAD: \"The video shows a chef preparing a meal\"\n - GOOD: \"A chef prepares a meal in a professional kitchen\"\n\n Write as if describing reality, not describing a recording of reality.\n </language_guidelines>`;\n\nconst AUDIO_ONLY_SYSTEM_PROMPT = dedent`\n <role>\n You are an audio content analyst specializing in transcript analysis and metadata generation.\n </role>\n\n <context>\n You receive transcript text from audio-only content (podcasts, audiobooks, music, etc.).\n Your task is to analyze the spoken/audio content and generate accurate, searchable metadata.\n </context>\n\n <transcript_guidance>\n - Carefully analyze the entire transcript to understand themes, topics, and key points\n - Extract key terminology, names, concepts, and specific language used\n - Identify the content type (interview, lecture, music, narration, etc.)\n - Note the tone, style, and any distinctive characteristics of the audio\n - Consider the intended audience and context based on language and content\n </transcript_guidance>\n\n <capabilities>\n - Extract meaning and themes from spoken/audio content\n - Identify subjects, topics, speakers, and narrative structure\n - Generate accurate, searchable metadata from audio-based content\n - Understand context and intent from transcript alone\n </capabilities>\n\n <constraints>\n - Only describe what is explicitly stated or strongly implied in the transcript\n - Do not fabricate details or make unsupported assumptions\n - Return structured data matching the requested schema\n - Focus entirely on audio/spoken content - there are no visual elements\n - Output only the JSON object; no markdown or extra text\n </constraints>\n\n <tone_guidance>\n Pay special attention to the <tone> section and lean heavily into those instructions.\n Adapt your entire analysis and writing style to match the specified tone - this should influence\n your word choice, personality, formality level, and overall presentation of the content.\n The tone instructions are not suggestions but core requirements for how you should express yourself.\n </tone_guidance>\n\n <language_guidelines>\n AVOID these meta-descriptive phrases that reference the medium rather than the content:\n - \"The audio shows...\" / \"The transcript shows...\"\n - \"In this recording...\" / \"This audio features...\"\n - \"The speaker says...\" / \"We can hear...\"\n - \"The clip contains...\" / \"The recording shows...\"\n\n INSTEAD, describe the content directly:\n - BAD: \"The audio features a discussion about climate change\"\n - GOOD: \"A panel discusses climate change impacts and solutions\"\n\n Write as if describing reality, not describing a recording of reality.\n </language_guidelines>`;\n\ninterface UserPromptContext {\n tone: ToneType;\n transcriptText?: string;\n isCleanTranscript?: boolean;\n promptOverrides?: SummarizationPromptOverrides;\n isAudioOnly?: boolean;\n}\n\nfunction buildUserPrompt({\n tone,\n transcriptText,\n isCleanTranscript = true,\n promptOverrides,\n isAudioOnly = false,\n}: UserPromptContext): string {\n // Build dynamic context sections\n const contextSections = [createToneSection(TONE_INSTRUCTIONS[tone])];\n\n if (transcriptText) {\n const format = isCleanTranscript ? \"plain text\" : \"WebVTT\";\n contextSections.push(createTranscriptSection(transcriptText, format));\n }\n\n // Use audio-only prompt builder for audio-only assets\n const promptBuilder = isAudioOnly ? audioOnlyPromptBuilder : summarizationPromptBuilder;\n return promptBuilder.buildWithContext(promptOverrides, contextSections);\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Implementation\n// ─────────────────────────────────────────────────────────────────────────────\n\ninterface AnalysisResponse {\n result: SummaryType;\n usage: TokenUsage;\n}\n\nasync function analyzeStoryboard(\n imageDataUrl: string,\n provider: SupportedProvider,\n modelId: string,\n userPrompt: string,\n systemPrompt: string,\n credentials?: WorkflowCredentialsInput,\n): Promise<AnalysisResponse> {\n \"use step\";\n const model = await createLanguageModelFromConfig(provider, modelId, credentials);\n\n const response = await generateText({\n model,\n output: SUMMARY_OUTPUT,\n messages: [\n {\n role: \"system\",\n content: systemPrompt,\n },\n {\n role: \"user\",\n content: [\n { type: \"text\", text: userPrompt },\n { type: \"image\", image: imageDataUrl },\n ],\n },\n ],\n });\n\n if (!response.output) {\n throw new Error(\"Summarization output missing\");\n }\n\n const parsed = summarySchema.parse(response.output);\n\n return {\n result: parsed,\n usage: {\n inputTokens: response.usage.inputTokens,\n outputTokens: response.usage.outputTokens,\n totalTokens: response.usage.totalTokens,\n reasoningTokens: response.usage.reasoningTokens,\n cachedInputTokens: response.usage.cachedInputTokens,\n },\n };\n}\n\nasync function analyzeAudioOnly(\n provider: SupportedProvider,\n modelId: string,\n userPrompt: string,\n systemPrompt: string,\n credentials?: WorkflowCredentialsInput,\n): Promise<AnalysisResponse> {\n \"use step\";\n const model = await createLanguageModelFromConfig(provider, modelId, credentials);\n\n const response = await generateText({\n model,\n output: SUMMARY_OUTPUT,\n messages: [\n {\n role: \"system\",\n content: systemPrompt,\n },\n {\n role: \"user\",\n content: userPrompt,\n },\n ],\n });\n\n if (!response.output) {\n throw new Error(\"Summarization output missing\");\n }\n\n const parsed = summarySchema.parse(response.output);\n\n return {\n result: parsed,\n usage: {\n inputTokens: response.usage.inputTokens,\n outputTokens: response.usage.outputTokens,\n totalTokens: response.usage.totalTokens,\n reasoningTokens: response.usage.reasoningTokens,\n cachedInputTokens: response.usage.cachedInputTokens,\n },\n };\n}\n\nfunction normalizeKeywords(keywords?: string[]): string[] {\n if (!Array.isArray(keywords) || keywords.length === 0) {\n return [];\n }\n\n const uniqueLowercase = new Set<string>();\n const normalized: string[] = [];\n\n for (const keyword of keywords) {\n const trimmed = keyword?.trim();\n if (!trimmed) {\n continue;\n }\n\n const lower = trimmed.toLowerCase();\n if (uniqueLowercase.has(lower)) {\n continue;\n }\n\n uniqueLowercase.add(lower);\n normalized.push(trimmed);\n\n if (normalized.length === SUMMARY_KEYWORD_LIMIT) {\n break;\n }\n }\n\n return normalized;\n}\n\nexport async function getSummaryAndTags(\n assetId: string,\n options?: SummarizationOptions,\n): Promise<SummaryAndTagsResult> {\n \"use workflow\";\n const {\n provider = \"openai\",\n model,\n tone = \"neutral\",\n includeTranscript = true,\n cleanTranscript = true,\n imageSubmissionMode = \"url\",\n imageDownloadOptions,\n promptOverrides,\n credentials,\n } = options ?? {};\n\n // Validate tone parameter\n if (!VALID_TONES.includes(tone)) {\n throw new Error(\n `Invalid tone \"${tone}\". Valid tones are: ${VALID_TONES.join(\", \")}`,\n );\n }\n\n const modelConfig = resolveLanguageModelConfig({\n ...options,\n model,\n provider: provider as SupportedProvider,\n });\n const workflowCredentials = credentials;\n\n // Fetch asset data from Mux and grab playback/transcript details\n const { asset: assetData, playbackId, policy } = await getPlaybackIdForAsset(assetId, workflowCredentials);\n\n const assetDurationSeconds = getAssetDurationSecondsFromAsset(assetData);\n\n // Detect if asset is audio-only\n const isAudioOnly = isAudioOnlyAsset(assetData);\n\n // Audio-only assets require transcripts since there's no visual content\n if (isAudioOnly && !includeTranscript) {\n throw new Error(\n \"Audio-only assets require a transcript. Set includeTranscript: true and ensure the asset has a ready text track (captions/subtitles).\",\n );\n }\n\n // Resolve signing context for signed playback IDs\n const signingContext = await resolveMuxSigningContext(workflowCredentials);\n if (policy === \"signed\" && !signingContext) {\n throw new Error(\n \"Signed playback ID requires signing credentials. \" +\n \"Set MUX_SIGNING_KEY and MUX_PRIVATE_KEY environment variables.\",\n );\n }\n\n const transcriptText =\n includeTranscript ?\n (await fetchTranscriptForAsset(assetData, playbackId, {\n cleanTranscript,\n shouldSign: policy === \"signed\",\n credentials: workflowCredentials,\n required: isAudioOnly,\n })).transcriptText :\n \"\";\n\n // Build the user prompt with all context and any overrides\n const userPrompt = buildUserPrompt({\n tone,\n transcriptText,\n isCleanTranscript: cleanTranscript,\n promptOverrides,\n isAudioOnly,\n });\n\n let analysisResponse: AnalysisResponse;\n let imageUrl: string | undefined;\n\n // Choose system prompt and analysis method based on asset type\n const systemPrompt = isAudioOnly ? AUDIO_ONLY_SYSTEM_PROMPT : SYSTEM_PROMPT;\n\n try {\n if (isAudioOnly) {\n // Audio-only analysis: skip storyboard, analyze transcript only\n analysisResponse = await analyzeAudioOnly(\n modelConfig.provider,\n modelConfig.modelId,\n userPrompt,\n systemPrompt,\n workflowCredentials,\n );\n } else {\n // Video analysis: fetch storyboard and analyze with visual content\n const storyboardUrl = await getStoryboardUrl(playbackId, 640, policy === \"signed\", workflowCredentials);\n imageUrl = storyboardUrl;\n\n if (imageSubmissionMode === \"base64\") {\n const downloadResult = await downloadImageAsBase64(storyboardUrl, imageDownloadOptions);\n analysisResponse = await analyzeStoryboard(\n downloadResult.base64Data,\n modelConfig.provider,\n modelConfig.modelId,\n userPrompt,\n systemPrompt,\n workflowCredentials,\n );\n } else {\n // URL-based submission with retry logic\n analysisResponse = await withRetry(() =>\n analyzeStoryboard(\n storyboardUrl,\n modelConfig.provider,\n modelConfig.modelId,\n userPrompt,\n systemPrompt,\n workflowCredentials,\n ));\n }\n }\n } catch (error: unknown) {\n const contentType = isAudioOnly ? \"audio\" : \"video\";\n throw new Error(\n `Failed to analyze ${contentType} content with ${provider}: ${\n error instanceof Error ? error.message : \"Unknown error\"\n }`,\n );\n }\n\n if (!analysisResponse.result) {\n throw new Error(`Failed to analyze video content for asset ${assetId}`);\n }\n\n if (!analysisResponse.result.title) {\n throw new Error(`Failed to generate title for asset ${assetId}`);\n }\n\n if (!analysisResponse.result.description) {\n throw new Error(`Failed to generate description for asset ${assetId}`);\n }\n\n return {\n assetId,\n title: analysisResponse.result.title,\n description: analysisResponse.result.description,\n tags: normalizeKeywords(analysisResponse.result.keywords),\n storyboardUrl: imageUrl, // undefined for audio-only assets\n usage: {\n ...analysisResponse.usage,\n metadata: {\n assetDurationSeconds,\n },\n },\n transcriptText: transcriptText || undefined,\n };\n}\n","/**\n * Language Code Conversion Utilities\n *\n * Provides bidirectional mapping between:\n * - ISO 639-1 (2-letter codes) - Used by browsers, BCP-47, most video players\n * - ISO 639-3 (3-letter codes) - Used by various APIs and language processing systems\n *\n * This is essential for interoperability between different systems:\n * - Mux uses ISO 639-1 for track language codes\n * - Browser players expect BCP-47 compliant codes (based on ISO 639-1)\n * - Some APIs require ISO 639-3 (3-letter) codes\n */\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Language Code Mapping\n// ─────────────────────────────────────────────────────────────────────────────\n\n/**\n * Mapping from ISO 639-1 (2-letter) to ISO 639-3 (3-letter) codes.\n * Covers the most common languages used in video translation.\n *\n * Reference: https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes\n */\nconst ISO639_1_TO_3 = {\n // Major world languages\n en: \"eng\", // English\n es: \"spa\", // Spanish\n fr: \"fra\", // French\n de: \"deu\", // German\n it: \"ita\", // Italian\n pt: \"por\", // Portuguese\n ru: \"rus\", // Russian\n zh: \"zho\", // Chinese\n ja: \"jpn\", // Japanese\n ko: \"kor\", // Korean\n ar: \"ara\", // Arabic\n hi: \"hin\", // Hindi\n\n // European languages\n nl: \"nld\", // Dutch\n pl: \"pol\", // Polish\n sv: \"swe\", // Swedish\n da: \"dan\", // Danish\n no: \"nor\", // Norwegian\n fi: \"fin\", // Finnish\n el: \"ell\", // Greek\n cs: \"ces\", // Czech\n hu: \"hun\", // Hungarian\n ro: \"ron\", // Romanian\n bg: \"bul\", // Bulgarian\n hr: \"hrv\", // Croatian\n sk: \"slk\", // Slovak\n sl: \"slv\", // Slovenian\n uk: \"ukr\", // Ukrainian\n tr: \"tur\", // Turkish\n\n // Asian languages\n th: \"tha\", // Thai\n vi: \"vie\", // Vietnamese\n id: \"ind\", // Indonesian\n ms: \"msa\", // Malay\n tl: \"tgl\", // Tagalog/Filipino\n\n // Other languages\n he: \"heb\", // Hebrew\n fa: \"fas\", // Persian/Farsi\n bn: \"ben\", // Bengali\n ta: \"tam\", // Tamil\n te: \"tel\", // Telugu\n mr: \"mar\", // Marathi\n gu: \"guj\", // Gujarati\n kn: \"kan\", // Kannada\n ml: \"mal\", // Malayalam\n pa: \"pan\", // Punjabi\n ur: \"urd\", // Urdu\n sw: \"swa\", // Swahili\n af: \"afr\", // Afrikaans\n ca: \"cat\", // Catalan\n eu: \"eus\", // Basque\n gl: \"glg\", // Galician\n is: \"isl\", // Icelandic\n et: \"est\", // Estonian\n lv: \"lav\", // Latvian\n lt: \"lit\", // Lithuanian\n} as const;\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Types\n// ─────────────────────────────────────────────────────────────────────────────\n\n/**\n * Supported ISO 639-1 two-letter language codes.\n * These are the language codes supported for translation workflows.\n */\nexport type SupportedISO639_1 = keyof typeof ISO639_1_TO_3;\n\n/**\n * Supported ISO 639-3 three-letter language codes.\n * These are the language codes supported for translation workflows.\n */\nexport type SupportedISO639_3 = (typeof ISO639_1_TO_3)[SupportedISO639_1];\n\n/** ISO 639-1 two-letter language code (e.g., \"en\", \"fr\", \"es\") */\nexport type ISO639_1 = SupportedISO639_1 | (string & {});\n\n/** ISO 639-3 three-letter language code (e.g., \"eng\", \"fra\", \"spa\") */\nexport type ISO639_3 = SupportedISO639_3 | (string & {});\n\n/** Structured language code result containing both formats */\nexport interface LanguageCodePair {\n /** ISO 639-1 two-letter code (BCP-47 compatible) */\n iso639_1: ISO639_1;\n /** ISO 639-3 three-letter code */\n iso639_3: ISO639_3;\n}\n\n/**\n * Reverse mapping from ISO 639-3 (3-letter) to ISO 639-1 (2-letter) codes.\n * Generated from ISO639_1_TO_3 for consistency.\n */\nconst ISO639_3_TO_1 = Object.fromEntries(\n Object.entries(ISO639_1_TO_3).map(([iso1, iso3]) => [iso3, iso1]),\n) as Record<SupportedISO639_3, SupportedISO639_1>;\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Conversion Functions\n// ─────────────────────────────────────────────────────────────────────────────\n\n/**\n * Converts an ISO 639-1 (2-letter) code to ISO 639-3 (3-letter) code.\n *\n * @param code - ISO 639-1 two-letter language code (e.g., \"en\", \"fr\")\n * @returns ISO 639-3 three-letter code, or the original if not found\n *\n * @example\n * ```typescript\n * toISO639_3(\"en\") // \"eng\"\n * toISO639_3(\"fr\") // \"fra\"\n * toISO639_3(\"ja\") // \"jpn\"\n * ```\n */\nexport function toISO639_3(code: string): ISO639_3 {\n const normalized = code.toLowerCase().trim();\n\n // If it's already a 3-letter code, return as-is\n if (normalized.length === 3) {\n return normalized;\n }\n\n return (ISO639_1_TO_3 as Record<string, string>)[normalized] ?? normalized;\n}\n\n/**\n * Converts an ISO 639-3 (3-letter) code to ISO 639-1 (2-letter) code.\n *\n * @param code - ISO 639-3 three-letter language code (e.g., \"eng\", \"fra\")\n * @returns ISO 639-1 two-letter code, or the original if not found\n *\n * @example\n * ```typescript\n * toISO639_1(\"eng\") // \"en\"\n * toISO639_1(\"fra\") // \"fr\"\n * toISO639_1(\"jpn\") // \"ja\"\n * ```\n */\nexport function toISO639_1(code: string): ISO639_1 {\n const normalized = code.toLowerCase().trim();\n\n // If it's already a 2-letter code, return as-is\n if (normalized.length === 2) {\n return normalized;\n }\n\n return (ISO639_3_TO_1 as Record<string, string>)[normalized] ?? normalized;\n}\n\n/**\n * Returns both ISO 639-1 and ISO 639-3 codes for a given language code.\n * Accepts either format as input and normalizes to both.\n *\n * @param code - Language code in either ISO 639-1 or ISO 639-3 format\n * @returns Object containing both code formats\n *\n * @example\n * ```typescript\n * getLanguageCodePair(\"en\") // { iso639_1: \"en\", iso639_3: \"eng\" }\n * getLanguageCodePair(\"fra\") // { iso639_1: \"fr\", iso639_3: \"fra\" }\n * ```\n */\nexport function getLanguageCodePair(code: string): LanguageCodePair {\n const normalized = code.toLowerCase().trim();\n\n if (normalized.length === 2) {\n // Input is ISO 639-1\n return {\n iso639_1: normalized,\n iso639_3: toISO639_3(normalized),\n };\n } else if (normalized.length === 3) {\n // Input is ISO 639-3\n return {\n iso639_1: toISO639_1(normalized),\n iso639_3: normalized,\n };\n }\n\n // Unknown format, return as-is for both\n return {\n iso639_1: normalized,\n iso639_3: normalized,\n };\n}\n\n/**\n * Validates if a code is a known ISO 639-1 code.\n *\n * @param code - Code to validate\n * @returns true if the code is a known ISO 639-1 code\n */\nexport function isValidISO639_1(code: string): boolean {\n return code.length === 2 && code.toLowerCase() in ISO639_1_TO_3;\n}\n\n/**\n * Validates if a code is a known ISO 639-3 code.\n *\n * @param code - Code to validate\n * @returns true if the code is a known ISO 639-3 code\n */\nexport function isValidISO639_3(code: string): boolean {\n return code.length === 3 && code.toLowerCase() in ISO639_3_TO_1;\n}\n\n/**\n * Gets the human-readable language name for a given code.\n *\n * @param code - Language code in either ISO 639-1 or ISO 639-3 format\n * @returns Human-readable language name (e.g., \"English\", \"French\")\n */\nexport function getLanguageName(code: string): string {\n const iso639_1 = toISO639_1(code);\n try {\n const displayNames = new Intl.DisplayNames([\"en\"], { type: \"language\" });\n return displayNames.of(iso639_1) ?? code.toUpperCase();\n } catch {\n return code.toUpperCase();\n }\n}\n","import { createPresignedGetUrl, putObjectToS3 } from \"@mux/ai/lib/s3-sigv4\";\nimport type {\n StorageAdapter,\n StoragePresignGetObjectInput,\n StoragePutObjectInput,\n} from \"@mux/ai/types\";\n\nfunction requireCredentials(\n accessKeyId: string | undefined,\n secretAccessKey: string | undefined,\n): { accessKeyId: string; secretAccessKey: string } {\n if (!accessKeyId || !secretAccessKey) {\n throw new Error(\n \"S3 credentials are required for default storage operations. \" +\n \"Provide S3_ACCESS_KEY_ID and S3_SECRET_ACCESS_KEY or pass options.storageAdapter.\",\n );\n }\n\n return { accessKeyId, secretAccessKey };\n}\n\nexport async function putObjectWithStorageAdapter(\n input: StoragePutObjectInput,\n adapter?: StorageAdapter,\n): Promise<void> {\n if (adapter) {\n await adapter.putObject(input);\n return;\n }\n\n const credentials = requireCredentials(input.accessKeyId, input.secretAccessKey);\n await putObjectToS3({\n accessKeyId: credentials.accessKeyId,\n secretAccessKey: credentials.secretAccessKey,\n endpoint: input.endpoint,\n region: input.region,\n bucket: input.bucket,\n key: input.key,\n body: input.body,\n contentType: input.contentType,\n });\n}\n\nexport async function createPresignedGetUrlWithStorageAdapter(\n input: StoragePresignGetObjectInput,\n adapter?: StorageAdapter,\n): Promise<string> {\n if (adapter) {\n return adapter.createPresignedGetUrl(input);\n }\n\n const credentials = requireCredentials(input.accessKeyId, input.secretAccessKey);\n return createPresignedGetUrl({\n accessKeyId: credentials.accessKeyId,\n secretAccessKey: credentials.secretAccessKey,\n endpoint: input.endpoint,\n region: input.region,\n bucket: input.bucket,\n key: input.key,\n expiresInSeconds: input.expiresInSeconds,\n });\n}\n","import env from \"@mux/ai/env\";\nimport { getApiKeyFromEnv } from \"@mux/ai/lib/client-factory\";\nimport { getLanguageCodePair, toISO639_1, toISO639_3 } from \"@mux/ai/lib/language-codes\";\nimport type { LanguageCodePair, SupportedISO639_1 } from \"@mux/ai/lib/language-codes\";\nimport { getAssetDurationSecondsFromAsset, getPlaybackIdForAsset } from \"@mux/ai/lib/mux-assets\";\nimport {\n createPresignedGetUrlWithStorageAdapter,\n putObjectWithStorageAdapter,\n} from \"@mux/ai/lib/storage-adapter\";\nimport { signUrl } from \"@mux/ai/lib/url-signing\";\nimport { resolveMuxClient } from \"@mux/ai/lib/workflow-credentials\";\nimport type {\n MuxAIOptions,\n StorageAdapter,\n TokenUsage,\n WorkflowCredentialsInput,\n} from \"@mux/ai/types\";\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Types\n// ─────────────────────────────────────────────────────────────────────────────\n\n/** Output returned from `translateAudio`. */\nexport interface AudioTranslationResult {\n assetId: string;\n /** Target language code (ISO 639-1 two-letter format). */\n targetLanguageCode: SupportedISO639_1;\n /**\n * Target language codes in both ISO 639-1 (2-letter) and ISO 639-3 (3-letter) formats.\n * Use `iso639_1` for browser players (BCP-47 compliant) and `iso639_3` for ElevenLabs API.\n */\n targetLanguage: LanguageCodePair;\n dubbingId: string;\n uploadedTrackId?: string;\n presignedUrl?: string;\n /** Workflow usage metadata (asset duration, thumbnails, etc.). */\n usage?: TokenUsage;\n}\n\n/** Configuration accepted by `translateAudio`. */\nexport interface AudioTranslationOptions extends MuxAIOptions {\n /** Audio dubbing provider (currently ElevenLabs only). */\n provider?: \"elevenlabs\";\n /** Number of speakers supplied to ElevenLabs (0 = auto-detect, default). */\n numSpeakers?: number;\n /** Optional override for the S3-compatible endpoint used for uploads. */\n s3Endpoint?: string;\n /** S3 region (defaults to env.S3_REGION or 'auto'). */\n s3Region?: string;\n /** Bucket that will store dubbed audio files. */\n s3Bucket?: string;\n /**\n * When true (default) the dubbed audio file is uploaded to the configured\n * bucket and attached to the Mux asset.\n */\n uploadToMux?: boolean;\n /** Optional storage adapter override for upload + presign operations. */\n storageAdapter?: StorageAdapter;\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Implementation\n// ─────────────────────────────────────────────────────────────────────────────\n\nconst STATIC_RENDITION_POLL_INTERVAL_MS = 5000;\nconst STATIC_RENDITION_MAX_ATTEMPTS = 36; // ~3 minutes\n\nasync function sleep(ms: number): Promise<void> {\n \"use step\";\n await new Promise(resolve => setTimeout(resolve, ms));\n}\n\nfunction getReadyAudioStaticRendition(asset: any) {\n const files = asset.static_renditions?.files as any[] | undefined;\n if (!files || files.length === 0) {\n return undefined;\n }\n\n return files.find(\n rendition => rendition.name === \"audio.m4a\" && rendition.status === \"ready\",\n );\n}\n\nconst hasReadyAudioStaticRendition = (asset: any) => Boolean(getReadyAudioStaticRendition(asset));\n\nasync function requestStaticRenditionCreation(\n assetId: string,\n credentials?: WorkflowCredentialsInput,\n) {\n \"use step\";\n const muxClient = await resolveMuxClient(credentials);\n const mux = await muxClient.createClient();\n try {\n await mux.video.assets.createStaticRendition(assetId, {\n resolution: \"audio-only\",\n });\n } catch (error: any) {\n const statusCode = error?.status ?? error?.statusCode;\n const messages: string[] | undefined = error?.error?.messages;\n const alreadyDefined =\n messages?.some(message => message.toLowerCase().includes(\"already defined\")) ??\n error?.message?.toLowerCase().includes(\"already defined\");\n\n if (statusCode === 409 || alreadyDefined) {\n return;\n }\n\n const message = error instanceof Error ? error.message : \"Unknown error\";\n throw new Error(`Failed to request static rendition from Mux: ${message}`);\n }\n}\n\nasync function waitForAudioStaticRendition({\n assetId,\n initialAsset,\n credentials,\n}: {\n assetId: string;\n initialAsset: any;\n credentials?: WorkflowCredentialsInput;\n}): Promise<any> {\n \"use step\";\n const muxClient = await resolveMuxClient(credentials);\n const mux = await muxClient.createClient();\n let currentAsset = initialAsset;\n\n if (hasReadyAudioStaticRendition(currentAsset)) {\n return currentAsset;\n }\n\n const status = currentAsset.static_renditions?.status ?? \"not_requested\";\n\n if (status === \"not_requested\" || status === undefined) {\n await requestStaticRenditionCreation(assetId, credentials);\n } else if (status === \"errored\") {\n await requestStaticRenditionCreation(assetId, credentials);\n } else {\n console.warn(`ℹ️ Static rendition already ${status}. Waiting for it to finish...`);\n }\n\n for (let attempt = 1; attempt <= STATIC_RENDITION_MAX_ATTEMPTS; attempt++) {\n await sleep(STATIC_RENDITION_POLL_INTERVAL_MS);\n currentAsset = await mux.video.assets.retrieve(assetId);\n\n if (hasReadyAudioStaticRendition(currentAsset)) {\n return currentAsset;\n }\n\n const currentStatus = currentAsset.static_renditions?.status || \"unknown\";\n console.warn(\n `⌛ Waiting for static rendition (attempt ${attempt}/${STATIC_RENDITION_MAX_ATTEMPTS}) → ${currentStatus}`,\n );\n\n if (currentStatus === \"errored\") {\n throw new Error(\n \"Mux failed to create the static rendition for this asset. Please check the asset in the Mux dashboard.\",\n );\n }\n }\n\n throw new Error(\n \"Timed out waiting for the static rendition to become ready. Please try again in a moment.\",\n );\n}\n\nasync function fetchAudioFromMux(audioUrl: string): Promise<ArrayBuffer> {\n \"use step\";\n\n const audioResponse = await fetch(audioUrl);\n if (!audioResponse.ok) {\n throw new Error(`Failed to fetch audio file: ${audioResponse.statusText}`);\n }\n\n return audioResponse.arrayBuffer();\n}\n\nasync function createElevenLabsDubbingJob({\n audioBuffer,\n assetId,\n elevenLabsLangCode,\n numSpeakers,\n credentials,\n}: {\n audioBuffer: ArrayBuffer;\n assetId: string;\n elevenLabsLangCode: string;\n numSpeakers: number;\n credentials?: WorkflowCredentialsInput;\n}): Promise<string> {\n \"use step\";\n const elevenLabsApiKey = await getApiKeyFromEnv(\"elevenlabs\", credentials);\n\n const audioBlob = new Blob([audioBuffer], { type: \"audio/mp4\" });\n\n const formData = new FormData();\n formData.append(\"file\", audioBlob);\n formData.append(\"target_lang\", elevenLabsLangCode);\n formData.append(\"num_speakers\", numSpeakers.toString());\n formData.append(\"name\", `Mux Asset ${assetId} - auto to ${elevenLabsLangCode}`);\n\n const dubbingResponse = await fetch(\"https://api.elevenlabs.io/v1/dubbing\", {\n method: \"POST\",\n headers: {\n \"xi-api-key\": elevenLabsApiKey,\n },\n body: formData,\n });\n\n if (!dubbingResponse.ok) {\n throw new Error(`ElevenLabs API error: ${dubbingResponse.statusText}`);\n }\n\n const dubbingData = await dubbingResponse.json() as any;\n return dubbingData.dubbing_id;\n}\n\nasync function checkElevenLabsDubbingStatus({\n dubbingId,\n credentials,\n}: {\n dubbingId: string;\n credentials?: WorkflowCredentialsInput;\n}): Promise<{ status: string; targetLanguages: string[] }> {\n \"use step\";\n const elevenLabsApiKey = await getApiKeyFromEnv(\"elevenlabs\", credentials);\n\n const statusResponse = await fetch(`https://api.elevenlabs.io/v1/dubbing/${dubbingId}`, {\n headers: {\n \"xi-api-key\": elevenLabsApiKey,\n },\n });\n\n if (!statusResponse.ok) {\n throw new Error(`Status check failed: ${statusResponse.statusText}`);\n }\n\n const statusData = await statusResponse.json() as any;\n return {\n status: statusData.status,\n targetLanguages: statusData.target_languages ?? [],\n };\n}\n\nasync function downloadDubbedAudioFromElevenLabs({\n dubbingId,\n languageCode,\n credentials,\n}: {\n dubbingId: string;\n languageCode: string;\n credentials?: WorkflowCredentialsInput;\n}): Promise<ArrayBuffer> {\n \"use step\";\n const elevenLabsApiKey = await getApiKeyFromEnv(\"elevenlabs\", credentials);\n\n const audioUrl = `https://api.elevenlabs.io/v1/dubbing/${dubbingId}/audio/${languageCode}`;\n const audioResponse = await fetch(audioUrl, {\n headers: {\n \"xi-api-key\": elevenLabsApiKey,\n },\n });\n\n if (!audioResponse.ok) {\n throw new Error(`Failed to fetch dubbed audio: ${audioResponse.statusText}`);\n }\n\n return audioResponse.arrayBuffer();\n}\n\nasync function uploadDubbedAudioToS3({\n dubbedAudioBuffer,\n assetId,\n toLanguageCode,\n s3Endpoint,\n s3Region,\n s3Bucket,\n storageAdapter,\n}: {\n dubbedAudioBuffer: ArrayBuffer;\n assetId: string;\n toLanguageCode: string;\n s3Endpoint: string;\n s3Region: string;\n s3Bucket: string;\n storageAdapter?: StorageAdapter;\n}): Promise<string> {\n \"use step\";\n\n const s3AccessKeyId = env.S3_ACCESS_KEY_ID;\n const s3SecretAccessKey = env.S3_SECRET_ACCESS_KEY;\n\n // Create unique key for the audio file\n const audioKey = `audio-translations/${assetId}/auto-to-${toLanguageCode}-${Date.now()}.m4a`;\n\n await putObjectWithStorageAdapter({\n accessKeyId: s3AccessKeyId,\n secretAccessKey: s3SecretAccessKey,\n endpoint: s3Endpoint,\n region: s3Region,\n bucket: s3Bucket,\n key: audioKey,\n body: new Uint8Array(dubbedAudioBuffer),\n contentType: \"audio/mp4\",\n }, storageAdapter);\n\n const presignedUrl = await createPresignedGetUrlWithStorageAdapter({\n accessKeyId: s3AccessKeyId,\n secretAccessKey: s3SecretAccessKey,\n endpoint: s3Endpoint,\n region: s3Region,\n bucket: s3Bucket,\n key: audioKey,\n expiresInSeconds: 3600,\n }, storageAdapter);\n\n console.warn(`✅ Audio uploaded successfully to: ${audioKey}`);\n console.warn(`🔗 Generated presigned URL (expires in 1 hour)`);\n\n return presignedUrl;\n}\n\nasync function createAudioTrackOnMux(\n assetId: string,\n languageCode: string,\n presignedUrl: string,\n credentials?: WorkflowCredentialsInput,\n): Promise<string> {\n \"use step\";\n const muxClient = await resolveMuxClient(credentials);\n const mux = await muxClient.createClient();\n const languageName = new Intl.DisplayNames([\"en\"], { type: \"language\" }).of(languageCode) || languageCode.toUpperCase();\n const trackName = `${languageName} (auto-dubbed)`;\n\n const trackResponse = await mux.video.assets.createTrack(assetId, {\n type: \"audio\",\n language_code: languageCode,\n name: trackName,\n url: presignedUrl,\n });\n\n if (!trackResponse.id) {\n throw new Error(\"Failed to create audio track: no track ID returned from Mux\");\n }\n\n return trackResponse.id;\n}\n\nexport async function translateAudio(\n assetId: string,\n toLanguageCode: string,\n options: AudioTranslationOptions = {},\n): Promise<AudioTranslationResult> {\n \"use workflow\";\n // Uses the default audio track on your asset, language is auto-detected by ElevenLabs\n const {\n provider = \"elevenlabs\",\n numSpeakers = 0, // 0 = auto-detect\n uploadToMux = true,\n storageAdapter,\n credentials: providedCredentials,\n } = options;\n\n if (provider !== \"elevenlabs\") {\n throw new Error(\"Only ElevenLabs provider is currently supported for audio translation\");\n }\n\n const credentials = providedCredentials;\n const effectiveStorageAdapter = storageAdapter;\n\n // S3 configuration\n const s3Endpoint = options.s3Endpoint ?? env.S3_ENDPOINT;\n const s3Region = options.s3Region ?? env.S3_REGION ?? \"auto\";\n const s3Bucket = options.s3Bucket ?? env.S3_BUCKET;\n const s3AccessKeyId = env.S3_ACCESS_KEY_ID;\n const s3SecretAccessKey = env.S3_SECRET_ACCESS_KEY;\n\n if (uploadToMux && (!s3Endpoint || !s3Bucket || (!effectiveStorageAdapter && (!s3AccessKeyId || !s3SecretAccessKey)))) {\n throw new Error(\"Storage configuration is required for uploading to Mux. Provide s3Endpoint and s3Bucket. If no storageAdapter is supplied, also provide s3AccessKeyId and s3SecretAccessKey in options or set S3_ENDPOINT, S3_BUCKET, S3_ACCESS_KEY_ID, and S3_SECRET_ACCESS_KEY environment variables.\");\n }\n\n // Fetch asset data and playback ID from Mux\n const { asset: initialAsset, playbackId, policy } = await getPlaybackIdForAsset(assetId, credentials);\n const assetDurationSeconds = getAssetDurationSecondsFromAsset(initialAsset);\n\n // Check for audio-only static rendition\n\n let currentAsset = initialAsset;\n if (!hasReadyAudioStaticRendition(currentAsset)) {\n console.warn(\"❌ No ready audio static rendition found. Requesting one now...\");\n currentAsset = await waitForAudioStaticRendition({\n assetId,\n initialAsset: currentAsset,\n credentials,\n });\n }\n\n const audioRendition = getReadyAudioStaticRendition(currentAsset);\n\n if (!audioRendition) {\n throw new Error(\n \"Unable to obtain an audio-only static rendition for this asset. Please verify static renditions are enabled in Mux.\",\n );\n }\n\n // Build audio URL (signed if needed)\n let audioUrl = `https://stream.mux.com/${playbackId}/audio.m4a`;\n if (policy === \"signed\") {\n audioUrl = await signUrl(audioUrl, playbackId, undefined, \"video\", undefined, credentials);\n }\n\n // Fetch audio from Mux\n console.warn(\"🎙️ Fetching audio from Mux...\");\n\n let audioBuffer: ArrayBuffer;\n try {\n audioBuffer = await fetchAudioFromMux(audioUrl);\n } catch (error) {\n throw new Error(`Failed to fetch audio from Mux: ${error instanceof Error ? error.message : \"Unknown error\"}`);\n }\n\n // Create dubbing job in ElevenLabs\n console.warn(\"🎙️ Creating dubbing job in ElevenLabs...\");\n\n // ElevenLabs uses ISO 639-3 (3-letter) codes, so normalize the input\n const elevenLabsLangCode = toISO639_3(toLanguageCode);\n console.warn(`🔍 Creating dubbing job for asset ${assetId} with language code: ${elevenLabsLangCode}`);\n\n let dubbingId: string;\n try {\n dubbingId = await createElevenLabsDubbingJob({\n audioBuffer,\n assetId,\n elevenLabsLangCode,\n numSpeakers,\n credentials,\n });\n console.warn(`✅ Dubbing job created with ID: ${dubbingId}`);\n } catch (error) {\n throw new Error(`Failed to create ElevenLabs dubbing job: ${error instanceof Error ? error.message : \"Unknown error\"}`);\n }\n\n // Poll for completion\n console.warn(\"⏳ Waiting for dubbing to complete...\");\n\n let dubbingStatus: string = \"dubbing\";\n let pollAttempts = 0;\n const maxPollAttempts = 180; // 30 minutes at 10s intervals\n let targetLanguages: string[] = [];\n\n while (dubbingStatus === \"dubbing\" && pollAttempts < maxPollAttempts) {\n await sleep(10000); // Wait 10 seconds\n pollAttempts++;\n\n try {\n const statusResult = await checkElevenLabsDubbingStatus({\n dubbingId,\n credentials,\n });\n dubbingStatus = statusResult.status;\n targetLanguages = statusResult.targetLanguages;\n\n if (dubbingStatus === \"failed\") {\n throw new Error(\"ElevenLabs dubbing job failed\");\n }\n } catch (error) {\n throw new Error(`Failed to check dubbing status: ${error instanceof Error ? error.message : \"Unknown error\"}`);\n }\n }\n\n if (dubbingStatus !== \"dubbed\") {\n throw new Error(`Dubbing job timed out or failed. Final status: ${dubbingStatus}`);\n }\n\n console.warn(\"✅ Dubbing completed successfully!\");\n\n // If uploadToMux is false, just return the dubbing info\n // Return ISO 639-1 (2-letter) code for consistency with Mux/player expectations\n if (!uploadToMux) {\n const targetLanguage = getLanguageCodePair(toLanguageCode);\n return {\n assetId,\n targetLanguageCode: targetLanguage.iso639_1 as SupportedISO639_1,\n targetLanguage,\n dubbingId,\n };\n }\n\n // Download dubbed audio from ElevenLabs\n console.warn(\"📥 Downloading dubbed audio from ElevenLabs...\");\n\n let dubbedAudioBuffer: ArrayBuffer;\n\n try {\n // Use the language code from the ElevenLabs status response\n // ElevenLabs returns target_languages array with the exact codes available for download\n const requestedLangCode = toISO639_3(toLanguageCode);\n\n // Find the matching language code from ElevenLabs response\n // First try exact match, then try case-insensitive match\n let downloadLangCode = targetLanguages.find(\n lang => lang === requestedLangCode,\n ) ?? targetLanguages.find(\n lang => lang.toLowerCase() === requestedLangCode.toLowerCase(),\n );\n\n // Fallback to first available target language if no match found\n if (!downloadLangCode && targetLanguages.length > 0) {\n downloadLangCode = targetLanguages[0];\n console.warn(`⚠️ Requested language \"${requestedLangCode}\" not found in target_languages. Using \"${downloadLangCode}\" instead.`);\n }\n\n // If still no language code, fall back to the original behavior\n if (!downloadLangCode) {\n downloadLangCode = requestedLangCode;\n console.warn(`⚠️ No target_languages available from ElevenLabs status. Using requested language code: ${requestedLangCode}`);\n }\n\n dubbedAudioBuffer = await downloadDubbedAudioFromElevenLabs({\n dubbingId,\n languageCode: downloadLangCode,\n credentials,\n });\n console.warn(\"✅ Dubbed audio downloaded successfully!\");\n } catch (error) {\n throw new Error(`Failed to download dubbed audio: ${error instanceof Error ? error.message : \"Unknown error\"}`);\n }\n\n // Upload to S3-compatible storage\n console.warn(\"📤 Uploading dubbed audio to S3-compatible storage...\");\n\n let presignedUrl: string;\n\n try {\n presignedUrl = await uploadDubbedAudioToS3({\n dubbedAudioBuffer,\n assetId,\n toLanguageCode,\n s3Endpoint: s3Endpoint!,\n s3Region,\n s3Bucket: s3Bucket!,\n storageAdapter: effectiveStorageAdapter,\n });\n } catch (error) {\n throw new Error(`Failed to upload audio to S3: ${error instanceof Error ? error.message : \"Unknown error\"}`);\n }\n\n // Add translated audio track to Mux asset\n console.warn(\"📹 Adding dubbed audio track to Mux asset...\");\n\n let uploadedTrackId: string | undefined;\n // Mux uses ISO 639-1 (2-letter) codes for track language_code\n const muxLangCode = toISO639_1(toLanguageCode);\n\n try {\n uploadedTrackId = await createAudioTrackOnMux(assetId, muxLangCode, presignedUrl, credentials);\n const languageName = new Intl.DisplayNames([\"en\"], { type: \"language\" }).of(muxLangCode) || muxLangCode.toUpperCase();\n const trackName = `${languageName} (auto-dubbed)`;\n console.warn(`✅ Track added to Mux asset with ID: ${uploadedTrackId}`);\n console.warn(`📋 Track name: \"${trackName}\"`);\n } catch (error) {\n console.warn(`⚠️ Failed to add audio track to Mux asset: ${error instanceof Error ? error.message : \"Unknown error\"}`);\n console.warn(\"🔗 You can manually add the track using this presigned URL:\");\n console.warn(presignedUrl);\n }\n\n const targetLanguage = getLanguageCodePair(toLanguageCode);\n return {\n assetId,\n targetLanguageCode: targetLanguage.iso639_1 as SupportedISO639_1,\n targetLanguage,\n dubbingId,\n uploadedTrackId,\n presignedUrl,\n usage: {\n metadata: {\n assetDurationSeconds,\n },\n },\n };\n}\n","import { generateText, Output } from \"ai\";\nimport { z } from \"zod\";\n\nimport env from \"@mux/ai/env\";\nimport { getLanguageCodePair, getLanguageName } from \"@mux/ai/lib/language-codes\";\nimport type { LanguageCodePair, SupportedISO639_1 } from \"@mux/ai/lib/language-codes\";\nimport {\n getAssetDurationSecondsFromAsset,\n getPlaybackIdForAsset,\n isAudioOnlyAsset,\n} from \"@mux/ai/lib/mux-assets\";\nimport { createLanguageModelFromConfig, resolveLanguageModelConfig } from \"@mux/ai/lib/providers\";\nimport type { ModelIdByProvider, SupportedProvider } from \"@mux/ai/lib/providers\";\nimport {\n createPresignedGetUrlWithStorageAdapter,\n putObjectWithStorageAdapter,\n} from \"@mux/ai/lib/storage-adapter\";\nimport {\n resolveMuxClient,\n resolveMuxSigningContext,\n} from \"@mux/ai/lib/workflow-credentials\";\nimport { buildTranscriptUrl, getReadyTextTracks } from \"@mux/ai/primitives/transcripts\";\nimport type {\n MuxAIOptions,\n StorageAdapter,\n TokenUsage,\n WorkflowCredentialsInput,\n} from \"@mux/ai/types\";\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Types\n// ─────────────────────────────────────────────────────────────────────────────\n\n/** Output returned from `translateCaptions`. */\nexport interface TranslationResult {\n assetId: string;\n /** Source language code (ISO 639-1 two-letter format). */\n sourceLanguageCode: SupportedISO639_1;\n /** Target language code (ISO 639-1 two-letter format). */\n targetLanguageCode: SupportedISO639_1;\n /**\n * Source language codes in both ISO 639-1 (2-letter) and ISO 639-3 (3-letter) formats.\n * Use `iso639_1` for browser players (BCP-47 compliant) and `iso639_3` for APIs that require it.\n */\n sourceLanguage: LanguageCodePair;\n /**\n * Target language codes in both ISO 639-1 (2-letter) and ISO 639-3 (3-letter) formats.\n * Use `iso639_1` for browser players (BCP-47 compliant) and `iso639_3` for APIs that require it.\n */\n targetLanguage: LanguageCodePair;\n originalVtt: string;\n translatedVtt: string;\n uploadedTrackId?: string;\n presignedUrl?: string;\n /** Token usage from the AI provider (for efficiency/cost analysis). */\n usage?: TokenUsage;\n}\n\n/** Configuration accepted by `translateCaptions`. */\nexport interface TranslationOptions<P extends SupportedProvider = SupportedProvider> extends MuxAIOptions {\n /** Provider responsible for the translation. */\n provider: P;\n /** Provider-specific chat model identifier. */\n model?: ModelIdByProvider[P];\n /** Optional override for the S3-compatible endpoint used for uploads. */\n s3Endpoint?: string;\n /** S3 region (defaults to env.S3_REGION or 'auto'). */\n s3Region?: string;\n /** Bucket that will store translated VTT files. */\n s3Bucket?: string;\n /**\n * When true (default) the translated VTT is uploaded to the configured\n * bucket and attached to the Mux asset.\n */\n uploadToMux?: boolean;\n /** Optional storage adapter override for upload + presign operations. */\n storageAdapter?: StorageAdapter;\n}\n\n/** Schema used when requesting caption translation from a language model. */\nexport const translationSchema = z.object({\n translation: z.string(),\n});\n\n/** Inferred shape returned by `translationSchema`. */\nexport type TranslationPayload = z.infer<typeof translationSchema>;\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Implementation\n// ─────────────────────────────────────────────────────────────────────────────\n\nasync function fetchVttFromMux(vttUrl: string): Promise<string> {\n \"use step\";\n\n const vttResponse = await fetch(vttUrl);\n if (!vttResponse.ok) {\n throw new Error(`Failed to fetch VTT file: ${vttResponse.statusText}`);\n }\n\n return vttResponse.text();\n}\n\nasync function translateVttWithAI({\n vttContent,\n fromLanguageCode,\n toLanguageCode,\n provider,\n modelId,\n credentials,\n}: {\n vttContent: string;\n fromLanguageCode: string;\n toLanguageCode: string;\n provider: SupportedProvider;\n modelId: string;\n credentials?: WorkflowCredentialsInput;\n}): Promise<{ translatedVtt: string; usage: TokenUsage }> {\n \"use step\";\n\n const model = await createLanguageModelFromConfig(provider, modelId, credentials);\n\n const response = await generateText({\n model,\n output: Output.object({ schema: translationSchema }),\n messages: [\n {\n role: \"user\",\n content: `Translate the following VTT subtitle file from ${fromLanguageCode} to ${toLanguageCode}. Preserve all timestamps and VTT formatting exactly as they appear. Return JSON with a single key \"translation\" containing the translated VTT.\\n\\n${vttContent}`,\n },\n ],\n });\n\n return {\n translatedVtt: response.output.translation,\n usage: {\n inputTokens: response.usage.inputTokens,\n outputTokens: response.usage.outputTokens,\n totalTokens: response.usage.totalTokens,\n reasoningTokens: response.usage.reasoningTokens,\n cachedInputTokens: response.usage.cachedInputTokens,\n },\n };\n}\n\nasync function uploadVttToS3({\n translatedVtt,\n assetId,\n fromLanguageCode,\n toLanguageCode,\n s3Endpoint,\n s3Region,\n s3Bucket,\n storageAdapter,\n}: {\n translatedVtt: string;\n assetId: string;\n fromLanguageCode: string;\n toLanguageCode: string;\n s3Endpoint: string;\n s3Region: string;\n s3Bucket: string;\n storageAdapter?: StorageAdapter;\n}): Promise<string> {\n \"use step\";\n\n const s3AccessKeyId = env.S3_ACCESS_KEY_ID;\n const s3SecretAccessKey = env.S3_SECRET_ACCESS_KEY;\n\n // Create unique key for the VTT file\n const vttKey = `translations/${assetId}/${fromLanguageCode}-to-${toLanguageCode}-${Date.now()}.vtt`;\n\n await putObjectWithStorageAdapter({\n accessKeyId: s3AccessKeyId,\n secretAccessKey: s3SecretAccessKey,\n endpoint: s3Endpoint,\n region: s3Region,\n bucket: s3Bucket,\n key: vttKey,\n body: translatedVtt,\n contentType: \"text/vtt\",\n }, storageAdapter);\n\n return createPresignedGetUrlWithStorageAdapter({\n accessKeyId: s3AccessKeyId,\n secretAccessKey: s3SecretAccessKey,\n endpoint: s3Endpoint,\n region: s3Region,\n bucket: s3Bucket,\n key: vttKey,\n expiresInSeconds: 3600,\n }, storageAdapter);\n}\n\nasync function createTextTrackOnMux(\n assetId: string,\n languageCode: string,\n trackName: string,\n presignedUrl: string,\n credentials?: WorkflowCredentialsInput,\n): Promise<string> {\n \"use step\";\n const muxClient = await resolveMuxClient(credentials);\n const mux = await muxClient.createClient();\n const trackResponse = await mux.video.assets.createTrack(assetId, {\n type: \"text\",\n text_type: \"subtitles\",\n language_code: languageCode,\n name: trackName,\n url: presignedUrl,\n });\n\n if (!trackResponse.id) {\n throw new Error(\"Failed to create text track: no track ID returned from Mux\");\n }\n\n return trackResponse.id;\n}\n\nexport async function translateCaptions<P extends SupportedProvider = SupportedProvider>(\n assetId: string,\n fromLanguageCode: string,\n toLanguageCode: string,\n options: TranslationOptions<P>,\n): Promise<TranslationResult> {\n \"use workflow\";\n const {\n provider = \"openai\",\n model,\n s3Endpoint: providedS3Endpoint,\n s3Region: providedS3Region,\n s3Bucket: providedS3Bucket,\n uploadToMux: uploadToMuxOption,\n storageAdapter,\n credentials: providedCredentials,\n } = options;\n const credentials = providedCredentials;\n const effectiveStorageAdapter = storageAdapter;\n\n // S3 configuration\n const s3Endpoint = providedS3Endpoint ?? env.S3_ENDPOINT;\n const s3Region = providedS3Region ?? env.S3_REGION ?? \"auto\";\n const s3Bucket = providedS3Bucket ?? env.S3_BUCKET;\n const s3AccessKeyId = env.S3_ACCESS_KEY_ID;\n const s3SecretAccessKey = env.S3_SECRET_ACCESS_KEY;\n const uploadToMux = uploadToMuxOption !== false; // Default to true\n\n const modelConfig = resolveLanguageModelConfig({\n ...options,\n model,\n provider: provider as SupportedProvider,\n });\n\n if (uploadToMux && (!s3Endpoint || !s3Bucket || (!effectiveStorageAdapter && (!s3AccessKeyId || !s3SecretAccessKey)))) {\n throw new Error(\"Storage configuration is required for uploading to Mux. Provide s3Endpoint and s3Bucket. If no storageAdapter is supplied, also provide s3AccessKeyId and s3SecretAccessKey in options or set S3_ENDPOINT, S3_BUCKET, S3_ACCESS_KEY_ID, and S3_SECRET_ACCESS_KEY environment variables.\");\n }\n\n // Fetch asset data and playback ID from Mux\n const { asset: assetData, playbackId, policy } = await getPlaybackIdForAsset(assetId, credentials);\n const assetDurationSeconds = getAssetDurationSecondsFromAsset(assetData);\n const isAudioOnly = isAudioOnlyAsset(assetData);\n\n // Resolve signing context for signed playback IDs\n const signingContext = await resolveMuxSigningContext(credentials);\n if (policy === \"signed\" && !signingContext) {\n throw new Error(\n \"Signed playback ID requires signing credentials. \" +\n \"Set MUX_SIGNING_KEY and MUX_PRIVATE_KEY environment variables.\",\n );\n }\n\n // Find text track with the source language\n const readyTextTracks = getReadyTextTracks(assetData);\n if (!readyTextTracks.length) {\n throw new Error(\"No ready text tracks found for this asset\");\n }\n\n let sourceTextTrack = readyTextTracks.find(track =>\n track.text_type === \"subtitles\" &&\n track.language_code === fromLanguageCode,\n );\n\n if (!sourceTextTrack && isAudioOnly && readyTextTracks.length === 1) {\n sourceTextTrack = readyTextTracks[0];\n }\n\n if (!sourceTextTrack) {\n const availableLanguages = readyTextTracks\n .map(t => t.language_code)\n .filter(Boolean)\n .join(\", \");\n if (isAudioOnly) {\n throw new Error(\n `No transcript track found with language code '${fromLanguageCode}' for this asset. ` +\n `Audio-only assets require a transcript. Available languages: ${availableLanguages || \"none\"}`,\n );\n }\n throw new Error(\n `No ready text track found with language code '${fromLanguageCode}' for this asset. ` +\n `Available languages: ${availableLanguages || \"none\"}`,\n );\n }\n\n if (!sourceTextTrack.id) {\n throw new Error(\"Transcript track is missing an id\");\n }\n\n // Fetch the VTT file content (signed if needed)\n const vttUrl = await buildTranscriptUrl(playbackId, sourceTextTrack.id, policy === \"signed\", credentials);\n\n let vttContent: string;\n try {\n vttContent = await fetchVttFromMux(vttUrl);\n } catch (error) {\n throw new Error(`Failed to fetch VTT content: ${error instanceof Error ? error.message : \"Unknown error\"}`);\n }\n\n // Translate VTT content using configured provider via ai-sdk\n let translatedVtt: string;\n let usage: TokenUsage | undefined;\n\n try {\n const result = await translateVttWithAI({\n vttContent,\n fromLanguageCode,\n toLanguageCode,\n provider: modelConfig.provider,\n modelId: modelConfig.modelId,\n credentials,\n });\n translatedVtt = result.translatedVtt;\n usage = result.usage;\n } catch (error) {\n throw new Error(`Failed to translate VTT with ${modelConfig.provider}: ${error instanceof Error ? error.message : \"Unknown error\"}`);\n }\n\n const usageWithMetadata = usage ?\n {\n ...usage,\n metadata: {\n assetDurationSeconds,\n },\n } :\n undefined;\n\n // Resolve language code pairs for both source and target\n const sourceLanguage = getLanguageCodePair(fromLanguageCode);\n const targetLanguage = getLanguageCodePair(toLanguageCode);\n\n // If uploadToMux is false, just return the translation\n if (!uploadToMux) {\n return {\n assetId,\n sourceLanguageCode: fromLanguageCode as SupportedISO639_1,\n targetLanguageCode: toLanguageCode as SupportedISO639_1,\n sourceLanguage,\n targetLanguage,\n originalVtt: vttContent,\n translatedVtt,\n usage: usageWithMetadata,\n };\n }\n\n // Upload translated VTT to S3-compatible storage\n let presignedUrl: string;\n\n try {\n presignedUrl = await uploadVttToS3({\n translatedVtt,\n assetId,\n fromLanguageCode,\n toLanguageCode,\n s3Endpoint: s3Endpoint!,\n s3Region,\n s3Bucket: s3Bucket!,\n storageAdapter: effectiveStorageAdapter,\n });\n } catch (error) {\n throw new Error(`Failed to upload VTT to S3: ${error instanceof Error ? error.message : \"Unknown error\"}`);\n }\n\n // Add translated track to Mux asset\n let uploadedTrackId: string | undefined;\n\n try {\n const languageName = getLanguageName(toLanguageCode);\n const trackName = `${languageName} (auto-translated)`;\n\n uploadedTrackId = await createTextTrackOnMux(\n assetId,\n toLanguageCode,\n trackName,\n presignedUrl,\n credentials,\n );\n } catch (error) {\n console.warn(`Failed to add track to Mux asset: ${error instanceof Error ? error.message : \"Unknown error\"}`);\n }\n\n return {\n assetId,\n sourceLanguageCode: fromLanguageCode as SupportedISO639_1,\n targetLanguageCode: toLanguageCode as SupportedISO639_1,\n sourceLanguage,\n targetLanguage,\n originalVtt: vttContent,\n translatedVtt,\n uploadedTrackId,\n presignedUrl,\n usage: usageWithMetadata,\n };\n}\n"],"mappings":"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","names":["env","bytes","chunkText","z","BASE64_CHUNK_SIZE","bytesToBase64","z","generateText","Output","dedent","z","z","SYSTEM_PROMPT","dedent","buildUserPrompt","fetchImageAsBase64","generateText","Output","generateText","Output","dedent","z","z","generateText","Output","dedent","buildUserPrompt","DEFAULT_PROVIDER","generateText","Output","dedent","z","z","Output","dedent","SYSTEM_PROMPT","buildUserPrompt","analyzeStoryboard","generateText","message","targetLanguage","generateText","Output","z","z","generateText","Output"]}
1
+ {"version":3,"sources":["../package.json","../src/env.ts","../src/lib/workflow-crypto.ts","../src/lib/workflow-credentials.ts","../node_modules/@workflow/serde/src/index.ts","../src/lib/s3-sigv4.ts","../src/lib/workflow-serialization.ts","../src/lib/workflow-storage-client.ts","../src/primitives/index.ts","../src/lib/providers.ts","../src/lib/client-factory.ts","../src/primitives/heatmap.ts","../src/primitives/hotspots.ts","../src/lib/url-signing.ts","../src/primitives/storyboards.ts","../src/primitives/text-chunking.ts","../src/primitives/thumbnails.ts","../src/primitives/transcripts.ts","../src/workflows/index.ts","../src/workflows/ask-questions.ts","../src/lib/image-download.ts","../src/lib/mux-assets.ts","../src/lib/prompt-builder.ts","../src/lib/retry.ts","../src/workflows/burned-in-captions.ts","../src/workflows/chapters.ts","../src/workflows/embeddings.ts","../src/lib/sampling-plan.ts","../src/workflows/moderation.ts","../src/workflows/summarization.ts","../src/lib/language-codes.ts","../src/lib/storage-adapter.ts","../src/workflows/translate-audio.ts","../src/workflows/translate-captions.ts"],"sourcesContent":["{\n \"name\": \"@mux/ai\",\n \"type\": \"module\",\n \"version\": \"0.7.3\",\n \"description\": \"AI library for Mux\",\n \"author\": \"Mux\",\n \"license\": \"Apache-2.0\",\n \"homepage\": \"https://github.com/muxinc/ai#readme\",\n \"repository\": {\n \"type\": \"git\",\n \"url\": \"git+https://github.com/muxinc/ai.git\"\n },\n \"bugs\": {\n \"url\": \"https://github.com/muxinc/ai/issues\"\n },\n \"publishConfig\": {\n \"access\": \"public\",\n \"registry\": \"https://registry.npmjs.org/\"\n },\n \"keywords\": [\n \"mux\",\n \"video\",\n \"ai\",\n \"llm\",\n \"openai\",\n \"anthropic\",\n \"google\",\n \"gemini\",\n \"multimodal\",\n \"video-analysis\",\n \"summarization\",\n \"moderation\",\n \"captions\",\n \"translation\",\n \"dubbing\",\n \"chapters\",\n \"embeddings\",\n \"typescript\"\n ],\n \"exports\": {\n \".\": {\n \"types\": {\n \"import\": \"./dist/index.d.ts\"\n },\n \"import\": \"./dist/index.js\"\n },\n \"./primitives\": {\n \"types\": {\n \"import\": \"./dist/primitives/index.d.ts\"\n },\n \"import\": \"./dist/primitives/index.js\"\n },\n \"./workflows\": {\n \"types\": {\n \"import\": \"./dist/workflows/index.d.ts\"\n },\n \"import\": \"./dist/workflows/index.js\"\n }\n },\n \"main\": \"./dist/index.js\",\n \"module\": \"./dist/index.js\",\n \"types\": \"./dist/index.d.ts\",\n \"files\": [\n \"dist\"\n ],\n \"engines\": {\n \"node\": \">=21.0.0\"\n },\n \"scripts\": {\n \"build\": \"tsup\",\n \"dev\": \"tsup --watch\",\n \"test\": \"DOTENV_CONFIG_PATH=.env.test vitest run\",\n \"test:watch\": \"DOTENV_CONFIG_PATH=.env.test vitest\",\n \"test:ui\": \"DOTENV_CONFIG_PATH=.env.test vitest --ui\",\n \"test:integration\": \"DOTENV_CONFIG_PATH=.env.test vitest run tests/integration\",\n \"test:integration-workflowdevkit\": \"DOTENV_CONFIG_PATH=.env.test vitest run --config vitest.workflowdevkit.config.ts\",\n \"test:unit\": \"DOTENV_CONFIG_PATH=.env.test vitest run tests/unit\",\n \"test:eval\": \"DOTENV_CONFIG_PATH=.env.test evalite serve tests/eval\",\n \"lint\": \"eslint .\",\n \"lint:fix\": \"eslint . --fix\",\n \"typecheck\": \"tsc --noEmit\",\n \"example:ask-questions\": \"npx tsx examples/ask-questions/basic-example.ts\",\n \"example:ask-questions:multiple\": \"npx tsx examples/ask-questions/multiple-questions-example.ts\",\n \"example:chapters\": \"npx tsx examples/chapters/basic-example.ts\",\n \"example:chapters:compare\": \"npx tsx examples/chapters/provider-comparison.ts\",\n \"example:burned-in\": \"npx tsx examples/burned-in-captions/basic-example.ts\",\n \"example:burned-in:compare\": \"npx tsx examples/burned-in-captions/provider-comparison.ts\",\n \"example:summarization\": \"npx tsx examples/summarization/basic-example.ts\",\n \"example:summarization:audio-only\": \"npx tsx examples/summarization/audio-only-example.ts\",\n \"example:summarization:compare\": \"npx tsx examples/summarization/provider-comparison.ts\",\n \"example:signed-playback\": \"npx tsx examples/signed-playback/signed-playback.ts\",\n \"example:signed-playback:summarization\": \"npx tsx examples/signed-playback/signed-summarization.ts\",\n \"example:moderation\": \"npx tsx examples/moderation/basic-example.ts\",\n \"example:moderation:compare\": \"npx tsx examples/moderation/provider-comparison.ts\",\n \"example:embeddings\": \"npx tsx examples/embeddings/basic-example.ts\",\n \"example:translate-captions\": \"npx tsx examples/translate-captions/basic-example.ts\",\n \"example:translate-captions:aws-sdk\": \"npx tsx examples/translate-captions/aws-sdk-storage-adapter-example.ts\",\n \"example:translate-audio\": \"npx tsx examples/translate-audio/basic-example.ts\",\n \"evalite:post-results:dev\": \"npx tsx scripts/export-evalite-results.ts && npx tsx scripts/post-evalite-results.ts -d -k\",\n \"evalite:post-results:production\": \"npx tsx scripts/export-evalite-results.ts && npx tsx scripts/post-evalite-results.ts\",\n \"prepare\": \"husky\"\n },\n \"peerDependencies\": {\n \"workflow\": \">=4.1.0-beta.55\"\n },\n \"peerDependenciesMeta\": {\n \"workflow\": {\n \"optional\": true\n }\n },\n \"dependencies\": {\n \"@ai-sdk/anthropic\": \"^3.0.16\",\n \"@ai-sdk/google\": \"^3.0.10\",\n \"@ai-sdk/openai\": \"^3.0.12\",\n \"@mux/mux-node\": \"^12.5.0\",\n \"@noble/ciphers\": \"^2.1.1\",\n \"ai\": \"^6.0.42\",\n \"dedent\": \"^1.7.0\",\n \"dotenv\": \"^17.2.2\",\n \"dotenv-expand\": \"^12.0.3\",\n \"p-retry\": \"^7.0.0\",\n \"zod\": \"^3.25.76\"\n },\n \"devDependencies\": {\n \"@antfu/eslint-config\": \"^6.2.0\",\n \"@types/node\": \"^20.0.0\",\n \"@vitest/ui\": \"^4.0.13\",\n \"commander\": \"^14.0.2\",\n \"eslint\": \"^9.39.1\",\n \"eslint-plugin-format\": \"^1.0.2\",\n \"evalite\": \"1.0.0-beta.2\",\n \"husky\": \"^9.1.7\",\n \"nitro\": \"^3.0.1-alpha.1\",\n \"ts-node\": \"^10.9.2\",\n \"tsc-alias\": \"^1.8.16\",\n \"tsconfig-paths\": \"^4.2.0\",\n \"tsup\": \"^8.5.0\",\n \"typescript\": \"^5.0.0\",\n \"vitest\": \"^4.0.13\",\n \"workflow\": \"^4.1.0-beta.55\"\n }\n}\n","/* eslint-disable node/no-process-env */\n\nimport { z } from \"zod\";\n\nimport \"dotenv/config\";\n\nfunction optionalString(description: string, message?: string) {\n return z.preprocess(\n value => typeof value === \"string\" && value.trim().length === 0 ? undefined : value,\n z.string().trim().min(1, message).optional(),\n ).describe(description);\n}\n\n// eslint-disable-next-line unused-imports/no-unused-vars\nfunction requiredString(description: string, message?: string) {\n return z.preprocess(\n value => typeof value === \"string\" ? value.trim().length > 0 ? value.trim() : undefined : value,\n z.string().trim().min(1, message),\n ).describe(description);\n}\n\nconst EnvSchema = z.object({\n NODE_ENV: z.string().default(\"development\").describe(\"Runtime environment.\"),\n\n MUX_TOKEN_ID: optionalString(\"Mux access token ID.\", \"Required to access Mux APIs\"),\n MUX_TOKEN_SECRET: optionalString(\"Mux access token secret.\", \"Required to access Mux APIs\"),\n MUX_AI_WORKFLOW_SECRET_KEY: optionalString(\n \"Base64-encoded 32-byte key for workflow encryption/decryption.\",\n \"Workflow secret key\",\n ),\n EVALITE_INGEST_SECRET: optionalString(\n \"Shared secret for posting Evalite results.\",\n \"Evalite ingest secret\",\n ),\n\n MUX_SIGNING_KEY: optionalString(\"Mux signing key ID for signed playback URLs.\", \"Used to sign playback URLs\"),\n MUX_PRIVATE_KEY: optionalString(\"Mux signing private key for signed playback URLs.\", \"Used to sign playback URLs\"),\n\n // Test-only helpers (used by this repo's integration tests)\n MUX_TEST_ASSET_ID: optionalString(\"Mux asset ID used by integration tests.\", \"Mux test asset id\"),\n MUX_TEST_ASSET_ID_CHAPTERS: optionalString(\"Mux asset ID used by integration tests for chapters.\", \"Mux test asset id for chapters\"),\n MUX_TEST_ASSET_ID_VIOLENT: optionalString(\"Mux violent asset ID used by integration tests.\", \"Mux violent test asset id\"),\n MUX_TEST_ASSET_ID_BURNED_IN_CAPTIONS: optionalString(\n \"Mux burned-in captions asset ID used by integration tests.\",\n \"Mux burned-in captions test asset id\",\n ),\n MUX_TEST_ASSET_ID_BURNED_IN_CAPTIONS_2: optionalString(\n \"Mux burned-in captions asset ID 2 (a different asset) used by integration tests.\",\n \"Mux burned-in captions test asset id 2 (a different asset)\",\n ),\n MUX_TEST_ASSET_ID_WITHOUT_BURNED_IN_CAPTIONS: optionalString(\n \"Mux without burned-in captions asset ID used by integration tests.\",\n \"Mux without burned-in captions test asset id\",\n ),\n 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\"),\n 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\"),\n\n // AI Providers\n OPENAI_API_KEY: optionalString(\"OpenAI API key for OpenAI-backed workflows.\", \"OpenAI API key\"),\n ANTHROPIC_API_KEY: optionalString(\"Anthropic API key for Claude-backed workflows.\", \"Anthropic API key\"),\n GOOGLE_GENERATIVE_AI_API_KEY: optionalString(\"Google Generative AI API key for Gemini-backed workflows.\", \"Google Generative AI API key\"),\n\n ELEVENLABS_API_KEY: optionalString(\"ElevenLabs API key for audio translation.\", \"ElevenLabs API key\"),\n HIVE_API_KEY: optionalString(\"Hive Visual Moderation API key.\", \"Hive API key\"),\n\n // S3-Compatible Storage (required for translation & audio dubbing)\n S3_ENDPOINT: optionalString(\"S3-compatible endpoint for uploads.\", \"S3 endpoint\"),\n S3_REGION: optionalString(\"S3 region (defaults to 'auto' when omitted).\"),\n S3_BUCKET: optionalString(\"Bucket used for caption and audio uploads.\", \"S3 bucket\"),\n S3_ACCESS_KEY_ID: optionalString(\"Access key ID for S3-compatible uploads.\", \"S3 access key id\"),\n S3_SECRET_ACCESS_KEY: optionalString(\"Secret access key for S3-compatible uploads.\", \"S3 secret access key\"),\n S3_ALLOWED_ENDPOINT_HOSTS: optionalString(\n \"Comma-separated S3 endpoint allowlist (supports exact hosts and *.suffix patterns).\",\n ),\n\n EVALITE_RESULTS_ENDPOINT: optionalString(\n \"Full URL for posting Evalite results (e.g., https://example.com/api/evalite-results).\",\n \"Evalite results endpoint\",\n ),\n}).refine(\n (env) => {\n const hasMuxCredentials = Boolean(env.MUX_TOKEN_ID && env.MUX_TOKEN_SECRET);\n const hasWorkflowKey = Boolean(env.MUX_AI_WORKFLOW_SECRET_KEY);\n return hasMuxCredentials || hasWorkflowKey;\n },\n {\n message: \"Either MUX_TOKEN_ID + MUX_TOKEN_SECRET or MUX_AI_WORKFLOW_SECRET_KEY must be set.\",\n },\n);\n\nexport type Env = z.infer<typeof EnvSchema>;\n\nfunction parseEnv(): Env {\n const parsedEnv = EnvSchema.safeParse(process.env);\n\n if (!parsedEnv.success) {\n console.error(\"❌ Invalid env:\");\n console.error(JSON.stringify(parsedEnv.error.flatten().fieldErrors, null, 2));\n process.exit(1);\n }\n\n return parsedEnv.data;\n}\n\nconst env: Env = parseEnv();\n\nexport function reloadEnv(): Env {\n const parsed = parseEnv();\n Object.assign(env, parsed);\n return env;\n}\n\nexport { env };\nexport default env;\n","/**\n * Workflow Crypto\n *\n * Provides AES-256-GCM encryption for securely passing credentials to workflows.\n * Encrypted payloads are JSON-serializable and include version/algorithm metadata\n * for forward compatibility.\n */\n\nimport { gcm } from \"@noble/ciphers/aes.js\";\n\nconst BASE64_CHUNK_SIZE = 0x8000;\nconst BASE64_ALPHABET = \"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/\";\nconst BASE64_LOOKUP = (() => {\n const table = new Uint8Array(256);\n table.fill(255);\n for (let i = 0; i < BASE64_ALPHABET.length; i++) {\n table[BASE64_ALPHABET.charCodeAt(i)] = i;\n }\n return table;\n})();\n\n// Encryption parameters (AES-256-GCM with standard IV/tag sizes)\nconst WORKFLOW_ENCRYPTION_VERSION = 1;\nconst WORKFLOW_ENCRYPTION_ALGORITHM = \"aes-256-gcm\" as const;\nconst IV_LENGTH_BYTES = 12; // 96-bit IV per NIST recommendation for GCM\nconst AUTH_TAG_LENGTH_BYTES = 16; // 128-bit authentication tag\n\n/**\n * Structure of an encrypted payload (all fields are base64-encoded where applicable).\n *\n * The optional `kid` (key ID) field supports key rotation scenarios:\n * - Include a `kid` when encrypting to identify which key was used\n * - On decryption, read `payload.kid` to look up the correct key\n * - Keys should be invalidated/deleted after rotation, not kept indefinitely\n *\n * Security notes:\n * - `kid` is stored in plaintext (not encrypted) - don't put sensitive data in it\n * - Tampering with `kid` doesn't weaken security - wrong key = decryption fails\n */\nexport interface EncryptedPayload {\n v: typeof WORKFLOW_ENCRYPTION_VERSION;\n alg: typeof WORKFLOW_ENCRYPTION_ALGORITHM;\n kid?: string;\n iv: string;\n tag: string;\n ciphertext: string;\n}\n\n/** Branded type that preserves the original type information for decryption */\nexport type Encrypted<T> = EncryptedPayload & { __type?: T };\n\nfunction getWebCrypto(): NonNullable<typeof globalThis.crypto> {\n const webCrypto = globalThis.crypto;\n if (!webCrypto || typeof webCrypto.getRandomValues !== \"function\") {\n throw new Error(\"Web Crypto API is required in workflow functions.\");\n }\n return webCrypto;\n}\n\nfunction bytesToBase64(bytes: Uint8Array): string {\n if (bytes.length === 0) {\n return \"\";\n }\n const btoaImpl = typeof globalThis.btoa === \"function\" ? globalThis.btoa.bind(globalThis) : undefined;\n if (btoaImpl) {\n let binary = \"\";\n for (let i = 0; i < bytes.length; i += BASE64_CHUNK_SIZE) {\n const chunk = bytes.subarray(i, i + BASE64_CHUNK_SIZE);\n binary += String.fromCharCode(...chunk);\n }\n return btoaImpl(binary);\n }\n\n let output = \"\";\n for (let i = 0; i < bytes.length; i += 3) {\n const b0 = bytes[i] ?? 0;\n const b1 = bytes[i + 1] ?? 0;\n const b2 = bytes[i + 2] ?? 0;\n const triple = (b0 << 16) | (b1 << 8) | b2;\n output += BASE64_ALPHABET[(triple >> 18) & 63];\n output += BASE64_ALPHABET[(triple >> 12) & 63];\n output += i + 1 < bytes.length ? BASE64_ALPHABET[(triple >> 6) & 63] : \"=\";\n output += i + 2 < bytes.length ? BASE64_ALPHABET[triple & 63] : \"=\";\n }\n return output;\n}\n\nfunction normalizeBase64Input(value: string): string {\n const cleaned = value\n .replace(/\\s+/g, \"\")\n .replace(/-/g, \"+\")\n .replace(/_/g, \"/\");\n\n // Pad with '=' to make the length a multiple of 4 (required by standard base64).\n // When cleaned is empty, length is 0 and 0 % 4 === 0, so it passes through unchanged.\n return cleaned?.length % 4 === 0 ? cleaned : cleaned + \"=\".repeat(4 - (cleaned.length % 4));\n}\n\nfunction base64ToBytes(value: string, label: string): Uint8Array {\n if (!value) {\n throw new Error(`${label} is missing`);\n }\n const normalized = normalizeBase64Input(value);\n const atobImpl = typeof globalThis.atob === \"function\" ? globalThis.atob.bind(globalThis) : undefined;\n if (atobImpl) {\n let binary: string;\n try {\n binary = atobImpl(normalized);\n } catch {\n throw new Error(`${label} is not valid base64`);\n }\n if (!binary) {\n throw new Error(`${label} decoded to empty value`);\n }\n const bytes = new Uint8Array(binary.length);\n for (let i = 0; i < binary.length; i++) {\n bytes[i] = binary.charCodeAt(i);\n }\n return bytes;\n }\n\n if (normalized.length % 4 !== 0) {\n throw new Error(`${label} is not valid base64`);\n }\n\n const padding = normalized.endsWith(\"==\") ? 2 : normalized.endsWith(\"=\") ? 1 : 0;\n const outputLength = (normalized.length / 4) * 3 - padding;\n const bytes = new Uint8Array(outputLength);\n let offset = 0;\n\n for (let i = 0; i < normalized.length; i += 4) {\n const c0 = normalized.charCodeAt(i);\n const c1 = normalized.charCodeAt(i + 1);\n const c2 = normalized.charCodeAt(i + 2);\n const c3 = normalized.charCodeAt(i + 3);\n\n if (c0 === 61 || c1 === 61) {\n throw new Error(`${label} is not valid base64`);\n }\n\n const n0 = BASE64_LOOKUP[c0] ?? 255;\n const n1 = BASE64_LOOKUP[c1] ?? 255;\n const n2 = c2 === 61 ? 0 : (BASE64_LOOKUP[c2] ?? 255);\n const n3 = c3 === 61 ? 0 : (BASE64_LOOKUP[c3] ?? 255);\n\n if (n0 === 255 || n1 === 255 || n2 === 255 || n3 === 255) {\n throw new Error(`${label} is not valid base64`);\n }\n\n const triple = (n0 << 18) | (n1 << 12) | (n2 << 6) | n3;\n bytes[offset++] = (triple >> 16) & 0xFF;\n if (c2 !== 61) {\n bytes[offset++] = (triple >> 8) & 0xFF;\n }\n if (c3 !== 61) {\n bytes[offset++] = triple & 0xFF;\n }\n }\n\n if (!bytes.length) {\n throw new Error(`${label} decoded to empty value`);\n }\n\n return bytes;\n}\n\n/** Wraps base64ToBytes for encrypted payload fields, adding context to errors */\nfunction payloadBase64ToBytes(value: string, field: string): Uint8Array {\n try {\n return base64ToBytes(value, field);\n } catch (error) {\n const detail = error instanceof Error ? error.message : `${field} is invalid`;\n throw new Error(`Invalid encrypted payload: ${detail}`);\n }\n}\n\n/** Converts key to bytes and validates it's exactly 32 bytes (256 bits) */\nfunction normalizeKey(key: Uint8Array | string): Uint8Array {\n let keyBytes: Uint8Array;\n if (typeof key === \"string\") {\n try {\n keyBytes = base64ToBytes(key, \"value\");\n } catch (error) {\n const detail = error instanceof Error ? error.message : \"value is not valid base64\";\n throw new Error(`Invalid workflow secret key: ${detail}. Expected 32-byte base64 value.`);\n }\n } else {\n keyBytes = new Uint8Array(key);\n }\n\n if (keyBytes.length !== 32) {\n throw new Error(`Invalid workflow secret key: expected 32 bytes, got ${keyBytes.length}.`);\n }\n\n return keyBytes;\n}\n\n/** Type guard to check if a value is a valid encrypted payload structure */\nexport function isEncryptedPayload(value: unknown): value is EncryptedPayload {\n if (!value || typeof value !== \"object\") {\n return false;\n }\n\n const payload = value as EncryptedPayload;\n return (\n payload.v === WORKFLOW_ENCRYPTION_VERSION &&\n payload.alg === WORKFLOW_ENCRYPTION_ALGORITHM &&\n typeof payload.iv === \"string\" &&\n typeof payload.tag === \"string\" &&\n typeof payload.ciphertext === \"string\"\n );\n}\n\n/** Validates payload structure and cryptographic parameters before decryption */\nfunction assertEncryptedPayload(payload: EncryptedPayload): void {\n if (payload.v !== WORKFLOW_ENCRYPTION_VERSION) {\n throw new Error(\"Invalid encrypted payload: unsupported version.\");\n }\n\n if (payload.alg !== WORKFLOW_ENCRYPTION_ALGORITHM) {\n throw new Error(\"Invalid encrypted payload: unsupported algorithm.\");\n }\n\n const iv = payloadBase64ToBytes(payload.iv, \"iv\");\n const tag = payloadBase64ToBytes(payload.tag, \"tag\");\n\n if (iv.length !== IV_LENGTH_BYTES) {\n throw new Error(\"Invalid encrypted payload: iv length mismatch.\");\n }\n\n if (tag.length !== AUTH_TAG_LENGTH_BYTES) {\n throw new Error(\"Invalid encrypted payload: tag length mismatch.\");\n }\n\n payloadBase64ToBytes(payload.ciphertext, \"ciphertext\");\n}\n\n/**\n * Encrypts a value for secure transport to a workflow.\n *\n * @param value - Any JSON-serializable value (typically WorkflowCredentials)\n * @param key - 32-byte secret key (base64 string or Uint8Array)\n * @param keyId - Optional key identifier for rotation support (stored in plaintext)\n * @returns Encrypted payload with metadata, safe to pass through untrusted channels\n */\nexport async function encryptForWorkflow<T>(\n value: T,\n key: Uint8Array | string,\n keyId?: string,\n): Promise<Encrypted<T>> {\n const keyBytes = normalizeKey(key);\n const webCrypto = getWebCrypto();\n const iv = new Uint8Array(IV_LENGTH_BYTES);\n webCrypto.getRandomValues(iv); // Fresh IV for each encryption\n\n let serialized: string;\n try {\n serialized = JSON.stringify(value);\n } catch {\n throw new Error(\"Failed to serialize value for encryption.\");\n }\n\n const encoder = new TextEncoder();\n const plaintext = encoder.encode(serialized);\n const encryptedBytes = gcm(keyBytes, iv).encrypt(plaintext);\n const tag = encryptedBytes.slice(encryptedBytes.length - AUTH_TAG_LENGTH_BYTES);\n const ciphertext = encryptedBytes.slice(0, encryptedBytes.length - AUTH_TAG_LENGTH_BYTES);\n\n return {\n v: WORKFLOW_ENCRYPTION_VERSION,\n alg: WORKFLOW_ENCRYPTION_ALGORITHM,\n ...(keyId !== undefined && { kid: keyId }),\n iv: bytesToBase64(iv),\n tag: bytesToBase64(tag),\n ciphertext: bytesToBase64(ciphertext),\n };\n}\n\n/**\n * Decrypts and deserializes a workflow payload.\n *\n * @param payload - Encrypted payload from `encryptForWorkflow`\n * @param key - Same 32-byte secret key used for encryption\n * @returns The original decrypted value\n * @throws If payload is invalid, tampered with, or key is wrong\n */\nexport async function decryptFromWorkflow<T>(\n payload: EncryptedPayload,\n key: Uint8Array | string,\n): Promise<T> {\n if (!isEncryptedPayload(payload)) {\n throw new Error(\"Invalid encrypted payload.\");\n }\n\n assertEncryptedPayload(payload);\n\n const keyBytes = normalizeKey(key);\n const iv = payloadBase64ToBytes(payload.iv, \"iv\");\n const tag = payloadBase64ToBytes(payload.tag, \"tag\");\n const ciphertext = payloadBase64ToBytes(payload.ciphertext, \"ciphertext\");\n\n const combined = new Uint8Array(ciphertext.length + tag.length);\n combined.set(ciphertext);\n combined.set(tag, ciphertext.length);\n\n let plaintext: Uint8Array;\n try {\n plaintext = gcm(keyBytes, iv).decrypt(combined);\n } catch (error) {\n const message = (error as { message?: string } | undefined)?.message;\n throw new Error(`Failed to decrypt workflow payload. ${message ?? String(error)}`);\n }\n\n try {\n const decoder = new TextDecoder();\n return JSON.parse(decoder.decode(plaintext)) as T;\n } catch {\n throw new Error(\"Failed to parse decrypted payload.\");\n }\n}\n","/**\n * Workflow Credentials Management\n *\n * This module provides a unified way to resolve credentials from multiple sources:\n * 1. A custom credentials provider (set via `setWorkflowCredentialsProvider`)\n * 2. Encrypted credentials passed directly to workflow functions\n * 3. Environment variables as fallback\n *\n * Credentials are merged in order of precedence: direct input > provider > environment.\n */\nimport env from \"@mux/ai/env\";\nimport type { Env } from \"@mux/ai/env\";\nimport type { SigningContext } from \"@mux/ai/lib/url-signing\";\nimport { decryptFromWorkflow, isEncryptedPayload } from \"@mux/ai/lib/workflow-crypto\";\nimport type {\n WorkflowCredentials,\n WorkflowCredentialsInput,\n WorkflowMuxClient,\n} from \"@mux/ai/types\";\n\n/**\n * A function that returns workflow credentials, either synchronously or asynchronously.\n * Used to inject credentials from external sources (e.g., a secrets manager).\n */\nexport type WorkflowCredentialsProvider =\n () => Promise<WorkflowCredentials | undefined> | WorkflowCredentials | undefined;\n\n/** Module-level credentials provider, set via `setWorkflowCredentialsProvider` */\nlet workflowCredentialsProvider: WorkflowCredentialsProvider | undefined;\n\n/**\n * Registers a custom credentials provider for the module.\n * The provider will be called whenever credentials need to be resolved.\n */\nexport function setWorkflowCredentialsProvider(provider?: WorkflowCredentialsProvider): void {\n workflowCredentialsProvider = provider;\n}\n\n/**\n * Detects whether code is running inside a Workflow Dev Kit runtime.\n * getWorkflowMetadata throws when invoked outside a workflow.\n */\nasync function isWorkflowRuntime(): Promise<boolean> {\n try {\n const workflowModule = await import(\"workflow\");\n if (typeof workflowModule.getWorkflowMetadata !== \"function\") {\n return false;\n }\n workflowModule.getWorkflowMetadata();\n return true;\n } catch {\n return false;\n }\n}\n\n/**\n * Determines if we should enforce encrypted credentials.\n * This triggers in workflow runtimes or when the workflow secret key is set.\n */\nasync function shouldEnforceEncryptedCredentials(): Promise<boolean> {\n return Boolean(env.MUX_AI_WORKFLOW_SECRET_KEY) || await isWorkflowRuntime();\n}\n\n/**\n * Retrieves the workflow secret key from environment variables.\n * This key is used to decrypt encrypted credential payloads.\n */\nfunction getWorkflowSecretKeyFromEnv(): string {\n const key = env.MUX_AI_WORKFLOW_SECRET_KEY;\n if (!key) {\n throw new Error(\n \"Workflow secret key is required. Set MUX_AI_WORKFLOW_SECRET_KEY environment variable.\",\n );\n }\n return key;\n}\n\n/**\n * Invokes the registered credentials provider (if any) and validates the result.\n */\nasync function resolveProviderCredentials(): Promise<WorkflowCredentials | undefined> {\n if (!workflowCredentialsProvider) {\n return undefined;\n }\n\n const provided = await workflowCredentialsProvider();\n if (!provided) {\n return undefined;\n }\n\n if (typeof provided !== \"object\") {\n throw new TypeError(\"Workflow credentials provider must return an object.\");\n }\n\n return provided;\n}\n\n/**\n * Resolves workflow credentials by merging from multiple sources.\n *\n * Resolution order (later sources override earlier):\n * 1. Credentials from the registered provider\n * 2. Decrypted credentials (if input is an encrypted payload)\n * OR plain credentials object (if input is already decrypted)\n *\n * @param credentials - Optional credentials input\n * @returns Merged credentials object\n */\nexport async function resolveWorkflowCredentials(\n credentials?: WorkflowCredentialsInput,\n): Promise<WorkflowCredentials> {\n // Start with provider credentials as the base\n const providerCredentials = await resolveProviderCredentials();\n const resolved: WorkflowCredentials = providerCredentials ? { ...providerCredentials } : {};\n\n if (!credentials) {\n return resolved;\n }\n\n // Handle encrypted payloads by decrypting them first.\n if (isEncryptedPayload(credentials)) {\n try {\n const decrypted = await decryptFromWorkflow<WorkflowCredentials>(\n credentials,\n getWorkflowSecretKeyFromEnv(),\n );\n return { ...resolved, ...decrypted };\n } catch (error) {\n const detail = error instanceof Error ? error.message : \"Unknown error.\";\n throw new Error(`Failed to decrypt workflow credentials. ${detail}`);\n }\n }\n\n if (await shouldEnforceEncryptedCredentials()) {\n throw new Error(\n \"Plaintext workflow credentials are not allowed when using Workflow Dev Kit.\" +\n \" Pass encrypted credentials (encryptForWorkflow) or resolve secrets via environment variables.\",\n );\n }\n\n // Plain credentials object - merge directly.\n return { ...resolved, ...credentials };\n}\n\ninterface DirectMuxCredentials {\n tokenId: string;\n tokenSecret: string;\n signingKey?: string;\n privateKey?: string;\n}\n\nfunction readString(record: Record<string, unknown> | undefined, key: string): string | undefined {\n const value = record?.[key];\n return typeof value === \"string\" && value.length > 0 ? value : undefined;\n}\n\nfunction resolveDirectMuxCredentials(record: Record<string, unknown> | undefined): DirectMuxCredentials | undefined {\n const tokenId = readString(record, \"muxTokenId\");\n const tokenSecret = readString(record, \"muxTokenSecret\");\n const signingKey = readString(record, \"muxSigningKey\");\n const privateKey = readString(record, \"muxPrivateKey\");\n\n if (!tokenId && !tokenSecret && !signingKey && !privateKey) {\n return undefined;\n }\n\n if (!tokenId || !tokenSecret) {\n throw new Error(\n \"Both muxTokenId and muxTokenSecret are required when passing direct Mux workflow credentials.\",\n );\n }\n\n return {\n tokenId,\n tokenSecret,\n signingKey,\n privateKey,\n };\n}\n\nfunction createWorkflowMuxClient(options: DirectMuxCredentials): WorkflowMuxClient {\n return {\n async createClient() {\n // Dynamic import to avoid pulling mux-node into workflow VM bundles.\n const { default: MuxClient } = await import(\"@mux/mux-node\");\n return new MuxClient({\n tokenId: options.tokenId,\n tokenSecret: options.tokenSecret,\n });\n },\n getSigningKey() {\n return options.signingKey;\n },\n getPrivateKey() {\n return options.privateKey;\n },\n };\n}\n\n/**\n * Resolves a WorkflowMuxClient from workflow credentials or environment variables.\n *\n * Checks direct workflow credentials first (muxTokenId/muxTokenSecret),\n * then provider credentials, then falls back to MUX_TOKEN_ID / MUX_TOKEN_SECRET\n * (and optional MUX_SIGNING_KEY / MUX_PRIVATE_KEY) environment variables.\n *\n * @param credentials - Optional workflow credentials input\n * @returns A WorkflowMuxClient instance\n * @throws Error if Mux credentials are not available\n */\nexport async function resolveMuxClient(\n credentials?: WorkflowCredentialsInput,\n): Promise<WorkflowMuxClient> {\n const resolved = await resolveWorkflowCredentials(credentials);\n const resolvedRecord = resolved as Record<string, unknown>;\n const resolvedMuxCredentials = resolveDirectMuxCredentials(resolvedRecord);\n if (resolvedMuxCredentials) {\n return createWorkflowMuxClient(resolvedMuxCredentials);\n }\n\n // Fall back to environment variables\n const muxTokenId = env.MUX_TOKEN_ID;\n const muxTokenSecret = env.MUX_TOKEN_SECRET;\n\n if (!muxTokenId || !muxTokenSecret) {\n throw new Error(\n \"Mux credentials are required. Provide muxTokenId/muxTokenSecret via workflow credentials, or set MUX_TOKEN_ID and MUX_TOKEN_SECRET environment variables.\",\n );\n }\n\n return createWorkflowMuxClient({\n tokenId: muxTokenId,\n tokenSecret: muxTokenSecret,\n signingKey: env.MUX_SIGNING_KEY,\n privateKey: env.MUX_PRIVATE_KEY,\n });\n}\n\n/** Supported AI/ML provider identifiers for API key resolution. */\nexport type ApiKeyProvider = \"openai\" | \"anthropic\" | \"google\" | \"hive\" | \"elevenlabs\";\n\nfunction resolveProviderApiKeyFromCredentials(\n provider: ApiKeyProvider,\n resolved: WorkflowCredentials,\n): string {\n const record = resolved as Record<string, unknown>;\n const openaiApiKey = readString(record, \"openaiApiKey\");\n const anthropicApiKey = readString(record, \"anthropicApiKey\");\n const googleApiKey = readString(record, \"googleApiKey\");\n const hiveApiKey = readString(record, \"hiveApiKey\");\n const elevenLabsApiKey = readString(record, \"elevenLabsApiKey\");\n\n // Map each provider to its credential source and env var fallback\n const apiKeyMap: Record<ApiKeyProvider, string | undefined> = {\n openai: openaiApiKey ?? env.OPENAI_API_KEY,\n anthropic: anthropicApiKey ?? env.ANTHROPIC_API_KEY,\n google: googleApiKey ?? env.GOOGLE_GENERATIVE_AI_API_KEY,\n hive: hiveApiKey ?? env.HIVE_API_KEY,\n elevenlabs: elevenLabsApiKey ?? env.ELEVENLABS_API_KEY,\n };\n\n const apiKey = apiKeyMap[provider];\n if (!apiKey) {\n // Provide helpful error message with the correct env var name.\n // Using `satisfies` ensures these stay in sync with the Env schema.\n const envVarNames = {\n openai: \"OPENAI_API_KEY\",\n anthropic: \"ANTHROPIC_API_KEY\",\n google: \"GOOGLE_GENERATIVE_AI_API_KEY\",\n hive: \"HIVE_API_KEY\",\n elevenlabs: \"ELEVENLABS_API_KEY\",\n } as const satisfies Record<ApiKeyProvider, keyof Env>;\n\n throw new Error(\n `${provider} API key is required. Provide ${provider} credentials via workflow credentials or set ${envVarNames[provider]} environment variable.`,\n );\n }\n\n return apiKey;\n}\n/**\n * Resolves an API key for a specific AI/ML provider.\n *\n * Checks resolved workflow credentials first, then falls back to the\n * provider-specific environment variable.\n *\n * @param provider - The provider identifier (e.g., \"openai\", \"anthropic\")\n * @param credentials - Optional workflow credentials input\n * @returns The resolved API key string\n * @throws Error if no API key is available for the specified provider\n */\nexport async function resolveProviderApiKey(\n provider: ApiKeyProvider,\n credentials?: WorkflowCredentialsInput,\n): Promise<string> {\n const resolved = await resolveWorkflowCredentials(credentials);\n return resolveProviderApiKeyFromCredentials(provider, resolved);\n}\n\n/**\n * Resolves Mux URL signing context for generating signed playback URLs.\n *\n * Unlike other resolve functions, this returns undefined if signing keys\n * are not configured (signing is optional for public assets).\n *\n * @param credentials - Optional workflow credentials input\n * @returns SigningContext if keys are available, undefined otherwise\n */\nexport async function resolveMuxSigningContext(\n credentials?: WorkflowCredentialsInput,\n): Promise<SigningContext | undefined> {\n const resolved = await resolveWorkflowCredentials(credentials);\n const resolvedRecord = resolved as Record<string, unknown>;\n\n // Try direct credentials first, then fall back to environment variables.\n const keyId = readString(resolvedRecord, \"muxSigningKey\") ?? env.MUX_SIGNING_KEY;\n const keySecret =\n readString(resolvedRecord, \"muxPrivateKey\") ?? env.MUX_PRIVATE_KEY;\n\n if (!keyId || !keySecret) {\n return undefined;\n }\n\n return { keyId, keySecret };\n}\n",null,"import env from \"@mux/ai/env\";\n\nconst AWS4_ALGORITHM = \"AWS4-HMAC-SHA256\";\nconst AWS4_REQUEST_TERMINATOR = \"aws4_request\";\nconst AWS4_SERVICE = \"s3\";\n\n// Env flags for endpoint hardening.\n// - S3_ALLOWED_ENDPOINT_HOSTS=\"s3.amazonaws.com,*.r2.cloudflarestorage.com\"\n// restricts requests to explicit hostnames / wildcard suffixes.\nconst S3_ALLOWED_ENDPOINT_PATTERNS = parseEndpointAllowlist(\n env.S3_ALLOWED_ENDPOINT_HOSTS,\n);\n\ninterface S3Credentials {\n accessKeyId: string;\n secretAccessKey: string;\n}\n\ninterface S3Target {\n endpoint: string;\n region: string;\n bucket: string;\n key: string;\n}\n\nexport interface PutObjectOptions extends S3Target, S3Credentials {\n body: string | Uint8Array;\n contentType?: string;\n}\n\nexport interface PresignGetObjectOptions extends S3Target, S3Credentials {\n expiresInSeconds?: number;\n}\n\nfunction getCrypto() {\n const webCrypto = globalThis.crypto as any;\n if (!webCrypto?.subtle) {\n throw new Error(\"Web Crypto API is required for S3 signing.\");\n }\n\n return webCrypto;\n}\n\nconst textEncoder = new TextEncoder();\n\nfunction toBytes(value: string | Uint8Array): Uint8Array {\n return typeof value === \"string\" ? textEncoder.encode(value) : value;\n}\n\nfunction bytesToHex(bytes: Uint8Array): string {\n return Array.from(bytes)\n .map(byte => byte.toString(16).padStart(2, \"0\"))\n .join(\"\");\n}\n\nasync function sha256Hex(value: string | Uint8Array): Promise<string> {\n const digest = await getCrypto().subtle.digest(\"SHA-256\", toBytes(value));\n return bytesToHex(new Uint8Array(digest));\n}\n\nasync function hmacSha256Raw(key: Uint8Array, value: string): Promise<Uint8Array> {\n const cryptoKey = await getCrypto().subtle.importKey(\n \"raw\",\n key,\n { name: \"HMAC\", hash: \"SHA-256\" },\n false,\n [\"sign\"],\n );\n const signature = await getCrypto().subtle.sign(\"HMAC\", cryptoKey, textEncoder.encode(value));\n return new Uint8Array(signature);\n}\n\nasync function deriveSigningKey(\n secretAccessKey: string,\n shortDate: string,\n region: string,\n): Promise<Uint8Array> {\n const kDate = await hmacSha256Raw(textEncoder.encode(`AWS4${secretAccessKey}`), shortDate);\n const kRegion = await hmacSha256Raw(kDate, region);\n const kService = await hmacSha256Raw(kRegion, AWS4_SERVICE);\n return hmacSha256Raw(kService, AWS4_REQUEST_TERMINATOR);\n}\n\nfunction formatAmzDate(date = new Date()): { amzDate: string; shortDate: string } {\n const iso = date.toISOString(); // always UTC\n const shortDate = iso.slice(0, 10).replace(/-/g, \"\");\n const amzDate = `${iso.slice(0, 19).replace(/[-:]/g, \"\")}Z`;\n return { amzDate, shortDate };\n}\n\nfunction encodeRFC3986(value: string): string {\n return encodeURIComponent(value).replace(/[!'()*]/g, (char: string) =>\n `%${char.charCodeAt(0).toString(16).toUpperCase()}`);\n}\n\nfunction encodePath(path: string): string {\n return path.split(\"/\").map(segment => encodeRFC3986(segment)).join(\"/\");\n}\n\nfunction normalizeEndpoint(endpoint: string): URL {\n let url: URL;\n try {\n url = new URL(endpoint);\n } catch {\n throw new Error(`Invalid S3 endpoint: ${endpoint}`);\n }\n\n if (url.search || url.hash) {\n throw new Error(\"S3 endpoint must not include query params or hash fragments.\");\n }\n\n enforceEndpointPolicy(url);\n\n return url;\n}\n\nfunction parseEndpointAllowlist(allowlist: string | undefined): string[] {\n if (!allowlist) {\n return [];\n }\n\n return allowlist\n .split(\",\")\n .map(value => value.trim().toLowerCase())\n .filter(Boolean);\n}\n\nfunction hostnameMatchesPattern(hostname: string, pattern: string): boolean {\n if (pattern.startsWith(\"*.\")) {\n const suffix = pattern.slice(1); // \".example.com\"\n return hostname.endsWith(suffix) && hostname.length > suffix.length;\n }\n\n return hostname === pattern;\n}\n\nfunction enforceEndpointPolicy(url: URL): void {\n const hostname = url.hostname.toLowerCase();\n\n // Enforce secure transport for all S3 uploads/signing flows.\n if (url.protocol !== \"https:\") {\n throw new Error(\n `Insecure S3 endpoint protocol \"${url.protocol}\" is not allowed. Use HTTPS.`,\n );\n }\n\n // Optional allowlist enforcement to prevent exfiltration/SSRF-like endpoint misuse.\n // When unset, behavior remains backward compatible (any host allowed).\n if (\n S3_ALLOWED_ENDPOINT_PATTERNS.length > 0 &&\n !S3_ALLOWED_ENDPOINT_PATTERNS.some(pattern => hostnameMatchesPattern(hostname, pattern))\n ) {\n throw new Error(\n `S3 endpoint host \"${hostname}\" is not in S3_ALLOWED_ENDPOINT_HOSTS.`,\n );\n }\n}\n\nfunction buildCanonicalUri(endpoint: URL, bucket: string, key: string): string {\n const endpointPath =\n endpoint.pathname === \"/\" ? \"\" : encodePath(endpoint.pathname.replace(/\\/+$/, \"\"));\n const encodedBucket = encodeRFC3986(bucket);\n const encodedKey = encodePath(key);\n\n return `${endpointPath}/${encodedBucket}/${encodedKey}`;\n}\n\nfunction buildCanonicalQuery(params: Record<string, string>): string {\n return Object.entries(params)\n .sort(([a], [b]) => a.localeCompare(b))\n .map(([key, value]) => `${encodeRFC3986(key)}=${encodeRFC3986(value)}`)\n .join(\"&\");\n}\n\nasync function signString(\n secretAccessKey: string,\n shortDate: string,\n region: string,\n value: string,\n): Promise<string> {\n const signingKey = await deriveSigningKey(secretAccessKey, shortDate, region);\n const signatureBytes = await hmacSha256Raw(signingKey, value);\n return bytesToHex(signatureBytes);\n}\n\nfunction buildCredentialScope(shortDate: string, region: string): string {\n return `${shortDate}/${region}/${AWS4_SERVICE}/${AWS4_REQUEST_TERMINATOR}`;\n}\n\nexport async function putObjectToS3({\n accessKeyId,\n secretAccessKey,\n endpoint,\n region,\n bucket,\n key,\n body,\n contentType,\n}: PutObjectOptions): Promise<void> {\n const resolvedEndpoint = normalizeEndpoint(endpoint);\n const canonicalUri = buildCanonicalUri(resolvedEndpoint, bucket, key);\n const host = resolvedEndpoint.host;\n const normalizedContentType = contentType?.trim();\n const { amzDate, shortDate } = formatAmzDate();\n const payloadHash = await sha256Hex(body);\n\n const signingHeaders = [\n [\"host\", host],\n [\"x-amz-content-sha256\", payloadHash],\n [\"x-amz-date\", amzDate],\n ...(normalizedContentType ? [[\"content-type\", normalizedContentType] as const] : []),\n ].sort(([a], [b]) => a.localeCompare(b));\n const canonicalHeaders = signingHeaders.map(([name, value]) => `${name}:${value}`).join(\"\\n\");\n const signedHeaders = signingHeaders.map(([name]) => name).join(\";\");\n const canonicalRequest = [\n \"PUT\",\n canonicalUri,\n \"\",\n `${canonicalHeaders}\\n`,\n signedHeaders,\n payloadHash,\n ].join(\"\\n\");\n const credentialScope = buildCredentialScope(shortDate, region);\n const stringToSign = [\n AWS4_ALGORITHM,\n amzDate,\n credentialScope,\n await sha256Hex(canonicalRequest),\n ].join(\"\\n\");\n const signature = await signString(secretAccessKey, shortDate, region, stringToSign);\n const authorization = `${AWS4_ALGORITHM} Credential=${accessKeyId}/${credentialScope}, SignedHeaders=${signedHeaders}, Signature=${signature}`;\n const requestUrl = `${resolvedEndpoint.origin}${canonicalUri}`;\n\n const response = await fetch(requestUrl, {\n method: \"PUT\",\n headers: {\n \"Authorization\": authorization,\n \"x-amz-content-sha256\": payloadHash,\n \"x-amz-date\": amzDate,\n ...(normalizedContentType ? { \"content-type\": normalizedContentType } : {}),\n },\n body,\n });\n\n if (!response.ok) {\n const errorBody = await response.text().catch(() => \"\");\n const detail = errorBody ? ` ${errorBody}` : \"\";\n throw new Error(`S3 PUT failed (${response.status} ${response.statusText}).${detail}`);\n }\n}\n\nexport async function createPresignedGetUrl({\n accessKeyId,\n secretAccessKey,\n endpoint,\n region,\n bucket,\n key,\n expiresInSeconds = 3600,\n}: PresignGetObjectOptions): Promise<string> {\n const resolvedEndpoint = normalizeEndpoint(endpoint);\n const canonicalUri = buildCanonicalUri(resolvedEndpoint, bucket, key);\n const host = resolvedEndpoint.host;\n const { amzDate, shortDate } = formatAmzDate();\n const credentialScope = buildCredentialScope(shortDate, region);\n const signedHeaders = \"host\";\n const queryParams = {\n \"X-Amz-Algorithm\": AWS4_ALGORITHM,\n \"X-Amz-Credential\": `${accessKeyId}/${credentialScope}`,\n \"X-Amz-Date\": amzDate,\n \"X-Amz-Expires\": `${expiresInSeconds}`,\n \"X-Amz-SignedHeaders\": signedHeaders,\n };\n const canonicalQuery = buildCanonicalQuery(queryParams);\n const canonicalRequest = [\n \"GET\",\n canonicalUri,\n canonicalQuery,\n `host:${host}\\n`,\n signedHeaders,\n \"UNSIGNED-PAYLOAD\",\n ].join(\"\\n\");\n const stringToSign = [\n AWS4_ALGORITHM,\n amzDate,\n credentialScope,\n await sha256Hex(canonicalRequest),\n ].join(\"\\n\");\n const signature = await signString(secretAccessKey, shortDate, region, stringToSign);\n const queryWithSignature = `${canonicalQuery}&X-Amz-Signature=${signature}`;\n\n return `${resolvedEndpoint.origin}${canonicalUri}?${queryWithSignature}`;\n}\n","interface SerializableClass {\n classId?: string;\n}\n\ninterface SerializableClassConstructor {\n new (...args: never[]): unknown;\n classId?: string;\n}\n\ntype Registry = Map<string, SerializableClassConstructor>;\n\nconst WORKFLOW_CLASS_REGISTRY = Symbol.for(\"workflow-class-registry\");\n\n/**\n * Registers a class in Workflow's global serialization registry.\n *\n * This mirrors Workflow DevKit's internal `registerSerializationClass` behavior\n * without requiring consumers to import private workflow internals.\n */\nexport function registerWorkflowSerializationClass(classId: string, cls: SerializableClassConstructor): void {\n const globalObject = globalThis as typeof globalThis & {\n [WORKFLOW_CLASS_REGISTRY]?: Registry;\n };\n\n let registry = globalObject[WORKFLOW_CLASS_REGISTRY];\n if (!registry) {\n registry = new Map<string, SerializableClassConstructor>();\n globalObject[WORKFLOW_CLASS_REGISTRY] = registry;\n }\n\n registry.set(classId, cls);\n\n const serializableClass = cls as SerializableClass;\n if (serializableClass.classId !== classId) {\n Object.defineProperty(cls, \"classId\", {\n value: classId,\n writable: false,\n enumerable: false,\n configurable: false,\n });\n }\n}\n","import { WORKFLOW_DESERIALIZE, WORKFLOW_SERIALIZE } from \"@workflow/serde\";\n\nimport {\n createPresignedGetUrl,\n putObjectToS3,\n} from \"@mux/ai/lib/s3-sigv4\";\nimport { registerWorkflowSerializationClass } from \"@mux/ai/lib/workflow-serialization\";\nimport type {\n StoragePresignGetObjectInput,\n StoragePutObjectInput,\n} from \"@mux/ai/types\";\n\nexport interface WorkflowStorageClientOptions {\n accessKeyId?: string;\n secretAccessKey?: string;\n}\n\n/**\n * Serializable storage client wrapper for workflow boundaries.\n *\n * By default, this uses the internal SigV4 implementation to keep object\n * operations compatible across edge/ESM and Node runtimes.\n */\nexport class WorkflowStorageClient {\n static classId = \"WorkflowStorageClient\";\n\n private readonly accessKeyId?: string;\n private readonly secretAccessKey?: string;\n\n constructor(options: WorkflowStorageClientOptions = {}) {\n this.accessKeyId = options.accessKeyId;\n this.secretAccessKey = options.secretAccessKey;\n }\n\n private resolveCredentials(input: {\n accessKeyId?: string;\n secretAccessKey?: string;\n }): { accessKeyId: string; secretAccessKey: string } {\n const accessKeyId = input.accessKeyId ?? this.accessKeyId;\n const secretAccessKey = input.secretAccessKey ?? this.secretAccessKey;\n\n if (!accessKeyId || !secretAccessKey) {\n throw new Error(\n \"Storage credentials are required. \" +\n \"Provide accessKeyId/secretAccessKey in WorkflowStorageClient options \" +\n \"or in the storage operation input.\",\n );\n }\n\n return { accessKeyId, secretAccessKey };\n }\n\n async putObject(input: StoragePutObjectInput): Promise<void> {\n const credentials = this.resolveCredentials(input);\n await putObjectToS3({\n accessKeyId: credentials.accessKeyId,\n secretAccessKey: credentials.secretAccessKey,\n endpoint: input.endpoint,\n region: input.region,\n bucket: input.bucket,\n key: input.key,\n body: input.body,\n contentType: input.contentType,\n });\n }\n\n async createPresignedGetUrl(input: StoragePresignGetObjectInput): Promise<string> {\n const credentials = this.resolveCredentials(input);\n return createPresignedGetUrl({\n accessKeyId: credentials.accessKeyId,\n secretAccessKey: credentials.secretAccessKey,\n endpoint: input.endpoint,\n region: input.region,\n bucket: input.bucket,\n key: input.key,\n expiresInSeconds: input.expiresInSeconds,\n });\n }\n\n static [WORKFLOW_SERIALIZE](instance: WorkflowStorageClient): WorkflowStorageClientOptions {\n return {\n accessKeyId: instance.accessKeyId,\n secretAccessKey: instance.secretAccessKey,\n };\n }\n\n static [WORKFLOW_DESERIALIZE](this: typeof WorkflowStorageClient, value: WorkflowStorageClientOptions): WorkflowStorageClient {\n return new this(value);\n }\n}\n\nregisterWorkflowSerializationClass(WorkflowStorageClient.classId, WorkflowStorageClient);\n\nfunction isSerializedWorkflowStorageClient(value: unknown): value is WorkflowStorageClientOptions {\n if (!value || typeof value !== \"object\") {\n return false;\n }\n\n const candidate = value as WorkflowStorageClientOptions;\n return \"accessKeyId\" in candidate || \"secretAccessKey\" in candidate;\n}\n\nexport function normalizeWorkflowStorageClient(value: unknown): WorkflowStorageClient | undefined {\n if (value instanceof WorkflowStorageClient) {\n return value;\n }\n\n if (isSerializedWorkflowStorageClient(value)) {\n return new WorkflowStorageClient(value);\n }\n\n return undefined;\n}\n\nexport function createWorkflowStorageClient(\n options: WorkflowStorageClientOptions = {},\n): WorkflowStorageClient {\n return new WorkflowStorageClient(options);\n}\n","// primitives public surface intentionally minimal; provider plumbing lives in lib/providers\nexport * from \"./heatmap\";\nexport * from \"./hotspots\";\nexport * from \"./storyboards\";\nexport * from \"./text-chunking\";\nexport * from \"./thumbnails\";\nexport * from \"./transcripts\";\n","import { createAnthropic } from \"@ai-sdk/anthropic\";\nimport { createGoogleGenerativeAI } from \"@ai-sdk/google\";\nimport { createOpenAI } from \"@ai-sdk/openai\";\n\nimport env from \"@mux/ai/env\";\nimport { resolveProviderApiKey } from \"@mux/ai/lib/workflow-credentials\";\nimport type { MuxAIOptions, WorkflowCredentialsInput } from \"@mux/ai/types\";\n\nimport type { EmbeddingModel, LanguageModel } from \"ai\";\n\nexport type SupportedProvider = \"openai\" | \"anthropic\" | \"google\";\nexport type SupportedEmbeddingProvider = \"openai\" | \"google\";\n\n// Model ID unions inferred from ai-sdk provider call signatures\ntype OpenAIModelId = Parameters<ReturnType<typeof createOpenAI>[\"chat\"]>[0];\ntype AnthropicModelId = Parameters<ReturnType<typeof createAnthropic>[\"chat\"]>[0];\ntype GoogleModelId = Parameters<ReturnType<typeof createGoogleGenerativeAI>[\"chat\"]>[0];\n\ntype OpenAIEmbeddingModelId = Parameters<ReturnType<typeof createOpenAI>[\"embedding\"]>[0];\ntype GoogleEmbeddingModelId = Parameters<ReturnType<typeof createGoogleGenerativeAI>[\"textEmbeddingModel\"]>[0];\n\nexport interface ModelIdByProvider {\n openai: OpenAIModelId;\n anthropic: AnthropicModelId;\n google: GoogleModelId;\n}\n\nexport interface EmbeddingModelIdByProvider {\n openai: OpenAIEmbeddingModelId;\n google: GoogleEmbeddingModelId;\n}\n\nexport interface ModelRequestOptions<P extends SupportedProvider = SupportedProvider> extends MuxAIOptions {\n provider?: P;\n model?: ModelIdByProvider[P];\n}\n\nexport interface ResolvedModel<P extends SupportedProvider = SupportedProvider> {\n provider: P;\n modelId: ModelIdByProvider[P];\n model: LanguageModel;\n}\n\nexport const DEFAULT_LANGUAGE_MODELS: { [K in SupportedProvider]: ModelIdByProvider[K] } = {\n openai: \"gpt-5.1\",\n anthropic: \"claude-sonnet-4-5\",\n google: \"gemini-3-flash-preview\",\n};\n\nconst DEFAULT_EMBEDDING_MODELS: { [K in SupportedEmbeddingProvider]: EmbeddingModelIdByProvider[K] } = {\n openai: \"text-embedding-3-small\",\n google: \"gemini-embedding-001\",\n};\n\nexport function resolveLanguageModelConfig<P extends SupportedProvider = SupportedProvider>(\n options: ModelRequestOptions<P> = {},\n): { provider: P; modelId: ModelIdByProvider[P] } {\n const provider = options.provider || (\"openai\" as P);\n const modelId = (options.model || DEFAULT_LANGUAGE_MODELS[provider]) as ModelIdByProvider[P];\n\n return { provider, modelId };\n}\n\nexport function resolveEmbeddingModelConfig<P extends SupportedEmbeddingProvider = \"openai\">(\n options: MuxAIOptions & { provider?: P; model?: EmbeddingModelIdByProvider[P] } = {},\n): { provider: P; modelId: EmbeddingModelIdByProvider[P] } {\n const provider = options.provider || (\"openai\" as P);\n const modelId = (options.model || DEFAULT_EMBEDDING_MODELS[provider]) as EmbeddingModelIdByProvider[P];\n\n return { provider, modelId };\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Model Pricing\n// ─────────────────────────────────────────────────────────────────────────────\n//\n// Pricing is in USD per million tokens. These values are used for cost estimation\n// in evaluations and should be periodically verified against official sources.\n//\n// Sources (as of December 2025):\n// - OpenAI: https://openai.com/api/pricing\n// - Anthropic: https://www.anthropic.com/pricing\n// - Google: https://ai.google.dev/pricing\n// ─────────────────────────────────────────────────────────────────────────────\n\n/**\n * Pricing structure for a language model.\n * All costs are in USD per million tokens.\n */\nexport interface ModelPricing {\n /** Cost per million input tokens (USD). */\n inputPerMillion: number;\n /** Cost per million output tokens (USD). */\n outputPerMillion: number;\n /** Cost per million cached input tokens (USD), if supported. */\n cachedInputPerMillion?: number;\n /** URL to the official pricing page for verification. */\n pricingUrl: string;\n}\n\n/**\n * Pricing data for the default language models.\n * Used for cost estimation in evaluations and expense tracking.\n *\n * @remarks\n * Prices are subject to change. Verify against official sources before production use.\n */\nexport const THIRD_PARTY_MODEL_PRICING: { [K in SupportedProvider]: ModelPricing } = {\n // OpenAI GPT-5.1\n // Reference: https://openai.com/api/pricing\n openai: {\n inputPerMillion: 1.25,\n outputPerMillion: 10.00,\n cachedInputPerMillion: 0.125,\n pricingUrl: \"https://openai.com/api/pricing\",\n },\n\n // Anthropic Claude Sonnet 4.5\n // Reference: https://www.anthropic.com/pricing\n anthropic: {\n inputPerMillion: 3.00,\n outputPerMillion: 15.00,\n cachedInputPerMillion: 0.30, // Prompt caching read cost (≤200K tokens)\n pricingUrl: \"https://www.anthropic.com/pricing\",\n },\n\n // Google Gemini 3 Flash Preview\n // Reference: https://ai.google.dev/pricing\n google: {\n inputPerMillion: 0.50,\n outputPerMillion: 3.00,\n cachedInputPerMillion: 0.05, // Context caching price\n pricingUrl: \"https://ai.google.dev/pricing\",\n },\n};\n\n/**\n * Calculates the estimated cost for a request based on token usage.\n *\n * @param provider - The AI provider used\n * @param inputTokens - Number of input tokens consumed\n * @param outputTokens - Number of output tokens generated\n * @param cachedInputTokens - Number of input tokens served from cache (optional)\n * @returns Estimated cost in USD\n *\n * @example\n * ```typescript\n * const cost = calculateCost('openai', 2000, 500);\n * console.log(`Estimated cost: $${cost.toFixed(6)}`);\n * ```\n */\nexport function calculateCost(\n provider: SupportedProvider,\n inputTokens: number,\n outputTokens: number,\n cachedInputTokens: number = 0,\n): number {\n const pricing = THIRD_PARTY_MODEL_PRICING[provider];\n\n // Adjust input tokens: cached tokens are charged at cached rate, rest at full rate\n const uncachedInputTokens = Math.max(0, inputTokens - cachedInputTokens);\n\n const inputCost = (uncachedInputTokens / 1_000_000) * pricing.inputPerMillion;\n const outputCost = (outputTokens / 1_000_000) * pricing.outputPerMillion;\n let cachedCost = 0;\n if (pricing.cachedInputPerMillion) {\n cachedCost = (cachedInputTokens / 1_000_000) * pricing.cachedInputPerMillion;\n }\n\n return inputCost + outputCost + cachedCost;\n}\n\nfunction requireEnv(value: string | undefined, name: string): string {\n if (!value) {\n throw new Error(`Missing ${name}. Set ${name} in your environment or pass it in options.`);\n }\n return value;\n}\n\n/**\n * Creates a language model instance from serializable config.\n * Use this in steps to instantiate models from config passed through workflow.\n * Fetches credentials internally from environment variables to avoid exposing them in step I/O.\n */\nexport async function createLanguageModelFromConfig<P extends SupportedProvider = SupportedProvider>(\n provider: P,\n modelId: ModelIdByProvider[P],\n credentials?: WorkflowCredentialsInput,\n): Promise<LanguageModel> {\n switch (provider) {\n case \"openai\": {\n const apiKey = await resolveProviderApiKey(\"openai\", credentials);\n const openai = createOpenAI({ apiKey });\n return openai(modelId);\n }\n case \"anthropic\": {\n const apiKey = await resolveProviderApiKey(\"anthropic\", credentials);\n const anthropic = createAnthropic({ apiKey });\n return anthropic(modelId);\n }\n case \"google\": {\n const apiKey = await resolveProviderApiKey(\"google\", credentials);\n const google = createGoogleGenerativeAI({ apiKey });\n return google(modelId);\n }\n default: {\n const exhaustiveCheck: never = provider;\n throw new Error(`Unsupported provider: ${exhaustiveCheck}`);\n }\n }\n}\n\n/**\n * Creates an embedding model instance from serializable config.\n * Use this in steps to instantiate embedding models from config passed through workflow.\n * Fetches credentials internally from environment variables to avoid exposing them in step I/O.\n */\nexport async function createEmbeddingModelFromConfig<\n P extends SupportedEmbeddingProvider = SupportedEmbeddingProvider,\n>(\n provider: P,\n modelId: EmbeddingModelIdByProvider[P],\n credentials?: WorkflowCredentialsInput,\n): Promise<EmbeddingModel> {\n switch (provider) {\n case \"openai\": {\n const apiKey = await resolveProviderApiKey(\"openai\", credentials);\n const openai = createOpenAI({ apiKey });\n return openai.embedding(modelId);\n }\n case \"google\": {\n const apiKey = await resolveProviderApiKey(\"google\", credentials);\n const google = createGoogleGenerativeAI({ apiKey });\n return google.textEmbeddingModel(modelId);\n }\n default: {\n const exhaustiveCheck: never = provider;\n throw new Error(`Unsupported embedding provider: ${exhaustiveCheck}`);\n }\n }\n}\n\n/**\n * Resolves a language model from a suggested provider.\n */\nexport function resolveLanguageModel<P extends SupportedProvider = SupportedProvider>(\n options: ModelRequestOptions<P> = {},\n): ResolvedModel<P> {\n const provider = options.provider || (\"openai\" as P);\n const modelId = (options.model || DEFAULT_LANGUAGE_MODELS[provider]) as ModelIdByProvider[P];\n\n switch (provider) {\n case \"openai\": {\n const apiKey = env.OPENAI_API_KEY;\n requireEnv(apiKey, \"OPENAI_API_KEY\");\n const openai = createOpenAI({\n apiKey,\n });\n\n return {\n provider,\n modelId,\n model: openai(modelId),\n };\n }\n case \"anthropic\": {\n const apiKey = env.ANTHROPIC_API_KEY;\n requireEnv(apiKey, \"ANTHROPIC_API_KEY\");\n const anthropic = createAnthropic({\n apiKey,\n });\n\n return {\n provider,\n modelId,\n model: anthropic(modelId),\n };\n }\n case \"google\": {\n const apiKey = env.GOOGLE_GENERATIVE_AI_API_KEY;\n requireEnv(apiKey, \"GOOGLE_GENERATIVE_AI_API_KEY\");\n const google = createGoogleGenerativeAI({\n apiKey,\n });\n\n return {\n provider,\n modelId,\n model: google(modelId),\n };\n }\n default: {\n const exhaustiveCheck: never = provider;\n throw new Error(`Unsupported provider: ${exhaustiveCheck}`);\n }\n }\n}\n\n/**\n * Resolves an embedding model from a suggested provider.\n */\nexport function resolveEmbeddingModel<P extends SupportedEmbeddingProvider = \"openai\">(\n options: MuxAIOptions & { provider?: P; model?: EmbeddingModelIdByProvider[P] } = {},\n): { provider: P; modelId: EmbeddingModelIdByProvider[P]; model: EmbeddingModel } {\n const provider = options.provider || (\"openai\" as P);\n const modelId = (options.model || DEFAULT_EMBEDDING_MODELS[provider]) as EmbeddingModelIdByProvider[P];\n\n switch (provider) {\n case \"openai\": {\n const apiKey = env.OPENAI_API_KEY;\n requireEnv(apiKey, \"OPENAI_API_KEY\");\n const openai = createOpenAI({\n apiKey,\n });\n\n return {\n provider,\n modelId,\n model: openai.embedding(modelId),\n };\n }\n case \"google\": {\n const apiKey = env.GOOGLE_GENERATIVE_AI_API_KEY;\n requireEnv(apiKey, \"GOOGLE_GENERATIVE_AI_API_KEY\");\n const google = createGoogleGenerativeAI({\n apiKey,\n });\n\n return {\n provider,\n modelId,\n model: google.textEmbeddingModel(modelId),\n };\n }\n default: {\n const exhaustiveCheck: never = provider;\n throw new Error(`Unsupported embedding provider: ${exhaustiveCheck}`);\n }\n }\n}\n","import type {\n ModelIdByProvider,\n ModelRequestOptions,\n SupportedProvider,\n} from \"@mux/ai/lib/providers\";\nimport {\n resolveLanguageModel,\n} from \"@mux/ai/lib/providers\";\nimport type { ApiKeyProvider } from \"@mux/ai/lib/workflow-credentials\";\nimport { resolveMuxClient, resolveProviderApiKey } from \"@mux/ai/lib/workflow-credentials\";\nimport type { WorkflowCredentialsInput, WorkflowMuxClient } from \"@mux/ai/types\";\n\n/**\n * Gets a WorkflowMuxClient from workflow credentials or environment variables.\n * Used internally by workflow steps to avoid passing credentials through step I/O.\n * Throws if Mux credentials are not available.\n */\nexport async function getMuxClientFromEnv(\n credentials?: WorkflowCredentialsInput,\n): Promise<WorkflowMuxClient> {\n return resolveMuxClient(credentials);\n}\n\n/**\n * Gets an API key from workflow credentials or environment variables for the specified provider.\n * Used internally by workflow steps to avoid passing credentials through step I/O.\n * Throws if the API key is not available.\n */\nexport async function getApiKeyFromEnv(\n provider: ApiKeyProvider,\n credentials?: WorkflowCredentialsInput,\n): Promise<string> {\n return resolveProviderApiKey(provider, credentials);\n}\n\nexport interface WorkflowConfig<P extends SupportedProvider = SupportedProvider> {\n muxClient: WorkflowMuxClient;\n provider: P;\n modelId: ModelIdByProvider[P];\n}\n\n/**\n * Resolves Mux client and model configuration for a workflow.\n * This function is NOT a workflow step to avoid exposing credentials in step I/O.\n */\nexport async function createWorkflowConfig<P extends SupportedProvider = SupportedProvider>(\n options: ModelRequestOptions<P>,\n provider?: P,\n): Promise<WorkflowConfig<P>> {\n const providerToUse = provider || options.provider || (\"openai\" as P);\n const muxClient = await resolveMuxClient(options.credentials);\n const resolved = resolveLanguageModel({\n ...options,\n provider: providerToUse,\n });\n\n return {\n muxClient,\n provider: resolved.provider,\n modelId: resolved.modelId,\n };\n}\n","import { getMuxClientFromEnv } from \"@mux/ai/lib/client-factory\";\nimport type { WorkflowCredentialsInput } from \"@mux/ai/types\";\n\nexport interface HeatmapOptions {\n /** Time window for results, e.g., ['7:days'] (default: ['7:days']) */\n timeframe?: string;\n /** Optional workflow credentials */\n credentials?: WorkflowCredentialsInput;\n}\n\n/** Raw API response structure from Mux Data API */\n// To be removed when the Mux Node SDK is updated\ninterface HeatmapApiResponse {\n asset_id?: string;\n video_id?: string;\n playback_id?: string;\n heatmap: number[];\n timeframe: [number, number];\n}\n\nexport interface HeatmapResponse {\n assetId?: string;\n videoId?: string;\n playbackId?: string;\n /** Array of 100 values representing engagement for each 1/100th of the video */\n heatmap: number[];\n timeframe: [number, number];\n}\n\n/**\n * Fetches engagement heatmap for a Mux asset.\n * Returns a length 100 array where each value represents how many times\n * that 1/100th of the video was watched.\n *\n * @param assetId - The Mux asset ID\n * @param options - Heatmap query options\n * @returns Heatmap data with 100 engagement values\n */\nexport async function getHeatmapForAsset(\n assetId: string,\n options: HeatmapOptions = {},\n): Promise<HeatmapResponse> {\n \"use step\";\n return fetchHeatmap(\"assets\", assetId, options);\n}\n\n/**\n * Fetches engagement heatmap for a Mux video ID.\n * Returns a length 100 array where each value represents how many times\n * that 1/100th of the video was watched.\n *\n * @param videoId - The Mux video ID\n * @param options - Heatmap query options\n * @returns Heatmap data with 100 engagement values\n */\nexport async function getHeatmapForVideo(\n videoId: string,\n options: HeatmapOptions = {},\n): Promise<HeatmapResponse> {\n \"use step\";\n return fetchHeatmap(\"videos\", videoId, options);\n}\n\n/**\n * Fetches engagement heatmap for a Mux playback ID.\n * Returns a length 100 array where each value represents how many times\n * that 1/100th of the video was watched.\n *\n * @param playbackId - The Mux playback ID\n * @param options - Heatmap query options\n * @returns Heatmap data with 100 engagement values\n */\nexport async function getHeatmapForPlaybackId(\n playbackId: string,\n options: HeatmapOptions = {},\n): Promise<HeatmapResponse> {\n \"use step\";\n return fetchHeatmap(\"playback-ids\", playbackId, options);\n}\n\n/**\n * Transforms the snake_case API response to camelCase for the public interface.\n * TODO: Remove when the Mux Node SDK is updated\n */\nfunction transformHeatmapResponse(\n response: HeatmapApiResponse,\n): HeatmapResponse {\n return {\n assetId: response.asset_id,\n videoId: response.video_id,\n playbackId: response.playback_id,\n heatmap: response.heatmap,\n timeframe: response.timeframe,\n };\n}\n\n/**\n * Internal helper to fetch heatmap from the Mux Data API.\n * Uses the raw HTTP methods on the Mux client since the SDK doesn't have\n * typed methods for the engagement endpoints yet.\n */\nasync function fetchHeatmap(\n identifierType: \"assets\" | \"videos\" | \"playback-ids\",\n id: string,\n options: HeatmapOptions,\n): Promise<HeatmapResponse> {\n \"use step\";\n const { timeframe = \"[24:hours]\", credentials } = options;\n\n const muxClient = await getMuxClientFromEnv(credentials);\n const mux = await muxClient.createClient();\n\n // Build query parameters\n const queryParams = new URLSearchParams();\n queryParams.append(\"timeframe[]\", timeframe);\n\n // Use the raw HTTP method since the SDK doesn't have typed engagement methods yet\n const path = `/data/v1/engagement/${identifierType}/${id}/heatmap?${queryParams.toString()}`;\n const response = await mux.get<unknown, HeatmapApiResponse>(path);\n\n return transformHeatmapResponse(response);\n}\n","import { getMuxClientFromEnv } from \"@mux/ai/lib/client-factory\";\nimport type { WorkflowCredentialsInput } from \"@mux/ai/types\";\n\nexport interface Hotspot {\n /** Inclusive start time in milliseconds */\n startMs: number;\n /** Exclusive end time in milliseconds */\n endMs: number;\n /** Hotspot score using distribution-based normalization (0-1) */\n score: number;\n}\n\nexport interface HotspotOptions {\n /** Maximum number of hotspots to return (default: 5) */\n limit?: number;\n /** Sort order: 'asc' or 'desc' (default: 'desc') */\n orderDirection?: \"asc\" | \"desc\";\n /** Order by field (default: 'score') */\n orderBy?: \"score\";\n /** Time window for results, e.g., ['7:days'] (default: ['7:days']) */\n timeframe?: string;\n /** Optional workflow credentials */\n credentials?: WorkflowCredentialsInput;\n}\n\n/** Raw API response structure from Mux Data API */\n// To be removed when the Mux Node SDK is updated\ninterface HotspotApiResponse {\n total_row_count: number | null;\n timeframe: [number, number];\n data: {\n asset_id?: string;\n video_id?: string;\n playback_id?: string;\n hotspots: Array<{\n start_ms: number;\n end_ms: number;\n score: number;\n }>;\n };\n}\n\nexport interface HotspotResponse {\n assetId?: string;\n videoId?: string;\n playbackId?: string;\n hotspots: Hotspot[];\n}\n\n/**\n * Fetches engagement hotspots for a Mux asset.\n * Returns the top N \"hot\" time ranges based on engagement data.\n *\n * @param assetId - The Mux asset ID\n * @param options - Hotspot query options\n * @returns Array of hotspots with time ranges and scores\n */\nexport async function getHotspotsForAsset(\n assetId: string,\n options: HotspotOptions = {},\n): Promise<Hotspot[]> {\n \"use step\";\n const response = await fetchHotspots(\"assets\", assetId, options);\n return response.hotspots;\n}\n\n/**\n * Fetches engagement hotspots for a Mux video ID.\n * Returns the top N \"hot\" time ranges based on engagement data.\n *\n * @param videoId - The Mux video ID\n * @param options - Hotspot query options\n * @returns Array of hotspots with time ranges and scores\n */\nexport async function getHotspotsForVideo(\n videoId: string,\n options: HotspotOptions = {},\n): Promise<Hotspot[]> {\n \"use step\";\n const response = await fetchHotspots(\"videos\", videoId, options);\n return response.hotspots;\n}\n\n/**\n * Fetches engagement hotspots for a Mux playback ID.\n * Returns the top N \"hot\" time ranges based on engagement data.\n *\n * @param playbackId - The Mux playback ID\n * @param options - Hotspot query options\n * @returns Array of hotspots with time ranges and scores\n */\nexport async function getHotspotsForPlaybackId(\n playbackId: string,\n options: HotspotOptions = {},\n): Promise<Hotspot[]> {\n \"use step\";\n const response = await fetchHotspots(\"playback-ids\", playbackId, options);\n return response.hotspots;\n}\n\n/**\n * Transforms the snake_case API response to camelCase for the public interface.\n * TODO: Remove when the Mux Node SDK is updated\n */\nfunction transformHotspotResponse(response: HotspotApiResponse): HotspotResponse {\n return {\n assetId: response.data.asset_id,\n videoId: response.data.video_id,\n playbackId: response.data.playback_id,\n hotspots: response.data.hotspots.map(h => ({\n startMs: h.start_ms,\n endMs: h.end_ms,\n score: h.score,\n })),\n };\n}\n\n/**\n * Internal helper to fetch hotspots from the Mux Data API.\n * Uses the raw HTTP methods on the Mux client since the SDK doesn't have\n * typed methods for the engagement endpoints yet.\n */\nasync function fetchHotspots(\n identifierType: \"assets\" | \"videos\" | \"playback-ids\",\n id: string,\n options: HotspotOptions,\n): Promise<HotspotResponse> {\n \"use step\";\n const {\n limit = 5,\n orderDirection = \"desc\",\n orderBy = \"score\",\n timeframe = \"[24:hours]\",\n credentials,\n } = options;\n\n const muxClient = await getMuxClientFromEnv(credentials);\n const mux = await muxClient.createClient();\n\n // Build query parameters\n const queryParams = new URLSearchParams();\n queryParams.append(\"limit\", String(limit));\n queryParams.append(\"order_direction\", orderDirection);\n queryParams.append(\"order_by\", orderBy);\n queryParams.append(\"timeframe[]\", timeframe);\n\n // Use the raw HTTP method since the SDK doesn't have typed engagement methods yet\n const path = `/data/v1/engagement/${identifierType}/${id}/hotspots?${queryParams.toString()}`;\n const response = await mux.get<unknown, HotspotApiResponse>(path);\n\n return transformHotspotResponse(response);\n}\n","import env from \"@mux/ai/env\";\nimport { resolveMuxSigningContext } from \"@mux/ai/lib/workflow-credentials\";\nimport type { WorkflowCredentialsInput } from \"@mux/ai/types\";\n\nimport type Mux from \"@mux/mux-node\";\n\n/**\n * Context required to sign URLs for signed playback IDs.\n */\nexport interface SigningContext {\n /** The signing key ID from Mux dashboard. */\n keyId: string;\n /** The base64-encoded private key from Mux dashboard. */\n keySecret: string;\n /** Token expiration time (e.g. '1h', '1d'). Defaults to '1h'. */\n expiration?: string;\n}\n\n/**\n * Token type determines which Mux service the token is valid for.\n */\nexport type TokenType = \"video\" | \"thumbnail\" | \"storyboard\" | \"gif\";\n\n/**\n * Resolves signing context from config or environment variables.\n * Returns undefined if signing keys are not configured.\n */\nexport function getMuxSigningContextFromEnv(): SigningContext | undefined {\n const keyId = env.MUX_SIGNING_KEY;\n const keySecret = env.MUX_PRIVATE_KEY;\n\n if (!keyId || !keySecret) {\n return undefined;\n }\n\n return { keyId, keySecret };\n}\n\n/**\n * Creates a Mux client configured for JWT signing.\n * This client is used internally for signing operations.\n */\nasync function createSigningClient(context: SigningContext): Promise<Mux> {\n // Dynamic import to prevent @mux/mux-node (and its transitive dep jose)\n // from being bundled into workflow VM code where `require` is unavailable.\n const { default: MuxClient } = await import(\"@mux/mux-node\");\n return new MuxClient({\n // These are not needed for signing, but the SDK requires them\n // Using empty strings as we only need the jwt functionality\n tokenId: env.MUX_TOKEN_ID || \"\",\n tokenSecret: env.MUX_TOKEN_SECRET || \"\",\n jwtSigningKey: context.keyId,\n jwtPrivateKey: context.keySecret,\n });\n}\n\n/**\n * Generates a signed token for a playback ID using the Mux SDK.\n *\n * @param playbackId - The Mux playback ID to sign\n * @param context - Signing context with key credentials\n * @param type - Token type (video, thumbnail, storyboard, gif)\n * @param params - Additional parameters for thumbnail/storyboard tokens (values will be stringified)\n * @returns Signed JWT token\n */\nexport async function signPlaybackId(\n playbackId: string,\n context: SigningContext,\n type: TokenType = \"video\",\n params?: Record<string, string | number>,\n): Promise<string> {\n \"use step\";\n const client = await createSigningClient(context);\n\n // Convert params to Record<string, string> as required by the SDK\n const stringParams = params ?\n Object.fromEntries(\n Object.entries(params).map(([key, value]) => [key, String(value)]),\n ) :\n undefined;\n\n return client.jwt.signPlaybackId(playbackId, {\n type,\n expiration: context.expiration || \"1h\",\n params: stringParams,\n });\n}\n\n/**\n * Appends a signed token to a Mux URL.\n *\n * @param url - The base Mux URL (e.g. https://image.mux.com/{playbackId}/thumbnail.png)\n * @param playbackId - The Mux playback ID\n * @param context - Signing context with key credentials\n * @param type - Token type for the URL\n * @param params - Additional parameters for the token\n * @returns URL with token query parameter appended\n */\nexport async function signUrl(\n url: string,\n playbackId: string,\n context?: SigningContext,\n type: TokenType = \"video\",\n params?: Record<string, string | number>,\n credentials?: WorkflowCredentialsInput,\n): Promise<string> {\n \"use step\";\n const resolvedContext = context ?? await resolveMuxSigningContext(credentials);\n if (!resolvedContext) {\n throw new Error(\n \"Signed playback ID requires signing credentials. \" +\n \"Provide muxSigningKey and muxPrivateKey via workflow credentials or set MUX_SIGNING_KEY and MUX_PRIVATE_KEY environment variables.\",\n );\n }\n const token = await signPlaybackId(playbackId, resolvedContext, type, params);\n const separator = url.includes(\"?\") ? \"&\" : \"?\";\n return `${url}${separator}token=${token}`;\n}\n","import { signUrl } from \"@mux/ai/lib/url-signing\";\nimport type { WorkflowCredentialsInput } from \"@mux/ai/types\";\n\nexport const DEFAULT_STORYBOARD_WIDTH = 640;\n\n/**\n * Generates a storyboard URL for the given playback ID.\n * If shouldSign is true, the URL will be signed with a token using credentials from environment variables.\n *\n * @param playbackId - The Mux playback ID\n * @param width - Width of the storyboard in pixels (default: 640)\n * @param shouldSign - Flag for whether or not to use signed playback IDs (default: false)\n * @returns Storyboard URL (signed if shouldSign is true)\n */\nexport async function getStoryboardUrl(\n playbackId: string,\n width: number = DEFAULT_STORYBOARD_WIDTH,\n shouldSign: boolean = false,\n credentials?: WorkflowCredentialsInput,\n): Promise<string> {\n \"use step\";\n const baseUrl = `https://image.mux.com/${playbackId}/storyboard.png`;\n\n if (shouldSign) {\n return signUrl(baseUrl, playbackId, undefined, \"storyboard\", { width }, credentials);\n }\n\n return `${baseUrl}?width=${width}`;\n}\n","import type { VTTCue } from \"@mux/ai/primitives/transcripts\";\nimport type { ChunkingStrategy, TextChunk } from \"@mux/ai/types\";\n\n/**\n * Simple token counter that approximates tokens by word count.\n * For production use with OpenAI, consider using a proper tokenizer like tiktoken.\n * This approximation is generally close enough for chunking purposes (1 token ≈ 0.75 words).\n */\nexport function estimateTokenCount(text: string): number {\n const words = text.trim().split(/\\s+/).length;\n return Math.ceil(words / 0.75);\n}\n\n/**\n * Chunks text into overlapping segments based on token count.\n *\n * @param text - The text to chunk\n * @param maxTokens - Maximum tokens per chunk\n * @param overlapTokens - Number of tokens to overlap between chunks\n * @returns Array of text chunks with metadata\n */\nexport function chunkByTokens(\n text: string,\n maxTokens: number,\n overlapTokens: number = 0,\n): TextChunk[] {\n if (!text.trim()) {\n return [];\n }\n\n const chunks: TextChunk[] = [];\n const words = text.trim().split(/\\s+/);\n\n // Convert tokens to approximate word count\n const wordsPerChunk = Math.floor(maxTokens * 0.75);\n const overlapWords = Math.floor(overlapTokens * 0.75);\n\n let chunkIndex = 0;\n let currentPosition = 0;\n\n while (currentPosition < words.length) {\n const chunkWords = words.slice(\n currentPosition,\n currentPosition + wordsPerChunk,\n );\n const chunkText = chunkWords.join(\" \");\n const tokenCount = estimateTokenCount(chunkText);\n\n chunks.push({\n id: `chunk-${chunkIndex}`,\n text: chunkText,\n tokenCount,\n });\n\n // Move forward by chunk size minus overlap\n currentPosition += wordsPerChunk - overlapWords;\n chunkIndex++;\n\n // Prevent infinite loop if overlap is too large\n if (currentPosition <= (chunkIndex - 1) * (wordsPerChunk - overlapWords)) {\n break;\n }\n }\n\n return chunks;\n}\n\n/**\n * Creates a TextChunk from a group of VTT cues.\n */\nfunction createChunkFromCues(cues: VTTCue[], index: number): TextChunk {\n const text = cues.map(c => c.text).join(\" \");\n return {\n id: `chunk-${index}`,\n text,\n tokenCount: estimateTokenCount(text),\n startTime: cues[0].startTime,\n endTime: cues[cues.length - 1].endTime,\n };\n}\n\n/**\n * Chunks VTT cues into groups that respect natural cue boundaries.\n * Splits at cue boundaries rather than mid-sentence, preserving accurate timestamps.\n *\n * @param cues - Array of VTT cues to chunk\n * @param maxTokens - Maximum tokens per chunk\n * @param overlapCues - Number of cues to overlap between chunks (default: 2)\n * @returns Array of text chunks with accurate start/end times\n */\nexport function chunkVTTCues(\n cues: VTTCue[],\n maxTokens: number,\n overlapCues: number = 2,\n): TextChunk[] {\n if (cues.length === 0)\n return [];\n\n const chunks: TextChunk[] = [];\n let currentCues: VTTCue[] = [];\n let currentTokens = 0;\n let chunkIndex = 0;\n\n for (let i = 0; i < cues.length; i++) {\n const cue = cues[i];\n const cueTokens = estimateTokenCount(cue.text);\n\n // If adding this cue would exceed limit, finalize current chunk\n if (currentTokens + cueTokens > maxTokens && currentCues.length > 0) {\n chunks.push(createChunkFromCues(currentCues, chunkIndex));\n chunkIndex++;\n\n // Start new chunk with overlap from end of previous\n const overlapStart = Math.max(0, currentCues.length - overlapCues);\n currentCues = currentCues.slice(overlapStart);\n currentTokens = currentCues.reduce(\n (sum, c) => sum + estimateTokenCount(c.text),\n 0,\n );\n }\n\n currentCues.push(cue);\n currentTokens += cueTokens;\n }\n\n // Don't forget the last chunk\n if (currentCues.length > 0) {\n chunks.push(createChunkFromCues(currentCues, chunkIndex));\n }\n\n return chunks;\n}\n\n/**\n * Chunks text according to the specified strategy.\n *\n * @param text - The text to chunk\n * @param strategy - The chunking strategy to use\n * @returns Array of text chunks\n */\nexport function chunkText(text: string, strategy: ChunkingStrategy): TextChunk[] {\n switch (strategy.type) {\n case \"token\": {\n return chunkByTokens(text, strategy.maxTokens, strategy.overlap ?? 0);\n }\n default: {\n const exhaustiveCheck: never = strategy as never;\n throw new Error(`Unsupported chunking strategy: ${exhaustiveCheck}`);\n }\n }\n}\n","import { signUrl } from \"@mux/ai/lib/url-signing\";\nimport type { WorkflowCredentialsInput } from \"@mux/ai/types\";\n\nexport interface ThumbnailOptions {\n /** Interval between thumbnails in seconds (default: 10) */\n interval?: number;\n /** Width of the thumbnail in pixels (default: 640) */\n width?: number;\n /** Flag for whether or not to use signed playback IDs (default: false) */\n shouldSign?: boolean;\n /** Maximum number of thumbnails to generate. When set, samples are evenly distributed with first and last frames pinned. */\n maxSamples?: number;\n /** Workflow credentials for signing (optional). */\n credentials?: WorkflowCredentialsInput;\n}\n\n/**\n * Generates thumbnail URLs at regular intervals based on video duration.\n * If shouldSign is true, the URLs will be signed with tokens using credentials from environment variables.\n *\n * @param playbackId - The Mux playback ID\n * @param duration - Video duration in seconds\n * @param options - Thumbnail generation options\n * @returns Array of thumbnail URLs (signed if shouldSign is true)\n */\nexport async function getThumbnailUrls(\n playbackId: string,\n duration: number,\n options: ThumbnailOptions = {},\n): Promise<string[]> {\n \"use step\";\n const { interval = 10, width = 640, shouldSign = false, maxSamples, credentials } = options;\n let timestamps: number[] = [];\n\n if (duration <= 50) {\n const spacing = duration / 6;\n for (let i = 1; i <= 5; i++) {\n timestamps.push(Math.round(i * spacing));\n }\n } else {\n for (let time = 0; time < duration; time += interval) {\n timestamps.push(time);\n }\n }\n\n // Apply maxSamples cap if specified and we have more timestamps than the limit\n if (maxSamples !== undefined && timestamps.length > maxSamples) {\n const newTimestamps: number[] = [];\n\n // Always include first frame\n newTimestamps.push(0);\n\n // If maxSamples >= 2, add evenly distributed middle frames and last frame\n if (maxSamples >= 2) {\n const spacing = duration / (maxSamples - 1);\n for (let i = 1; i < maxSamples - 1; i++) {\n newTimestamps.push(spacing * i);\n }\n // Always include last frame\n newTimestamps.push(duration);\n }\n\n timestamps = newTimestamps;\n }\n\n const baseUrl = `https://image.mux.com/${playbackId}/thumbnail.png`;\n\n const urlPromises = timestamps.map(async (time) => {\n if (shouldSign) {\n return signUrl(baseUrl, playbackId, undefined, \"thumbnail\", { time, width }, credentials);\n }\n\n return `${baseUrl}?time=${time}&width=${width}`;\n });\n\n return Promise.all(urlPromises);\n}\n","import { signUrl } from \"@mux/ai/lib/url-signing\";\nimport type { AssetTextTrack, MuxAsset, WorkflowCredentialsInput } from \"@mux/ai/types\";\n\n/** A single cue from a VTT file with timing info. */\nexport interface VTTCue {\n startTime: number;\n endTime: number;\n text: string;\n}\n\nexport interface TranscriptFetchOptions {\n languageCode?: string;\n cleanTranscript?: boolean;\n /** Optional signing context for signed playback IDs */\n shouldSign?: boolean;\n credentials?: WorkflowCredentialsInput;\n /**\n * When true, throws if no usable transcript can be retrieved (no ready text track,\n * missing track id, fetch error, or empty transcript).\n *\n * Default behavior is non-fatal and returns an empty `transcriptText`.\n */\n required?: boolean;\n}\n\nexport interface TranscriptResult {\n transcriptText: string;\n transcriptUrl?: string;\n track?: AssetTextTrack;\n}\n\nexport function getReadyTextTracks(asset: MuxAsset): AssetTextTrack[] {\n return (asset.tracks || []).filter(\n track => track.type === \"text\" && track.status === \"ready\",\n );\n}\n\nexport function findCaptionTrack(asset: MuxAsset, languageCode?: string): AssetTextTrack | undefined {\n const tracks = getReadyTextTracks(asset);\n if (!tracks.length)\n return undefined;\n\n if (!languageCode) {\n return tracks[0];\n }\n\n return tracks.find(\n track =>\n track.text_type === \"subtitles\" &&\n track.language_code === languageCode,\n );\n}\n\nexport function extractTextFromVTT(vttContent: string): string {\n if (!vttContent.trim()) {\n return \"\";\n }\n\n const lines = vttContent.split(\"\\n\");\n const textLines: string[] = [];\n\n for (let i = 0; i < lines.length; i++) {\n const line = lines[i].trim();\n\n if (!line)\n continue;\n if (line === \"WEBVTT\")\n continue;\n if (line.startsWith(\"NOTE \"))\n continue;\n if (line.includes(\"-->\"))\n continue;\n if (/^[\\w-]+$/.test(line) && !line.includes(\" \"))\n continue;\n if (line.startsWith(\"STYLE\") || line.startsWith(\"REGION\"))\n continue;\n\n const cleanLine = line.replace(/<[^>]*>/g, \"\").trim();\n\n if (cleanLine) {\n textLines.push(cleanLine);\n }\n }\n\n return textLines.join(\" \").replace(/\\s+/g, \" \").trim();\n}\n\nexport function vttTimestampToSeconds(timestamp: string): number {\n const parts = timestamp.split(\":\");\n if (parts.length !== 3)\n return 0;\n\n const hours = Number.parseInt(parts[0], 10) || 0;\n const minutes = Number.parseInt(parts[1], 10) || 0;\n const seconds = Number.parseFloat(parts[2]) || 0;\n\n return hours * 3600 + minutes * 60 + seconds;\n}\n\n/**\n * Converts seconds to a human-readable timestamp.\n * Returns M:SS for durations under an hour, H:MM:SS for an hour or more.\n *\n * @param seconds - The number of seconds to convert\n * @returns A formatted timestamp string (e.g., \"2:05\" or \"01:02:05\")\n */\nexport function secondsToTimestamp(seconds: number): string {\n const rounded = Math.max(0, Math.floor(seconds));\n const hours = Math.floor(rounded / 3600);\n const minutes = Math.floor((rounded % 3600) / 60);\n const remainingSeconds = rounded % 60;\n\n if (hours > 0) {\n return `${hours.toString().padStart(2, \"0\")}:${minutes.toString().padStart(2, \"0\")}:${remainingSeconds.toString().padStart(2, \"0\")}`;\n }\n return `${minutes}:${remainingSeconds.toString().padStart(2, \"0\")}`;\n}\n\nexport function extractTimestampedTranscript(vttContent: string): string {\n if (!vttContent.trim()) {\n return \"\";\n }\n\n const lines = vttContent.split(\"\\n\");\n const segments: Array<{ time: number; text: string }> = [];\n\n for (let i = 0; i < lines.length; i++) {\n const line = lines[i].trim();\n\n if (line.includes(\"-->\")) {\n const startTime = line.split(\" --> \")[0].trim();\n const timeInSeconds = vttTimestampToSeconds(startTime);\n\n let j = i + 1;\n while (j < lines.length && !lines[j].trim()) {\n j++;\n }\n\n if (j < lines.length) {\n const text = lines[j].trim().replace(/<[^>]*>/g, \"\");\n if (text) {\n segments.push({ time: timeInSeconds, text });\n }\n }\n }\n }\n\n return segments\n .map(segment => `[${Math.floor(segment.time)}s] ${segment.text}`)\n .join(\"\\n\");\n}\n\n/**\n * Parses VTT content into structured cues with timing.\n *\n * @param vttContent - Raw VTT file content\n * @returns Array of VTT cues with start/end times and text\n */\nexport function parseVTTCues(vttContent: string): VTTCue[] {\n if (!vttContent.trim())\n return [];\n\n const lines = vttContent.split(\"\\n\");\n const cues: VTTCue[] = [];\n\n for (let i = 0; i < lines.length; i++) {\n const line = lines[i].trim();\n\n if (line.includes(\"-->\")) {\n const [startStr, endStr] = line.split(\" --> \").map(s => s.trim());\n const startTime = vttTimestampToSeconds(startStr);\n const endTime = vttTimestampToSeconds(endStr.split(\" \")[0]); // Handle cue settings\n\n // Collect text lines until empty line or next timestamp\n const textLines: string[] = [];\n let j = i + 1;\n while (j < lines.length && lines[j].trim() && !lines[j].includes(\"-->\")) {\n const cleanLine = lines[j].trim().replace(/<[^>]*>/g, \"\");\n if (cleanLine)\n textLines.push(cleanLine);\n j++;\n }\n\n if (textLines.length > 0) {\n cues.push({\n startTime,\n endTime,\n text: textLines.join(\" \"),\n });\n }\n }\n }\n\n return cues;\n}\n\n/**\n * Builds a transcript URL for the given playback ID and track ID.\n * If a signing context is provided, the URL will be signed with a token.\n *\n * @param playbackId - The Mux playback ID\n * @param trackId - The text track ID\n * @param shouldSign - Flag for whether or not to use signed playback IDs\n * @returns Transcript URL (signed if context provided)\n */\nexport async function buildTranscriptUrl(\n playbackId: string,\n trackId: string,\n shouldSign: boolean = false,\n credentials?: WorkflowCredentialsInput,\n): Promise<string> {\n \"use step\";\n const baseUrl = `https://stream.mux.com/${playbackId}/text/${trackId}.vtt`;\n\n if (shouldSign) {\n return signUrl(baseUrl, playbackId, undefined, \"video\", undefined, credentials);\n }\n\n return baseUrl;\n}\n\nexport async function fetchTranscriptForAsset(\n asset: MuxAsset,\n playbackId: string,\n options: TranscriptFetchOptions = {},\n): Promise<TranscriptResult> {\n \"use step\";\n const {\n languageCode,\n cleanTranscript = true,\n shouldSign,\n credentials,\n required = false,\n } = options;\n const track = findCaptionTrack(asset, languageCode);\n\n if (!track) {\n if (required) {\n const availableLanguages = getReadyTextTracks(asset)\n .map(t => t.language_code)\n .filter(Boolean)\n .join(\", \");\n throw new Error(\n `No transcript track found${languageCode ? ` for language '${languageCode}'` : \"\"}. Available languages: ${availableLanguages || \"none\"}`,\n );\n }\n return { transcriptText: \"\" };\n }\n\n if (!track.id) {\n if (required) {\n throw new Error(\"Transcript track is missing an id\");\n }\n return { transcriptText: \"\", track };\n }\n\n const transcriptUrl = await buildTranscriptUrl(playbackId, track.id, shouldSign, credentials);\n\n try {\n const response = await fetch(transcriptUrl);\n if (!response.ok) {\n if (required) {\n throw new Error(`Failed to fetch transcript (HTTP ${response.status})`);\n }\n return { transcriptText: \"\", transcriptUrl, track };\n }\n\n const rawVtt = await response.text();\n const transcriptText = cleanTranscript ? extractTextFromVTT(rawVtt) : rawVtt;\n\n if (required && !transcriptText.trim()) {\n throw new Error(\"Transcript is empty\");\n }\n\n return { transcriptText, transcriptUrl, track };\n } catch (error) {\n if (required) {\n throw new Error(\n `Failed to fetch transcript: ${error instanceof Error ? error.message : \"Unknown error\"}`,\n );\n }\n console.warn(\"Failed to fetch transcript:\", error);\n return { transcriptText: \"\", transcriptUrl, track };\n }\n}\n","export * from \"./ask-questions\";\nexport * from \"./burned-in-captions\";\nexport * from \"./chapters\";\nexport * from \"./embeddings\";\nexport * from \"./moderation\";\nexport * from \"./summarization\";\nexport * from \"./translate-audio\";\nexport * from \"./translate-captions\";\n","import { generateText, Output } from \"ai\";\nimport dedent from \"dedent\";\nimport { z } from \"zod\";\n\nimport type { ImageDownloadOptions } from \"@mux/ai/lib/image-download\";\nimport { downloadImageAsBase64 } from \"@mux/ai/lib/image-download\";\nimport { getAssetDurationSecondsFromAsset, getPlaybackIdForAsset } from \"@mux/ai/lib/mux-assets\";\nimport { createPromptBuilder, createTranscriptSection } from \"@mux/ai/lib/prompt-builder\";\nimport { createLanguageModelFromConfig, resolveLanguageModelConfig } from \"@mux/ai/lib/providers\";\nimport type { ModelIdByProvider, SupportedProvider } from \"@mux/ai/lib/providers\";\nimport { withRetry } from \"@mux/ai/lib/retry\";\nimport { resolveMuxSigningContext } from \"@mux/ai/lib/workflow-credentials\";\nimport { getStoryboardUrl } from \"@mux/ai/primitives/storyboards\";\nimport { fetchTranscriptForAsset } from \"@mux/ai/primitives/transcripts\";\nimport type { ImageSubmissionMode, MuxAIOptions, TokenUsage, WorkflowCredentialsInput } from \"@mux/ai/types\";\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Types\n// ─────────────────────────────────────────────────────────────────────────────\n\n/** A single yes/no question to be answered about video content. */\nexport interface Question {\n /** The question text */\n question: string;\n}\n\n/** A single answer to a question. */\nexport interface QuestionAnswer {\n /** The original question */\n question: string;\n /** Answer selected from the allowed options */\n answer: string;\n /** Confidence score between 0 and 1 */\n confidence: number;\n /** Reasoning explaining the answer based on observable evidence */\n reasoning: string;\n}\n\n/** Configuration options for askQuestions workflow. */\nexport interface AskQuestionsOptions extends MuxAIOptions {\n /** AI provider to run (defaults to 'openai'). */\n provider?: SupportedProvider;\n /** Provider-specific chat model identifier. */\n model?: ModelIdByProvider[SupportedProvider];\n /** Allowed answers for each question (defaults to [\"yes\", \"no\"]). */\n answerOptions?: string[];\n /** Fetch transcript alongside storyboard (defaults to true). */\n includeTranscript?: boolean;\n /** Strip timestamps/markup from transcripts (defaults to true). */\n cleanTranscript?: boolean;\n /** How storyboard should be delivered to the provider (defaults to 'url'). */\n imageSubmissionMode?: ImageSubmissionMode;\n /** Fine-tune storyboard downloads when imageSubmissionMode === 'base64'. */\n imageDownloadOptions?: ImageDownloadOptions;\n /** Storyboard width in pixels (defaults to 640). */\n storyboardWidth?: number;\n}\n\n/** Structured return payload for askQuestions workflow. */\nexport interface AskQuestionsResult {\n /** Asset ID passed into the workflow. */\n assetId: string;\n /** Array of answers for each question. */\n answers: QuestionAnswer[];\n /** Storyboard image URL that was analyzed. */\n storyboardUrl: string;\n /** Token usage from the AI provider (for efficiency/cost analysis). */\n usage?: TokenUsage;\n /** Raw transcript text used for analysis (when includeTranscript is true). */\n transcriptText?: string;\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Zod Schemas\n// ─────────────────────────────────────────────────────────────────────────────\n\n/** Zod schema for a single answer. */\nexport const questionAnswerSchema = z.object({\n question: z.string(),\n answer: z.string(),\n confidence: z.number(),\n reasoning: z.string(),\n});\n\nexport type QuestionAnswerType = z.infer<typeof questionAnswerSchema>;\n\nfunction createAskQuestionsSchema(allowedAnswers: [string, ...string[]]) {\n const answerSchema = z.enum(allowedAnswers);\n\n return z.object({\n answers: z.array(\n questionAnswerSchema.extend({\n answer: answerSchema,\n }),\n ),\n });\n}\n\ntype AskQuestionsSchema = ReturnType<typeof createAskQuestionsSchema>;\nexport type AskQuestionsType = z.infer<AskQuestionsSchema>;\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Prompts\n// ─────────────────────────────────────────────────────────────────────────────\n\nconst SYSTEM_PROMPT = dedent`\n <role>\n You are a video content analyst specializing in classification tasks.\n Your job is to answer questions about video content based on storyboard\n images and optional transcript data.\n </role>\n\n <context>\n You will receive:\n - A storyboard image containing multiple sequential frames from a video\n - A list of questions about the video content\n - Optionally, a transcript of the audio/dialogue\n\n The storyboard frames are arranged in a grid and represent the visual\n progression of the content over time. Read frames left-to-right,\n top-to-bottom to understand the temporal sequence.\n </context>\n\n <transcript_guidance>\n When a transcript is provided:\n - Use it to understand spoken content, dialogue, and audio context\n - Correlate transcript with visual frames for comprehensive analysis\n - Consider both visual and audio evidence when answering questions\n - If transcript and visuals conflict, trust the visual evidence\n </transcript_guidance>\n\n <task>\n For each question provided, you must:\n 1. Analyze the storyboard frames and transcript (if provided)\n 2. Answer with ONLY the allowed response options - no other values are acceptable\n 3. Provide a confidence score between 0 and 1 reflecting your certainty\n 4. Explain your reasoning based on observable evidence\n </task>\n\n <answer_guidelines>\n - Choose the affirmative option only if you have clear evidence supporting it\n - Choose the negative/contradicting option if evidence contradicts or if insufficient evidence exists\n - Confidence should reflect the clarity and strength of evidence:\n * 0.9-1.0: Clear, unambiguous evidence\n * 0.7-0.9: Strong evidence with minor ambiguity\n * 0.5-0.7: Moderate evidence or some conflicting signals\n * 0.3-0.5: Weak evidence or significant ambiguity\n * 0.0-0.3: Very uncertain, minimal relevant evidence\n - Reasoning should cite specific visual or audio evidence\n - Be precise: cite specific frames, objects, actions, or transcript quotes\n </answer_guidelines>\n\n <constraints>\n - You MUST answer every question with one of the allowed response options\n - Only describe observable evidence from frames or transcript\n - Do not fabricate details or make unsupported assumptions\n - Return structured data matching the requested schema exactly\n </constraints>\n\n <language_guidelines>\n When explaining reasoning:\n - Describe content directly, not the medium\n - BAD: \"The video shows a person running\"\n - GOOD: \"A person runs through a park\"\n - Be specific and evidence-based\n </language_guidelines>`;\n\nfunction buildSystemPrompt(allowedAnswers: string[]): string {\n const answerList = allowedAnswers.map(answer => `\"${answer}\"`).join(\", \");\n\n return `${SYSTEM_PROMPT}\\n\\n${dedent`\n <response_options>\n Allowed answers: ${answerList}\n </response_options>\n `}`;\n}\n\ntype AskQuestionsPromptSections = \"questions\";\n\nconst askQuestionsPromptBuilder = createPromptBuilder<AskQuestionsPromptSections>({\n template: {\n questions: {\n tag: \"questions\",\n content: \"Please answer the following yes/no questions about this video:\",\n },\n },\n sectionOrder: [\"questions\"],\n});\n\nfunction buildUserPrompt(\n questions: Question[],\n transcriptText?: string,\n isCleanTranscript: boolean = true,\n): string {\n const questionsList = questions\n .map((q, idx) => `${idx + 1}. ${q.question}`)\n .join(\"\\n\");\n\n const questionsContent = dedent`\n Please answer the following yes/no questions about this video:\n\n ${questionsList}`;\n\n if (!transcriptText) {\n return askQuestionsPromptBuilder.build({ questions: questionsContent });\n }\n\n const format = isCleanTranscript ? \"plain text\" : \"WebVTT\";\n const transcriptSection = createTranscriptSection(transcriptText, format);\n\n return askQuestionsPromptBuilder.buildWithContext(\n { questions: questionsContent },\n [transcriptSection],\n );\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Implementation\n// ─────────────────────────────────────────────────────────────────────────────\n\ninterface AnalysisResponse {\n result: AskQuestionsType;\n usage: TokenUsage;\n}\n\nasync function fetchImageAsBase64(\n imageUrl: string,\n imageDownloadOptions?: ImageDownloadOptions,\n): Promise<string> {\n \"use step\";\n\n const downloadResult = await downloadImageAsBase64(imageUrl, imageDownloadOptions);\n return downloadResult.base64Data;\n}\n\nasync function analyzeQuestionsWithStoryboard(\n imageDataUrl: string,\n provider: SupportedProvider,\n modelId: string,\n userPrompt: string,\n systemPrompt: string,\n allowedAnswers: [string, ...string[]],\n credentials?: WorkflowCredentialsInput,\n): Promise<AnalysisResponse> {\n \"use step\";\n const model = await createLanguageModelFromConfig(provider, modelId, credentials);\n const responseSchema = createAskQuestionsSchema(allowedAnswers);\n\n const response = await generateText({\n model,\n output: Output.object({ schema: responseSchema }),\n experimental_telemetry: { isEnabled: true },\n messages: [\n {\n role: \"system\",\n content: systemPrompt,\n },\n {\n role: \"user\",\n content: [\n { type: \"text\", text: userPrompt },\n { type: \"image\", image: imageDataUrl },\n ],\n },\n ],\n });\n\n return {\n result: {\n answers: response.output.answers.map(answer => ({\n ...answer,\n // Strip numbering prefix (e.g., \"1. \" or \"2. \") from questions\n question: answer.question.replace(/^\\d+\\.\\s*/, \"\"),\n confidence: Math.min(1, Math.max(0, answer.confidence)),\n })),\n },\n usage: {\n inputTokens: response.usage.inputTokens,\n outputTokens: response.usage.outputTokens,\n totalTokens: response.usage.totalTokens,\n reasoningTokens: response.usage.reasoningTokens,\n cachedInputTokens: response.usage.cachedInputTokens,\n },\n };\n}\n\n/**\n * Answer questions about a Mux video asset by analyzing storyboard frames and transcript.\n * Defaults to yes/no answers unless `answerOptions` are provided.\n *\n * This workflow takes a list of questions and returns structured answers with confidence\n * scores and reasoning for each question. All questions are processed in a single LLM call for\n * efficiency.\n *\n * @param assetId - The Mux asset ID to analyze\n * @param questions - Array of questions to answer (each must have a 'question' field)\n * @param options - Configuration options for the workflow\n * @returns Structured answers with confidence scores and reasoning\n *\n * @example\n * ```typescript\n * const result = await askQuestions(\"abc123\", [\n * { question: \"Does this video contain cooking?\" },\n * { question: \"Are there people visible in the video?\" },\n * ]);\n *\n * console.log(result.answers[0]);\n * // {\n * // question: \"Does this video contain cooking?\",\n * // answer: \"yes\",\n * // confidence: 0.95,\n * // reasoning: \"A chef prepares ingredients and cooks in a kitchen throughout the video.\"\n * // }\n * ```\n */\nexport async function askQuestions(\n assetId: string,\n questions: Question[],\n options?: AskQuestionsOptions,\n): Promise<AskQuestionsResult> {\n \"use workflow\";\n\n // Validate questions array is non-empty\n if (!questions || questions.length === 0) {\n throw new Error(\"At least one question must be provided\");\n }\n\n // Validate each question has valid text\n questions.forEach((q, idx) => {\n if (!q.question || typeof q.question !== \"string\" || !q.question.trim()) {\n throw new Error(\n `Question at index ${idx} is invalid: must have non-empty 'question' field`,\n );\n }\n });\n\n const {\n provider = \"openai\",\n model,\n answerOptions,\n includeTranscript = true,\n cleanTranscript = true,\n imageSubmissionMode = \"url\",\n imageDownloadOptions,\n storyboardWidth = 640,\n credentials,\n } = options ?? {};\n\n const normalizedAnswerOptions = Array.from(\n new Set(\n (answerOptions?.length ? answerOptions : [\"yes\", \"no\"])\n .map(option => option.trim())\n .filter(Boolean),\n ),\n );\n\n if (!normalizedAnswerOptions.length) {\n throw new Error(\"answerOptions must include at least one non-empty value\");\n }\n\n const allowedAnswers = normalizedAnswerOptions as [string, ...string[]];\n\n const modelConfig = resolveLanguageModelConfig({\n ...options,\n model,\n provider: provider as SupportedProvider,\n });\n // Fetch asset data and playback ID from Mux\n const { asset: assetData, playbackId, policy } = await getPlaybackIdForAsset(assetId, credentials);\n\n const assetDurationSeconds = getAssetDurationSecondsFromAsset(assetData);\n\n // Resolve signing context for signed playback IDs\n const signingContext = await resolveMuxSigningContext(credentials);\n if (policy === \"signed\" && !signingContext) {\n throw new Error(\n \"Signed playback ID requires signing credentials. \" +\n \"Set MUX_SIGNING_KEY and MUX_PRIVATE_KEY environment variables.\",\n );\n }\n\n const transcriptText =\n includeTranscript ?\n (await fetchTranscriptForAsset(assetData, playbackId, {\n cleanTranscript,\n shouldSign: policy === \"signed\",\n })).transcriptText :\n \"\";\n\n // Build the user prompt with questions and optional transcript\n const userPrompt = buildUserPrompt(questions, transcriptText, cleanTranscript);\n const systemPrompt = buildSystemPrompt(normalizedAnswerOptions);\n\n // Generate storyboard URL (signed if needed)\n const imageUrl = await getStoryboardUrl(\n playbackId,\n storyboardWidth,\n policy === \"signed\",\n credentials,\n );\n\n let analysisResponse: AnalysisResponse;\n\n try {\n if (imageSubmissionMode === \"base64\") {\n const base64Data = await fetchImageAsBase64(imageUrl, imageDownloadOptions);\n analysisResponse = await analyzeQuestionsWithStoryboard(\n base64Data,\n modelConfig.provider,\n modelConfig.modelId,\n userPrompt,\n systemPrompt,\n allowedAnswers,\n credentials,\n );\n } else {\n // URL-based submission with retry\n analysisResponse = await withRetry(() =>\n analyzeQuestionsWithStoryboard(\n imageUrl,\n modelConfig.provider,\n modelConfig.modelId,\n userPrompt,\n systemPrompt,\n allowedAnswers,\n credentials,\n ),\n );\n }\n } catch (error: unknown) {\n throw new Error(\n `Failed to analyze questions with ${provider}: ${\n error instanceof Error ? error.message : \"Unknown error\"\n }`,\n );\n }\n\n if (!analysisResponse.result?.answers) {\n throw new Error(`Failed to get answers for asset ${assetId}`);\n }\n\n // Validate we got answers for all questions\n if (analysisResponse.result.answers.length !== questions.length) {\n throw new Error(\n `Expected ${questions.length} answers but received ${analysisResponse.result.answers.length}`,\n );\n }\n\n return {\n assetId,\n answers: analysisResponse.result.answers,\n storyboardUrl: imageUrl,\n usage: {\n ...analysisResponse.usage,\n metadata: {\n assetDurationSeconds,\n },\n },\n transcriptText: transcriptText || undefined,\n };\n}\n","import pRetry, { AbortError } from \"p-retry\";\n\nconst BASE64_CHUNK_SIZE = 0x8000;\n\nfunction bytesToBase64(bytes: Uint8Array): string {\n if (bytes.length === 0) {\n return \"\";\n }\n let binary = \"\";\n for (let i = 0; i < bytes.length; i += BASE64_CHUNK_SIZE) {\n const chunk = bytes.subarray(i, i + BASE64_CHUNK_SIZE);\n binary += String.fromCharCode(...chunk);\n }\n if (typeof globalThis.btoa !== \"function\") {\n throw new TypeError(\"Base64 encoder is not available in this environment.\");\n }\n return globalThis.btoa(binary);\n}\n\nexport interface ImageDownloadOptions {\n /** Request timeout in milliseconds (default: 10000) */\n timeout?: number;\n /** Maximum number of retry attempts (default: 3) */\n retries?: number;\n /** Base delay between retries in milliseconds (default: 1000) */\n retryDelay?: number;\n /** Maximum delay between retries in milliseconds (default: 10000) */\n maxRetryDelay?: number;\n /** Whether to use exponential backoff (default: true) */\n exponentialBackoff?: boolean;\n}\n\nexport interface ImageDownloadResult {\n /** Base64 encoded image data with data URI prefix (e.g., \"data:image/png;base64,iVBORw0K...\") */\n base64Data: string;\n /** Raw image bytes for multipart/form-data uploads */\n buffer: Uint8Array;\n /** Original image URL */\n url: string;\n /** Content type of the downloaded image */\n contentType: string;\n /** Size of the downloaded image in bytes */\n sizeBytes: number;\n /** Number of retry attempts made (0 if successful on first try) */\n attempts: number;\n}\n\nexport interface AnthropicFileUploadResult {\n /** Anthropic Files API file ID */\n fileId: string;\n /** Original image URL */\n url: string;\n /** Content type of the uploaded image */\n contentType: string;\n /** Size of the uploaded image in bytes */\n sizeBytes: number;\n}\n\nconst DEFAULT_OPTIONS: Required<ImageDownloadOptions> = {\n timeout: 10000,\n retries: 3,\n retryDelay: 1000,\n maxRetryDelay: 10000,\n exponentialBackoff: true,\n};\n\n/**\n * Downloads an image from a URL and converts it to base64 with robust retry logic\n *\n * @param url - The image URL to download\n * @param options - Download configuration options\n * @returns Promise resolving to ImageDownloadResult with base64 data and metadata\n * @throws Error if download fails after all retries\n */\nexport async function downloadImageAsBase64(\n url: string,\n options: ImageDownloadOptions = {},\n): Promise<ImageDownloadResult> {\n \"use step\";\n const opts = { ...DEFAULT_OPTIONS, ...options };\n let attemptCount = 0;\n\n return pRetry(\n async () => {\n attemptCount++;\n const controller = new AbortController();\n const timeoutId = setTimeout(() => controller.abort(), opts.timeout);\n\n try {\n const response = await fetch(url, {\n signal: controller.signal,\n headers: {\n \"User-Agent\": \"@mux/ai image downloader\",\n },\n });\n\n clearTimeout(timeoutId);\n\n if (!response.ok) {\n // Don't retry 4xx errors (except 429 rate limiting)\n if (response.status >= 400 && response.status < 500 && response.status !== 429) {\n throw new AbortError(`HTTP ${response.status}: ${response.statusText}`);\n }\n throw new Error(`HTTP ${response.status}: ${response.statusText}`);\n }\n\n const contentType = response.headers.get(\"content-type\");\n if (!contentType?.startsWith(\"image/\")) {\n throw new AbortError(`Invalid content type: ${contentType}. Expected image/*`);\n }\n\n const arrayBuffer = await response.arrayBuffer();\n const buffer = new Uint8Array(arrayBuffer);\n\n if (buffer.length === 0) {\n throw new AbortError(\"Downloaded image is empty\");\n }\n\n // Convert to base64 with data URI prefix\n const base64Data = `data:${contentType};base64,${bytesToBase64(buffer)}`;\n\n return {\n base64Data,\n buffer,\n url,\n contentType,\n sizeBytes: buffer.length,\n attempts: attemptCount,\n };\n } catch (error) {\n clearTimeout(timeoutId);\n\n // If it's an AbortError (non-retryable), re-throw it\n if (error instanceof AbortError) {\n throw error;\n }\n\n // For network errors, timeout errors, etc., wrap in retryable error\n if (error instanceof Error) {\n if (error.name === \"AbortError\") {\n throw new Error(`Request timeout after ${opts.timeout}ms`);\n }\n throw new Error(`Download failed: ${error.message}`);\n }\n\n throw new Error(\"Unknown download error\");\n }\n },\n {\n retries: opts.retries,\n minTimeout: opts.retryDelay,\n maxTimeout: opts.maxRetryDelay,\n factor: opts.exponentialBackoff ? 2 : 1,\n randomize: true, // Add jitter to prevent thundering herd\n onFailedAttempt: (error) => {\n console.warn(`Image download attempt ${error.attemptNumber} failed for ${url}`);\n if (error.retriesLeft > 0) {\n console.warn(`Retrying... (${error.retriesLeft} attempts left)`);\n }\n },\n },\n );\n}\n\n/**\n * Downloads multiple images concurrently with controlled concurrency\n *\n * @param urls - Array of image URLs to download\n * @param options - Download configuration options\n * @param maxConcurrent - Maximum concurrent downloads (default: 5)\n * @returns Promise resolving to array of ImageDownloadResult (in same order as input URLs)\n */\nexport async function downloadImagesAsBase64(\n urls: string[],\n options: ImageDownloadOptions = {},\n maxConcurrent: number = 5,\n): Promise<ImageDownloadResult[]> {\n \"use step\";\n const results: ImageDownloadResult[] = [];\n\n for (let i = 0; i < urls.length; i += maxConcurrent) {\n const batch = urls.slice(i, i + maxConcurrent);\n const batchPromises = batch.map(url => downloadImageAsBase64(url, options));\n const batchResults = await Promise.all(batchPromises);\n results.push(...batchResults);\n }\n\n return results;\n}\n\n/**\n * Uploads an image to Anthropic Files API for use in messages\n *\n * @param url - The image URL to download and upload\n * @param anthropicApiKey - Anthropic API key\n * @param options - Download configuration options\n * @returns Promise resolving to AnthropicFileUploadResult with file ID and metadata\n * @throws Error if download or upload fails\n */\nexport async function uploadImageToAnthropicFiles(\n url: string,\n anthropicApiKey: string,\n options: ImageDownloadOptions = {},\n): Promise<AnthropicFileUploadResult> {\n \"use step\";\n // First download the image\n const downloadResult = await downloadImageAsBase64(url, options);\n\n // Create form data for Files API upload\n const formData = new FormData();\n\n // Create a Blob from the buffer for form data\n const imageBlob = new Blob([downloadResult.buffer], {\n type: downloadResult.contentType,\n });\n\n // Get file extension from content type\n const extension = downloadResult.contentType.split(\"/\")[1] || \"png\";\n formData.append(\"file\", imageBlob, `image.${extension}`);\n\n // Upload to Anthropic Files API\n const response = await fetch(\"https://api.anthropic.com/v1/files\", {\n method: \"POST\",\n headers: {\n \"x-api-key\": anthropicApiKey,\n \"anthropic-version\": \"2023-06-01\",\n \"anthropic-beta\": \"files-api-2025-04-14\",\n // Don't set Content-Type header - let fetch set it with boundary for multipart\n },\n body: formData,\n });\n\n if (!response.ok) {\n const errorText = await response.text();\n throw new Error(`Anthropic Files API error: ${response.status} ${response.statusText} - ${errorText}`);\n }\n\n const fileResult = await response.json() as { id: string };\n\n return {\n fileId: fileResult.id,\n url: downloadResult.url,\n contentType: downloadResult.contentType,\n sizeBytes: downloadResult.sizeBytes,\n };\n}\n","import { getMuxClientFromEnv } from \"@mux/ai/lib/client-factory\";\nimport type { MuxAsset, PlaybackAsset, PlaybackPolicy, WorkflowCredentialsInput, WorkflowMuxClient } from \"@mux/ai/types\";\n\n/**\n * Finds a usable playback ID for the given asset.\n * Prefers public playback IDs, falls back to signed if no public is available.\n * Throws an error if no public or signed playback ID is found.\n */\n// Use the asset payload we already fetched to avoid extra Mux Video API calls.\nfunction getPlaybackId(asset: MuxAsset): { id: string; policy: PlaybackPolicy } {\n const playbackIds = asset.playback_ids || [];\n\n // First, try to find a public playback ID\n const publicPlaybackId = playbackIds.find(pid => pid.policy === \"public\");\n if (publicPlaybackId?.id) {\n return { id: publicPlaybackId.id, policy: \"public\" };\n }\n\n // Fall back to signed playback ID\n const signedPlaybackId = playbackIds.find(pid => pid.policy === \"signed\");\n if (signedPlaybackId?.id) {\n return { id: signedPlaybackId.id, policy: \"signed\" };\n }\n\n throw new Error(\n \"No public or signed playback ID found for this asset. \" +\n \"A public or signed playback ID is required. DRM playback IDs are not currently supported.\",\n );\n}\n\n/**\n * Determines if an asset is audio-only by checking if it has any video tracks.\n * Returns true if the asset has at least one audio track and no video tracks.\n */\nexport function isAudioOnlyAsset(asset: MuxAsset): boolean {\n const hasAudioTrack = asset.tracks?.some(track => track.type === \"audio\") ?? false;\n const hasVideoTrack = asset.tracks?.some(track => track.type === \"video\") ?? false;\n return hasAudioTrack && !hasVideoTrack;\n}\n\nfunction toPlaybackAsset(asset: MuxAsset): PlaybackAsset {\n const { id: playbackId, policy } = getPlaybackId(asset);\n return { asset, playbackId, policy };\n}\n\nexport async function getPlaybackIdForAsset(\n assetId: string,\n credentials?: WorkflowCredentialsInput,\n): Promise<PlaybackAsset> {\n \"use step\";\n // Centralize the Mux Video API fetch so callers can reuse the same asset payload\n // for playback IDs, duration, and other derived fields without double-hitting.\n // Note: getMuxAsset still resolves Mux token ID/secret from env or provided\n // credentials to preserve multi-tenant behavior.\n const asset = await getMuxAsset(assetId, credentials);\n return toPlaybackAsset(asset);\n}\n\nexport async function getPlaybackIdForAssetWithClient(\n assetId: string,\n muxClient: WorkflowMuxClient,\n): Promise<PlaybackAsset> {\n // Do not mark this as a workflow step: muxClient contains function properties\n // (e.g. createClient) that are not serializable across step boundaries.\n const asset = await getMuxAssetWithClient(assetId, muxClient);\n return toPlaybackAsset(asset);\n}\n\n/**\n * Fetches the Mux asset once so callers can derive playback IDs, duration, tracks,\n * and other metadata from a single Video API call.\n */\nexport async function getMuxAsset(\n assetId: string,\n credentials?: WorkflowCredentialsInput,\n): Promise<MuxAsset> {\n \"use step\";\n const muxClient = await getMuxClientFromEnv(credentials);\n const mux = await muxClient.createClient();\n return mux.video.assets.retrieve(assetId);\n}\n\nexport async function getMuxAssetWithClient(\n assetId: string,\n muxClient: WorkflowMuxClient,\n): Promise<MuxAsset> {\n // Do not mark this as a workflow step for the same serialization reason as above.\n const client = await muxClient.createClient();\n return client.video.assets.retrieve(assetId);\n}\n\nexport async function getAssetDurationSeconds(\n assetId: string,\n credentials?: WorkflowCredentialsInput,\n): Promise<number | undefined> {\n \"use step\";\n // Keep this helper, but route through getMuxAsset so a caller that already\n // fetched the asset can prefer getAssetDurationSecondsFromAsset instead.\n const asset = await getMuxAsset(assetId, credentials);\n return getAssetDurationSecondsFromAsset(asset);\n}\n\n// Use this when you already have the asset to avoid another Video API round trip.\nexport function getAssetDurationSecondsFromAsset(asset: MuxAsset): number | undefined {\n const duration = asset.duration;\n return typeof duration === \"number\" && Number.isFinite(duration) ? duration : undefined;\n}\n\nexport function getVideoTrackDurationSecondsFromAsset(asset: MuxAsset): number | undefined {\n const videoTrack = asset.tracks?.find(track => track.type === \"video\");\n const duration = (videoTrack as { duration?: unknown } | undefined)?.duration;\n return typeof duration === \"number\" && Number.isFinite(duration) ? duration : undefined;\n}\n\nexport function getVideoTrackMaxFrameRateFromAsset(asset: MuxAsset): number | undefined {\n const videoTrack = asset.tracks?.find(track => track.type === \"video\");\n const maxFrameRate = (videoTrack as { max_frame_rate?: unknown } | undefined)?.max_frame_rate;\n return typeof maxFrameRate === \"number\" && Number.isFinite(maxFrameRate) && maxFrameRate > 0 ?\n maxFrameRate :\n undefined;\n}\n","// ─────────────────────────────────────────────────────────────────────────────\n// Types\n// ─────────────────────────────────────────────────────────────────────────────\n\n/**\n * A single section of a prompt, rendered as an XML-like tag.\n */\nexport interface PromptSection {\n /** The XML tag name for this section */\n tag: string;\n /** The content inside the tag */\n content: string;\n /** Optional attributes to add to the tag (e.g., { format: \"plain text\" }) */\n attributes?: Record<string, string>;\n}\n\n/**\n * Configuration for building a prompt section.\n * Can be a full PromptSection object, just a string (content only), or undefined to use default.\n */\nexport type SectionOverride = string | PromptSection | undefined;\n\n/**\n * A template defining the default sections for a prompt.\n * Keys are section identifiers, values are the default PromptSection definitions.\n */\nexport type PromptTemplate<TSections extends string> = Record<TSections, PromptSection>;\n\n/**\n * User-provided overrides for prompt sections.\n * Each key can override the corresponding section's content or full definition.\n */\nexport type PromptOverrides<TSections extends string> = Partial<Record<TSections, SectionOverride>>;\n\n/**\n * Configuration for the prompt builder.\n */\nexport interface PromptBuilderConfig<TSections extends string> {\n /** Default sections that make up the prompt template */\n template: PromptTemplate<TSections>;\n /** Order in which sections should appear in the final prompt */\n sectionOrder: TSections[];\n}\n\n/**\n * A configured prompt builder instance with methods to build prompts.\n */\nexport interface PromptBuilder<TSections extends string> {\n /** The default template sections */\n template: PromptTemplate<TSections>;\n /** Build a prompt string, optionally overriding specific sections */\n build: (overrides?: PromptOverrides<TSections>) => string;\n /** Build a prompt with additional dynamic sections appended */\n buildWithContext: (\n overrides?: PromptOverrides<TSections>,\n additionalSections?: PromptSection[],\n ) => string;\n /** Get a single section's content (useful for partial rendering) */\n getSection: (section: TSections, override?: SectionOverride) => string;\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Helpers\n// ─────────────────────────────────────────────────────────────────────────────\n\n/**\n * Renders a PromptSection as an XML-like string.\n */\nexport function renderSection(section: PromptSection): string {\n const { tag, content, attributes } = section;\n\n const XML_NAME_PATTERN = /^[A-Z_][\\w.:-]*$/i;\n\n const assertValidXmlName = (name: string, context: \"tag\" | \"attribute\"): void => {\n if (!XML_NAME_PATTERN.test(name)) {\n throw new Error(`Invalid XML ${context} name: \"${name}\"`);\n }\n };\n\n const escapeXmlText = (value: string): string =>\n value\n .replace(/&/g, \"&amp;\")\n .replace(/</g, \"&lt;\")\n .replace(/>/g, \"&gt;/\");\n\n const escapeXmlAttribute = (value: string): string =>\n escapeXmlText(value).replace(/\"/g, \"&quot;\");\n\n if (!content.trim()) {\n return \"\";\n }\n\n assertValidXmlName(tag, \"tag\");\n\n const attrString = attributes ?\n ` ${\n Object.entries(attributes)\n .map(([key, value]) => {\n assertValidXmlName(key, \"attribute\");\n return `${key}=\"${escapeXmlAttribute(value)}\"`;\n })\n .join(\" \")}` :\n \"\";\n\n const safeContent = escapeXmlText(content.trim());\n\n return `<${tag}${attrString}>\\n${safeContent}\\n</${tag}>`;\n}\n\n/**\n * Resolves a section override to a full PromptSection.\n */\nfunction resolveSection(\n defaultSection: PromptSection,\n override?: SectionOverride,\n): PromptSection {\n if (override === undefined) {\n return defaultSection;\n }\n\n if (typeof override === \"string\") {\n return { ...defaultSection, content: override };\n }\n\n return override;\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Factory\n// ─────────────────────────────────────────────────────────────────────────────\n\n/**\n * Creates a type-safe prompt builder from a template configuration.\n *\n * @example\n * ```typescript\n * const builder = createPromptBuilder({\n * template: {\n * task: { tag: 'task', content: 'Analyze the image...' },\n * tone: { tag: 'tone', content: 'Be professional.' },\n * },\n * sectionOrder: ['task', 'tone'],\n * });\n *\n * // Use defaults\n * const prompt1 = builder.build();\n *\n * // Override specific sections\n * const prompt2 = builder.build({\n * tone: 'Be casual and friendly.',\n * });\n * ```\n */\nexport function createPromptBuilder<TSections extends string>(\n config: PromptBuilderConfig<TSections>,\n): PromptBuilder<TSections> {\n const { template, sectionOrder } = config;\n\n const getSection = (section: TSections, override?: SectionOverride): string => {\n const resolved = resolveSection(template[section], override);\n return renderSection(resolved);\n };\n\n const build = (overrides?: PromptOverrides<TSections>): string => {\n const sections = sectionOrder\n .map(sectionKey => getSection(sectionKey, overrides?.[sectionKey]))\n .filter(Boolean);\n\n return sections.join(\"\\n\\n\");\n };\n\n const buildWithContext = (\n overrides?: PromptOverrides<TSections>,\n additionalSections?: PromptSection[],\n ): string => {\n const basePrompt = build(overrides);\n\n if (!additionalSections?.length) {\n return basePrompt;\n }\n\n const additional = additionalSections\n .map(renderSection)\n .filter(Boolean)\n .join(\"\\n\\n\");\n\n return additional ? `${basePrompt}\\n\\n${additional}` : basePrompt;\n };\n\n return {\n template,\n build,\n buildWithContext,\n getSection,\n };\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Common Helpers\n// ─────────────────────────────────────────────────────────────────────────────\n\n/**\n * Creates a transcript section for inclusion in prompts.\n */\nexport function createTranscriptSection(\n transcriptText: string,\n format: \"plain text\" | \"WebVTT\" = \"plain text\",\n): PromptSection {\n return {\n tag: \"transcript\",\n content: transcriptText,\n attributes: { format },\n };\n}\n\n/**\n * Creates a tone section for inclusion in prompts.\n */\nexport function createToneSection(instruction: string): PromptSection {\n return {\n tag: \"tone\",\n content: instruction,\n };\n}\n","/**\n * Retry configuration options\n */\nexport interface RetryOptions {\n maxRetries?: number;\n baseDelay?: number;\n maxDelay?: number;\n shouldRetry?: (error: Error, attempt: number) => boolean;\n}\n\nconst DEFAULT_RETRY_OPTIONS: Required<Omit<RetryOptions, \"shouldRetry\">> = {\n maxRetries: 3,\n baseDelay: 2000,\n maxDelay: 10000,\n};\n\n/**\n * Default retry condition - retries on timeout errors\n */\nfunction defaultShouldRetry(error: Error, _attempt: number): boolean {\n return Boolean(error.message && error.message.includes(\"Timeout while downloading\"));\n}\n\n/**\n * Calculates exponential backoff delay with jitter\n */\nfunction calculateDelay(attempt: number, baseDelay: number, maxDelay: number): number {\n const exponentialDelay = baseDelay * 2 ** (attempt - 1);\n const delayWithJitter = exponentialDelay * (0.5 + Math.random() * 0.5);\n return Math.min(delayWithJitter, maxDelay);\n}\n\n/**\n * Executes an async function with retry logic\n */\nexport async function withRetry<T>(\n fn: () => Promise<T>,\n {\n maxRetries = DEFAULT_RETRY_OPTIONS.maxRetries,\n baseDelay = DEFAULT_RETRY_OPTIONS.baseDelay,\n maxDelay = DEFAULT_RETRY_OPTIONS.maxDelay,\n shouldRetry = defaultShouldRetry,\n }: RetryOptions = {},\n): Promise<T> {\n let lastError: Error | undefined;\n\n for (let attempt = 0; attempt <= maxRetries; attempt++) {\n try {\n return await fn();\n } catch (error) {\n lastError = error instanceof Error ? error : new Error(String(error));\n\n const isLastAttempt = attempt === maxRetries;\n if (isLastAttempt || !shouldRetry(lastError, attempt + 1)) {\n throw lastError;\n }\n\n const delay = calculateDelay(attempt + 1, baseDelay, maxDelay);\n console.warn(\n `Attempt ${attempt + 1} failed: ${lastError.message}. Retrying in ${Math.round(delay)}ms...`,\n );\n await new Promise(resolve => setTimeout(resolve, delay));\n }\n }\n\n throw lastError || new Error(\"Retry failed with unknown error\");\n}\n","import { generateText, Output } from \"ai\";\nimport dedent from \"dedent\";\nimport { z } from \"zod\";\n\nimport type { ImageDownloadOptions } from \"@mux/ai/lib/image-download\";\nimport { downloadImageAsBase64 } from \"@mux/ai/lib/image-download\";\nimport { getAssetDurationSecondsFromAsset, getPlaybackIdForAsset } from \"@mux/ai/lib/mux-assets\";\nimport type { PromptOverrides } from \"@mux/ai/lib/prompt-builder\";\nimport { createPromptBuilder } from \"@mux/ai/lib/prompt-builder\";\nimport { createLanguageModelFromConfig, resolveLanguageModelConfig } from \"@mux/ai/lib/providers\";\nimport type { ModelIdByProvider, SupportedProvider } from \"@mux/ai/lib/providers\";\nimport { getStoryboardUrl } from \"@mux/ai/primitives/storyboards\";\nimport type {\n ImageSubmissionMode,\n MuxAIOptions,\n TokenUsage,\n WorkflowCredentialsInput,\n} from \"@mux/ai/types\";\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Types\n// ─────────────────────────────────────────────────────────────────────────────\n\n/** Structured payload returned from `hasBurnedInCaptions`. */\nexport interface BurnedInCaptionsResult {\n assetId: string;\n hasBurnedInCaptions: boolean;\n confidence: number;\n detectedLanguage: string | null;\n storyboardUrl: string;\n /** Token usage from the AI provider (for efficiency/cost analysis). */\n usage?: TokenUsage;\n}\n\n/**\n * Sections of the burned-in captions user prompt that can be overridden.\n * Use these to customize the AI's behavior for your specific use case.\n */\nexport type BurnedInCaptionsPromptSections =\n \"task\" |\n \"analysisSteps\" |\n \"positiveIndicators\" |\n \"negativeIndicators\";\n\n/**\n * Override specific sections of the burned-in captions prompt.\n * Each key corresponds to a section that can be customized.\n *\n * @example\n * ```typescript\n * const result = await hasBurnedInCaptions(assetId, {\n * promptOverrides: {\n * task: 'Detect any text overlays in the video frames.',\n * positiveIndicators: 'Classify as captions if text appears consistently.',\n * },\n * });\n * ```\n */\nexport type BurnedInCaptionsPromptOverrides = PromptOverrides<BurnedInCaptionsPromptSections>;\n\n/** Configuration accepted by `hasBurnedInCaptions`. */\nexport interface BurnedInCaptionsOptions extends MuxAIOptions {\n /** AI provider used for storyboard inspection (defaults to 'openai'). */\n provider?: SupportedProvider;\n /** Provider-specific model identifier. */\n model?: ModelIdByProvider[SupportedProvider];\n /** Transport used for storyboard submission (defaults to 'url'). */\n imageSubmissionMode?: ImageSubmissionMode;\n /** Download tuning used when `imageSubmissionMode` === 'base64'. */\n imageDownloadOptions?: ImageDownloadOptions;\n /**\n * Override specific sections of the user prompt.\n * Useful for customizing the AI's detection criteria for specific use cases.\n */\n promptOverrides?: BurnedInCaptionsPromptOverrides;\n}\n\n/** Schema used to validate burned-in captions analysis responses. */\nexport const burnedInCaptionsSchema = z.object({\n hasBurnedInCaptions: z.boolean(),\n confidence: z.number(),\n detectedLanguage: z.string().nullable(),\n});\n\n/** Inferred shape returned from the burned-in captions schema. */\nexport type BurnedInCaptionsAnalysis = z.infer<typeof burnedInCaptionsSchema>;\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Prompts\n// ─────────────────────────────────────────────────────────────────────────────\n\nconst SYSTEM_PROMPT = dedent`\n <role>\n You are an expert at analyzing video frames to detect burned-in captions (also called open captions or hardcoded subtitles).\n These are text overlays that are permanently embedded in the video image, common on TikTok, Instagram Reels, and other social media platforms.\n </role>\n\n <critical_note>\n Burned-in captions must appear consistently across MOST frames in the storyboard.\n Text appearing in only 1-2 frames at the end is typically marketing copy, taglines, or end-cards - NOT burned-in captions.\n </critical_note>\n\n <confidence_scoring>\n Use this rubric to determine your confidence score (0.0-1.0):\n\n - Score 1.0: Definitive captions - text overlays visible in most frames, consistent positioning, content changes between frames indicating dialogue/narration, clear caption-style formatting\n - Score 0.7-0.9: Strong evidence - captions visible across multiple frames with consistent placement, but minor ambiguity (e.g., some frames unclear, atypical styling)\n - Score 0.4-0.6: Moderate evidence - text present in several frames but uncertain classification (e.g., could be captions or persistent on-screen graphics, ambiguous formatting)\n - Score 0.1-0.3: Weak evidence - minimal text detected, appears in only a few frames, likely marketing copy or end-cards rather than captions\n - Score 0.0: No captions - no text overlays detected, or text is clearly not captions (logos, watermarks, scene content, single end-card)\n </confidence_scoring>\n\n <context>\n You receive storyboard images containing multiple sequential frames extracted from a video.\n These frames are arranged in a grid and represent the visual progression of the content over time.\n Read frames left-to-right, top-to-bottom to understand the temporal sequence.\n </context>\n\n <capabilities>\n - Detect and analyze text overlays in video frames\n - Distinguish between captions and other text elements (marketing, logos, UI)\n - Identify language of detected caption text\n - Assess confidence in caption detection\n </capabilities>\n\n <constraints>\n - Only classify as burned-in captions when evidence is clear across multiple frames\n - Base decisions on observable visual evidence\n - Return structured data matching the requested schema\n </constraints>`;\n\n/**\n * Prompt builder for the burned-in captions user prompt.\n * Sections can be individually overridden via `promptOverrides` in BurnedInCaptionsOptions.\n */\nconst burnedInCaptionsPromptBuilder = createPromptBuilder<BurnedInCaptionsPromptSections>({\n template: {\n task: {\n tag: \"task\",\n content: dedent`\n Analyze the provided video storyboard to detect burned-in captions (hardcoded subtitles).\n Count frames with text vs no text, note position consistency and whether text changes across frames.\n Decide if captions exist, with confidence (0.0-1.0) and detected language if any.`,\n },\n analysisSteps: {\n tag: \"analysis_steps\",\n content: dedent`\n 1. COUNT how many frames contain text overlays vs. how many don't\n 2. Check if text appears in consistent positions across multiple frames\n 3. Verify text changes content between frames (indicating dialogue/narration)\n 4. Ensure text has caption-style formatting (contrasting colors, readable fonts)\n 5. If captions are detected, identify the language of the text`,\n },\n positiveIndicators: {\n tag: \"classify_as_captions\",\n content: dedent`\n ONLY classify as burned-in captions if:\n - Text appears in multiple frames (not just 1-2 end frames)\n - Text positioning is consistent across those frames\n - Content suggests dialogue, narration, or subtitles (not marketing)\n - Formatting looks like captions (not graphics/logos)`,\n },\n negativeIndicators: {\n tag: \"not_captions\",\n content: dedent`\n DO NOT classify as burned-in captions:\n - Marketing taglines appearing only in final 1-2 frames\n - Single words or phrases that don't change between frames\n - Graphics, logos, watermarks, or UI elements\n - Text that's part of the original scene content\n - End-cards with calls-to-action or brand messaging`,\n },\n },\n sectionOrder: [\"task\", \"analysisSteps\", \"positiveIndicators\", \"negativeIndicators\"],\n});\n\nfunction buildUserPrompt(promptOverrides?: BurnedInCaptionsPromptOverrides): string {\n return burnedInCaptionsPromptBuilder.build(promptOverrides);\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Implementation\n// ─────────────────────────────────────────────────────────────────────────────\nconst DEFAULT_PROVIDER = \"openai\";\n\ninterface AnalysisResponse {\n result: BurnedInCaptionsAnalysis;\n usage: TokenUsage;\n}\n\nasync function fetchImageAsBase64(\n imageUrl: string,\n imageDownloadOptions?: ImageDownloadOptions,\n): Promise<string> {\n \"use step\";\n\n const downloadResult = await downloadImageAsBase64(imageUrl, imageDownloadOptions);\n return downloadResult.base64Data;\n}\n\nasync function analyzeStoryboard({\n imageDataUrl,\n provider,\n modelId,\n userPrompt,\n systemPrompt,\n credentials,\n}: {\n imageDataUrl: string;\n provider: SupportedProvider;\n modelId: string;\n userPrompt: string;\n systemPrompt: string;\n credentials?: WorkflowCredentialsInput;\n}): Promise<AnalysisResponse> {\n \"use step\";\n\n const model = await createLanguageModelFromConfig(provider, modelId, credentials);\n\n const response = await generateText({\n model,\n output: Output.object({ schema: burnedInCaptionsSchema }),\n experimental_telemetry: { isEnabled: true },\n messages: [\n {\n role: \"system\",\n content: systemPrompt,\n },\n {\n role: \"user\",\n content: [\n { type: \"text\", text: userPrompt },\n { type: \"image\", image: imageDataUrl },\n ],\n },\n ],\n });\n\n return {\n result: {\n ...response.output,\n confidence: Math.min(1, Math.max(0, response.output.confidence)),\n },\n usage: {\n inputTokens: response.usage.inputTokens,\n outputTokens: response.usage.outputTokens,\n totalTokens: response.usage.totalTokens,\n reasoningTokens: response.usage.reasoningTokens,\n cachedInputTokens: response.usage.cachedInputTokens,\n },\n };\n}\n\nexport async function hasBurnedInCaptions(\n assetId: string,\n options: BurnedInCaptionsOptions = {},\n): Promise<BurnedInCaptionsResult> {\n \"use workflow\";\n const {\n provider = DEFAULT_PROVIDER,\n model,\n imageSubmissionMode = \"url\",\n imageDownloadOptions,\n promptOverrides,\n credentials,\n ...config\n } = options;\n\n // Build the user prompt with any overrides\n const userPrompt = buildUserPrompt(promptOverrides);\n\n const modelConfig = resolveLanguageModelConfig({\n ...config,\n model,\n provider: provider as SupportedProvider,\n });\n const { asset: assetData, playbackId, policy } = await getPlaybackIdForAsset(assetId, credentials);\n const assetDurationSeconds = getAssetDurationSecondsFromAsset(assetData);\n\n const imageUrl = await getStoryboardUrl(playbackId, 640, policy === \"signed\", credentials);\n\n let analysisResponse: AnalysisResponse;\n\n if (imageSubmissionMode === \"base64\") {\n const base64Data = await fetchImageAsBase64(imageUrl, imageDownloadOptions);\n analysisResponse = await analyzeStoryboard({\n imageDataUrl: base64Data,\n provider: modelConfig.provider,\n modelId: modelConfig.modelId,\n userPrompt,\n systemPrompt: SYSTEM_PROMPT,\n credentials,\n });\n } else {\n analysisResponse = await analyzeStoryboard({\n imageDataUrl: imageUrl,\n provider: modelConfig.provider,\n modelId: modelConfig.modelId,\n userPrompt,\n systemPrompt: SYSTEM_PROMPT,\n credentials,\n });\n }\n\n if (!analysisResponse.result) {\n throw new Error(\"No analysis result received from AI provider\");\n }\n\n return {\n assetId,\n hasBurnedInCaptions: analysisResponse.result.hasBurnedInCaptions ?? false,\n confidence: analysisResponse.result.confidence ?? 0,\n detectedLanguage: analysisResponse.result.detectedLanguage ?? null,\n storyboardUrl: imageUrl,\n usage: {\n ...analysisResponse.usage,\n metadata: {\n assetDurationSeconds,\n },\n },\n };\n}\n","import { generateText, Output } from \"ai\";\nimport dedent from \"dedent\";\nimport { z } from \"zod\";\n\nimport {\n getAssetDurationSecondsFromAsset,\n getPlaybackIdForAsset,\n isAudioOnlyAsset,\n} from \"@mux/ai/lib/mux-assets\";\nimport type { PromptOverrides, PromptSection } from \"@mux/ai/lib/prompt-builder\";\nimport { createPromptBuilder } from \"@mux/ai/lib/prompt-builder\";\nimport { createLanguageModelFromConfig, resolveLanguageModelConfig } from \"@mux/ai/lib/providers\";\nimport type { ModelIdByProvider, SupportedProvider } from \"@mux/ai/lib/providers\";\nimport { withRetry } from \"@mux/ai/lib/retry\";\nimport { resolveMuxSigningContext } from \"@mux/ai/lib/workflow-credentials\";\nimport {\n extractTimestampedTranscript,\n fetchTranscriptForAsset,\n getReadyTextTracks,\n} from \"@mux/ai/primitives/transcripts\";\nimport type { MuxAIOptions, TokenUsage, WorkflowCredentialsInput } from \"@mux/ai/types\";\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Types\n// ─────────────────────────────────────────────────────────────────────────────\n\nexport const chapterSchema = z.object({\n startTime: z.number(),\n title: z.string(),\n});\n\nexport type Chapter = z.infer<typeof chapterSchema>;\n\nexport const chaptersSchema = z.object({\n chapters: z.array(chapterSchema),\n});\n\nexport type ChaptersType = z.infer<typeof chaptersSchema>;\n\n/** Structured return payload from `generateChapters`. */\nexport interface ChaptersResult {\n assetId: string;\n languageCode: string;\n chapters: Chapter[];\n /** Token usage from the AI provider (for efficiency/cost analysis). */\n usage?: TokenUsage;\n}\n\n/**\n * Sections of the chaptering user prompt that can be overridden.\n * Use these to customize the AI's behavior for your specific use case.\n */\nexport type ChaptersPromptSections =\n \"task\" |\n \"outputFormat\" |\n \"chapterGuidelines\" |\n \"titleGuidelines\";\n\n/**\n * Override specific sections of the chaptering prompt.\n * Each key corresponds to a section that can be customized.\n *\n * @example\n * ```typescript\n * const result = await generateChapters(assetId, \"en\", {\n * promptOverrides: {\n * titleGuidelines: \"Use short, punchy titles under 6 words.\",\n * },\n * });\n * ```\n */\nexport type ChaptersPromptOverrides = PromptOverrides<ChaptersPromptSections>;\n\n/** Configuration accepted by `generateChapters`. */\nexport interface ChaptersOptions extends MuxAIOptions {\n /** AI provider used to interpret the transcript (defaults to 'openai'). */\n provider?: SupportedProvider;\n /** Provider-specific model identifier. */\n model?: ModelIdByProvider[SupportedProvider];\n /**\n * Override specific sections of the user prompt.\n * Useful for customizing chaptering criteria for specific use cases.\n */\n promptOverrides?: ChaptersPromptOverrides;\n /**\n * Minimum number of chapters to generate per hour of content.\n * Defaults to 3.\n */\n minChaptersPerHour?: number;\n /**\n * Maximum number of chapters to generate per hour of content.\n * Defaults to 8.\n */\n maxChaptersPerHour?: number;\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Implementation\n// ─────────────────────────────────────────────────────────────────────────────\n\nasync function generateChaptersWithAI({\n provider,\n modelId,\n userPrompt,\n systemPrompt,\n credentials,\n}: {\n provider: SupportedProvider;\n modelId: string;\n userPrompt: string;\n systemPrompt: string;\n credentials?: WorkflowCredentialsInput;\n}): Promise<{ chapters: ChaptersType; usage: TokenUsage }> {\n \"use step\";\n\n const model = await createLanguageModelFromConfig(provider, modelId, credentials);\n\n const response = await withRetry(() =>\n generateText({\n model,\n output: Output.object({ schema: chaptersSchema }),\n messages: [\n {\n role: \"system\",\n content: systemPrompt,\n },\n {\n role: \"user\",\n content: userPrompt,\n },\n ],\n }),\n );\n\n return {\n chapters: response.output,\n usage: {\n inputTokens: response.usage.inputTokens,\n outputTokens: response.usage.outputTokens,\n totalTokens: response.usage.totalTokens,\n reasoningTokens: response.usage.reasoningTokens,\n cachedInputTokens: response.usage.cachedInputTokens,\n },\n };\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Prompts\n// ─────────────────────────────────────────────────────────────────────────────\n\n/**\n * Sections of the chaptering system prompt that can be overridden.\n * Use these to customize the AI's persona and constraints.\n */\nexport type ChapterSystemPromptSections = \"role\" | \"context\" | \"constraints\" | \"qualityGuidelines\";\n\n/**\n * Prompt builder for the chaptering system prompt.\n * Sections can be individually overridden for different content types.\n */\nconst chapterSystemPromptBuilder = createPromptBuilder<ChapterSystemPromptSections>({\n template: {\n role: {\n tag: \"role\",\n content: \"You are a video editor and transcript analyst specializing in segmenting content into logical chapters.\",\n },\n context: {\n tag: \"context\",\n content: dedent`\n You receive a timestamped transcript with lines in the form \"[12s] Caption text\".\n Use those timestamps as anchors to determine chapter start times in seconds.`,\n },\n constraints: {\n tag: \"constraints\",\n content: dedent`\n - Only use information present in the transcript\n - Return structured data that matches the requested JSON schema\n - Do not add commentary or extra text outside the JSON`,\n },\n qualityGuidelines: {\n tag: \"quality_guidelines\",\n content: dedent`\n - Create chapters at topic shifts or clear transitions\n - Keep chapter titles concise and descriptive\n - Ensure the first chapter starts at 0 seconds`,\n },\n },\n sectionOrder: [\"role\", \"context\", \"constraints\", \"qualityGuidelines\"],\n});\n\n/**\n * System prompt overrides for audio-only assets.\n * Adjusts the role and context to be audio-focused rather than video-focused.\n */\nconst AUDIO_ONLY_SYSTEM_PROMPT_OVERRIDES: Partial<Record<ChapterSystemPromptSections, string>> = {\n role: \"You are an audio editor and transcript analyst specializing in segmenting content into logical chapters.\",\n context: dedent`\n You receive a timestamped transcript from audio-only content with lines in the form \"[12s] Transcript text\".\n Use those timestamps as anchors to determine chapter start times in seconds.`,\n};\n\n/**\n * Prompt builder for the chaptering user prompt.\n * Sections can be individually overridden via `promptOverrides` in ChaptersOptions.\n */\nconst chaptersPromptBuilder = createPromptBuilder<ChaptersPromptSections>({\n template: {\n task: {\n tag: \"task\",\n content: \"Segment the transcript into logical chapters and provide a short title for each chapter.\",\n },\n outputFormat: {\n tag: \"output_format\",\n content: dedent`\n Return valid JSON in this exact shape:\n {\n \"chapters\": [\n {\"startTime\": 0, \"title\": \"Introduction\"},\n {\"startTime\": 45.5, \"title\": \"Main Topic Discussion\"},\n {\"startTime\": 120.0, \"title\": \"Conclusion\"}\n ]\n }`,\n },\n // Note: chapterGuidelines is dynamically generated in buildUserPrompt()\n // based on minChaptersPerHour/maxChaptersPerHour options\n chapterGuidelines: {\n tag: \"chapter_guidelines\",\n content: \"\", // Placeholder - always overridden with dynamic content\n },\n titleGuidelines: {\n tag: \"title_guidelines\",\n content: dedent`\n - Keep titles concise and descriptive\n - Avoid filler or generic labels like \"Chapter 1\"\n - Use the transcript's language and terminology`,\n },\n },\n sectionOrder: [\"task\", \"outputFormat\", \"chapterGuidelines\", \"titleGuidelines\"],\n});\n\nfunction buildUserPrompt({\n timestampedTranscript,\n promptOverrides,\n minChaptersPerHour = 3,\n maxChaptersPerHour = 8,\n}: {\n timestampedTranscript: string;\n promptOverrides?: ChaptersPromptOverrides;\n minChaptersPerHour?: number;\n maxChaptersPerHour?: number;\n}): string {\n const contextSections: PromptSection[] = [\n {\n tag: \"timestamped_transcript\",\n content: timestampedTranscript,\n attributes: { format: \"seconds\" },\n },\n ];\n\n // Build dynamic chapter guidelines with configurable min/max per hour\n const dynamicChapterGuidelines = dedent`\n - Create at least ${minChaptersPerHour} and at most ${maxChaptersPerHour} chapters per hour of content\n - Use start times in seconds (not HH:MM:SS)\n - Chapter start times should be non-decreasing\n - Do not include text before or after the JSON`;\n\n // Merge with any user-provided overrides (user overrides take precedence)\n const mergedOverrides: ChaptersPromptOverrides = {\n chapterGuidelines: dynamicChapterGuidelines,\n ...promptOverrides,\n };\n\n return chaptersPromptBuilder.buildWithContext(mergedOverrides, contextSections);\n}\n\nexport async function generateChapters(\n assetId: string,\n languageCode: string,\n options: ChaptersOptions = {},\n): Promise<ChaptersResult> {\n \"use workflow\";\n const {\n provider = \"openai\",\n model,\n promptOverrides,\n minChaptersPerHour,\n maxChaptersPerHour,\n credentials,\n } = options;\n\n const modelConfig = resolveLanguageModelConfig({\n ...options,\n model,\n provider: provider as SupportedProvider,\n });\n // Fetch asset and transcript\n const { asset: assetData, playbackId, policy } = await getPlaybackIdForAsset(assetId, credentials);\n const assetDurationSeconds = getAssetDurationSecondsFromAsset(assetData);\n const isAudioOnly = isAudioOnlyAsset(assetData);\n\n // Resolve signing context for signed playback IDs\n const signingContext = await resolveMuxSigningContext(credentials);\n if (policy === \"signed\" && !signingContext) {\n throw new Error(\n \"Signed playback ID requires signing credentials. \" +\n \"Set MUX_SIGNING_KEY and MUX_PRIVATE_KEY environment variables.\",\n );\n }\n\n const readyTextTracks = getReadyTextTracks(assetData);\n let transcriptResult = await fetchTranscriptForAsset(assetData, playbackId, {\n languageCode,\n cleanTranscript: false, // keep timestamps for chapter segmentation\n shouldSign: policy === \"signed\",\n credentials,\n });\n\n if (isAudioOnly && !transcriptResult.track && readyTextTracks.length === 1) {\n transcriptResult = await fetchTranscriptForAsset(assetData, playbackId, {\n cleanTranscript: false, // keep timestamps for chapter segmentation\n shouldSign: policy === \"signed\",\n credentials,\n required: true,\n });\n }\n\n if (!transcriptResult.track || !transcriptResult.transcriptText) {\n const availableLanguages = readyTextTracks\n .map(t => t.language_code)\n .filter(Boolean)\n .join(\", \");\n throw new Error(\n `No caption track found for language '${languageCode}'. Available languages: ${availableLanguages || \"none\"}`,\n );\n }\n\n const timestampedTranscript = extractTimestampedTranscript(transcriptResult.transcriptText);\n if (!timestampedTranscript) {\n const contentLabel = isAudioOnly ? \"transcript\" : \"caption track\";\n throw new Error(`No usable content found in ${contentLabel}`);\n }\n\n const userPrompt = buildUserPrompt({\n timestampedTranscript,\n promptOverrides,\n minChaptersPerHour,\n maxChaptersPerHour,\n });\n\n // Generate chapters using AI SDK\n let chaptersData: { chapters: ChaptersType; usage: TokenUsage } | null = null;\n\n try {\n const systemPrompt = isAudioOnly ?\n chapterSystemPromptBuilder.build(AUDIO_ONLY_SYSTEM_PROMPT_OVERRIDES) :\n chapterSystemPromptBuilder.build();\n chaptersData = await generateChaptersWithAI({\n provider: modelConfig.provider,\n modelId: modelConfig.modelId,\n userPrompt,\n systemPrompt,\n credentials,\n });\n } catch (error) {\n throw new Error(\n `Failed to generate chapters with ${provider}: ${error instanceof Error ? error.message : \"Unknown error\"}`,\n );\n }\n\n if (!chaptersData || !chaptersData.chapters) {\n throw new Error(\"No chapters generated from AI response\");\n }\n\n // Validate and sort chapters\n const { chapters: chaptersPayload, usage } = chaptersData;\n const validChapters = chaptersPayload.chapters\n .filter(chapter => typeof chapter.startTime === \"number\" && typeof chapter.title === \"string\")\n .sort((a, b) => a.startTime - b.startTime);\n\n if (validChapters.length === 0) {\n throw new Error(\"No valid chapters found in AI response\");\n }\n\n // Ensure first chapter starts at 0\n if (validChapters[0].startTime !== 0) {\n validChapters[0].startTime = 0;\n }\n\n const usageWithMetadata: TokenUsage = {\n ...usage,\n metadata: {\n ...usage?.metadata,\n assetDurationSeconds,\n },\n };\n\n return {\n assetId,\n languageCode,\n chapters: validChapters,\n usage: usageWithMetadata,\n };\n}\n","import { embed } from \"ai\";\n\nimport {\n getAssetDurationSecondsFromAsset,\n getPlaybackIdForAsset,\n isAudioOnlyAsset,\n} from \"@mux/ai/lib/mux-assets\";\nimport type { EmbeddingModelIdByProvider, SupportedEmbeddingProvider } from \"@mux/ai/lib/providers\";\nimport { createEmbeddingModelFromConfig, resolveEmbeddingModelConfig } from \"@mux/ai/lib/providers\";\nimport { withRetry } from \"@mux/ai/lib/retry\";\nimport { resolveMuxSigningContext } from \"@mux/ai/lib/workflow-credentials\";\nimport { chunkText, chunkVTTCues } from \"@mux/ai/primitives/text-chunking\";\nimport { fetchTranscriptForAsset, getReadyTextTracks, parseVTTCues } from \"@mux/ai/primitives/transcripts\";\nimport type {\n ChunkEmbedding,\n ChunkingStrategy,\n MuxAIOptions,\n TextChunk,\n VideoEmbeddingsResult,\n WorkflowCredentialsInput,\n} from \"@mux/ai/types\";\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Types\n// ─────────────────────────────────────────────────────────────────────────────\n\n/** Configuration accepted by `generateEmbeddings`. */\nexport interface EmbeddingsOptions extends MuxAIOptions {\n /** AI provider used to generate embeddings (defaults to 'openai'). */\n provider?: SupportedEmbeddingProvider;\n /** Provider-specific model identifier (defaults to text-embedding-3-small for OpenAI). */\n model?: EmbeddingModelIdByProvider[SupportedEmbeddingProvider];\n /** Language code for transcript selection (defaults to first available). */\n languageCode?: string;\n /** Chunking strategy configuration (defaults to token-based with 500 tokens, 100 overlap). */\n chunkingStrategy?: ChunkingStrategy;\n /** Maximum number of chunks to process concurrently (defaults to 5). */\n batchSize?: number;\n}\n\n/** Alias for embedding results (supports video or audio transcripts). */\nexport type EmbeddingsResult = VideoEmbeddingsResult;\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Implementation\n// ─────────────────────────────────────────────────────────────────────────────\n\n/**\n * Averages multiple embedding vectors into a single vector.\n *\n * @param embeddings - Array of embedding vectors to average\n * @returns Single averaged embedding vector\n */\nfunction averageEmbeddings(embeddings: number[][]): number[] {\n if (embeddings.length === 0) {\n return [];\n }\n\n const dimensions = embeddings[0].length;\n const averaged = Array.from({ length: dimensions }, () => 0);\n\n for (const embedding of embeddings) {\n for (let i = 0; i < dimensions; i++) {\n averaged[i] += embedding[i];\n }\n }\n\n for (let i = 0; i < dimensions; i++) {\n averaged[i] /= embeddings.length;\n }\n\n return averaged;\n}\n\n/**\n * Generates embedding for a single text chunk using the specified AI provider.\n *\n * @param options - Configuration object\n * @param options.chunk - Text chunk to embed\n * @param options.provider - AI provider for embedding generation\n * @param options.modelId - Provider-specific model identifier\n * @param options.credentials - Optional workflow credentials for API access\n * @returns Chunk embedding with metadata\n */\nasync function generateSingleChunkEmbedding({\n chunk,\n provider,\n modelId,\n credentials,\n}: {\n chunk: TextChunk;\n provider: SupportedEmbeddingProvider;\n modelId: string;\n credentials?: WorkflowCredentialsInput;\n}): Promise<ChunkEmbedding> {\n \"use step\";\n\n const model = await createEmbeddingModelFromConfig(provider, modelId, credentials);\n const response = await withRetry(() =>\n embed({\n model,\n value: chunk.text,\n }),\n );\n\n return {\n chunkId: chunk.id,\n embedding: response.embedding,\n metadata: {\n startTime: chunk.startTime,\n endTime: chunk.endTime,\n tokenCount: chunk.tokenCount,\n },\n };\n}\n\n/**\n * Generates vector embeddings for a media asset's transcript.\n *\n * This function:\n * 1. Fetches the transcript from Mux\n * 2. Chunks the transcript according to the specified strategy\n * 3. Generates embeddings for each chunk using the specified AI provider\n * 4. Returns both individual chunk embeddings and an averaged embedding\n *\n * @param assetId - Mux asset ID\n * @param options - Configuration options\n * @returns Embeddings result with chunks and averaged embedding\n *\n * @example\n * ```typescript\n * const embeddings = await generateEmbeddings(\"asset-id\", {\n * provider: \"openai\",\n * chunkingStrategy: { type: \"token\", maxTokens: 500, overlap: 100 },\n * });\n *\n * // Store in vector database\n * for (const chunk of embeddings.chunks) {\n * await db.insert({\n * assetId: embeddings.assetId,\n * chunkId: chunk.chunkId,\n * embedding: chunk.embedding,\n * metadata: chunk.metadata,\n * });\n * }\n * ```\n */\nasync function generateEmbeddingsInternal(\n assetId: string,\n options: EmbeddingsOptions = {},\n): Promise<EmbeddingsResult> {\n const {\n provider = \"openai\",\n model,\n languageCode,\n chunkingStrategy = { type: \"token\", maxTokens: 500, overlap: 100 } as ChunkingStrategy,\n batchSize = 5,\n credentials,\n } = options;\n\n const embeddingModel = resolveEmbeddingModelConfig({ ...options, provider, model });\n // Fetch asset and playback ID\n const { asset: assetData, playbackId, policy } = await getPlaybackIdForAsset(assetId, credentials);\n const assetDurationSeconds = getAssetDurationSecondsFromAsset(assetData);\n const isAudioOnly = isAudioOnlyAsset(assetData);\n\n // Resolve signing context for signed playback IDs\n const signingContext = await resolveMuxSigningContext(credentials);\n if (policy === \"signed\" && !signingContext) {\n throw new Error(\n \"Signed playback ID requires signing credentials. \" +\n \"Set MUX_SIGNING_KEY and MUX_PRIVATE_KEY environment variables.\",\n );\n }\n\n // Fetch transcript (raw VTT for VTT strategy, cleaned text otherwise)\n const readyTextTracks = getReadyTextTracks(assetData);\n const useVttChunking = chunkingStrategy.type === \"vtt\";\n let transcriptResult = await fetchTranscriptForAsset(assetData, playbackId, {\n languageCode,\n cleanTranscript: !useVttChunking,\n shouldSign: policy === \"signed\",\n credentials,\n });\n\n if (isAudioOnly && !transcriptResult.track && readyTextTracks.length === 1) {\n transcriptResult = await fetchTranscriptForAsset(assetData, playbackId, {\n cleanTranscript: !useVttChunking,\n shouldSign: policy === \"signed\",\n credentials,\n });\n }\n\n if (!transcriptResult.track || !transcriptResult.transcriptText) {\n const availableLanguages = readyTextTracks\n .map(t => t.language_code)\n .filter(Boolean)\n .join(\", \");\n if (isAudioOnly) {\n throw new Error(\n `No transcript track found${languageCode ? ` for language '${languageCode}'` : \"\"}. ` +\n `Audio-only assets require a transcript. Available languages: ${availableLanguages || \"none\"}`,\n );\n }\n throw new Error(\n `No caption track found${languageCode ? ` for language '${languageCode}'` : \"\"}. Available languages: ${availableLanguages || \"none\"}`,\n );\n }\n\n const transcriptText = transcriptResult.transcriptText;\n if (!transcriptText.trim()) {\n throw new Error(\"Transcript is empty\");\n }\n\n // Chunk the transcript\n const chunks = useVttChunking ?\n chunkVTTCues(\n parseVTTCues(transcriptText),\n chunkingStrategy.maxTokens,\n chunkingStrategy.overlapCues,\n ) :\n chunkText(transcriptText, chunkingStrategy);\n if (chunks.length === 0) {\n throw new Error(\"No chunks generated from transcript\");\n }\n\n // Generate embeddings for all chunks (process in batches)\n const chunkEmbeddings: ChunkEmbedding[] = [];\n try {\n for (let i = 0; i < chunks.length; i += batchSize) {\n const batch = chunks.slice(i, i + batchSize);\n\n const batchResults = await Promise.all(\n batch.map(chunk =>\n generateSingleChunkEmbedding({\n chunk,\n provider: embeddingModel.provider,\n modelId: embeddingModel.modelId as string,\n credentials,\n }),\n ),\n );\n\n chunkEmbeddings.push(...batchResults);\n }\n } catch (error) {\n throw new Error(\n `Failed to generate embeddings with ${provider}: ${error instanceof Error ? error.message : \"Unknown error\"}`,\n );\n }\n\n if (chunkEmbeddings.length === 0) {\n throw new Error(\"No embeddings generated\");\n }\n\n // Calculate averaged embedding\n const averagedEmbedding = averageEmbeddings(chunkEmbeddings.map(ce => ce.embedding));\n\n // Calculate total tokens\n const totalTokens = chunks.reduce((sum, chunk) => sum + chunk.tokenCount, 0);\n\n return {\n assetId,\n chunks: chunkEmbeddings,\n averagedEmbedding,\n provider,\n model: embeddingModel.modelId,\n metadata: {\n totalChunks: chunks.length,\n totalTokens,\n chunkingStrategy: JSON.stringify(chunkingStrategy),\n embeddingDimensions: chunkEmbeddings[0].embedding.length,\n generatedAt: new Date().toISOString(),\n },\n usage: {\n metadata: {\n assetDurationSeconds,\n },\n },\n };\n}\n\nexport async function generateEmbeddings(\n assetId: string,\n options: EmbeddingsOptions = {},\n): Promise<EmbeddingsResult> {\n \"use workflow\";\n return generateEmbeddingsInternal(assetId, options);\n}\n\n/**\n * @deprecated Use {@link generateEmbeddings} instead. This name will be removed in a future release.\n */\nexport async function generateVideoEmbeddings(\n assetId: string,\n options: EmbeddingsOptions = {},\n): Promise<EmbeddingsResult> {\n \"use workflow\";\n console.warn(\"generateVideoEmbeddings is deprecated. Use generateEmbeddings instead.\");\n return generateEmbeddingsInternal(assetId, options);\n}\n","export interface SamplingPlanOptionsV1 {\n duration_sec: number;\n min_candidates?: number;\n max_candidates?: number;\n trim_start_sec?: number;\n trim_end_sec?: number;\n fps?: number;\n base_cadence_hz?: number;\n anchor_percents?: number[];\n anchor_window_sec?: number;\n}\n\nconst DEFAULT_FPS = 30;\n\nexport function roundToNearestFrameMs(tsMs: number, fps: number = DEFAULT_FPS): number {\n const frameMs = 1000 / fps;\n return Math.round(Math.round(tsMs / frameMs) * frameMs * 100) / 100;\n}\n\n/**\n * Compute a bounded, duration‑aware set of candidate timestamps (ms) for thumbnail extraction.\n *\n * High-level approach:\n * - Trim the ends of the video to avoid low‑information regions like fades or buffers.\n * - Pick a sampling cadence (Hz) based on duration, unless the caller specifies one.\n * - Evenly distribute a base set of timestamps across the usable span.\n * - If we still have headroom up to `maxCandidates`, sprinkle extra timestamps inside small\n * windows around a few semantic anchors (percent positions like 20%, 50%, 80%) to\n * increase diversity and catch likely interesting frames.\n * - Snap all timestamps to the nearest frame boundary and enforce bounds + uniqueness.\n *\n * Inputs (from `SamplingPlanOptions`):\n * - durationSec: total media duration in seconds\n * - minCandidates/maxCandidates: hard lower/upper bounds for candidate count\n * - trimStartSec/trimEndSec: seconds trimmed from the start/end when sampling\n * - fps: frames per second used to quantize timestamps to frame boundaries\n * - baseCadenceHz: optional explicit sampling cadence; overrides duration‑based default\n * - anchorPercents: list of normalized positions [0..1] to seed diversity windows\n * - anchorWindowSec: window size (seconds) centered around each anchor percent\n */\nexport function planSamplingTimestamps(options: SamplingPlanOptionsV1): number[] {\n const DEFAULT_MIN_CANDIDATES = 10;\n const DEFAULT_MAX_CANDIDATES = 30;\n const {\n duration_sec,\n min_candidates = DEFAULT_MIN_CANDIDATES,\n max_candidates = DEFAULT_MAX_CANDIDATES,\n trim_start_sec = 1.0,\n trim_end_sec = 1.0,\n fps = DEFAULT_FPS,\n base_cadence_hz,\n anchor_percents = [0.2, 0.5, 0.8],\n anchor_window_sec = 1.5,\n } = options;\n\n // The span of the video we consider eligible for sampling after trimming.\n const usableSec = Math.max(0, duration_sec - (trim_start_sec + trim_end_sec));\n if (usableSec <= 0)\n return [];\n\n // Determine sampling cadence (samples per second). Shorter videos get denser sampling\n // to ensure enough candidates; longer videos are sparser to respect API caps and cost.\n const cadenceHz =\n base_cadence_hz ??\n (duration_sec < 15 ? 3 : duration_sec < 60 ? 2 : duration_sec < 180 ? 1.5 : 1);\n\n // Target number of candidates = cadence * usable duration, clamped within bounds.\n let target = Math.round(usableSec * cadenceHz);\n target = Math.max(min_candidates, Math.min(max_candidates, target));\n\n // Evenly spread the base timestamps across the usable span by dividing it into\n // `target` segments and sampling near each segment's center to avoid boundary bias.\n const stepSec = usableSec / target;\n const t0 = trim_start_sec;\n const base: number[] = [];\n for (let i = 0; i < target; i++) {\n const tsSec = t0 + (i + 0.5) * stepSec; // center of each segment\n base.push(tsSec * 1000);\n }\n\n // If we still have budget up to `maxCandidates`, allocate extra samples around a few\n // anchor percent positions to capture likely compelling frames (e.g., intro, midpoint).\n const slack = Math.max(0, max_candidates - base.length);\n const extra: number[] = [];\n if (slack > 0 && anchor_percents.length > 0) {\n // Distribute remaining budget across anchors; at least one per anchor if any slack.\n // Cap per-anchor samples to avoid blowing up when max_candidates is very large\n // relative to the base count — a 1.5 s window only holds ~45 frames at 30 fps.\n const perAnchor = Math.max(1, Math.min(5, Math.floor(slack / anchor_percents.length)));\n for (const p of anchor_percents) {\n // Compute the window center in seconds, clamped within the usable span.\n // Note: 1e-3 = 0.001 seconds (1 ms). We use a tiny epsilon to avoid exact\n // boundary positions which can cause zero-width windows or duplicate frames\n // after frame-quantization due to floating-point rounding.\n const centerSec = Math.min(\n t0 + usableSec - 1e-3, // nudge just inside the end bound\n Math.max(t0 + 1e-3, duration_sec * p), // nudge just inside the start bound\n );\n // Define a small window around the center and split it into `perAnchor` slots.\n const startSec = Math.max(t0, centerSec - anchor_window_sec / 2);\n const endSec = Math.min(t0 + usableSec, centerSec + anchor_window_sec / 2);\n if (endSec <= startSec)\n continue;\n const wStep = (endSec - startSec) / perAnchor;\n for (let i = 0; i < perAnchor; i++) {\n const tsSec = startSec + (i + 0.5) * wStep;\n extra.push(tsSec * 1000);\n }\n }\n }\n\n // Combine base and anchor‑window samples, snap to frame boundaries, and hard‑enforce\n // trimmed bounds for safety. Rounding to frames ensures stable, cache‑friendly URLs.\n const all = base.concat(extra)\n .map(ms => roundToNearestFrameMs(ms, fps))\n .filter(ms => ms >= trim_start_sec * 1000 && ms <= (duration_sec - trim_end_sec) * 1000);\n\n // Remove duplicates introduced by frame rounding, sort chronologically, and enforce\n // the global cap again just in case.\n const uniqSorted = Array.from(new Set(all)).sort((a, b) => a - b);\n return uniqSorted.slice(0, max_candidates);\n}\n","import { getApiKeyFromEnv } from \"@mux/ai/lib/client-factory\";\nimport type { ImageDownloadOptions } from \"@mux/ai/lib/image-download\";\nimport { downloadImagesAsBase64 } from \"@mux/ai/lib/image-download\";\nimport {\n getAssetDurationSecondsFromAsset,\n getPlaybackIdForAsset,\n getVideoTrackDurationSecondsFromAsset,\n getVideoTrackMaxFrameRateFromAsset,\n isAudioOnlyAsset,\n} from \"@mux/ai/lib/mux-assets\";\nimport { planSamplingTimestamps } from \"@mux/ai/lib/sampling-plan\";\nimport { signUrl } from \"@mux/ai/lib/url-signing\";\nimport { resolveMuxSigningContext } from \"@mux/ai/lib/workflow-credentials\";\nimport { getThumbnailUrls } from \"@mux/ai/primitives/thumbnails\";\nimport { fetchTranscriptForAsset, getReadyTextTracks } from \"@mux/ai/primitives/transcripts\";\nimport type {\n ImageSubmissionMode,\n MuxAIOptions,\n TokenUsage,\n WorkflowCredentialsInput,\n} from \"@mux/ai/types\";\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Types\n// ─────────────────────────────────────────────────────────────────────────────\n\n/** Per-thumbnail moderation result returned from `getModerationScores`. */\nexport interface ThumbnailModerationScore {\n url: string;\n sexual: number;\n violence: number;\n error: boolean;\n errorMessage?: string;\n}\n\n/** Aggregated moderation payload returned from `getModerationScores`. */\nexport interface ModerationResult {\n assetId: string;\n /** Whether moderation ran on thumbnails (video) or transcript text (audio-only). */\n mode: \"thumbnails\" | \"transcript\";\n /** Convenience flag so callers can understand why `thumbnailScores` may contain a transcript entry. */\n isAudioOnly: boolean;\n thumbnailScores: ThumbnailModerationScore[];\n /** Workflow usage metadata (asset duration, thumbnails, etc.). */\n usage?: TokenUsage;\n maxScores: {\n sexual: number;\n violence: number;\n };\n exceedsThreshold: boolean;\n thresholds: {\n sexual: number;\n violence: number;\n };\n}\n\n/** Provider list accepted by `getModerationScores`. */\nexport type ModerationProvider = \"openai\" | \"hive\";\n\nexport type HiveModerationSource =\n | { kind: \"url\"; value: string } |\n { kind: \"file\"; buffer: Uint8Array; contentType: string };\n\nexport interface HiveModerationOutput {\n classes?: Array<{\n class: string;\n score: number;\n }>;\n}\n\n/** Configuration accepted by `getModerationScores`. */\nexport interface ModerationOptions extends MuxAIOptions {\n /** Provider used for moderation (defaults to 'openai'). */\n provider?: ModerationProvider;\n /** OpenAI moderation model identifier (defaults to 'omni-moderation-latest'). */\n model?: string;\n /**\n * Optional transcript language code used when moderating audio-only assets.\n * If omitted, the first ready text track will be used.\n */\n languageCode?: string;\n /** Override the default sexual/violence thresholds (0-1). */\n thresholds?: {\n sexual?: number;\n violence?: number;\n };\n /** Interval between storyboard thumbnails in seconds (defaults to 10). */\n thumbnailInterval?: number;\n /** Width of storyboard thumbnails in pixels (defaults to 640). */\n thumbnailWidth?: number;\n /** Maximum number of thumbnails to sample (defaults to unlimited). When set, samples are evenly distributed with first and last frames pinned. */\n maxSamples?: number;\n /** Max concurrent moderation requests (defaults to 5). */\n maxConcurrent?: number;\n /** Transport used for thumbnails (defaults to 'url'). */\n imageSubmissionMode?: ImageSubmissionMode;\n /** Download tuning used when `imageSubmissionMode` === 'base64'. */\n imageDownloadOptions?: ImageDownloadOptions;\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Implementation\n// ─────────────────────────────────────────────────────────────────────────────\n\nconst DEFAULT_THRESHOLDS = {\n sexual: 0.7,\n violence: 0.8,\n};\n\nconst DEFAULT_PROVIDER = \"openai\";\n\nconst HIVE_ENDPOINT = \"https://api.thehive.ai/api/v2/task/sync\";\nconst HIVE_SEXUAL_CATEGORIES = [\n \"general_nsfw\",\n \"general_suggestive\",\n \"yes_sexual_activity\",\n \"sex_toys\",\n \"nudity_female\",\n \"nudity_male\",\n];\n\nconst HIVE_VIOLENCE_CATEGORIES = [\n \"gun_in_hand\",\n \"gun_not_in_hand\",\n \"knife_in_hand\",\n \"very_bloody\",\n \"other_blood\",\n \"hanging\",\n \"noose\",\n \"human_corpse\",\n \"emaciated_body\",\n \"self_harm\",\n \"animal_abuse\",\n \"fights\",\n \"garm_death_injury_or_military_conflict\",\n];\n\nasync function processConcurrently<T>(\n items: any[],\n processor: (item: any) => Promise<T>,\n maxConcurrent: number = 5,\n): Promise<T[]> {\n \"use step\";\n const results: T[] = [];\n\n for (let i = 0; i < items.length; i += maxConcurrent) {\n const batch = items.slice(i, i + maxConcurrent);\n const batchPromises = batch.map(processor);\n const batchResults = await Promise.all(batchPromises);\n results.push(...batchResults);\n }\n\n return results;\n}\n\nasync function moderateImageWithOpenAI(entry: {\n url: string;\n image: string;\n model: string;\n credentials?: WorkflowCredentialsInput;\n}): Promise<ThumbnailModerationScore> {\n \"use step\";\n const apiKey = await getApiKeyFromEnv(\"openai\", entry.credentials);\n try {\n const res = await fetch(\"https://api.openai.com/v1/moderations\", {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n \"Authorization\": `Bearer ${apiKey}`,\n },\n body: JSON.stringify({\n model: entry.model,\n input: [\n {\n type: \"image_url\",\n image_url: {\n url: entry.image,\n },\n },\n ],\n }),\n });\n\n const json: any = await res.json();\n if (!res.ok) {\n throw new Error(\n `OpenAI moderation error: ${res.status} ${res.statusText} - ${JSON.stringify(json)}`,\n );\n }\n\n const categoryScores = json.results?.[0]?.category_scores || {};\n\n return {\n url: entry.url,\n sexual: categoryScores.sexual || 0,\n violence: categoryScores.violence || 0,\n error: false,\n };\n } catch (error) {\n console.error(\"OpenAI moderation failed:\", error);\n return {\n url: entry.url,\n sexual: 0,\n violence: 0,\n error: true,\n errorMessage: error instanceof Error ? error.message : String(error),\n };\n }\n}\n\nasync function requestOpenAIModeration(\n imageUrls: string[],\n model: string,\n maxConcurrent: number = 5,\n submissionMode: \"url\" | \"base64\" = \"url\",\n downloadOptions?: ImageDownloadOptions,\n credentials?: WorkflowCredentialsInput,\n): Promise<ThumbnailModerationScore[]> {\n \"use step\";\n const targetUrls =\n submissionMode === \"base64\" ?\n (await downloadImagesAsBase64(imageUrls, downloadOptions, maxConcurrent)).map(\n img => ({ url: img.url, image: img.base64Data, model, credentials }),\n ) :\n imageUrls.map(url => ({ url, image: url, model, credentials }));\n\n return processConcurrently(targetUrls, moderateImageWithOpenAI, maxConcurrent);\n}\n\nasync function requestOpenAITextModeration(\n text: string,\n model: string,\n url: string,\n credentials?: WorkflowCredentialsInput,\n): Promise<ThumbnailModerationScore> {\n \"use step\";\n const apiKey = await getApiKeyFromEnv(\"openai\", credentials);\n try {\n const res = await fetch(\"https://api.openai.com/v1/moderations\", {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n \"Authorization\": `Bearer ${apiKey}`,\n },\n body: JSON.stringify({\n model,\n input: text,\n }),\n });\n\n const json: any = await res.json();\n if (!res.ok) {\n throw new Error(\n `OpenAI moderation error: ${res.status} ${res.statusText} - ${JSON.stringify(json)}`,\n );\n }\n\n const categoryScores = json.results?.[0]?.category_scores || {};\n\n return {\n url,\n sexual: categoryScores.sexual || 0,\n violence: categoryScores.violence || 0,\n error: false,\n };\n } catch (error) {\n console.error(\"OpenAI text moderation failed:\", error);\n return {\n url,\n sexual: 0,\n violence: 0,\n error: true,\n errorMessage: error instanceof Error ? error.message : String(error),\n };\n }\n}\n\nfunction chunkTextByUtf16CodeUnits(text: string, maxUnits: number): string[] {\n if (!text.trim()) {\n return [];\n }\n if (text.length <= maxUnits) {\n return [text];\n }\n const chunks: string[] = [];\n for (let i = 0; i < text.length; i += maxUnits) {\n const chunk = text.slice(i, i + maxUnits).trim();\n if (chunk) {\n chunks.push(chunk);\n }\n }\n return chunks;\n}\n\nasync function requestOpenAITranscriptModeration(\n transcriptText: string,\n model: string,\n maxConcurrent: number = 5,\n credentials?: WorkflowCredentialsInput,\n): Promise<ThumbnailModerationScore[]> {\n \"use step\";\n // OpenAI supports larger inputs, but chunking avoids pathological single-request sizes and\n // mirrors our \"max over segments\" behavior used for thumbnail moderation.\n const chunks = chunkTextByUtf16CodeUnits(transcriptText, 10_000);\n if (!chunks.length) {\n return [\n { url: \"transcript:0\", sexual: 0, violence: 0, error: true, errorMessage: \"No transcript chunks to moderate\" },\n ];\n }\n const targets = chunks.map((chunk, idx) => ({\n chunk,\n url: `transcript:${idx}`,\n }));\n return processConcurrently(\n targets,\n async entry => requestOpenAITextModeration(entry.chunk, model, entry.url, credentials),\n maxConcurrent,\n );\n}\n\nfunction getHiveCategoryScores(\n classes: NonNullable<HiveModerationOutput[\"classes\"]>,\n categoryNames: string[],\n): number {\n const scoreMap = Object.fromEntries(\n classes.map(c => [c.class, c.score]),\n );\n const scores = categoryNames.map(category => scoreMap[category] || 0);\n return Math.max(...scores, 0);\n}\n\nasync function moderateImageWithHive(entry: {\n url: string;\n source: HiveModerationSource;\n credentials?: WorkflowCredentialsInput;\n}): Promise<ThumbnailModerationScore> {\n \"use step\";\n const apiKey = await getApiKeyFromEnv(\"hive\", entry.credentials);\n try {\n const formData = new FormData();\n\n if (entry.source.kind === \"url\") {\n formData.append(\"url\", entry.source.value);\n } else {\n const extension = entry.source.contentType.split(\"/\")[1] || \"jpg\";\n const blob = new Blob([entry.source.buffer], {\n type: entry.source.contentType,\n });\n formData.append(\"media\", blob, `thumbnail.${extension}`);\n }\n\n const controller = new AbortController();\n const timeout = setTimeout(() => controller.abort(), 15_000);\n let res: Response;\n try {\n res = await fetch(HIVE_ENDPOINT, {\n method: \"POST\",\n headers: {\n Accept: \"application/json\",\n Authorization: `Token ${apiKey}`,\n },\n body: formData,\n signal: controller.signal,\n });\n } catch (err: any) {\n if (err?.name === \"AbortError\") {\n throw new Error(\"Hive request timed out after 15s\");\n }\n throw err;\n } finally {\n clearTimeout(timeout);\n }\n\n const json: any = await res.json().catch(() => undefined);\n\n if (!res.ok) {\n throw new Error(\n `Hive moderation error: ${res.status} ${res.statusText} - ${JSON.stringify(json)}`,\n );\n }\n\n if (json?.return_code != null && json.return_code !== 0) {\n throw new Error(\n `Hive API error (return_code ${json.return_code}): ${json.message || \"Unknown error\"}`,\n );\n }\n\n // Extract scores from Hive response\n // Hive returns scores in status[0].response.output[0].classes as array of {class, score}\n const classes = json?.status?.[0]?.response?.output?.[0]?.classes;\n if (!Array.isArray(classes)) {\n throw new TypeError(\n `Unexpected Hive response structure: ${JSON.stringify(json)}`,\n );\n }\n\n const sexual = getHiveCategoryScores(classes, HIVE_SEXUAL_CATEGORIES);\n const violence = getHiveCategoryScores(classes, HIVE_VIOLENCE_CATEGORIES);\n\n return {\n url: entry.url,\n sexual,\n violence,\n error: false,\n };\n } catch (error) {\n return {\n url: entry.url,\n sexual: 0,\n violence: 0,\n error: true,\n errorMessage: error instanceof Error ? error.message : String(error),\n };\n }\n}\n\nasync function requestHiveModeration(\n imageUrls: string[],\n maxConcurrent: number = 5,\n submissionMode: \"url\" | \"base64\" = \"url\",\n downloadOptions?: ImageDownloadOptions,\n credentials?: WorkflowCredentialsInput,\n): Promise<ThumbnailModerationScore[]> {\n \"use step\";\n\n const targets: Array<{ url: string; source: HiveModerationSource; credentials?: WorkflowCredentialsInput }> =\n submissionMode === \"base64\" ?\n (await downloadImagesAsBase64(imageUrls, downloadOptions, maxConcurrent)).map(img => ({\n url: img.url,\n source: {\n kind: \"file\",\n buffer: img.buffer,\n contentType: img.contentType,\n },\n credentials,\n })) :\n imageUrls.map(url => ({\n url,\n source: { kind: \"url\", value: url },\n credentials,\n }));\n\n return await processConcurrently(targets, moderateImageWithHive, maxConcurrent);\n}\n\nasync function getThumbnailUrlsFromTimestamps(\n playbackId: string,\n timestampsMs: number[],\n options: {\n width: number;\n shouldSign: boolean;\n credentials?: WorkflowCredentialsInput;\n },\n): Promise<string[]> {\n \"use step\";\n const { width, shouldSign, credentials } = options;\n const baseUrl = `https://image.mux.com/${playbackId}/thumbnail.png`;\n\n const urlPromises = timestampsMs.map(async (tsMs) => {\n const time = Number((tsMs / 1000).toFixed(2));\n if (shouldSign) {\n return signUrl(baseUrl, playbackId, undefined, \"thumbnail\", { time, width }, credentials);\n }\n\n return `${baseUrl}?time=${time}&width=${width}`;\n });\n\n return Promise.all(urlPromises);\n}\n\n/**\n * Moderate a Mux asset.\n * - Video assets: moderates storyboard thumbnails (image moderation)\n * - Audio-only assets: moderates transcript text (text moderation)\n *\n * Provider notes:\n * - provider 'openai' uses OpenAI's hosted moderation endpoint (requires OPENAI_API_KEY)\n * Ref: https://platform.openai.com/docs/guides/moderation\n * - provider 'hive' uses Hive's moderation API for thumbnails only (requires HIVE_API_KEY)\n */\nexport async function getModerationScores(\n assetId: string,\n options: ModerationOptions = {},\n): Promise<ModerationResult> {\n \"use workflow\";\n const {\n provider = DEFAULT_PROVIDER,\n model = provider === \"openai\" ? \"omni-moderation-latest\" : undefined,\n languageCode,\n thresholds = DEFAULT_THRESHOLDS,\n thumbnailInterval = 10,\n thumbnailWidth = 640,\n maxSamples,\n maxConcurrent = 5,\n imageSubmissionMode = \"url\",\n imageDownloadOptions,\n credentials: providedCredentials,\n } = options;\n const credentials = providedCredentials;\n // Fetch asset data and playback ID from Mux via helper\n const { asset, playbackId, policy } = await getPlaybackIdForAsset(assetId, credentials);\n const videoTrackDurationSeconds = getVideoTrackDurationSecondsFromAsset(asset);\n const videoTrackFps = getVideoTrackMaxFrameRateFromAsset(asset);\n const assetDurationSeconds = getAssetDurationSecondsFromAsset(asset);\n // Use the shorter of video-track and asset duration so thumbnail timestamps never\n // exceed the renderable range reported by the Mux thumbnail service.\n const candidateDurations = [videoTrackDurationSeconds, assetDurationSeconds].filter(\n (d): d is number => d != null,\n );\n const duration = candidateDurations.length > 0 ? Math.min(...candidateDurations) : 0;\n const isAudioOnly = isAudioOnlyAsset(asset);\n\n // Resolve signing context for signed playback IDs\n const signingContext = await resolveMuxSigningContext(credentials);\n if (policy === \"signed\" && !signingContext) {\n throw new Error(\n \"Signed playback ID requires signing credentials. \" +\n \"Set MUX_SIGNING_KEY and MUX_PRIVATE_KEY environment variables.\",\n );\n }\n\n let thumbnailScores: ThumbnailModerationScore[];\n let mode: ModerationResult[\"mode\"] = \"thumbnails\";\n let thumbnailCount: number | undefined;\n\n if (isAudioOnly) {\n mode = \"transcript\";\n const readyTextTracks = getReadyTextTracks(asset);\n let transcriptResult = await fetchTranscriptForAsset(asset, playbackId, {\n languageCode,\n cleanTranscript: true,\n shouldSign: policy === \"signed\",\n credentials,\n required: true,\n });\n\n // Audio-only assets may have a single ready text track that isn't \"subtitles\" (e.g. transcripts).\n // If a language-specific subtitle wasn't found but there's exactly one track, fall back to it.\n if (!transcriptResult.track && readyTextTracks.length === 1) {\n transcriptResult = await fetchTranscriptForAsset(asset, playbackId, {\n cleanTranscript: true,\n shouldSign: policy === \"signed\",\n credentials,\n required: true,\n });\n }\n\n if (provider === \"openai\") {\n thumbnailScores = await requestOpenAITranscriptModeration(\n transcriptResult.transcriptText,\n model || \"omni-moderation-latest\",\n maxConcurrent,\n credentials,\n );\n } else if (provider === \"hive\") {\n throw new Error(\"Hive does not support transcript moderation in this workflow. Use provider: 'openai' for audio-only assets.\");\n } else {\n throw new Error(`Unsupported moderation provider: ${provider}`);\n }\n } else {\n const thumbnailUrls = maxSamples === undefined ?\n // Generate thumbnail URLs (signed if needed) using existing interval-based logic.\n await getThumbnailUrls(playbackId, duration, {\n interval: thumbnailInterval,\n width: thumbnailWidth,\n shouldSign: policy === \"signed\",\n credentials,\n }) :\n // In maxSamples mode, sample valid timestamps over the trimmed usable span.\n // Use proportional trims (≈ duration/6, capped at 5s) to stay well inside the\n // renderable range — Mux can't always serve thumbnails at the very edges.\n await getThumbnailUrlsFromTimestamps(\n playbackId,\n planSamplingTimestamps({\n duration_sec: duration,\n max_candidates: maxSamples,\n trim_start_sec: duration > 2 ? Math.min(5, Math.max(1, duration / 6)) : 0,\n trim_end_sec: duration > 2 ? Math.min(5, Math.max(1, duration / 6)) : 0,\n fps: videoTrackFps,\n base_cadence_hz: thumbnailInterval > 0 ? 1 / thumbnailInterval : undefined,\n }),\n {\n width: thumbnailWidth,\n shouldSign: policy === \"signed\",\n credentials,\n },\n );\n thumbnailCount = thumbnailUrls.length;\n\n if (provider === \"openai\") {\n thumbnailScores = await requestOpenAIModeration(\n thumbnailUrls,\n model || \"omni-moderation-latest\",\n maxConcurrent,\n imageSubmissionMode,\n imageDownloadOptions,\n credentials,\n );\n } else if (provider === \"hive\") {\n thumbnailScores = await requestHiveModeration(\n thumbnailUrls,\n maxConcurrent,\n imageSubmissionMode,\n imageDownloadOptions,\n credentials,\n );\n } else {\n throw new Error(`Unsupported moderation provider: ${provider}`);\n }\n }\n\n const failed = thumbnailScores.filter(s => s.error);\n if (failed.length > 0) {\n const details = failed.map(s => `${s.url}: ${s.errorMessage || \"Unknown error\"}`).join(\"; \");\n throw new Error(\n `Moderation failed for ${failed.length}/${thumbnailScores.length} thumbnail(s): ${details}`,\n );\n }\n\n // Find highest scores across all thumbnails\n const maxSexual = Math.max(...thumbnailScores.map(s => s.sexual));\n const maxViolence = Math.max(...thumbnailScores.map(s => s.violence));\n\n const finalThresholds = { ...DEFAULT_THRESHOLDS, ...thresholds };\n\n return {\n assetId,\n mode,\n isAudioOnly,\n thumbnailScores,\n usage: {\n metadata: {\n assetDurationSeconds: duration,\n ...(thumbnailCount === undefined ? {} : { thumbnailCount }),\n },\n },\n maxScores: {\n sexual: maxSexual,\n violence: maxViolence,\n },\n exceedsThreshold: maxSexual > finalThresholds.sexual || maxViolence > finalThresholds.violence,\n thresholds: finalThresholds,\n };\n}\n","import { generateText, Output } from \"ai\";\nimport dedent from \"dedent\";\nimport { z } from \"zod\";\n\nimport type { ImageDownloadOptions } from \"@mux/ai/lib/image-download\";\nimport { downloadImageAsBase64 } from \"@mux/ai/lib/image-download\";\nimport {\n getAssetDurationSecondsFromAsset,\n getPlaybackIdForAsset,\n isAudioOnlyAsset,\n} from \"@mux/ai/lib/mux-assets\";\nimport type {\n PromptOverrides,\n} from \"@mux/ai/lib/prompt-builder\";\nimport {\n createPromptBuilder,\n createToneSection,\n createTranscriptSection,\n} from \"@mux/ai/lib/prompt-builder\";\nimport { createLanguageModelFromConfig, resolveLanguageModelConfig } from \"@mux/ai/lib/providers\";\nimport type { ModelIdByProvider, SupportedProvider } from \"@mux/ai/lib/providers\";\nimport { withRetry } from \"@mux/ai/lib/retry\";\nimport {\n resolveMuxSigningContext,\n} from \"@mux/ai/lib/workflow-credentials\";\nimport { getStoryboardUrl } from \"@mux/ai/primitives/storyboards\";\nimport { fetchTranscriptForAsset } from \"@mux/ai/primitives/transcripts\";\nimport type {\n ImageSubmissionMode,\n MuxAIOptions,\n TokenUsage,\n ToneType,\n WorkflowCredentialsInput,\n} from \"@mux/ai/types\";\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Types\n// ─────────────────────────────────────────────────────────────────────────────\n\nexport const SUMMARY_KEYWORD_LIMIT = 10;\n\nexport const summarySchema = z.object({\n keywords: z.array(z.string()),\n title: z.string(),\n description: z.string(),\n}).strict();\n\nexport type SummaryType = z.infer<typeof summarySchema>;\n\nconst SUMMARY_OUTPUT = Output.object({\n name: \"summary_metadata\",\n description: \"Structured summary with title, description, and keywords.\",\n schema: summarySchema,\n});\n\n/** Structured return payload for `getSummaryAndTags`. */\nexport interface SummaryAndTagsResult {\n /** Asset ID passed into the workflow. */\n assetId: string;\n /** Short headline generated from the storyboard. */\n title: string;\n /** Longer description of the detected content. */\n description: string;\n /** Up to 10 keywords extracted by the model. */\n tags: string[];\n /** Storyboard image URL that was analyzed (undefined for audio-only assets). */\n storyboardUrl?: string;\n /** Token usage from the AI provider (for efficiency/cost analysis). */\n usage?: TokenUsage;\n /** Raw transcript text used for analysis (when includeTranscript is true). */\n transcriptText?: string;\n}\n\n/**\n * Sections of the summarization user prompt that can be overridden.\n * Use these to customize the AI's behavior for your specific use case.\n */\nexport type SummarizationPromptSections =\n | \"task\" |\n \"title\" |\n \"description\" |\n \"keywords\" |\n \"qualityGuidelines\";\n\n/**\n * Override specific sections of the summarization prompt.\n * Each key corresponds to a section that can be customized.\n *\n * @example\n * ```typescript\n * const result = await getSummaryAndTags(assetId, {\n * promptOverrides: {\n * task: 'Generate SEO-optimized metadata for this product video.',\n * title: 'Create a click-worthy title under 60 characters for YouTube.',\n * },\n * });\n * ```\n */\nexport type SummarizationPromptOverrides = PromptOverrides<SummarizationPromptSections>;\n\n/** Configuration accepted by `getSummaryAndTags`. */\nexport interface SummarizationOptions extends MuxAIOptions {\n /** AI provider to run (defaults to 'openai'). */\n provider?: SupportedProvider;\n /** Provider-specific chat model identifier. */\n model?: ModelIdByProvider[SupportedProvider];\n /** Prompt tone shim applied to the system instruction (defaults to 'neutral'). */\n tone?: ToneType;\n /** Fetch the transcript and send it alongside the storyboard (defaults to true). */\n includeTranscript?: boolean;\n /** Strip timestamps/markup from transcripts before including them (defaults to true). */\n cleanTranscript?: boolean;\n /** How storyboard frames should be delivered to the provider (defaults to 'url'). */\n imageSubmissionMode?: ImageSubmissionMode;\n /** Fine-tune storyboard downloads when `imageSubmissionMode` === 'base64'. */\n imageDownloadOptions?: ImageDownloadOptions;\n /**\n * Override specific sections of the user prompt.\n * Useful for customizing the AI's output for specific use cases (SEO, social media, etc.)\n */\n promptOverrides?: SummarizationPromptOverrides;\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Prompts\n// ─────────────────────────────────────────────────────────────────────────────\n\nconst VALID_TONES = [\"neutral\", \"playful\", \"professional\"] as const;\n\nconst TONE_INSTRUCTIONS: Record<ToneType, string> = {\n neutral: \"Provide a clear, straightforward analysis.\",\n 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!\",\n professional: \"Provide a professional, executive-level analysis suitable for business reporting.\",\n};\n\n/**\n * Prompt builder for the summarization user prompt.\n * Sections can be individually overridden via `promptOverrides` in SummarizationOptions.\n */\nconst summarizationPromptBuilder = createPromptBuilder<SummarizationPromptSections>({\n template: {\n task: {\n tag: \"task\",\n content: \"Analyze the storyboard frames and generate metadata that captures the essence of the video content.\",\n },\n title: {\n tag: \"title_requirements\",\n content: dedent`\n A short, compelling headline that immediately communicates the subject or action.\n Aim for brevity - typically under 10 words. Think of how a news headline or video card title would read.\n Start with the primary subject, action, or topic - never begin with \"A video of\" or similar phrasing.\n Use active, specific language.`,\n },\n description: {\n tag: \"description_requirements\",\n content: dedent`\n A concise summary (2-4 sentences) that describes what happens across the video.\n Cover the main subjects, actions, setting, and any notable progression visible across frames.\n Write in present tense. Be specific about observable details rather than making assumptions.\n If the transcript provides dialogue or narration, incorporate key points but prioritize visual content.`,\n },\n keywords: {\n tag: \"keywords_requirements\",\n content: dedent`\n Specific, searchable terms (up to ${SUMMARY_KEYWORD_LIMIT}) that capture:\n - Primary subjects (people, animals, objects)\n - Actions and activities being performed\n - Setting and environment\n - Notable objects or tools\n - Style or genre (if applicable)\n Prefer concrete nouns and action verbs over abstract concepts.\n Use lowercase. Avoid redundant or overly generic terms like \"video\" or \"content\".`,\n },\n qualityGuidelines: {\n tag: \"quality_guidelines\",\n content: dedent`\n - Examine all frames to understand the full context and progression\n - Be precise: \"golden retriever\" is better than \"dog\" when identifiable\n - Capture the narrative: what begins, develops, and concludes\n - Balance brevity with informativeness`,\n },\n },\n sectionOrder: [\"task\", \"title\", \"description\", \"keywords\", \"qualityGuidelines\"],\n});\n\n/**\n * Prompt builder for audio-only content.\n * Focuses on transcript analysis without visual references.\n */\nconst audioOnlyPromptBuilder = createPromptBuilder<SummarizationPromptSections>({\n template: {\n task: {\n tag: \"task\",\n content: \"Analyze the transcript and generate metadata that captures the essence of the audio content.\",\n },\n title: {\n tag: \"title_requirements\",\n content: dedent`\n A short, compelling headline that immediately communicates the subject or topic.\n Aim for brevity - typically under 10 words. Think of how a podcast title or audio description would read.\n Start with the primary subject, action, or topic - never begin with \"An audio of\" or similar phrasing.\n Use active, specific language.`,\n },\n description: {\n tag: \"description_requirements\",\n content: dedent`\n A concise summary (2-4 sentences) that describes the audio content.\n Cover the main topics, speakers, themes, and any notable progression in the discussion or narration.\n Write in present tense. Be specific about what is discussed or presented rather than making assumptions.\n Focus on the spoken content and any key insights, dialogue, or narrative elements.`,\n },\n keywords: {\n tag: \"keywords_requirements\",\n content: dedent`\n Specific, searchable terms (up to ${SUMMARY_KEYWORD_LIMIT}) that capture:\n - Primary topics and themes\n - Speakers or presenters (if named)\n - Key concepts and terminology\n - Content type (interview, lecture, music, etc.)\n - Genre or style (if applicable)\n Prefer concrete nouns and relevant terms over abstract concepts.\n Use lowercase. Avoid redundant or overly generic terms like \"audio\" or \"content\".`,\n },\n qualityGuidelines: {\n tag: \"quality_guidelines\",\n content: dedent`\n - Analyze the full transcript to understand context and themes\n - Be precise: use specific terminology when mentioned\n - Capture the narrative: what is introduced, discussed, and concluded\n - Balance brevity with informativeness`,\n },\n },\n sectionOrder: [\"task\", \"title\", \"description\", \"keywords\", \"qualityGuidelines\"],\n});\n\nconst SYSTEM_PROMPT = dedent`\n <role>\n You are a video content analyst specializing in storyboard interpretation and multimodal analysis.\n </role>\n\n <context>\n You receive storyboard images containing multiple sequential frames extracted from a video.\n These frames are arranged in a grid and represent the visual progression of the content over time.\n Read frames left-to-right, top-to-bottom to understand the temporal sequence.\n </context>\n\n <transcript_guidance>\n When a transcript is provided alongside the storyboard:\n - Use it to understand spoken content, dialogue, narration, and audio context\n - Correlate transcript content with visual frames to build a complete picture\n - Extract key terminology, names, and specific language used by speakers\n - Let the transcript inform keyword selection, especially for topics not visually obvious\n - Prioritize visual content for the description, but enrich it with transcript insights\n - If transcript and visuals conflict, trust the visual evidence\n </transcript_guidance>\n\n <capabilities>\n - Extract meaning from visual sequences\n - Identify subjects, actions, settings, and narrative arcs\n - Generate accurate, searchable metadata\n - Synthesize visual and transcript information when provided\n </capabilities>\n\n <constraints>\n - Only describe what is clearly observable in the frames or explicitly stated in the transcript\n - Do not fabricate details or make unsupported assumptions\n - Return structured data matching the requested schema\n - Output only the JSON object; no markdown or extra text\n </constraints>\n\n <tone_guidance>\n Pay special attention to the <tone> section and lean heavily into those instructions.\n Adapt your entire analysis and writing style to match the specified tone - this should influence\n your word choice, personality, formality level, and overall presentation of the content.\n The tone instructions are not suggestions but core requirements for how you should express yourself.\n </tone_guidance>\n\n <language_guidelines>\n AVOID these meta-descriptive phrases that reference the medium rather than the content:\n - \"The image shows...\" / \"The storyboard shows...\"\n - \"In this video...\" / \"This video features...\"\n - \"The frames depict...\" / \"The footage shows...\"\n - \"We can see...\" / \"You can see...\"\n - \"The clip shows...\" / \"The scene shows...\"\n\n INSTEAD, describe the content directly:\n - BAD: \"The video shows a chef preparing a meal\"\n - GOOD: \"A chef prepares a meal in a professional kitchen\"\n\n Write as if describing reality, not describing a recording of reality.\n </language_guidelines>`;\n\nconst AUDIO_ONLY_SYSTEM_PROMPT = dedent`\n <role>\n You are an audio content analyst specializing in transcript analysis and metadata generation.\n </role>\n\n <context>\n You receive transcript text from audio-only content (podcasts, audiobooks, music, etc.).\n Your task is to analyze the spoken/audio content and generate accurate, searchable metadata.\n </context>\n\n <transcript_guidance>\n - Carefully analyze the entire transcript to understand themes, topics, and key points\n - Extract key terminology, names, concepts, and specific language used\n - Identify the content type (interview, lecture, music, narration, etc.)\n - Note the tone, style, and any distinctive characteristics of the audio\n - Consider the intended audience and context based on language and content\n </transcript_guidance>\n\n <capabilities>\n - Extract meaning and themes from spoken/audio content\n - Identify subjects, topics, speakers, and narrative structure\n - Generate accurate, searchable metadata from audio-based content\n - Understand context and intent from transcript alone\n </capabilities>\n\n <constraints>\n - Only describe what is explicitly stated or strongly implied in the transcript\n - Do not fabricate details or make unsupported assumptions\n - Return structured data matching the requested schema\n - Focus entirely on audio/spoken content - there are no visual elements\n - Output only the JSON object; no markdown or extra text\n </constraints>\n\n <tone_guidance>\n Pay special attention to the <tone> section and lean heavily into those instructions.\n Adapt your entire analysis and writing style to match the specified tone - this should influence\n your word choice, personality, formality level, and overall presentation of the content.\n The tone instructions are not suggestions but core requirements for how you should express yourself.\n </tone_guidance>\n\n <language_guidelines>\n AVOID these meta-descriptive phrases that reference the medium rather than the content:\n - \"The audio shows...\" / \"The transcript shows...\"\n - \"In this recording...\" / \"This audio features...\"\n - \"The speaker says...\" / \"We can hear...\"\n - \"The clip contains...\" / \"The recording shows...\"\n\n INSTEAD, describe the content directly:\n - BAD: \"The audio features a discussion about climate change\"\n - GOOD: \"A panel discusses climate change impacts and solutions\"\n\n Write as if describing reality, not describing a recording of reality.\n </language_guidelines>`;\n\ninterface UserPromptContext {\n tone: ToneType;\n transcriptText?: string;\n isCleanTranscript?: boolean;\n promptOverrides?: SummarizationPromptOverrides;\n isAudioOnly?: boolean;\n}\n\nfunction buildUserPrompt({\n tone,\n transcriptText,\n isCleanTranscript = true,\n promptOverrides,\n isAudioOnly = false,\n}: UserPromptContext): string {\n // Build dynamic context sections\n const contextSections = [createToneSection(TONE_INSTRUCTIONS[tone])];\n\n if (transcriptText) {\n const format = isCleanTranscript ? \"plain text\" : \"WebVTT\";\n contextSections.push(createTranscriptSection(transcriptText, format));\n }\n\n // Use audio-only prompt builder for audio-only assets\n const promptBuilder = isAudioOnly ? audioOnlyPromptBuilder : summarizationPromptBuilder;\n return promptBuilder.buildWithContext(promptOverrides, contextSections);\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Implementation\n// ─────────────────────────────────────────────────────────────────────────────\n\ninterface AnalysisResponse {\n result: SummaryType;\n usage: TokenUsage;\n}\n\nasync function analyzeStoryboard(\n imageDataUrl: string,\n provider: SupportedProvider,\n modelId: string,\n userPrompt: string,\n systemPrompt: string,\n credentials?: WorkflowCredentialsInput,\n): Promise<AnalysisResponse> {\n \"use step\";\n const model = await createLanguageModelFromConfig(provider, modelId, credentials);\n\n const response = await generateText({\n model,\n output: SUMMARY_OUTPUT,\n messages: [\n {\n role: \"system\",\n content: systemPrompt,\n },\n {\n role: \"user\",\n content: [\n { type: \"text\", text: userPrompt },\n { type: \"image\", image: imageDataUrl },\n ],\n },\n ],\n });\n\n if (!response.output) {\n throw new Error(\"Summarization output missing\");\n }\n\n const parsed = summarySchema.parse(response.output);\n\n return {\n result: parsed,\n usage: {\n inputTokens: response.usage.inputTokens,\n outputTokens: response.usage.outputTokens,\n totalTokens: response.usage.totalTokens,\n reasoningTokens: response.usage.reasoningTokens,\n cachedInputTokens: response.usage.cachedInputTokens,\n },\n };\n}\n\nasync function analyzeAudioOnly(\n provider: SupportedProvider,\n modelId: string,\n userPrompt: string,\n systemPrompt: string,\n credentials?: WorkflowCredentialsInput,\n): Promise<AnalysisResponse> {\n \"use step\";\n const model = await createLanguageModelFromConfig(provider, modelId, credentials);\n\n const response = await generateText({\n model,\n output: SUMMARY_OUTPUT,\n messages: [\n {\n role: \"system\",\n content: systemPrompt,\n },\n {\n role: \"user\",\n content: userPrompt,\n },\n ],\n });\n\n if (!response.output) {\n throw new Error(\"Summarization output missing\");\n }\n\n const parsed = summarySchema.parse(response.output);\n\n return {\n result: parsed,\n usage: {\n inputTokens: response.usage.inputTokens,\n outputTokens: response.usage.outputTokens,\n totalTokens: response.usage.totalTokens,\n reasoningTokens: response.usage.reasoningTokens,\n cachedInputTokens: response.usage.cachedInputTokens,\n },\n };\n}\n\nfunction normalizeKeywords(keywords?: string[]): string[] {\n if (!Array.isArray(keywords) || keywords.length === 0) {\n return [];\n }\n\n const uniqueLowercase = new Set<string>();\n const normalized: string[] = [];\n\n for (const keyword of keywords) {\n const trimmed = keyword?.trim();\n if (!trimmed) {\n continue;\n }\n\n const lower = trimmed.toLowerCase();\n if (uniqueLowercase.has(lower)) {\n continue;\n }\n\n uniqueLowercase.add(lower);\n normalized.push(trimmed);\n\n if (normalized.length === SUMMARY_KEYWORD_LIMIT) {\n break;\n }\n }\n\n return normalized;\n}\n\nexport async function getSummaryAndTags(\n assetId: string,\n options?: SummarizationOptions,\n): Promise<SummaryAndTagsResult> {\n \"use workflow\";\n const {\n provider = \"openai\",\n model,\n tone = \"neutral\",\n includeTranscript = true,\n cleanTranscript = true,\n imageSubmissionMode = \"url\",\n imageDownloadOptions,\n promptOverrides,\n credentials,\n } = options ?? {};\n\n // Validate tone parameter\n if (!VALID_TONES.includes(tone)) {\n throw new Error(\n `Invalid tone \"${tone}\". Valid tones are: ${VALID_TONES.join(\", \")}`,\n );\n }\n\n const modelConfig = resolveLanguageModelConfig({\n ...options,\n model,\n provider: provider as SupportedProvider,\n });\n const workflowCredentials = credentials;\n\n // Fetch asset data from Mux and grab playback/transcript details\n const { asset: assetData, playbackId, policy } = await getPlaybackIdForAsset(assetId, workflowCredentials);\n\n const assetDurationSeconds = getAssetDurationSecondsFromAsset(assetData);\n\n // Detect if asset is audio-only\n const isAudioOnly = isAudioOnlyAsset(assetData);\n\n // Audio-only assets require transcripts since there's no visual content\n if (isAudioOnly && !includeTranscript) {\n throw new Error(\n \"Audio-only assets require a transcript. Set includeTranscript: true and ensure the asset has a ready text track (captions/subtitles).\",\n );\n }\n\n // Resolve signing context for signed playback IDs\n const signingContext = await resolveMuxSigningContext(workflowCredentials);\n if (policy === \"signed\" && !signingContext) {\n throw new Error(\n \"Signed playback ID requires signing credentials. \" +\n \"Set MUX_SIGNING_KEY and MUX_PRIVATE_KEY environment variables.\",\n );\n }\n\n const transcriptText =\n includeTranscript ?\n (await fetchTranscriptForAsset(assetData, playbackId, {\n cleanTranscript,\n shouldSign: policy === \"signed\",\n credentials: workflowCredentials,\n required: isAudioOnly,\n })).transcriptText :\n \"\";\n\n // Build the user prompt with all context and any overrides\n const userPrompt = buildUserPrompt({\n tone,\n transcriptText,\n isCleanTranscript: cleanTranscript,\n promptOverrides,\n isAudioOnly,\n });\n\n let analysisResponse: AnalysisResponse;\n let imageUrl: string | undefined;\n\n // Choose system prompt and analysis method based on asset type\n const systemPrompt = isAudioOnly ? AUDIO_ONLY_SYSTEM_PROMPT : SYSTEM_PROMPT;\n\n try {\n if (isAudioOnly) {\n // Audio-only analysis: skip storyboard, analyze transcript only\n analysisResponse = await analyzeAudioOnly(\n modelConfig.provider,\n modelConfig.modelId,\n userPrompt,\n systemPrompt,\n workflowCredentials,\n );\n } else {\n // Video analysis: fetch storyboard and analyze with visual content\n const storyboardUrl = await getStoryboardUrl(playbackId, 640, policy === \"signed\", workflowCredentials);\n imageUrl = storyboardUrl;\n\n if (imageSubmissionMode === \"base64\") {\n const downloadResult = await downloadImageAsBase64(storyboardUrl, imageDownloadOptions);\n analysisResponse = await analyzeStoryboard(\n downloadResult.base64Data,\n modelConfig.provider,\n modelConfig.modelId,\n userPrompt,\n systemPrompt,\n workflowCredentials,\n );\n } else {\n // URL-based submission with retry logic\n analysisResponse = await withRetry(() =>\n analyzeStoryboard(\n storyboardUrl,\n modelConfig.provider,\n modelConfig.modelId,\n userPrompt,\n systemPrompt,\n workflowCredentials,\n ));\n }\n }\n } catch (error: unknown) {\n const contentType = isAudioOnly ? \"audio\" : \"video\";\n throw new Error(\n `Failed to analyze ${contentType} content with ${provider}: ${\n error instanceof Error ? error.message : \"Unknown error\"\n }`,\n );\n }\n\n if (!analysisResponse.result) {\n throw new Error(`Failed to analyze video content for asset ${assetId}`);\n }\n\n if (!analysisResponse.result.title) {\n throw new Error(`Failed to generate title for asset ${assetId}`);\n }\n\n if (!analysisResponse.result.description) {\n throw new Error(`Failed to generate description for asset ${assetId}`);\n }\n\n return {\n assetId,\n title: analysisResponse.result.title,\n description: analysisResponse.result.description,\n tags: normalizeKeywords(analysisResponse.result.keywords),\n storyboardUrl: imageUrl, // undefined for audio-only assets\n usage: {\n ...analysisResponse.usage,\n metadata: {\n assetDurationSeconds,\n },\n },\n transcriptText: transcriptText || undefined,\n };\n}\n","/**\n * Language Code Conversion Utilities\n *\n * Provides bidirectional mapping between:\n * - ISO 639-1 (2-letter codes) - Used by browsers, BCP-47, most video players\n * - ISO 639-3 (3-letter codes) - Used by various APIs and language processing systems\n *\n * This is essential for interoperability between different systems:\n * - Mux uses ISO 639-1 for track language codes\n * - Browser players expect BCP-47 compliant codes (based on ISO 639-1)\n * - Some APIs require ISO 639-3 (3-letter) codes\n */\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Language Code Mapping\n// ─────────────────────────────────────────────────────────────────────────────\n\n/**\n * Mapping from ISO 639-1 (2-letter) to ISO 639-3 (3-letter) codes.\n * Covers the most common languages used in video translation.\n *\n * Reference: https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes\n */\nconst ISO639_1_TO_3 = {\n // Major world languages\n en: \"eng\", // English\n es: \"spa\", // Spanish\n fr: \"fra\", // French\n de: \"deu\", // German\n it: \"ita\", // Italian\n pt: \"por\", // Portuguese\n ru: \"rus\", // Russian\n zh: \"zho\", // Chinese\n ja: \"jpn\", // Japanese\n ko: \"kor\", // Korean\n ar: \"ara\", // Arabic\n hi: \"hin\", // Hindi\n\n // European languages\n nl: \"nld\", // Dutch\n pl: \"pol\", // Polish\n sv: \"swe\", // Swedish\n da: \"dan\", // Danish\n no: \"nor\", // Norwegian\n fi: \"fin\", // Finnish\n el: \"ell\", // Greek\n cs: \"ces\", // Czech\n hu: \"hun\", // Hungarian\n ro: \"ron\", // Romanian\n bg: \"bul\", // Bulgarian\n hr: \"hrv\", // Croatian\n sk: \"slk\", // Slovak\n sl: \"slv\", // Slovenian\n uk: \"ukr\", // Ukrainian\n tr: \"tur\", // Turkish\n\n // Asian languages\n th: \"tha\", // Thai\n vi: \"vie\", // Vietnamese\n id: \"ind\", // Indonesian\n ms: \"msa\", // Malay\n tl: \"tgl\", // Tagalog/Filipino\n\n // Other languages\n he: \"heb\", // Hebrew\n fa: \"fas\", // Persian/Farsi\n bn: \"ben\", // Bengali\n ta: \"tam\", // Tamil\n te: \"tel\", // Telugu\n mr: \"mar\", // Marathi\n gu: \"guj\", // Gujarati\n kn: \"kan\", // Kannada\n ml: \"mal\", // Malayalam\n pa: \"pan\", // Punjabi\n ur: \"urd\", // Urdu\n sw: \"swa\", // Swahili\n af: \"afr\", // Afrikaans\n ca: \"cat\", // Catalan\n eu: \"eus\", // Basque\n gl: \"glg\", // Galician\n is: \"isl\", // Icelandic\n et: \"est\", // Estonian\n lv: \"lav\", // Latvian\n lt: \"lit\", // Lithuanian\n} as const;\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Types\n// ─────────────────────────────────────────────────────────────────────────────\n\n/**\n * Supported ISO 639-1 two-letter language codes.\n * These are the language codes supported for translation workflows.\n */\nexport type SupportedISO639_1 = keyof typeof ISO639_1_TO_3;\n\n/**\n * Supported ISO 639-3 three-letter language codes.\n * These are the language codes supported for translation workflows.\n */\nexport type SupportedISO639_3 = (typeof ISO639_1_TO_3)[SupportedISO639_1];\n\n/** ISO 639-1 two-letter language code (e.g., \"en\", \"fr\", \"es\") */\nexport type ISO639_1 = SupportedISO639_1 | (string & {});\n\n/** ISO 639-3 three-letter language code (e.g., \"eng\", \"fra\", \"spa\") */\nexport type ISO639_3 = SupportedISO639_3 | (string & {});\n\n/** Structured language code result containing both formats */\nexport interface LanguageCodePair {\n /** ISO 639-1 two-letter code (BCP-47 compatible) */\n iso639_1: ISO639_1;\n /** ISO 639-3 three-letter code */\n iso639_3: ISO639_3;\n}\n\n/**\n * Reverse mapping from ISO 639-3 (3-letter) to ISO 639-1 (2-letter) codes.\n * Generated from ISO639_1_TO_3 for consistency.\n */\nconst ISO639_3_TO_1 = Object.fromEntries(\n Object.entries(ISO639_1_TO_3).map(([iso1, iso3]) => [iso3, iso1]),\n) as Record<SupportedISO639_3, SupportedISO639_1>;\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Conversion Functions\n// ─────────────────────────────────────────────────────────────────────────────\n\n/**\n * Converts an ISO 639-1 (2-letter) code to ISO 639-3 (3-letter) code.\n *\n * @param code - ISO 639-1 two-letter language code (e.g., \"en\", \"fr\")\n * @returns ISO 639-3 three-letter code, or the original if not found\n *\n * @example\n * ```typescript\n * toISO639_3(\"en\") // \"eng\"\n * toISO639_3(\"fr\") // \"fra\"\n * toISO639_3(\"ja\") // \"jpn\"\n * ```\n */\nexport function toISO639_3(code: string): ISO639_3 {\n const normalized = code.toLowerCase().trim();\n\n // If it's already a 3-letter code, return as-is\n if (normalized.length === 3) {\n return normalized;\n }\n\n return (ISO639_1_TO_3 as Record<string, string>)[normalized] ?? normalized;\n}\n\n/**\n * Converts an ISO 639-3 (3-letter) code to ISO 639-1 (2-letter) code.\n *\n * @param code - ISO 639-3 three-letter language code (e.g., \"eng\", \"fra\")\n * @returns ISO 639-1 two-letter code, or the original if not found\n *\n * @example\n * ```typescript\n * toISO639_1(\"eng\") // \"en\"\n * toISO639_1(\"fra\") // \"fr\"\n * toISO639_1(\"jpn\") // \"ja\"\n * ```\n */\nexport function toISO639_1(code: string): ISO639_1 {\n const normalized = code.toLowerCase().trim();\n\n // If it's already a 2-letter code, return as-is\n if (normalized.length === 2) {\n return normalized;\n }\n\n return (ISO639_3_TO_1 as Record<string, string>)[normalized] ?? normalized;\n}\n\n/**\n * Returns both ISO 639-1 and ISO 639-3 codes for a given language code.\n * Accepts either format as input and normalizes to both.\n *\n * @param code - Language code in either ISO 639-1 or ISO 639-3 format\n * @returns Object containing both code formats\n *\n * @example\n * ```typescript\n * getLanguageCodePair(\"en\") // { iso639_1: \"en\", iso639_3: \"eng\" }\n * getLanguageCodePair(\"fra\") // { iso639_1: \"fr\", iso639_3: \"fra\" }\n * ```\n */\nexport function getLanguageCodePair(code: string): LanguageCodePair {\n const normalized = code.toLowerCase().trim();\n\n if (normalized.length === 2) {\n // Input is ISO 639-1\n return {\n iso639_1: normalized,\n iso639_3: toISO639_3(normalized),\n };\n } else if (normalized.length === 3) {\n // Input is ISO 639-3\n return {\n iso639_1: toISO639_1(normalized),\n iso639_3: normalized,\n };\n }\n\n // Unknown format, return as-is for both\n return {\n iso639_1: normalized,\n iso639_3: normalized,\n };\n}\n\n/**\n * Validates if a code is a known ISO 639-1 code.\n *\n * @param code - Code to validate\n * @returns true if the code is a known ISO 639-1 code\n */\nexport function isValidISO639_1(code: string): boolean {\n return code.length === 2 && code.toLowerCase() in ISO639_1_TO_3;\n}\n\n/**\n * Validates if a code is a known ISO 639-3 code.\n *\n * @param code - Code to validate\n * @returns true if the code is a known ISO 639-3 code\n */\nexport function isValidISO639_3(code: string): boolean {\n return code.length === 3 && code.toLowerCase() in ISO639_3_TO_1;\n}\n\n/**\n * Gets the human-readable language name for a given code.\n *\n * @param code - Language code in either ISO 639-1 or ISO 639-3 format\n * @returns Human-readable language name (e.g., \"English\", \"French\")\n */\nexport function getLanguageName(code: string): string {\n const iso639_1 = toISO639_1(code);\n try {\n const displayNames = new Intl.DisplayNames([\"en\"], { type: \"language\" });\n return displayNames.of(iso639_1) ?? code.toUpperCase();\n } catch {\n return code.toUpperCase();\n }\n}\n","import { createPresignedGetUrl, putObjectToS3 } from \"@mux/ai/lib/s3-sigv4\";\nimport type {\n StorageAdapter,\n StoragePresignGetObjectInput,\n StoragePutObjectInput,\n} from \"@mux/ai/types\";\n\nfunction requireCredentials(\n accessKeyId: string | undefined,\n secretAccessKey: string | undefined,\n): { accessKeyId: string; secretAccessKey: string } {\n if (!accessKeyId || !secretAccessKey) {\n throw new Error(\n \"S3 credentials are required for default storage operations. \" +\n \"Provide S3_ACCESS_KEY_ID and S3_SECRET_ACCESS_KEY or pass options.storageAdapter.\",\n );\n }\n\n return { accessKeyId, secretAccessKey };\n}\n\nexport async function putObjectWithStorageAdapter(\n input: StoragePutObjectInput,\n adapter?: StorageAdapter,\n): Promise<void> {\n if (adapter) {\n await adapter.putObject(input);\n return;\n }\n\n const credentials = requireCredentials(input.accessKeyId, input.secretAccessKey);\n await putObjectToS3({\n accessKeyId: credentials.accessKeyId,\n secretAccessKey: credentials.secretAccessKey,\n endpoint: input.endpoint,\n region: input.region,\n bucket: input.bucket,\n key: input.key,\n body: input.body,\n contentType: input.contentType,\n });\n}\n\nexport async function createPresignedGetUrlWithStorageAdapter(\n input: StoragePresignGetObjectInput,\n adapter?: StorageAdapter,\n): Promise<string> {\n if (adapter) {\n return adapter.createPresignedGetUrl(input);\n }\n\n const credentials = requireCredentials(input.accessKeyId, input.secretAccessKey);\n return createPresignedGetUrl({\n accessKeyId: credentials.accessKeyId,\n secretAccessKey: credentials.secretAccessKey,\n endpoint: input.endpoint,\n region: input.region,\n bucket: input.bucket,\n key: input.key,\n expiresInSeconds: input.expiresInSeconds,\n });\n}\n","import env from \"@mux/ai/env\";\nimport { getApiKeyFromEnv } from \"@mux/ai/lib/client-factory\";\nimport { getLanguageCodePair, toISO639_1, toISO639_3 } from \"@mux/ai/lib/language-codes\";\nimport type { LanguageCodePair, SupportedISO639_1 } from \"@mux/ai/lib/language-codes\";\nimport { getAssetDurationSecondsFromAsset, getPlaybackIdForAsset } from \"@mux/ai/lib/mux-assets\";\nimport {\n createPresignedGetUrlWithStorageAdapter,\n putObjectWithStorageAdapter,\n} from \"@mux/ai/lib/storage-adapter\";\nimport { signUrl } from \"@mux/ai/lib/url-signing\";\nimport { resolveMuxClient } from \"@mux/ai/lib/workflow-credentials\";\nimport type {\n MuxAIOptions,\n StorageAdapter,\n TokenUsage,\n WorkflowCredentialsInput,\n} from \"@mux/ai/types\";\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Types\n// ─────────────────────────────────────────────────────────────────────────────\n\n/** Output returned from `translateAudio`. */\nexport interface AudioTranslationResult {\n assetId: string;\n /** Target language code (ISO 639-1 two-letter format). */\n targetLanguageCode: SupportedISO639_1;\n /**\n * Target language codes in both ISO 639-1 (2-letter) and ISO 639-3 (3-letter) formats.\n * Use `iso639_1` for browser players (BCP-47 compliant) and `iso639_3` for ElevenLabs API.\n */\n targetLanguage: LanguageCodePair;\n dubbingId: string;\n uploadedTrackId?: string;\n presignedUrl?: string;\n /** Workflow usage metadata (asset duration, thumbnails, etc.). */\n usage?: TokenUsage;\n}\n\n/** Configuration accepted by `translateAudio`. */\nexport interface AudioTranslationOptions extends MuxAIOptions {\n /** Audio dubbing provider (currently ElevenLabs only). */\n provider?: \"elevenlabs\";\n /** Number of speakers supplied to ElevenLabs (0 = auto-detect, default). */\n numSpeakers?: number;\n /** Optional override for the S3-compatible endpoint used for uploads. */\n s3Endpoint?: string;\n /** S3 region (defaults to env.S3_REGION or 'auto'). */\n s3Region?: string;\n /** Bucket that will store dubbed audio files. */\n s3Bucket?: string;\n /**\n * When true (default) the dubbed audio file is uploaded to the configured\n * bucket and attached to the Mux asset.\n */\n uploadToMux?: boolean;\n /** Optional storage adapter override for upload + presign operations. */\n storageAdapter?: StorageAdapter;\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Implementation\n// ─────────────────────────────────────────────────────────────────────────────\n\nconst STATIC_RENDITION_POLL_INTERVAL_MS = 5000;\nconst STATIC_RENDITION_MAX_ATTEMPTS = 36; // ~3 minutes\n\nasync function sleep(ms: number): Promise<void> {\n \"use step\";\n await new Promise(resolve => setTimeout(resolve, ms));\n}\n\nfunction getReadyAudioStaticRendition(asset: any) {\n const files = asset.static_renditions?.files as any[] | undefined;\n if (!files || files.length === 0) {\n return undefined;\n }\n\n return files.find(\n rendition => rendition.name === \"audio.m4a\" && rendition.status === \"ready\",\n );\n}\n\nconst hasReadyAudioStaticRendition = (asset: any) => Boolean(getReadyAudioStaticRendition(asset));\n\nasync function requestStaticRenditionCreation(\n assetId: string,\n credentials?: WorkflowCredentialsInput,\n) {\n \"use step\";\n const muxClient = await resolveMuxClient(credentials);\n const mux = await muxClient.createClient();\n try {\n await mux.video.assets.createStaticRendition(assetId, {\n resolution: \"audio-only\",\n });\n } catch (error: any) {\n const statusCode = error?.status ?? error?.statusCode;\n const messages: string[] | undefined = error?.error?.messages;\n const alreadyDefined =\n messages?.some(message => message.toLowerCase().includes(\"already defined\")) ??\n error?.message?.toLowerCase().includes(\"already defined\");\n\n if (statusCode === 409 || alreadyDefined) {\n return;\n }\n\n const message = error instanceof Error ? error.message : \"Unknown error\";\n throw new Error(`Failed to request static rendition from Mux: ${message}`);\n }\n}\n\nasync function waitForAudioStaticRendition({\n assetId,\n initialAsset,\n credentials,\n}: {\n assetId: string;\n initialAsset: any;\n credentials?: WorkflowCredentialsInput;\n}): Promise<any> {\n \"use step\";\n const muxClient = await resolveMuxClient(credentials);\n const mux = await muxClient.createClient();\n let currentAsset = initialAsset;\n\n if (hasReadyAudioStaticRendition(currentAsset)) {\n return currentAsset;\n }\n\n const status = currentAsset.static_renditions?.status ?? \"not_requested\";\n\n if (status === \"not_requested\" || status === undefined) {\n await requestStaticRenditionCreation(assetId, credentials);\n } else if (status === \"errored\") {\n await requestStaticRenditionCreation(assetId, credentials);\n } else {\n console.warn(`ℹ️ Static rendition already ${status}. Waiting for it to finish...`);\n }\n\n for (let attempt = 1; attempt <= STATIC_RENDITION_MAX_ATTEMPTS; attempt++) {\n await sleep(STATIC_RENDITION_POLL_INTERVAL_MS);\n currentAsset = await mux.video.assets.retrieve(assetId);\n\n if (hasReadyAudioStaticRendition(currentAsset)) {\n return currentAsset;\n }\n\n const currentStatus = currentAsset.static_renditions?.status || \"unknown\";\n console.warn(\n `⌛ Waiting for static rendition (attempt ${attempt}/${STATIC_RENDITION_MAX_ATTEMPTS}) → ${currentStatus}`,\n );\n\n if (currentStatus === \"errored\") {\n throw new Error(\n \"Mux failed to create the static rendition for this asset. Please check the asset in the Mux dashboard.\",\n );\n }\n }\n\n throw new Error(\n \"Timed out waiting for the static rendition to become ready. Please try again in a moment.\",\n );\n}\n\nasync function fetchAudioFromMux(audioUrl: string): Promise<ArrayBuffer> {\n \"use step\";\n\n const audioResponse = await fetch(audioUrl);\n if (!audioResponse.ok) {\n throw new Error(`Failed to fetch audio file: ${audioResponse.statusText}`);\n }\n\n return audioResponse.arrayBuffer();\n}\n\nasync function createElevenLabsDubbingJob({\n audioBuffer,\n assetId,\n elevenLabsLangCode,\n numSpeakers,\n credentials,\n}: {\n audioBuffer: ArrayBuffer;\n assetId: string;\n elevenLabsLangCode: string;\n numSpeakers: number;\n credentials?: WorkflowCredentialsInput;\n}): Promise<string> {\n \"use step\";\n const elevenLabsApiKey = await getApiKeyFromEnv(\"elevenlabs\", credentials);\n\n const audioBlob = new Blob([audioBuffer], { type: \"audio/mp4\" });\n\n const formData = new FormData();\n formData.append(\"file\", audioBlob);\n formData.append(\"target_lang\", elevenLabsLangCode);\n formData.append(\"num_speakers\", numSpeakers.toString());\n formData.append(\"name\", `Mux Asset ${assetId} - auto to ${elevenLabsLangCode}`);\n\n const dubbingResponse = await fetch(\"https://api.elevenlabs.io/v1/dubbing\", {\n method: \"POST\",\n headers: {\n \"xi-api-key\": elevenLabsApiKey,\n },\n body: formData,\n });\n\n if (!dubbingResponse.ok) {\n throw new Error(`ElevenLabs API error: ${dubbingResponse.statusText}`);\n }\n\n const dubbingData = await dubbingResponse.json() as any;\n return dubbingData.dubbing_id;\n}\n\nasync function checkElevenLabsDubbingStatus({\n dubbingId,\n credentials,\n}: {\n dubbingId: string;\n credentials?: WorkflowCredentialsInput;\n}): Promise<{ status: string; targetLanguages: string[] }> {\n \"use step\";\n const elevenLabsApiKey = await getApiKeyFromEnv(\"elevenlabs\", credentials);\n\n const statusResponse = await fetch(`https://api.elevenlabs.io/v1/dubbing/${dubbingId}`, {\n headers: {\n \"xi-api-key\": elevenLabsApiKey,\n },\n });\n\n if (!statusResponse.ok) {\n throw new Error(`Status check failed: ${statusResponse.statusText}`);\n }\n\n const statusData = await statusResponse.json() as any;\n return {\n status: statusData.status,\n targetLanguages: statusData.target_languages ?? [],\n };\n}\n\nasync function downloadDubbedAudioFromElevenLabs({\n dubbingId,\n languageCode,\n credentials,\n}: {\n dubbingId: string;\n languageCode: string;\n credentials?: WorkflowCredentialsInput;\n}): Promise<ArrayBuffer> {\n \"use step\";\n const elevenLabsApiKey = await getApiKeyFromEnv(\"elevenlabs\", credentials);\n\n const audioUrl = `https://api.elevenlabs.io/v1/dubbing/${dubbingId}/audio/${languageCode}`;\n const audioResponse = await fetch(audioUrl, {\n headers: {\n \"xi-api-key\": elevenLabsApiKey,\n },\n });\n\n if (!audioResponse.ok) {\n throw new Error(`Failed to fetch dubbed audio: ${audioResponse.statusText}`);\n }\n\n return audioResponse.arrayBuffer();\n}\n\nasync function uploadDubbedAudioToS3({\n dubbedAudioBuffer,\n assetId,\n toLanguageCode,\n s3Endpoint,\n s3Region,\n s3Bucket,\n storageAdapter,\n}: {\n dubbedAudioBuffer: ArrayBuffer;\n assetId: string;\n toLanguageCode: string;\n s3Endpoint: string;\n s3Region: string;\n s3Bucket: string;\n storageAdapter?: StorageAdapter;\n}): Promise<string> {\n \"use step\";\n\n const s3AccessKeyId = env.S3_ACCESS_KEY_ID;\n const s3SecretAccessKey = env.S3_SECRET_ACCESS_KEY;\n\n // Create unique key for the audio file\n const audioKey = `audio-translations/${assetId}/auto-to-${toLanguageCode}-${Date.now()}.m4a`;\n\n await putObjectWithStorageAdapter({\n accessKeyId: s3AccessKeyId,\n secretAccessKey: s3SecretAccessKey,\n endpoint: s3Endpoint,\n region: s3Region,\n bucket: s3Bucket,\n key: audioKey,\n body: new Uint8Array(dubbedAudioBuffer),\n contentType: \"audio/mp4\",\n }, storageAdapter);\n\n const presignedUrl = await createPresignedGetUrlWithStorageAdapter({\n accessKeyId: s3AccessKeyId,\n secretAccessKey: s3SecretAccessKey,\n endpoint: s3Endpoint,\n region: s3Region,\n bucket: s3Bucket,\n key: audioKey,\n expiresInSeconds: 3600,\n }, storageAdapter);\n\n console.warn(`✅ Audio uploaded successfully to: ${audioKey}`);\n console.warn(`🔗 Generated presigned URL (expires in 1 hour)`);\n\n return presignedUrl;\n}\n\nasync function createAudioTrackOnMux(\n assetId: string,\n languageCode: string,\n presignedUrl: string,\n credentials?: WorkflowCredentialsInput,\n): Promise<string> {\n \"use step\";\n const muxClient = await resolveMuxClient(credentials);\n const mux = await muxClient.createClient();\n const languageName = new Intl.DisplayNames([\"en\"], { type: \"language\" }).of(languageCode) || languageCode.toUpperCase();\n const trackName = `${languageName} (auto-dubbed)`;\n\n const trackResponse = await mux.video.assets.createTrack(assetId, {\n type: \"audio\",\n language_code: languageCode,\n name: trackName,\n url: presignedUrl,\n });\n\n if (!trackResponse.id) {\n throw new Error(\"Failed to create audio track: no track ID returned from Mux\");\n }\n\n return trackResponse.id;\n}\n\nexport async function translateAudio(\n assetId: string,\n toLanguageCode: string,\n options: AudioTranslationOptions = {},\n): Promise<AudioTranslationResult> {\n \"use workflow\";\n // Uses the default audio track on your asset, language is auto-detected by ElevenLabs\n const {\n provider = \"elevenlabs\",\n numSpeakers = 0, // 0 = auto-detect\n uploadToMux = true,\n storageAdapter,\n credentials: providedCredentials,\n } = options;\n\n if (provider !== \"elevenlabs\") {\n throw new Error(\"Only ElevenLabs provider is currently supported for audio translation\");\n }\n\n const credentials = providedCredentials;\n const effectiveStorageAdapter = storageAdapter;\n\n // S3 configuration\n const s3Endpoint = options.s3Endpoint ?? env.S3_ENDPOINT;\n const s3Region = options.s3Region ?? env.S3_REGION ?? \"auto\";\n const s3Bucket = options.s3Bucket ?? env.S3_BUCKET;\n const s3AccessKeyId = env.S3_ACCESS_KEY_ID;\n const s3SecretAccessKey = env.S3_SECRET_ACCESS_KEY;\n\n if (uploadToMux && (!s3Endpoint || !s3Bucket || (!effectiveStorageAdapter && (!s3AccessKeyId || !s3SecretAccessKey)))) {\n throw new Error(\"Storage configuration is required for uploading to Mux. Provide s3Endpoint and s3Bucket. If no storageAdapter is supplied, also provide s3AccessKeyId and s3SecretAccessKey in options or set S3_ENDPOINT, S3_BUCKET, S3_ACCESS_KEY_ID, and S3_SECRET_ACCESS_KEY environment variables.\");\n }\n\n // Fetch asset data and playback ID from Mux\n const { asset: initialAsset, playbackId, policy } = await getPlaybackIdForAsset(assetId, credentials);\n const assetDurationSeconds = getAssetDurationSecondsFromAsset(initialAsset);\n\n // Check for audio-only static rendition\n\n let currentAsset = initialAsset;\n if (!hasReadyAudioStaticRendition(currentAsset)) {\n console.warn(\"❌ No ready audio static rendition found. Requesting one now...\");\n currentAsset = await waitForAudioStaticRendition({\n assetId,\n initialAsset: currentAsset,\n credentials,\n });\n }\n\n const audioRendition = getReadyAudioStaticRendition(currentAsset);\n\n if (!audioRendition) {\n throw new Error(\n \"Unable to obtain an audio-only static rendition for this asset. Please verify static renditions are enabled in Mux.\",\n );\n }\n\n // Build audio URL (signed if needed)\n let audioUrl = `https://stream.mux.com/${playbackId}/audio.m4a`;\n if (policy === \"signed\") {\n audioUrl = await signUrl(audioUrl, playbackId, undefined, \"video\", undefined, credentials);\n }\n\n // Fetch audio from Mux\n console.warn(\"🎙️ Fetching audio from Mux...\");\n\n let audioBuffer: ArrayBuffer;\n try {\n audioBuffer = await fetchAudioFromMux(audioUrl);\n } catch (error) {\n throw new Error(`Failed to fetch audio from Mux: ${error instanceof Error ? error.message : \"Unknown error\"}`);\n }\n\n // Create dubbing job in ElevenLabs\n console.warn(\"🎙️ Creating dubbing job in ElevenLabs...\");\n\n // ElevenLabs uses ISO 639-3 (3-letter) codes, so normalize the input\n const elevenLabsLangCode = toISO639_3(toLanguageCode);\n console.warn(`🔍 Creating dubbing job for asset ${assetId} with language code: ${elevenLabsLangCode}`);\n\n let dubbingId: string;\n try {\n dubbingId = await createElevenLabsDubbingJob({\n audioBuffer,\n assetId,\n elevenLabsLangCode,\n numSpeakers,\n credentials,\n });\n console.warn(`✅ Dubbing job created with ID: ${dubbingId}`);\n } catch (error) {\n throw new Error(`Failed to create ElevenLabs dubbing job: ${error instanceof Error ? error.message : \"Unknown error\"}`);\n }\n\n // Poll for completion\n console.warn(\"⏳ Waiting for dubbing to complete...\");\n\n let dubbingStatus: string = \"dubbing\";\n let pollAttempts = 0;\n const maxPollAttempts = 180; // 30 minutes at 10s intervals\n let targetLanguages: string[] = [];\n\n while (dubbingStatus === \"dubbing\" && pollAttempts < maxPollAttempts) {\n await sleep(10000); // Wait 10 seconds\n pollAttempts++;\n\n try {\n const statusResult = await checkElevenLabsDubbingStatus({\n dubbingId,\n credentials,\n });\n dubbingStatus = statusResult.status;\n targetLanguages = statusResult.targetLanguages;\n\n if (dubbingStatus === \"failed\") {\n throw new Error(\"ElevenLabs dubbing job failed\");\n }\n } catch (error) {\n throw new Error(`Failed to check dubbing status: ${error instanceof Error ? error.message : \"Unknown error\"}`);\n }\n }\n\n if (dubbingStatus !== \"dubbed\") {\n throw new Error(`Dubbing job timed out or failed. Final status: ${dubbingStatus}`);\n }\n\n console.warn(\"✅ Dubbing completed successfully!\");\n\n // If uploadToMux is false, just return the dubbing info\n // Return ISO 639-1 (2-letter) code for consistency with Mux/player expectations\n if (!uploadToMux) {\n const targetLanguage = getLanguageCodePair(toLanguageCode);\n return {\n assetId,\n targetLanguageCode: targetLanguage.iso639_1 as SupportedISO639_1,\n targetLanguage,\n dubbingId,\n };\n }\n\n // Download dubbed audio from ElevenLabs\n console.warn(\"📥 Downloading dubbed audio from ElevenLabs...\");\n\n let dubbedAudioBuffer: ArrayBuffer;\n\n try {\n // Use the language code from the ElevenLabs status response\n // ElevenLabs returns target_languages array with the exact codes available for download\n const requestedLangCode = toISO639_3(toLanguageCode);\n\n // Find the matching language code from ElevenLabs response\n // First try exact match, then try case-insensitive match\n let downloadLangCode = targetLanguages.find(\n lang => lang === requestedLangCode,\n ) ?? targetLanguages.find(\n lang => lang.toLowerCase() === requestedLangCode.toLowerCase(),\n );\n\n // Fallback to first available target language if no match found\n if (!downloadLangCode && targetLanguages.length > 0) {\n downloadLangCode = targetLanguages[0];\n console.warn(`⚠️ Requested language \"${requestedLangCode}\" not found in target_languages. Using \"${downloadLangCode}\" instead.`);\n }\n\n // If still no language code, fall back to the original behavior\n if (!downloadLangCode) {\n downloadLangCode = requestedLangCode;\n console.warn(`⚠️ No target_languages available from ElevenLabs status. Using requested language code: ${requestedLangCode}`);\n }\n\n dubbedAudioBuffer = await downloadDubbedAudioFromElevenLabs({\n dubbingId,\n languageCode: downloadLangCode,\n credentials,\n });\n console.warn(\"✅ Dubbed audio downloaded successfully!\");\n } catch (error) {\n throw new Error(`Failed to download dubbed audio: ${error instanceof Error ? error.message : \"Unknown error\"}`);\n }\n\n // Upload to S3-compatible storage\n console.warn(\"📤 Uploading dubbed audio to S3-compatible storage...\");\n\n let presignedUrl: string;\n\n try {\n presignedUrl = await uploadDubbedAudioToS3({\n dubbedAudioBuffer,\n assetId,\n toLanguageCode,\n s3Endpoint: s3Endpoint!,\n s3Region,\n s3Bucket: s3Bucket!,\n storageAdapter: effectiveStorageAdapter,\n });\n } catch (error) {\n throw new Error(`Failed to upload audio to S3: ${error instanceof Error ? error.message : \"Unknown error\"}`);\n }\n\n // Add translated audio track to Mux asset\n console.warn(\"📹 Adding dubbed audio track to Mux asset...\");\n\n let uploadedTrackId: string | undefined;\n // Mux uses ISO 639-1 (2-letter) codes for track language_code\n const muxLangCode = toISO639_1(toLanguageCode);\n\n try {\n uploadedTrackId = await createAudioTrackOnMux(assetId, muxLangCode, presignedUrl, credentials);\n const languageName = new Intl.DisplayNames([\"en\"], { type: \"language\" }).of(muxLangCode) || muxLangCode.toUpperCase();\n const trackName = `${languageName} (auto-dubbed)`;\n console.warn(`✅ Track added to Mux asset with ID: ${uploadedTrackId}`);\n console.warn(`📋 Track name: \"${trackName}\"`);\n } catch (error) {\n console.warn(`⚠️ Failed to add audio track to Mux asset: ${error instanceof Error ? error.message : \"Unknown error\"}`);\n console.warn(\"🔗 You can manually add the track using this presigned URL:\");\n console.warn(presignedUrl);\n }\n\n const targetLanguage = getLanguageCodePair(toLanguageCode);\n return {\n assetId,\n targetLanguageCode: targetLanguage.iso639_1 as SupportedISO639_1,\n targetLanguage,\n dubbingId,\n uploadedTrackId,\n presignedUrl,\n usage: {\n metadata: {\n assetDurationSeconds,\n },\n },\n };\n}\n","import { generateText, Output } from \"ai\";\nimport { z } from \"zod\";\n\nimport env from \"@mux/ai/env\";\nimport { getLanguageCodePair, getLanguageName } from \"@mux/ai/lib/language-codes\";\nimport type { LanguageCodePair, SupportedISO639_1 } from \"@mux/ai/lib/language-codes\";\nimport {\n getAssetDurationSecondsFromAsset,\n getPlaybackIdForAsset,\n isAudioOnlyAsset,\n} from \"@mux/ai/lib/mux-assets\";\nimport { createLanguageModelFromConfig, resolveLanguageModelConfig } from \"@mux/ai/lib/providers\";\nimport type { ModelIdByProvider, SupportedProvider } from \"@mux/ai/lib/providers\";\nimport {\n createPresignedGetUrlWithStorageAdapter,\n putObjectWithStorageAdapter,\n} from \"@mux/ai/lib/storage-adapter\";\nimport {\n resolveMuxClient,\n resolveMuxSigningContext,\n} from \"@mux/ai/lib/workflow-credentials\";\nimport { buildTranscriptUrl, getReadyTextTracks } from \"@mux/ai/primitives/transcripts\";\nimport type {\n MuxAIOptions,\n StorageAdapter,\n TokenUsage,\n WorkflowCredentialsInput,\n} from \"@mux/ai/types\";\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Types\n// ─────────────────────────────────────────────────────────────────────────────\n\n/** Output returned from `translateCaptions`. */\nexport interface TranslationResult {\n assetId: string;\n /** Source language code (ISO 639-1 two-letter format). */\n sourceLanguageCode: SupportedISO639_1;\n /** Target language code (ISO 639-1 two-letter format). */\n targetLanguageCode: SupportedISO639_1;\n /**\n * Source language codes in both ISO 639-1 (2-letter) and ISO 639-3 (3-letter) formats.\n * Use `iso639_1` for browser players (BCP-47 compliant) and `iso639_3` for APIs that require it.\n */\n sourceLanguage: LanguageCodePair;\n /**\n * Target language codes in both ISO 639-1 (2-letter) and ISO 639-3 (3-letter) formats.\n * Use `iso639_1` for browser players (BCP-47 compliant) and `iso639_3` for APIs that require it.\n */\n targetLanguage: LanguageCodePair;\n originalVtt: string;\n translatedVtt: string;\n uploadedTrackId?: string;\n presignedUrl?: string;\n /** Token usage from the AI provider (for efficiency/cost analysis). */\n usage?: TokenUsage;\n}\n\n/** Configuration accepted by `translateCaptions`. */\nexport interface TranslationOptions<P extends SupportedProvider = SupportedProvider> extends MuxAIOptions {\n /** Provider responsible for the translation. */\n provider: P;\n /** Provider-specific chat model identifier. */\n model?: ModelIdByProvider[P];\n /** Optional override for the S3-compatible endpoint used for uploads. */\n s3Endpoint?: string;\n /** S3 region (defaults to env.S3_REGION or 'auto'). */\n s3Region?: string;\n /** Bucket that will store translated VTT files. */\n s3Bucket?: string;\n /**\n * When true (default) the translated VTT is uploaded to the configured\n * bucket and attached to the Mux asset.\n */\n uploadToMux?: boolean;\n /** Optional storage adapter override for upload + presign operations. */\n storageAdapter?: StorageAdapter;\n}\n\n/** Schema used when requesting caption translation from a language model. */\nexport const translationSchema = z.object({\n translation: z.string(),\n});\n\n/** Inferred shape returned by `translationSchema`. */\nexport type TranslationPayload = z.infer<typeof translationSchema>;\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Implementation\n// ─────────────────────────────────────────────────────────────────────────────\n\nasync function fetchVttFromMux(vttUrl: string): Promise<string> {\n \"use step\";\n\n const vttResponse = await fetch(vttUrl);\n if (!vttResponse.ok) {\n throw new Error(`Failed to fetch VTT file: ${vttResponse.statusText}`);\n }\n\n return vttResponse.text();\n}\n\nasync function translateVttWithAI({\n vttContent,\n fromLanguageCode,\n toLanguageCode,\n provider,\n modelId,\n credentials,\n}: {\n vttContent: string;\n fromLanguageCode: string;\n toLanguageCode: string;\n provider: SupportedProvider;\n modelId: string;\n credentials?: WorkflowCredentialsInput;\n}): Promise<{ translatedVtt: string; usage: TokenUsage }> {\n \"use step\";\n\n const model = await createLanguageModelFromConfig(provider, modelId, credentials);\n\n const response = await generateText({\n model,\n output: Output.object({ schema: translationSchema }),\n messages: [\n {\n role: \"user\",\n content: `Translate the following VTT subtitle file from ${fromLanguageCode} to ${toLanguageCode}. Preserve all timestamps and VTT formatting exactly as they appear. Return JSON with a single key \"translation\" containing the translated VTT.\\n\\n${vttContent}`,\n },\n ],\n });\n\n return {\n translatedVtt: response.output.translation,\n usage: {\n inputTokens: response.usage.inputTokens,\n outputTokens: response.usage.outputTokens,\n totalTokens: response.usage.totalTokens,\n reasoningTokens: response.usage.reasoningTokens,\n cachedInputTokens: response.usage.cachedInputTokens,\n },\n };\n}\n\nasync function uploadVttToS3({\n translatedVtt,\n assetId,\n fromLanguageCode,\n toLanguageCode,\n s3Endpoint,\n s3Region,\n s3Bucket,\n storageAdapter,\n}: {\n translatedVtt: string;\n assetId: string;\n fromLanguageCode: string;\n toLanguageCode: string;\n s3Endpoint: string;\n s3Region: string;\n s3Bucket: string;\n storageAdapter?: StorageAdapter;\n}): Promise<string> {\n \"use step\";\n\n const s3AccessKeyId = env.S3_ACCESS_KEY_ID;\n const s3SecretAccessKey = env.S3_SECRET_ACCESS_KEY;\n\n // Create unique key for the VTT file\n const vttKey = `translations/${assetId}/${fromLanguageCode}-to-${toLanguageCode}-${Date.now()}.vtt`;\n\n await putObjectWithStorageAdapter({\n accessKeyId: s3AccessKeyId,\n secretAccessKey: s3SecretAccessKey,\n endpoint: s3Endpoint,\n region: s3Region,\n bucket: s3Bucket,\n key: vttKey,\n body: translatedVtt,\n contentType: \"text/vtt\",\n }, storageAdapter);\n\n return createPresignedGetUrlWithStorageAdapter({\n accessKeyId: s3AccessKeyId,\n secretAccessKey: s3SecretAccessKey,\n endpoint: s3Endpoint,\n region: s3Region,\n bucket: s3Bucket,\n key: vttKey,\n expiresInSeconds: 3600,\n }, storageAdapter);\n}\n\nasync function createTextTrackOnMux(\n assetId: string,\n languageCode: string,\n trackName: string,\n presignedUrl: string,\n credentials?: WorkflowCredentialsInput,\n): Promise<string> {\n \"use step\";\n const muxClient = await resolveMuxClient(credentials);\n const mux = await muxClient.createClient();\n const trackResponse = await mux.video.assets.createTrack(assetId, {\n type: \"text\",\n text_type: \"subtitles\",\n language_code: languageCode,\n name: trackName,\n url: presignedUrl,\n });\n\n if (!trackResponse.id) {\n throw new Error(\"Failed to create text track: no track ID returned from Mux\");\n }\n\n return trackResponse.id;\n}\n\nexport async function translateCaptions<P extends SupportedProvider = SupportedProvider>(\n assetId: string,\n fromLanguageCode: string,\n toLanguageCode: string,\n options: TranslationOptions<P>,\n): Promise<TranslationResult> {\n \"use workflow\";\n const {\n provider = \"openai\",\n model,\n s3Endpoint: providedS3Endpoint,\n s3Region: providedS3Region,\n s3Bucket: providedS3Bucket,\n uploadToMux: uploadToMuxOption,\n storageAdapter,\n credentials: providedCredentials,\n } = options;\n const credentials = providedCredentials;\n const effectiveStorageAdapter = storageAdapter;\n\n // S3 configuration\n const s3Endpoint = providedS3Endpoint ?? env.S3_ENDPOINT;\n const s3Region = providedS3Region ?? env.S3_REGION ?? \"auto\";\n const s3Bucket = providedS3Bucket ?? env.S3_BUCKET;\n const s3AccessKeyId = env.S3_ACCESS_KEY_ID;\n const s3SecretAccessKey = env.S3_SECRET_ACCESS_KEY;\n const uploadToMux = uploadToMuxOption !== false; // Default to true\n\n const modelConfig = resolveLanguageModelConfig({\n ...options,\n model,\n provider: provider as SupportedProvider,\n });\n\n if (uploadToMux && (!s3Endpoint || !s3Bucket || (!effectiveStorageAdapter && (!s3AccessKeyId || !s3SecretAccessKey)))) {\n throw new Error(\"Storage configuration is required for uploading to Mux. Provide s3Endpoint and s3Bucket. If no storageAdapter is supplied, also provide s3AccessKeyId and s3SecretAccessKey in options or set S3_ENDPOINT, S3_BUCKET, S3_ACCESS_KEY_ID, and S3_SECRET_ACCESS_KEY environment variables.\");\n }\n\n // Fetch asset data and playback ID from Mux\n const { asset: assetData, playbackId, policy } = await getPlaybackIdForAsset(assetId, credentials);\n const assetDurationSeconds = getAssetDurationSecondsFromAsset(assetData);\n const isAudioOnly = isAudioOnlyAsset(assetData);\n\n // Resolve signing context for signed playback IDs\n const signingContext = await resolveMuxSigningContext(credentials);\n if (policy === \"signed\" && !signingContext) {\n throw new Error(\n \"Signed playback ID requires signing credentials. \" +\n \"Set MUX_SIGNING_KEY and MUX_PRIVATE_KEY environment variables.\",\n );\n }\n\n // Find text track with the source language\n const readyTextTracks = getReadyTextTracks(assetData);\n if (!readyTextTracks.length) {\n throw new Error(\"No ready text tracks found for this asset\");\n }\n\n let sourceTextTrack = readyTextTracks.find(track =>\n track.text_type === \"subtitles\" &&\n track.language_code === fromLanguageCode,\n );\n\n if (!sourceTextTrack && isAudioOnly && readyTextTracks.length === 1) {\n sourceTextTrack = readyTextTracks[0];\n }\n\n if (!sourceTextTrack) {\n const availableLanguages = readyTextTracks\n .map(t => t.language_code)\n .filter(Boolean)\n .join(\", \");\n if (isAudioOnly) {\n throw new Error(\n `No transcript track found with language code '${fromLanguageCode}' for this asset. ` +\n `Audio-only assets require a transcript. Available languages: ${availableLanguages || \"none\"}`,\n );\n }\n throw new Error(\n `No ready text track found with language code '${fromLanguageCode}' for this asset. ` +\n `Available languages: ${availableLanguages || \"none\"}`,\n );\n }\n\n if (!sourceTextTrack.id) {\n throw new Error(\"Transcript track is missing an id\");\n }\n\n // Fetch the VTT file content (signed if needed)\n const vttUrl = await buildTranscriptUrl(playbackId, sourceTextTrack.id, policy === \"signed\", credentials);\n\n let vttContent: string;\n try {\n vttContent = await fetchVttFromMux(vttUrl);\n } catch (error) {\n throw new Error(`Failed to fetch VTT content: ${error instanceof Error ? error.message : \"Unknown error\"}`);\n }\n\n // Translate VTT content using configured provider via ai-sdk\n let translatedVtt: string;\n let usage: TokenUsage | undefined;\n\n try {\n const result = await translateVttWithAI({\n vttContent,\n fromLanguageCode,\n toLanguageCode,\n provider: modelConfig.provider,\n modelId: modelConfig.modelId,\n credentials,\n });\n translatedVtt = result.translatedVtt;\n usage = result.usage;\n } catch (error) {\n throw new Error(`Failed to translate VTT with ${modelConfig.provider}: ${error instanceof Error ? error.message : \"Unknown error\"}`);\n }\n\n const usageWithMetadata = usage ?\n {\n ...usage,\n metadata: {\n assetDurationSeconds,\n },\n } :\n undefined;\n\n // Resolve language code pairs for both source and target\n const sourceLanguage = getLanguageCodePair(fromLanguageCode);\n const targetLanguage = getLanguageCodePair(toLanguageCode);\n\n // If uploadToMux is false, just return the translation\n if (!uploadToMux) {\n return {\n assetId,\n sourceLanguageCode: fromLanguageCode as SupportedISO639_1,\n targetLanguageCode: toLanguageCode as SupportedISO639_1,\n sourceLanguage,\n targetLanguage,\n originalVtt: vttContent,\n translatedVtt,\n usage: usageWithMetadata,\n };\n }\n\n // Upload translated VTT to S3-compatible storage\n let presignedUrl: string;\n\n try {\n presignedUrl = await uploadVttToS3({\n translatedVtt,\n assetId,\n fromLanguageCode,\n toLanguageCode,\n s3Endpoint: s3Endpoint!,\n s3Region,\n s3Bucket: s3Bucket!,\n storageAdapter: effectiveStorageAdapter,\n });\n } catch (error) {\n throw new Error(`Failed to upload VTT to S3: ${error instanceof Error ? error.message : \"Unknown error\"}`);\n }\n\n // Add translated track to Mux asset\n let uploadedTrackId: string | undefined;\n\n try {\n const languageName = getLanguageName(toLanguageCode);\n const trackName = `${languageName} (auto-translated)`;\n\n uploadedTrackId = await createTextTrackOnMux(\n assetId,\n toLanguageCode,\n trackName,\n presignedUrl,\n credentials,\n );\n } catch (error) {\n console.warn(`Failed to add track to Mux asset: ${error instanceof Error ? error.message : \"Unknown error\"}`);\n }\n\n return {\n assetId,\n sourceLanguageCode: fromLanguageCode as SupportedISO639_1,\n targetLanguageCode: toLanguageCode as SupportedISO639_1,\n sourceLanguage,\n targetLanguage,\n originalVtt: vttContent,\n translatedVtt,\n uploadedTrackId,\n presignedUrl,\n usage: usageWithMetadata,\n };\n}\n"],"mappings":"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","names":["env","bytes","chunkText","z","BASE64_CHUNK_SIZE","bytesToBase64","z","generateText","Output","dedent","z","z","SYSTEM_PROMPT","dedent","buildUserPrompt","fetchImageAsBase64","generateText","Output","generateText","Output","dedent","z","z","generateText","Output","dedent","buildUserPrompt","DEFAULT_PROVIDER","generateText","Output","dedent","z","z","Output","dedent","SYSTEM_PROMPT","buildUserPrompt","analyzeStoryboard","generateText","message","targetLanguage","generateText","Output","z","z","generateText","Output"]}