@mux/ai 0.15.1 → 0.16.1
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-yn3fUDgv.d.ts → index-BfsJvx3r.d.ts} +3 -3
- package/dist/index.d.ts +2 -2
- package/dist/index.js +72 -58
- package/dist/index.js.map +1 -1
- package/dist/primitives/index.js +1 -1
- package/dist/primitives/index.js.map +1 -1
- package/dist/workflows/index.d.ts +1 -1
- package/dist/workflows/index.js +71 -57
- package/dist/workflows/index.js.map +1 -1
- package/package.json +1 -1
|
@@ -34,10 +34,12 @@ interface EmbeddingModelIdByProvider {
|
|
|
34
34
|
google: GoogleEmbeddingModelId;
|
|
35
35
|
}
|
|
36
36
|
|
|
37
|
-
/** A single
|
|
37
|
+
/** A single question to be answered about asset content. */
|
|
38
38
|
interface Question {
|
|
39
39
|
/** The question text */
|
|
40
40
|
question: string;
|
|
41
|
+
/** Allowed answers for this question (defaults to ["yes", "no"]). */
|
|
42
|
+
answerOptions?: string[];
|
|
41
43
|
}
|
|
42
44
|
/** A single answer to a question. */
|
|
43
45
|
interface QuestionAnswer {
|
|
@@ -60,8 +62,6 @@ interface AskQuestionsOptions extends MuxAIOptions {
|
|
|
60
62
|
model?: ModelIdByProvider[SupportedProvider];
|
|
61
63
|
/** BCP 47 language code of the caption track to use (e.g. "en", "fr"). When omitted, prefers English if available. */
|
|
62
64
|
languageCode?: string;
|
|
63
|
-
/** Allowed answers for each question (defaults to ["yes", "no"]). */
|
|
64
|
-
answerOptions?: string[];
|
|
65
65
|
/** Fetch transcript alongside storyboard (defaults to true, required for audio-only assets). */
|
|
66
66
|
includeTranscript?: boolean;
|
|
67
67
|
/** Strip timestamps/markup from transcripts (defaults to true). */
|
package/dist/index.d.ts
CHANGED
|
@@ -2,14 +2,14 @@ import { W as WorkflowCredentials, S as StoragePutObjectInput, a as StoragePresi
|
|
|
2
2
|
export { A as AssetTextTrack, C as ChunkEmbedding, b as ChunkingStrategy, E as Encrypted, c as EncryptedPayload, I as ImageSubmissionMode, M as MuxAIOptions, d as MuxAsset, P as PlaybackAsset, e as PlaybackPolicy, f as StorageAdapter, T as TextChunk, g as TokenChunkingConfig, h as TokenUsage, i as ToneType, U as UsageMetadata, V as VTTChunkingConfig, j as VideoEmbeddingsResult, k as WorkflowCredentialsInput, l as WorkflowMuxClient, m as decryptFromWorkflow, n as encryptForWorkflow } from './shots-BFYOfBgN.js';
|
|
3
3
|
import { WORKFLOW_SERIALIZE, WORKFLOW_DESERIALIZE } from '@workflow/serde';
|
|
4
4
|
export { i as primitives } from './index-CuBu_u5-.js';
|
|
5
|
-
export { i as workflows } from './index-
|
|
5
|
+
export { i as workflows } from './index-BfsJvx3r.js';
|
|
6
6
|
import '@mux/mux-node';
|
|
7
7
|
import 'zod';
|
|
8
8
|
import '@ai-sdk/anthropic';
|
|
9
9
|
import '@ai-sdk/google';
|
|
10
10
|
import '@ai-sdk/openai';
|
|
11
11
|
|
|
12
|
-
var version = "0.
|
|
12
|
+
var version = "0.16.1";
|
|
13
13
|
|
|
14
14
|
type MuxAiErrorType = "validation_error" | "processing_error" | "timeout_error";
|
|
15
15
|
/**
|
package/dist/index.js
CHANGED
|
@@ -5,7 +5,7 @@ var __export = (target, all) => {
|
|
|
5
5
|
};
|
|
6
6
|
|
|
7
7
|
// package.json
|
|
8
|
-
var version = "0.
|
|
8
|
+
var version = "0.16.1";
|
|
9
9
|
|
|
10
10
|
// src/lib/mux-ai-error.ts
|
|
11
11
|
var MuxAiError = class extends Error {
|
|
@@ -13,7 +13,7 @@ var MuxAiError = class extends Error {
|
|
|
13
13
|
super(message);
|
|
14
14
|
/** Structural brand for duck-type identification. */
|
|
15
15
|
this.__robots_error = true;
|
|
16
|
-
this.name = "
|
|
16
|
+
this.name = "FatalError";
|
|
17
17
|
this.publicType = opts?.type ?? "processing_error";
|
|
18
18
|
this.publicMessage = message;
|
|
19
19
|
this.retryable = opts?.retryable ?? false;
|
|
@@ -2160,7 +2160,7 @@ function renderSection(section) {
|
|
|
2160
2160
|
throw new Error(`Invalid XML ${context} name: "${name}"`);
|
|
2161
2161
|
}
|
|
2162
2162
|
};
|
|
2163
|
-
const escapeXmlText = (value) => value.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">
|
|
2163
|
+
const escapeXmlText = (value) => value.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
|
2164
2164
|
const escapeXmlAttribute = (value) => escapeXmlText(value).replace(/"/g, """);
|
|
2165
2165
|
if (!content.trim()) {
|
|
2166
2166
|
return "";
|
|
@@ -2273,7 +2273,7 @@ async function withRetry(fn, {
|
|
|
2273
2273
|
|
|
2274
2274
|
// src/workflows/ask-questions.ts
|
|
2275
2275
|
var questionAnswerSchema = z2.object({
|
|
2276
|
-
question: z2.string(),
|
|
2276
|
+
question: z2.string().describe("The full text of the original question"),
|
|
2277
2277
|
answer: z2.string().nullable(),
|
|
2278
2278
|
confidence: z2.number(),
|
|
2279
2279
|
reasoning: z2.string(),
|
|
@@ -2319,14 +2319,17 @@ var SYSTEM_PROMPT = dedent`
|
|
|
2319
2319
|
<task>
|
|
2320
2320
|
For each question provided, you must:
|
|
2321
2321
|
1. Analyze the storyboard frames and transcript (if provided)
|
|
2322
|
-
2. Answer with ONLY the allowed response options - no other values are acceptable
|
|
2322
|
+
2. Answer with ONLY the allowed response options listed for that question - no other values are acceptable
|
|
2323
2323
|
3. Provide a confidence score between 0 and 1 reflecting your certainty
|
|
2324
2324
|
4. Explain your reasoning based on observable evidence
|
|
2325
2325
|
</task>
|
|
2326
2326
|
|
|
2327
2327
|
<answer_guidelines>
|
|
2328
|
-
-
|
|
2329
|
-
- Choose
|
|
2328
|
+
- Each question is presented as its own <question> block with a <text> element (the question) and an <allowed_answers> element (the permitted response values)
|
|
2329
|
+
- Choose only from the values listed in that question's <allowed_answers> element
|
|
2330
|
+
- Questions may have any set of allowed answers. Always read the <allowed_answers> for each question and select the best matching option based on the evidence
|
|
2331
|
+
- Select the answer best supported by observable evidence from the content
|
|
2332
|
+
- When evidence is ambiguous but some signal exists, select the most conservative option and use a low confidence score. If the question cannot be answered at all from the content, skip it per the relevance_filtering rules
|
|
2330
2333
|
- Confidence should reflect the clarity and strength of evidence:
|
|
2331
2334
|
* 0.9-1.0: Clear, unambiguous evidence
|
|
2332
2335
|
* 0.7-0.9: Strong evidence with minor ambiguity
|
|
@@ -2372,7 +2375,7 @@ var SYSTEM_PROMPT = dedent`
|
|
|
2372
2375
|
</relevance_filtering>
|
|
2373
2376
|
|
|
2374
2377
|
<constraints>
|
|
2375
|
-
- You MUST answer every relevant question with one of
|
|
2378
|
+
- You MUST answer every relevant question with one of its own listed allowed response options
|
|
2376
2379
|
- Skip irrelevant questions as described in relevance_filtering
|
|
2377
2380
|
- Only describe observable evidence from frames or transcript
|
|
2378
2381
|
- Do not fabricate details or make unsupported assumptions
|
|
@@ -2407,14 +2410,17 @@ var AUDIO_ONLY_SYSTEM_PROMPT = dedent`
|
|
|
2407
2410
|
<task>
|
|
2408
2411
|
For each question provided, you must:
|
|
2409
2412
|
1. Analyze the transcript
|
|
2410
|
-
2. Answer with ONLY the allowed response options - no other values are acceptable
|
|
2413
|
+
2. Answer with ONLY the allowed response options listed for that question - no other values are acceptable
|
|
2411
2414
|
3. Provide a confidence score between 0 and 1 reflecting your certainty
|
|
2412
2415
|
4. Explain your reasoning based on observable evidence
|
|
2413
2416
|
</task>
|
|
2414
2417
|
|
|
2415
2418
|
<answer_guidelines>
|
|
2416
|
-
-
|
|
2417
|
-
- Choose
|
|
2419
|
+
- Each question is presented as its own <question> block with a <text> element (the question) and an <allowed_answers> element (the permitted response values)
|
|
2420
|
+
- Choose only from the values listed in that question's <allowed_answers> element
|
|
2421
|
+
- Questions may have any set of allowed answers. Always read the <allowed_answers> for each question and select the best matching option based on the evidence
|
|
2422
|
+
- Select the answer best supported by observable evidence from the content
|
|
2423
|
+
- When evidence is ambiguous but some signal exists, select the most conservative option and use a low confidence score. If the question cannot be answered at all from the content, skip it per the relevance_filtering rules
|
|
2418
2424
|
- Confidence should reflect the clarity and strength of evidence:
|
|
2419
2425
|
* 0.9-1.0: Clear, unambiguous evidence
|
|
2420
2426
|
* 0.7-0.9: Strong evidence with minor ambiguity
|
|
@@ -2460,7 +2466,7 @@ var AUDIO_ONLY_SYSTEM_PROMPT = dedent`
|
|
|
2460
2466
|
</relevance_filtering>
|
|
2461
2467
|
|
|
2462
2468
|
<constraints>
|
|
2463
|
-
- You MUST answer every relevant question with one of
|
|
2469
|
+
- You MUST answer every relevant question with one of its own listed allowed response options
|
|
2464
2470
|
- Skip irrelevant questions as described in relevance_filtering
|
|
2465
2471
|
- Only describe observable evidence from transcript content
|
|
2466
2472
|
- Do not fabricate details or make unsupported assumptions
|
|
@@ -2475,46 +2481,43 @@ var AUDIO_ONLY_SYSTEM_PROMPT = dedent`
|
|
|
2475
2481
|
- GOOD: "The speaker describes running through a park"
|
|
2476
2482
|
- Be specific and evidence-based
|
|
2477
2483
|
</language_guidelines>`;
|
|
2478
|
-
var askQuestionsPromptBuilder = createPromptBuilder({
|
|
2479
|
-
template: {
|
|
2480
|
-
questions: {
|
|
2481
|
-
tag: "questions",
|
|
2482
|
-
content: "Please answer the following questions about this content:"
|
|
2483
|
-
}
|
|
2484
|
-
},
|
|
2485
|
-
sectionOrder: ["questions"]
|
|
2486
|
-
});
|
|
2487
2484
|
function buildUserPrompt({
|
|
2488
2485
|
questions,
|
|
2489
|
-
allowedAnswers,
|
|
2490
2486
|
transcriptText,
|
|
2491
2487
|
isCleanTranscript = true,
|
|
2492
2488
|
isAudioOnly = false
|
|
2493
2489
|
}) {
|
|
2494
|
-
const
|
|
2495
|
-
const
|
|
2496
|
-
|
|
2497
|
-
|
|
2498
|
-
|
|
2499
|
-
|
|
2500
|
-
const
|
|
2501
|
-
|
|
2502
|
-
|
|
2503
|
-
|
|
2504
|
-
|
|
2505
|
-
|
|
2506
|
-
|
|
2507
|
-
|
|
2508
|
-
|
|
2509
|
-
${
|
|
2490
|
+
const contentDescriptor = isAudioOnly ? "audio content using transcript evidence" : "content";
|
|
2491
|
+
const taskContent = dedent`
|
|
2492
|
+
Answer each question in the <questions> block below about the ${contentDescriptor}.
|
|
2493
|
+
Use only the values listed inside each question's own <allowed_answers> element.
|
|
2494
|
+
Return one answer per question, in the order the questions appear.
|
|
2495
|
+
If a question cannot be answered from the provided content, skip it as described in the system instructions.`;
|
|
2496
|
+
const taskSection = `<task>
|
|
2497
|
+
${taskContent}
|
|
2498
|
+
</task>`;
|
|
2499
|
+
const questionBlocks = questions.map((q, idx) => {
|
|
2500
|
+
const textTag = renderSection({ tag: "text", content: q.question });
|
|
2501
|
+
const answersTag = renderSection({
|
|
2502
|
+
tag: "allowed_answers",
|
|
2503
|
+
content: q.answerOptions.map((a) => `"${a}"`).join(", ")
|
|
2504
|
+
});
|
|
2505
|
+
return `<question number="${idx + 1}">
|
|
2506
|
+
${textTag}
|
|
2507
|
+
${answersTag}
|
|
2508
|
+
</question>`;
|
|
2509
|
+
}).join("\n\n");
|
|
2510
|
+
const questionsSection = `<questions>
|
|
2511
|
+
${questionBlocks}
|
|
2512
|
+
</questions>`;
|
|
2513
|
+
const sections = [];
|
|
2514
|
+
if (transcriptText) {
|
|
2515
|
+
const format = isCleanTranscript ? "plain text" : "WebVTT";
|
|
2516
|
+
sections.push(renderSection(createTranscriptSection(transcriptText, format)));
|
|
2510
2517
|
}
|
|
2511
|
-
|
|
2512
|
-
|
|
2513
|
-
return
|
|
2514
|
-
|
|
2515
|
-
${questionsSection}
|
|
2516
|
-
|
|
2517
|
-
${responseOptions}`;
|
|
2518
|
+
sections.push(taskSection);
|
|
2519
|
+
sections.push(questionsSection);
|
|
2520
|
+
return sections.join("\n\n");
|
|
2518
2521
|
}
|
|
2519
2522
|
async function fetchImageAsBase64(imageUrl, imageDownloadOptions) {
|
|
2520
2523
|
"use step";
|
|
@@ -2583,7 +2586,6 @@ async function askQuestions(assetId, questions, options) {
|
|
|
2583
2586
|
provider = "openai",
|
|
2584
2587
|
model,
|
|
2585
2588
|
languageCode,
|
|
2586
|
-
answerOptions,
|
|
2587
2589
|
includeTranscript = true,
|
|
2588
2590
|
cleanTranscript = true,
|
|
2589
2591
|
imageSubmissionMode = "url",
|
|
@@ -2591,15 +2593,23 @@ async function askQuestions(assetId, questions, options) {
|
|
|
2591
2593
|
storyboardWidth = 640,
|
|
2592
2594
|
credentials
|
|
2593
2595
|
} = options ?? {};
|
|
2594
|
-
const
|
|
2595
|
-
|
|
2596
|
-
|
|
2597
|
-
|
|
2596
|
+
const normalizedQuestions = questions.map((q, idx) => {
|
|
2597
|
+
const options2 = Array.from(
|
|
2598
|
+
new Set(
|
|
2599
|
+
(q.answerOptions?.length ? q.answerOptions : ["yes", "no"]).map((o) => o.trim()).filter(Boolean)
|
|
2600
|
+
)
|
|
2601
|
+
);
|
|
2602
|
+
if (!options2.length) {
|
|
2603
|
+
throw new MuxAiError(
|
|
2604
|
+
`Question at index ${idx} has invalid answerOptions: must include at least one non-empty value.`,
|
|
2605
|
+
{ type: "validation_error" }
|
|
2606
|
+
);
|
|
2607
|
+
}
|
|
2608
|
+
return { ...q, answerOptions: options2 };
|
|
2609
|
+
});
|
|
2610
|
+
const allowedAnswers = Array.from(
|
|
2611
|
+
new Set(normalizedQuestions.flatMap((q) => q.answerOptions))
|
|
2598
2612
|
);
|
|
2599
|
-
if (!normalizedAnswerOptions.length) {
|
|
2600
|
-
throw new MuxAiError("answerOptions must include at least one non-empty value.", { type: "validation_error" });
|
|
2601
|
-
}
|
|
2602
|
-
const allowedAnswers = normalizedAnswerOptions;
|
|
2603
2613
|
const modelConfig = resolveLanguageModelConfig({
|
|
2604
2614
|
...options,
|
|
2605
2615
|
model,
|
|
@@ -2630,8 +2640,7 @@ async function askQuestions(assetId, questions, options) {
|
|
|
2630
2640
|
}) : void 0;
|
|
2631
2641
|
const transcriptText = transcriptResult?.transcriptText ?? "";
|
|
2632
2642
|
const userPrompt = buildUserPrompt({
|
|
2633
|
-
questions,
|
|
2634
|
-
allowedAnswers,
|
|
2643
|
+
questions: normalizedQuestions,
|
|
2635
2644
|
transcriptText,
|
|
2636
2645
|
isCleanTranscript: cleanTranscript,
|
|
2637
2646
|
isAudioOnly
|
|
@@ -2694,10 +2703,15 @@ async function askQuestions(assetId, questions, options) {
|
|
|
2694
2703
|
`Failed to generate answers for all questions for asset ${assetId}.`
|
|
2695
2704
|
);
|
|
2696
2705
|
}
|
|
2697
|
-
const answers = analysisResponse.result.answers.map((raw) => {
|
|
2706
|
+
const answers = analysisResponse.result.answers.map((raw, idx) => {
|
|
2698
2707
|
const isSkipped = raw.skipped || raw.answer === SKIP_SENTINEL;
|
|
2708
|
+
if (!isSkipped && !normalizedQuestions[idx].answerOptions.includes(raw.answer)) {
|
|
2709
|
+
throw new MuxAiError(
|
|
2710
|
+
`Answer "${raw.answer}" for question ${idx} is not in allowed options: ${normalizedQuestions[idx].answerOptions.join(", ")}`
|
|
2711
|
+
);
|
|
2712
|
+
}
|
|
2699
2713
|
return {
|
|
2700
|
-
// Strip numbering prefix (e.g
|
|
2714
|
+
// Strip numbering prefix (e.g. "1. ") if the LLM prepends one
|
|
2701
2715
|
question: raw.question.replace(/^\d+\.\s*/, ""),
|
|
2702
2716
|
confidence: isSkipped ? 0 : Math.min(1, Math.max(0, raw.confidence)),
|
|
2703
2717
|
reasoning: raw.reasoning,
|