mulmocast 1.2.45 → 1.2.46
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/lib/actions/translate.js +3 -2
- package/lib/agents/add_bgm_agent.js +10 -3
- package/lib/agents/image_genai_agent.js +25 -7
- package/lib/agents/image_openai_agent.js +16 -5
- package/lib/agents/image_replicate_agent.js +16 -4
- package/lib/agents/lipsync_replicate_agent.js +22 -6
- package/lib/agents/movie_genai_agent.js +16 -4
- package/lib/agents/movie_replicate_agent.js +19 -7
- package/lib/agents/sound_effect_replicate_agent.js +13 -3
- package/lib/agents/tavily_agent.js +5 -2
- package/lib/agents/tts_elevenlabs_agent.js +13 -4
- package/lib/agents/tts_google_agent.js +4 -1
- package/lib/agents/tts_nijivoice_agent.js +10 -3
- package/lib/agents/tts_openai_agent.js +10 -3
- package/lib/utils/error_cause.d.ts +91 -3
- package/lib/utils/error_cause.js +106 -21
- package/package.json +1 -1
package/lib/actions/translate.js
CHANGED
|
@@ -8,6 +8,7 @@ import { splitText } from "../utils/string.js";
|
|
|
8
8
|
import { settings2GraphAIConfig, beatId, multiLingualObjectToArray } from "../utils/utils.js";
|
|
9
9
|
import { getMultiLingual } from "../utils/context.js";
|
|
10
10
|
import { currentMulmoScriptVersion } from "../utils/const.js";
|
|
11
|
+
import { translateApiKeyMissingError } from "../utils/error_cause.js";
|
|
11
12
|
import { getOutputMultilingualFilePath, mkdir, writingMessage, hashSHA256 } from "../utils/file.js";
|
|
12
13
|
import { translateSystemPrompt, translatePrompts } from "../utils/prompt.js";
|
|
13
14
|
import { MulmoStudioContextMethods } from "../methods/mulmo_studio_context.js";
|
|
@@ -241,7 +242,7 @@ export const translateBeat = async (index, context, targetLangs, args) => {
|
|
|
241
242
|
try {
|
|
242
243
|
const { outputMultilingualFilePath } = getOutputMultilingualFilePathAndMkdir(context);
|
|
243
244
|
const config = settings2GraphAIConfig(settings, process.env);
|
|
244
|
-
assert(!!config?.openAIAgent?.apiKey, "The OPENAI_API_KEY environment variable is missing or empty");
|
|
245
|
+
assert(!!config?.openAIAgent?.apiKey, "The OPENAI_API_KEY environment variable is missing or empty", false, translateApiKeyMissingError());
|
|
245
246
|
const graph = new GraphAI(beatGraph, { ...vanillaAgents, fileWriteAgent, openAIAgent }, { agentFilters, config });
|
|
246
247
|
graph.injectValue("context", context);
|
|
247
248
|
graph.injectValue("targetLangs", targetLangs);
|
|
@@ -276,7 +277,7 @@ export const translate = async (context, args) => {
|
|
|
276
277
|
? args?.targetLangs
|
|
277
278
|
: [...new Set([context.lang, context.studio.script.captionParams?.lang].filter((x) => !isNull(x)))];
|
|
278
279
|
const config = settings2GraphAIConfig(settings, process.env);
|
|
279
|
-
assert(!!config?.openAIAgent?.apiKey, "The OPENAI_API_KEY environment variable is missing or empty");
|
|
280
|
+
assert(!!config?.openAIAgent?.apiKey, "The OPENAI_API_KEY environment variable is missing or empty", false, translateApiKeyMissingError());
|
|
280
281
|
const graph = new GraphAI(translate_graph_data, { ...vanillaAgents, fileWriteAgent, openAIAgent }, { agentFilters, config });
|
|
281
282
|
graph.injectValue("context", context);
|
|
282
283
|
graph.injectValue("targetLangs", targetLangs);
|
|
@@ -2,14 +2,19 @@ import { GraphAILogger } from "graphai";
|
|
|
2
2
|
import { FfmpegContextAddInput, FfmpegContextInit, FfmpegContextGenerateOutput, ffmpegGetMediaDuration } from "../utils/ffmpeg_utils.js";
|
|
3
3
|
import { MulmoStudioContextMethods } from "../methods/mulmo_studio_context.js";
|
|
4
4
|
import { isFile } from "../utils/file.js";
|
|
5
|
+
import { agentGenerationError, agentFileNotExistError, audioAction, audioFileTarget } from "../utils/error_cause.js";
|
|
5
6
|
const addBGMAgent = async ({ namedInputs, params, }) => {
|
|
6
7
|
const { voiceFile, outputFile, context } = namedInputs;
|
|
7
8
|
const { musicFile } = params;
|
|
8
9
|
if (!isFile(voiceFile)) {
|
|
9
|
-
throw new Error(`AddBGMAgent voiceFile not exist: ${voiceFile}
|
|
10
|
+
throw new Error(`AddBGMAgent voiceFile not exist: ${voiceFile}`, {
|
|
11
|
+
cause: agentFileNotExistError("addBGMAgent", audioAction, audioFileTarget, voiceFile),
|
|
12
|
+
});
|
|
10
13
|
}
|
|
11
14
|
if (!musicFile.match(/^http/) && !isFile(musicFile)) {
|
|
12
|
-
throw new Error(`AddBGMAgent musicFile not exist: ${musicFile}
|
|
15
|
+
throw new Error(`AddBGMAgent musicFile not exist: ${musicFile}`, {
|
|
16
|
+
cause: agentFileNotExistError("addBGMAgent", audioAction, audioFileTarget, musicFile),
|
|
17
|
+
});
|
|
13
18
|
}
|
|
14
19
|
const { duration: speechDuration } = await ffmpegGetMediaDuration(voiceFile);
|
|
15
20
|
const introPadding = MulmoStudioContextMethods.getIntroPadding(context);
|
|
@@ -30,7 +35,9 @@ const addBGMAgent = async ({ namedInputs, params, }) => {
|
|
|
30
35
|
}
|
|
31
36
|
catch (e) {
|
|
32
37
|
GraphAILogger.log(e);
|
|
33
|
-
throw new Error(`AddBGMAgent ffmpeg run Error
|
|
38
|
+
throw new Error(`AddBGMAgent ffmpeg run Error`, {
|
|
39
|
+
cause: agentGenerationError("addBGMAgent", audioAction, audioFileTarget),
|
|
40
|
+
});
|
|
34
41
|
}
|
|
35
42
|
};
|
|
36
43
|
const addBGMAgentInfo = {
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import fs from "fs";
|
|
2
2
|
import { GraphAILogger } from "graphai";
|
|
3
3
|
import { provider2ImageAgent } from "../utils/provider2agent.js";
|
|
4
|
+
import { apiKeyMissingError, agentGenerationError, agentInvalidResponseError, imageAction, imageFileTarget, hasCause } from "../utils/error_cause.js";
|
|
4
5
|
import { GoogleGenAI, PersonGeneration } from "@google/genai";
|
|
5
6
|
import { blankImagePath, blankSquareImagePath, blankVerticalImagePath } from "../utils/file.js";
|
|
6
7
|
const getAspectRatio = (canvasSize) => {
|
|
@@ -20,7 +21,9 @@ export const imageGenAIAgent = async ({ namedInputs, params, config, }) => {
|
|
|
20
21
|
const model = params.model ?? provider2ImageAgent["google"].defaultModel;
|
|
21
22
|
const apiKey = config?.apiKey;
|
|
22
23
|
if (!apiKey) {
|
|
23
|
-
throw new Error("Google GenAI API key is required (GEMINI_API_KEY)"
|
|
24
|
+
throw new Error("Google GenAI API key is required (GEMINI_API_KEY)", {
|
|
25
|
+
cause: apiKeyMissingError("imageGenAIAgent", imageAction, "GEMINI_API_KEY"),
|
|
26
|
+
});
|
|
24
27
|
}
|
|
25
28
|
try {
|
|
26
29
|
const ai = new GoogleGenAI({ apiKey });
|
|
@@ -44,7 +47,9 @@ export const imageGenAIAgent = async ({ namedInputs, params, config, }) => {
|
|
|
44
47
|
});
|
|
45
48
|
const response = await ai.models.generateContent({ model, contents });
|
|
46
49
|
if (!response.candidates?.[0]?.content?.parts) {
|
|
47
|
-
throw new Error("ERROR: generateContent returned no candidates"
|
|
50
|
+
throw new Error("ERROR: generateContent returned no candidates", {
|
|
51
|
+
cause: agentInvalidResponseError("imageGenAIAgent", imageAction, imageFileTarget),
|
|
52
|
+
});
|
|
48
53
|
}
|
|
49
54
|
for (const part of response.candidates[0].content.parts) {
|
|
50
55
|
if (part.text) {
|
|
@@ -53,13 +58,17 @@ export const imageGenAIAgent = async ({ namedInputs, params, config, }) => {
|
|
|
53
58
|
else if (part.inlineData) {
|
|
54
59
|
const imageData = part.inlineData.data;
|
|
55
60
|
if (!imageData) {
|
|
56
|
-
throw new Error("ERROR: generateContent returned no image data"
|
|
61
|
+
throw new Error("ERROR: generateContent returned no image data", {
|
|
62
|
+
cause: agentInvalidResponseError("imageGenAIAgent", imageAction, imageFileTarget),
|
|
63
|
+
});
|
|
57
64
|
}
|
|
58
65
|
const buffer = Buffer.from(imageData, "base64");
|
|
59
66
|
return { buffer };
|
|
60
67
|
}
|
|
61
68
|
}
|
|
62
|
-
throw new Error("ERROR: generateContent returned no image data"
|
|
69
|
+
throw new Error("ERROR: generateContent returned no image data", {
|
|
70
|
+
cause: agentInvalidResponseError("imageGenAIAgent", imageAction, imageFileTarget),
|
|
71
|
+
});
|
|
63
72
|
}
|
|
64
73
|
else {
|
|
65
74
|
const response = await ai.models.generateImages({
|
|
@@ -73,18 +82,27 @@ export const imageGenAIAgent = async ({ namedInputs, params, config, }) => {
|
|
|
73
82
|
},
|
|
74
83
|
});
|
|
75
84
|
if (!response.generatedImages || response.generatedImages.length === 0) {
|
|
76
|
-
throw new Error("ERROR: generateImage returned no generated images"
|
|
85
|
+
throw new Error("ERROR: generateImage returned no generated images", {
|
|
86
|
+
cause: agentInvalidResponseError("imageGenAIAgent", imageAction, imageFileTarget),
|
|
87
|
+
});
|
|
77
88
|
}
|
|
78
89
|
const image = response.generatedImages[0].image;
|
|
79
90
|
if (image && image.imageBytes) {
|
|
80
91
|
return { buffer: Buffer.from(image.imageBytes, "base64") };
|
|
81
92
|
}
|
|
82
|
-
throw new Error("ERROR: generateImage returned no image bytes"
|
|
93
|
+
throw new Error("ERROR: generateImage returned no image bytes", {
|
|
94
|
+
cause: agentInvalidResponseError("imageGenAIAgent", imageAction, imageFileTarget),
|
|
95
|
+
});
|
|
83
96
|
}
|
|
84
97
|
}
|
|
85
98
|
catch (error) {
|
|
86
99
|
GraphAILogger.info("Failed to generate image:", error);
|
|
87
|
-
|
|
100
|
+
if (hasCause(error) && error.cause) {
|
|
101
|
+
throw error;
|
|
102
|
+
}
|
|
103
|
+
throw new Error("Failed to generate image with Google GenAI", {
|
|
104
|
+
cause: agentGenerationError("imageGenAIAgent", imageAction, imageFileTarget),
|
|
105
|
+
});
|
|
88
106
|
}
|
|
89
107
|
};
|
|
90
108
|
const imageGenAIAgentInfo = {
|
|
@@ -3,13 +3,16 @@ import path from "path";
|
|
|
3
3
|
import { GraphAILogger } from "graphai";
|
|
4
4
|
import OpenAI, { toFile } from "openai";
|
|
5
5
|
import { provider2ImageAgent } from "../utils/provider2agent.js";
|
|
6
|
+
import { apiKeyMissingError, agentGenerationError, agentInvalidResponseError, imageAction, imageFileTarget } from "../utils/error_cause.js";
|
|
6
7
|
// https://platform.openai.com/docs/guides/image-generation
|
|
7
8
|
export const imageOpenaiAgent = async ({ namedInputs, params, config, }) => {
|
|
8
9
|
const { prompt, referenceImages } = namedInputs;
|
|
9
10
|
const { moderation, canvasSize, quality } = params;
|
|
10
11
|
const { apiKey, baseURL } = { ...config };
|
|
11
12
|
if (!apiKey) {
|
|
12
|
-
throw new Error("OpenAI API key is required (OPENAI_API_KEY)"
|
|
13
|
+
throw new Error("OpenAI API key is required (OPENAI_API_KEY)", {
|
|
14
|
+
cause: apiKeyMissingError("imageOpenaiAgent", imageAction, "OPENAI_API_KEY"),
|
|
15
|
+
});
|
|
13
16
|
}
|
|
14
17
|
const model = params.model ?? provider2ImageAgent["openai"].defaultModel;
|
|
15
18
|
const openai = new OpenAI({ apiKey, baseURL });
|
|
@@ -67,25 +70,33 @@ export const imageOpenaiAgent = async ({ namedInputs, params, config, }) => {
|
|
|
67
70
|
}
|
|
68
71
|
catch (error) {
|
|
69
72
|
GraphAILogger.info("Failed to generate image:", error.message);
|
|
70
|
-
throw
|
|
73
|
+
throw new Error("Failed to generate image with OpenAI", {
|
|
74
|
+
cause: agentGenerationError("imageOpenaiAgent", imageAction, imageFileTarget),
|
|
75
|
+
});
|
|
71
76
|
}
|
|
72
77
|
})();
|
|
73
78
|
if (!response.data) {
|
|
74
|
-
throw new Error(`response.data is undefined: ${response}
|
|
79
|
+
throw new Error(`response.data is undefined: ${response}`, {
|
|
80
|
+
cause: agentInvalidResponseError("imageOpenaiAgent", imageAction, imageFileTarget),
|
|
81
|
+
});
|
|
75
82
|
}
|
|
76
83
|
const url = response.data[0].url;
|
|
77
84
|
if (!url) {
|
|
78
85
|
// For gpt-image-1
|
|
79
86
|
const image_base64 = response.data[0].b64_json;
|
|
80
87
|
if (!image_base64) {
|
|
81
|
-
throw new Error(`response.data[0].b64_json is undefined: ${response}
|
|
88
|
+
throw new Error(`response.data[0].b64_json is undefined: ${response}`, {
|
|
89
|
+
cause: agentInvalidResponseError("imageOpenaiAgent", imageAction, imageFileTarget),
|
|
90
|
+
});
|
|
82
91
|
}
|
|
83
92
|
return { buffer: Buffer.from(image_base64, "base64") };
|
|
84
93
|
}
|
|
85
94
|
// For dall-e-3
|
|
86
95
|
const res = await fetch(url);
|
|
87
96
|
if (!res.ok) {
|
|
88
|
-
throw new Error(`Failed to fetch ${url}: ${res.status} ${res.statusText}
|
|
97
|
+
throw new Error(`Failed to fetch ${url}: ${res.status} ${res.statusText}`, {
|
|
98
|
+
cause: agentGenerationError("imageOpenaiAgent", imageAction, imageFileTarget),
|
|
99
|
+
});
|
|
89
100
|
}
|
|
90
101
|
// 2. Read the response as an ArrayBuffer
|
|
91
102
|
const arrayBuffer = await res.arrayBuffer();
|
|
@@ -2,6 +2,7 @@ import { readFileSync } from "fs";
|
|
|
2
2
|
import { GraphAILogger } from "graphai";
|
|
3
3
|
import Replicate from "replicate";
|
|
4
4
|
import { getAspectRatio } from "./movie_replicate_agent.js";
|
|
5
|
+
import { apiKeyMissingError, agentGenerationError, agentInvalidResponseError, imageAction, imageFileTarget, hasCause } from "../utils/error_cause.js";
|
|
5
6
|
import { provider2ImageAgent } from "../utils/provider2agent.js";
|
|
6
7
|
export const imageReplicateAgent = async ({ namedInputs, params, config, }) => {
|
|
7
8
|
const { prompt, referenceImages } = namedInputs;
|
|
@@ -9,7 +10,9 @@ export const imageReplicateAgent = async ({ namedInputs, params, config, }) => {
|
|
|
9
10
|
const model = params.model ?? provider2ImageAgent.replicate.defaultModel;
|
|
10
11
|
const apiKey = config?.apiKey;
|
|
11
12
|
if (!apiKey) {
|
|
12
|
-
throw new Error("Replicate API key is required (REPLICATE_API_TOKEN)"
|
|
13
|
+
throw new Error("Replicate API key is required (REPLICATE_API_TOKEN)", {
|
|
14
|
+
cause: apiKeyMissingError("imageReplicateAgent", imageAction, "REPLICATE_API_TOKEN"),
|
|
15
|
+
});
|
|
13
16
|
}
|
|
14
17
|
const replicate = new Replicate({
|
|
15
18
|
auth: apiKey,
|
|
@@ -31,17 +34,26 @@ export const imageReplicateAgent = async ({ namedInputs, params, config, }) => {
|
|
|
31
34
|
const imageUrl = output[0].url();
|
|
32
35
|
const imageResponse = await fetch(imageUrl);
|
|
33
36
|
if (!imageResponse.ok) {
|
|
34
|
-
throw new Error(`Error downloading
|
|
37
|
+
throw new Error(`Error downloading image: ${imageResponse.status} - ${imageResponse.statusText}`, {
|
|
38
|
+
cause: agentGenerationError("imageReplicateAgent", imageAction, imageFileTarget),
|
|
39
|
+
});
|
|
35
40
|
}
|
|
36
41
|
const arrayBuffer = await imageResponse.arrayBuffer();
|
|
37
42
|
const buffer = Buffer.from(arrayBuffer);
|
|
38
43
|
return { buffer };
|
|
39
44
|
}
|
|
40
|
-
throw new Error("ERROR: generateImage returned undefined"
|
|
45
|
+
throw new Error("ERROR: generateImage returned undefined", {
|
|
46
|
+
cause: agentInvalidResponseError("imageReplicateAgent", imageAction, imageFileTarget),
|
|
47
|
+
});
|
|
41
48
|
}
|
|
42
49
|
catch (error) {
|
|
43
50
|
GraphAILogger.info("Replicate generation error:", error);
|
|
44
|
-
|
|
51
|
+
if (hasCause(error) && error.cause) {
|
|
52
|
+
throw error;
|
|
53
|
+
}
|
|
54
|
+
throw new Error("Failed to generate image with Replicate", {
|
|
55
|
+
cause: agentGenerationError("imageReplicateAgent", imageAction, imageFileTarget),
|
|
56
|
+
});
|
|
45
57
|
}
|
|
46
58
|
};
|
|
47
59
|
const imageReplicateAgentInfo = {
|
|
@@ -2,24 +2,31 @@ import { readFileSync, existsSync } from "fs";
|
|
|
2
2
|
import { GraphAILogger } from "graphai";
|
|
3
3
|
import Replicate from "replicate";
|
|
4
4
|
import { provider2LipSyncAgent } from "../utils/provider2agent.js";
|
|
5
|
+
import { apiKeyMissingError, agentGenerationError, agentFileNotExistError, imageAction, movieFileTarget, audioFileTarget, hasCause, } from "../utils/error_cause.js";
|
|
5
6
|
export const lipSyncReplicateAgent = async ({ namedInputs, params, config, }) => {
|
|
6
7
|
const { movieFile, audioFile, imageFile } = namedInputs;
|
|
7
8
|
const apiKey = config?.apiKey;
|
|
8
9
|
const model = params.model ?? provider2LipSyncAgent.replicate.defaultModel;
|
|
9
10
|
if (!apiKey) {
|
|
10
|
-
throw new Error("Replicate API key is required (REPLICATE_API_TOKEN)"
|
|
11
|
+
throw new Error("Replicate API key is required (REPLICATE_API_TOKEN)", {
|
|
12
|
+
cause: apiKeyMissingError("lipSyncReplicateAgent", imageAction, "REPLICATE_API_TOKEN"),
|
|
13
|
+
});
|
|
11
14
|
}
|
|
12
15
|
const replicate = new Replicate({
|
|
13
16
|
auth: apiKey,
|
|
14
17
|
});
|
|
15
18
|
if (!audioFile || !existsSync(audioFile)) {
|
|
16
|
-
throw new Error(`lipSyncReplicateAgent audioFile not exist: ${audioFile}
|
|
19
|
+
throw new Error(`lipSyncReplicateAgent audioFile not exist: ${audioFile}`, {
|
|
20
|
+
cause: agentFileNotExistError("lipSyncReplicateAgent", imageAction, audioFileTarget, audioFile),
|
|
21
|
+
});
|
|
17
22
|
}
|
|
18
23
|
const audioBuffer = readFileSync(audioFile);
|
|
19
24
|
const videoBuffer = movieFile ? readFileSync(movieFile) : undefined;
|
|
20
25
|
const imageBuffer = imageFile ? readFileSync(imageFile) : undefined;
|
|
21
26
|
if (!videoBuffer && !imageBuffer) {
|
|
22
|
-
throw new Error("lipSyncReplicateAgent Either movieFile or imageFile is required"
|
|
27
|
+
throw new Error("lipSyncReplicateAgent Either movieFile or imageFile is required", {
|
|
28
|
+
cause: agentGenerationError("lipSyncReplicateAgent", imageAction, movieFileTarget),
|
|
29
|
+
});
|
|
23
30
|
}
|
|
24
31
|
const audioUri = `data:audio/wav;base64,${audioBuffer.toString("base64")}`;
|
|
25
32
|
const videoUri = videoBuffer ? `data:video/quicktime;base64,${videoBuffer.toString("base64")}` : undefined;
|
|
@@ -35,7 +42,9 @@ export const lipSyncReplicateAgent = async ({ namedInputs, params, config, }) =>
|
|
|
35
42
|
};
|
|
36
43
|
const modelParams = provider2LipSyncAgent.replicate.modelParams[model];
|
|
37
44
|
if (!modelParams) {
|
|
38
|
-
throw new Error(`Model ${model} is not supported
|
|
45
|
+
throw new Error(`Model ${model} is not supported`, {
|
|
46
|
+
cause: agentGenerationError("lipSyncReplicateAgent", imageAction, movieFileTarget),
|
|
47
|
+
});
|
|
39
48
|
}
|
|
40
49
|
const videoParam = modelParams.video;
|
|
41
50
|
const audioParam = modelParams.audio;
|
|
@@ -58,7 +67,9 @@ export const lipSyncReplicateAgent = async ({ namedInputs, params, config, }) =>
|
|
|
58
67
|
const videoUrl = output.url();
|
|
59
68
|
const videoResponse = await fetch(videoUrl);
|
|
60
69
|
if (!videoResponse.ok) {
|
|
61
|
-
throw new Error(`Error downloading video: ${videoResponse.status} - ${videoResponse.statusText}
|
|
70
|
+
throw new Error(`Error downloading video: ${videoResponse.status} - ${videoResponse.statusText}`, {
|
|
71
|
+
cause: agentGenerationError("lipSyncReplicateAgent", imageAction, movieFileTarget),
|
|
72
|
+
});
|
|
62
73
|
}
|
|
63
74
|
const arrayBuffer = await videoResponse.arrayBuffer();
|
|
64
75
|
return { buffer: Buffer.from(arrayBuffer) };
|
|
@@ -67,7 +78,12 @@ export const lipSyncReplicateAgent = async ({ namedInputs, params, config, }) =>
|
|
|
67
78
|
}
|
|
68
79
|
catch (error) {
|
|
69
80
|
GraphAILogger.info("Failed to generate lip sync:", error.message);
|
|
70
|
-
|
|
81
|
+
if (hasCause(error) && error.cause) {
|
|
82
|
+
throw error;
|
|
83
|
+
}
|
|
84
|
+
throw new Error("Failed to lipSync with Replicate", {
|
|
85
|
+
cause: agentGenerationError("lipSyncReplicateAgent", imageAction, movieFileTarget),
|
|
86
|
+
});
|
|
71
87
|
}
|
|
72
88
|
};
|
|
73
89
|
const lipSyncReplicateAgentInfo = {
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { readFileSync } from "fs";
|
|
2
2
|
import { GraphAILogger, sleep } from "graphai";
|
|
3
|
+
import { apiKeyMissingError, agentGenerationError, agentInvalidResponseError, imageAction, movieFileTarget, hasCause } from "../utils/error_cause.js";
|
|
3
4
|
import { GoogleGenAI, PersonGeneration } from "@google/genai";
|
|
4
5
|
export const getAspectRatio = (canvasSize) => {
|
|
5
6
|
if (canvasSize.width > canvasSize.height) {
|
|
@@ -19,7 +20,9 @@ export const movieGenAIAgent = async ({ namedInputs, params, config, }) => {
|
|
|
19
20
|
const duration = params.duration ?? 8;
|
|
20
21
|
const apiKey = config?.apiKey;
|
|
21
22
|
if (!apiKey) {
|
|
22
|
-
throw new Error("Google GenAI API key is required (GEMINI_API_KEY)"
|
|
23
|
+
throw new Error("Google GenAI API key is required (GEMINI_API_KEY)", {
|
|
24
|
+
cause: apiKeyMissingError("movieGenAIAgent", imageAction, "GEMINI_API_KEY"),
|
|
25
|
+
});
|
|
23
26
|
}
|
|
24
27
|
try {
|
|
25
28
|
const ai = new GoogleGenAI({ apiKey });
|
|
@@ -55,11 +58,15 @@ export const movieGenAIAgent = async ({ namedInputs, params, config, }) => {
|
|
|
55
58
|
response.operation = await ai.operations.getVideosOperation(response);
|
|
56
59
|
}
|
|
57
60
|
if (!response.operation.response?.generatedVideos) {
|
|
58
|
-
throw new Error(`No video: ${JSON.stringify(response.operation, null, 2)}
|
|
61
|
+
throw new Error(`No video: ${JSON.stringify(response.operation, null, 2)}`, {
|
|
62
|
+
cause: agentInvalidResponseError("movieGenAIAgent", imageAction, movieFileTarget),
|
|
63
|
+
});
|
|
59
64
|
}
|
|
60
65
|
const video = response.operation.response.generatedVideos[0].video;
|
|
61
66
|
if (!video) {
|
|
62
|
-
throw new Error(`No video: ${JSON.stringify(response.operation, null, 2)}
|
|
67
|
+
throw new Error(`No video: ${JSON.stringify(response.operation, null, 2)}`, {
|
|
68
|
+
cause: agentInvalidResponseError("movieGenAIAgent", imageAction, movieFileTarget),
|
|
69
|
+
});
|
|
63
70
|
}
|
|
64
71
|
await ai.files.download({
|
|
65
72
|
file: video,
|
|
@@ -70,7 +77,12 @@ export const movieGenAIAgent = async ({ namedInputs, params, config, }) => {
|
|
|
70
77
|
}
|
|
71
78
|
catch (error) {
|
|
72
79
|
GraphAILogger.info("Failed to generate movie:", error.message);
|
|
73
|
-
|
|
80
|
+
if (hasCause(error) && error.cause) {
|
|
81
|
+
throw error;
|
|
82
|
+
}
|
|
83
|
+
throw new Error("Failed to generate movie with Google GenAI", {
|
|
84
|
+
cause: agentGenerationError("movieGenAIAgent", imageAction, movieFileTarget),
|
|
85
|
+
});
|
|
74
86
|
}
|
|
75
87
|
};
|
|
76
88
|
const movieGenAIAgentInfo = {
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { readFileSync } from "fs";
|
|
2
2
|
import { GraphAILogger } from "graphai";
|
|
3
3
|
import Replicate from "replicate";
|
|
4
|
+
import { apiKeyMissingError, agentGenerationError, agentInvalidResponseError, imageAction, movieFileTarget } from "../utils/error_cause.js";
|
|
4
5
|
import { provider2MovieAgent } from "../utils/provider2agent.js";
|
|
5
6
|
async function generateMovie(model, apiKey, prompt, imagePath, aspectRatio, duration) {
|
|
6
7
|
const replicate = new Replicate({
|
|
@@ -28,7 +29,9 @@ async function generateMovie(model, apiKey, prompt, imagePath, aspectRatio, dura
|
|
|
28
29
|
input[start_image] = base64Image;
|
|
29
30
|
}
|
|
30
31
|
else if (start_image === undefined) {
|
|
31
|
-
throw new Error(`Model ${model} does not support image-to-video generation
|
|
32
|
+
throw new Error(`Model ${model} does not support image-to-video generation`, {
|
|
33
|
+
cause: agentGenerationError("movieReplicateAgent", imageAction, movieFileTarget),
|
|
34
|
+
});
|
|
32
35
|
}
|
|
33
36
|
else {
|
|
34
37
|
input.image = base64Image;
|
|
@@ -41,7 +44,9 @@ async function generateMovie(model, apiKey, prompt, imagePath, aspectRatio, dura
|
|
|
41
44
|
const videoUrl = output.url();
|
|
42
45
|
const videoResponse = await fetch(videoUrl);
|
|
43
46
|
if (!videoResponse.ok) {
|
|
44
|
-
throw new Error(`Error downloading video: ${videoResponse.status} - ${videoResponse.statusText}
|
|
47
|
+
throw new Error(`Error downloading video: ${videoResponse.status} - ${videoResponse.statusText}`, {
|
|
48
|
+
cause: agentGenerationError("movieReplicateAgent", imageAction, movieFileTarget),
|
|
49
|
+
});
|
|
45
50
|
}
|
|
46
51
|
const arrayBuffer = await videoResponse.arrayBuffer();
|
|
47
52
|
return Buffer.from(arrayBuffer);
|
|
@@ -71,7 +76,9 @@ export const movieReplicateAgent = async ({ namedInputs, params, config, }) => {
|
|
|
71
76
|
const aspectRatio = getAspectRatio(params.canvasSize);
|
|
72
77
|
const model = params.model ?? provider2MovieAgent.replicate.defaultModel;
|
|
73
78
|
if (!provider2MovieAgent.replicate.modelParams[model]) {
|
|
74
|
-
throw new Error(`Model ${model} is not supported
|
|
79
|
+
throw new Error(`Model ${model} is not supported`, {
|
|
80
|
+
cause: agentGenerationError("movieReplicateAgent", imageAction, movieFileTarget),
|
|
81
|
+
});
|
|
75
82
|
}
|
|
76
83
|
const duration = (() => {
|
|
77
84
|
const durations = provider2MovieAgent.replicate.modelParams[model].durations;
|
|
@@ -84,23 +91,28 @@ export const movieReplicateAgent = async ({ namedInputs, params, config, }) => {
|
|
|
84
91
|
}
|
|
85
92
|
})();
|
|
86
93
|
if (!provider2MovieAgent.replicate.modelParams[model].durations.includes(duration)) {
|
|
87
|
-
throw new Error(`Duration ${duration} is not supported for model ${model}. Supported durations: ${provider2MovieAgent.replicate.modelParams[model].durations.join(", ")}
|
|
94
|
+
throw new Error(`Duration ${duration} is not supported for model ${model}. Supported durations: ${provider2MovieAgent.replicate.modelParams[model].durations.join(", ")}`, {
|
|
95
|
+
cause: agentGenerationError("movieReplicateAgent", imageAction, movieFileTarget),
|
|
96
|
+
});
|
|
88
97
|
}
|
|
89
98
|
const apiKey = config?.apiKey;
|
|
90
99
|
if (!apiKey) {
|
|
91
|
-
throw new Error("Replicate API key is required (REPLICATE_API_TOKEN)"
|
|
100
|
+
throw new Error("Replicate API key is required (REPLICATE_API_TOKEN)", {
|
|
101
|
+
cause: apiKeyMissingError("movieReplicateAgent", imageAction, "REPLICATE_API_TOKEN"),
|
|
102
|
+
});
|
|
92
103
|
}
|
|
93
104
|
try {
|
|
94
105
|
const buffer = await generateMovie(model, apiKey, prompt, imagePath, aspectRatio, duration);
|
|
95
106
|
if (buffer) {
|
|
96
107
|
return { buffer };
|
|
97
108
|
}
|
|
98
|
-
throw new Error("ERROR: generateMovie returned undefined");
|
|
99
109
|
}
|
|
100
110
|
catch (error) {
|
|
101
111
|
GraphAILogger.info("Failed to generate movie:", error.message);
|
|
102
|
-
throw error;
|
|
103
112
|
}
|
|
113
|
+
throw new Error("ERROR: generateMovie returned undefined", {
|
|
114
|
+
cause: agentInvalidResponseError("movieReplicateAgent", imageAction, movieFileTarget),
|
|
115
|
+
});
|
|
104
116
|
};
|
|
105
117
|
const movieReplicateAgentInfo = {
|
|
106
118
|
name: "movieReplicateAgent",
|
|
@@ -2,12 +2,15 @@ import { readFileSync } from "fs";
|
|
|
2
2
|
import { GraphAILogger } from "graphai";
|
|
3
3
|
import Replicate from "replicate";
|
|
4
4
|
import { provider2SoundEffectAgent } from "../utils/provider2agent.js";
|
|
5
|
+
import { apiKeyMissingError, agentGenerationError, imageAction, movieFileTarget, hasCause } from "../utils/error_cause.js";
|
|
5
6
|
export const soundEffectReplicateAgent = async ({ namedInputs, params, config }) => {
|
|
6
7
|
const { prompt, movieFile } = namedInputs;
|
|
7
8
|
const apiKey = config?.apiKey;
|
|
8
9
|
const model = params.model ?? provider2SoundEffectAgent.replicate.defaultModel;
|
|
9
10
|
if (!apiKey) {
|
|
10
|
-
throw new Error("Replicate API key is required (REPLICATE_API_TOKEN)"
|
|
11
|
+
throw new Error("Replicate API key is required (REPLICATE_API_TOKEN)", {
|
|
12
|
+
cause: apiKeyMissingError("soundEffectReplicateAgent", imageAction, "REPLICATE_API_TOKEN"),
|
|
13
|
+
});
|
|
11
14
|
}
|
|
12
15
|
const replicate = new Replicate({
|
|
13
16
|
auth: apiKey,
|
|
@@ -32,7 +35,9 @@ export const soundEffectReplicateAgent = async ({ namedInputs, params, config })
|
|
|
32
35
|
const videoUrl = output.url();
|
|
33
36
|
const videoResponse = await fetch(videoUrl);
|
|
34
37
|
if (!videoResponse.ok) {
|
|
35
|
-
throw new Error(`Error downloading video: ${videoResponse.status} - ${videoResponse.statusText}
|
|
38
|
+
throw new Error(`Error downloading video: ${videoResponse.status} - ${videoResponse.statusText}`, {
|
|
39
|
+
cause: agentGenerationError("soundEffectReplicateAgent", imageAction, movieFileTarget),
|
|
40
|
+
});
|
|
36
41
|
}
|
|
37
42
|
const arrayBuffer = await videoResponse.arrayBuffer();
|
|
38
43
|
return { buffer: Buffer.from(arrayBuffer) };
|
|
@@ -41,7 +46,12 @@ export const soundEffectReplicateAgent = async ({ namedInputs, params, config })
|
|
|
41
46
|
}
|
|
42
47
|
catch (error) {
|
|
43
48
|
GraphAILogger.info("Failed to generate sound effect:", error.message);
|
|
44
|
-
|
|
49
|
+
if (hasCause(error) && error.cause) {
|
|
50
|
+
throw error;
|
|
51
|
+
}
|
|
52
|
+
throw new Error("Failed to generate sound effect with Replicate", {
|
|
53
|
+
cause: agentGenerationError("soundEffectReplicateAgent", imageAction, movieFileTarget),
|
|
54
|
+
});
|
|
45
55
|
}
|
|
46
56
|
};
|
|
47
57
|
const soundEffectReplicateAgentInfo = {
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { assert } from "graphai";
|
|
2
2
|
import { tavily } from "@tavily/core";
|
|
3
|
+
import { apiKeyMissingError, agentGenerationError } from "../utils/error_cause.js";
|
|
3
4
|
const getTavilyApiKey = (params, config) => {
|
|
4
5
|
if (params?.apiKey) {
|
|
5
6
|
return params.apiKey;
|
|
@@ -14,7 +15,7 @@ export const tavilySearchAgent = async ({ namedInputs, params, config, }) => {
|
|
|
14
15
|
assert(!!query, "tavilySearchAgent: query is required! set inputs: { query: 'search terms' }");
|
|
15
16
|
try {
|
|
16
17
|
const apiKey = getTavilyApiKey(params, config);
|
|
17
|
-
assert(apiKey, "Tavily API key is required. Please set the TAVILY_API_KEY environment variable or provide it in params/config.");
|
|
18
|
+
assert(apiKey, "Tavily API key is required. Please set the TAVILY_API_KEY environment variable or provide it in params/config.", false, apiKeyMissingError("tavilySearchAgent", "search", "TAVILY_API_KEY"));
|
|
18
19
|
const tvly = tavily({ apiKey });
|
|
19
20
|
// Convert params to SDK options format
|
|
20
21
|
const sdkOptions = {};
|
|
@@ -30,7 +31,9 @@ export const tavilySearchAgent = async ({ namedInputs, params, config, }) => {
|
|
|
30
31
|
return response;
|
|
31
32
|
}
|
|
32
33
|
catch (error) {
|
|
33
|
-
throw new Error(`Tavily search failed: ${error instanceof Error ? error.message : String(error)}
|
|
34
|
+
throw new Error(`Tavily search failed: ${error instanceof Error ? error.message : String(error)}`, {
|
|
35
|
+
cause: agentGenerationError("tavilySearchAgent", "search", "searchResults"),
|
|
36
|
+
});
|
|
34
37
|
}
|
|
35
38
|
};
|
|
36
39
|
const tavilySearchAgentInfo = {
|
|
@@ -1,14 +1,19 @@
|
|
|
1
1
|
import { GraphAILogger } from "graphai";
|
|
2
2
|
import { provider2TTSAgent } from "../utils/provider2agent.js";
|
|
3
|
+
import { apiKeyMissingError, agentGenerationError, audioAction, audioFileTarget } from "../utils/error_cause.js";
|
|
3
4
|
export const ttsElevenlabsAgent = async ({ namedInputs, params, config, }) => {
|
|
4
5
|
const { text } = namedInputs;
|
|
5
6
|
const { voice, model, stability, similarityBoost, suppressError } = params;
|
|
6
7
|
const apiKey = config?.apiKey;
|
|
7
8
|
if (!apiKey) {
|
|
8
|
-
throw new Error("ElevenLabs API key is required (ELEVENLABS_API_KEY)"
|
|
9
|
+
throw new Error("ElevenLabs API key is required (ELEVENLABS_API_KEY)", {
|
|
10
|
+
cause: apiKeyMissingError("ttsElevenlabsAgent", audioAction, "ELEVENLABS_API_KEY"),
|
|
11
|
+
});
|
|
9
12
|
}
|
|
10
13
|
if (!voice) {
|
|
11
|
-
throw new Error("ELEVENLABS Voice ID is required"
|
|
14
|
+
throw new Error("ELEVENLABS Voice ID is required", {
|
|
15
|
+
cause: agentGenerationError("ttsElevenlabsAgent", audioAction, audioFileTarget),
|
|
16
|
+
});
|
|
12
17
|
}
|
|
13
18
|
try {
|
|
14
19
|
const requestBody = {
|
|
@@ -30,7 +35,9 @@ export const ttsElevenlabsAgent = async ({ namedInputs, params, config, }) => {
|
|
|
30
35
|
body: JSON.stringify(requestBody),
|
|
31
36
|
});
|
|
32
37
|
if (!response.ok) {
|
|
33
|
-
throw new Error(`Eleven Labs API error: ${response.status} ${response.statusText}
|
|
38
|
+
throw new Error(`Eleven Labs API error: ${response.status} ${response.statusText}`, {
|
|
39
|
+
cause: agentGenerationError("ttsElevenlabsAgent", audioAction, audioFileTarget),
|
|
40
|
+
});
|
|
34
41
|
}
|
|
35
42
|
const arrayBuffer = await response.arrayBuffer();
|
|
36
43
|
const buffer = Buffer.from(arrayBuffer);
|
|
@@ -43,7 +50,9 @@ export const ttsElevenlabsAgent = async ({ namedInputs, params, config, }) => {
|
|
|
43
50
|
};
|
|
44
51
|
}
|
|
45
52
|
GraphAILogger.info(e);
|
|
46
|
-
throw new Error("TTS Eleven Labs Error"
|
|
53
|
+
throw new Error("TTS Eleven Labs Error", {
|
|
54
|
+
cause: agentGenerationError("ttsElevenlabsAgent", audioAction, audioFileTarget),
|
|
55
|
+
});
|
|
47
56
|
}
|
|
48
57
|
};
|
|
49
58
|
const ttsElevenlabsAgentInfo = {
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { GraphAILogger } from "graphai";
|
|
2
2
|
import * as textToSpeech from "@google-cloud/text-to-speech";
|
|
3
|
+
import { agentGenerationError, audioAction, audioFileTarget } from "../utils/error_cause.js";
|
|
3
4
|
const client = new textToSpeech.TextToSpeechClient();
|
|
4
5
|
export const ttsGoogleAgent = async ({ namedInputs, params }) => {
|
|
5
6
|
const { text } = namedInputs;
|
|
@@ -33,7 +34,9 @@ export const ttsGoogleAgent = async ({ namedInputs, params }) => {
|
|
|
33
34
|
};
|
|
34
35
|
}
|
|
35
36
|
GraphAILogger.info(e);
|
|
36
|
-
throw new Error("TTS Google Error"
|
|
37
|
+
throw new Error("TTS Google Error", {
|
|
38
|
+
cause: agentGenerationError("ttsGoogleAgent", audioAction, audioFileTarget),
|
|
39
|
+
});
|
|
37
40
|
}
|
|
38
41
|
};
|
|
39
42
|
const ttsGoogleAgentInfo = {
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { GraphAILogger } from "graphai";
|
|
2
|
+
import { apiKeyMissingError, agentGenerationError, audioAction, audioFileTarget } from "../utils/error_cause.js";
|
|
2
3
|
/*
|
|
3
4
|
const errorMessage = [
|
|
4
5
|
"TTS NijiVoice: No API key. ",
|
|
@@ -12,7 +13,9 @@ export const ttsNijivoiceAgent = async ({ params, namedInputs, config, }) => {
|
|
|
12
13
|
const { apiKey } = config ?? {};
|
|
13
14
|
const { text } = namedInputs;
|
|
14
15
|
if (!apiKey) {
|
|
15
|
-
throw new Error("NijiVoice API key is required (NIJIVOICE_API_KEY)"
|
|
16
|
+
throw new Error("NijiVoice API key is required (NIJIVOICE_API_KEY)", {
|
|
17
|
+
cause: apiKeyMissingError("ttsNijivoiceAgent", audioAction, "NIJIVOICE_API_KEY"),
|
|
18
|
+
});
|
|
16
19
|
}
|
|
17
20
|
const url = `https://api.nijivoice.com/api/platform/v1/voice-actors/${voice}/generate-voice`;
|
|
18
21
|
const options = {
|
|
@@ -42,7 +45,9 @@ export const ttsNijivoiceAgent = async ({ params, namedInputs, config, }) => {
|
|
|
42
45
|
};
|
|
43
46
|
}
|
|
44
47
|
GraphAILogger.info(voiceJson);
|
|
45
|
-
throw new Error("TTS Nijivoice Error"
|
|
48
|
+
throw new Error("TTS Nijivoice Error", {
|
|
49
|
+
cause: agentGenerationError("ttsNijivoiceAgent", audioAction, audioFileTarget),
|
|
50
|
+
});
|
|
46
51
|
}
|
|
47
52
|
catch (e) {
|
|
48
53
|
if (suppressError) {
|
|
@@ -51,7 +56,9 @@ export const ttsNijivoiceAgent = async ({ params, namedInputs, config, }) => {
|
|
|
51
56
|
};
|
|
52
57
|
}
|
|
53
58
|
GraphAILogger.info(e);
|
|
54
|
-
throw new Error("TTS Nijivoice Error"
|
|
59
|
+
throw new Error("TTS Nijivoice Error", {
|
|
60
|
+
cause: agentGenerationError("ttsNijivoiceAgent", audioAction, audioFileTarget),
|
|
61
|
+
});
|
|
55
62
|
}
|
|
56
63
|
};
|
|
57
64
|
const ttsNijivoiceAgentInfo = {
|
|
@@ -1,12 +1,15 @@
|
|
|
1
1
|
import { GraphAILogger } from "graphai";
|
|
2
2
|
import OpenAI from "openai";
|
|
3
3
|
import { provider2TTSAgent } from "../utils/provider2agent.js";
|
|
4
|
+
import { apiKeyMissingError, agentGenerationError, audioAction, audioFileTarget } from "../utils/error_cause.js";
|
|
4
5
|
export const ttsOpenaiAgent = async ({ namedInputs, params, config, }) => {
|
|
5
6
|
const { text } = namedInputs;
|
|
6
7
|
const { model, voice, suppressError, instructions } = params;
|
|
7
8
|
const { apiKey, baseURL } = config ?? {};
|
|
8
9
|
if (!apiKey) {
|
|
9
|
-
throw new Error("OpenAI API key is required (OPENAI_API_KEY)"
|
|
10
|
+
throw new Error("OpenAI API key is required (OPENAI_API_KEY)", {
|
|
11
|
+
cause: apiKeyMissingError("ttsOpenaiAgent", audioAction, "OPENAI_API_KEY"),
|
|
12
|
+
});
|
|
10
13
|
}
|
|
11
14
|
const openai = new OpenAI({ apiKey, baseURL });
|
|
12
15
|
try {
|
|
@@ -33,12 +36,16 @@ export const ttsOpenaiAgent = async ({ namedInputs, params, config, }) => {
|
|
|
33
36
|
if (e && typeof e === "object" && "error" in e) {
|
|
34
37
|
GraphAILogger.info("tts_openai_agent: ");
|
|
35
38
|
GraphAILogger.info(e.error);
|
|
36
|
-
throw new Error("TTS OpenAI Error: " + JSON.stringify(e.error, null, 2)
|
|
39
|
+
throw new Error("TTS OpenAI Error: " + JSON.stringify(e.error, null, 2), {
|
|
40
|
+
cause: agentGenerationError("ttsOpenaiAgent", audioAction, audioFileTarget),
|
|
41
|
+
});
|
|
37
42
|
}
|
|
38
43
|
else if (e instanceof Error) {
|
|
39
44
|
GraphAILogger.info("tts_openai_agent: ");
|
|
40
45
|
GraphAILogger.info(e.message);
|
|
41
|
-
throw new Error("TTS OpenAI Error: " + e.message
|
|
46
|
+
throw new Error("TTS OpenAI Error: " + e.message, {
|
|
47
|
+
cause: agentGenerationError("ttsOpenaiAgent", audioAction, audioFileTarget),
|
|
48
|
+
});
|
|
42
49
|
}
|
|
43
50
|
}
|
|
44
51
|
};
|
|
@@ -1,39 +1,98 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Error Definitions for i18n Notifications
|
|
3
|
+
* ----------------------------------------
|
|
4
|
+
* This file provides a set of standardized error action/type/target constants
|
|
5
|
+
* and factory functions that enrich error objects with structured metadata.
|
|
6
|
+
*
|
|
7
|
+
* ## Purpose
|
|
8
|
+
* - Attach `cause` data to `Error` objects for vue-i18n.
|
|
9
|
+
* - Generate consistent i18n keys in the form:
|
|
10
|
+
* notify.error.{action}.{type}[.{target}]
|
|
11
|
+
* - Provide contextual data (e.g., beatIndex, fileName, agentName) for use in
|
|
12
|
+
* translation strings.
|
|
13
|
+
*
|
|
14
|
+
* ## Constants
|
|
15
|
+
* - `type`: describes the error type (e.g., fileNotExist, urlFileNotFound).
|
|
16
|
+
* - `action`: the operation being performed (e.g., movie, image, audio).
|
|
17
|
+
* - `target`: the resource involved (e.g., audioFile, imageFile).
|
|
18
|
+
*
|
|
19
|
+
* ## Factory Functions
|
|
20
|
+
* Each factory function (e.g., getAudioInputIdsError, audioCheckerError) returns
|
|
21
|
+
* a plain object with:
|
|
22
|
+
* - `type` → error type constant
|
|
23
|
+
* - `action` → action constant
|
|
24
|
+
* - `target` → target constant (optional)
|
|
25
|
+
* - `agentName` → name of the agent/tool that produced the error
|
|
26
|
+
* - `beatIndex` → index of the beat in the current script
|
|
27
|
+
* - `fileName` → file name or identifier (if relevant)
|
|
28
|
+
*
|
|
29
|
+
* ## i18n Integration
|
|
30
|
+
* - The UI consumes the generated `{ action, type, target, ... }` to build the i18n key.
|
|
31
|
+
* - Example:
|
|
32
|
+
* notify.error.movie.fileNotExist.audioFile
|
|
33
|
+
* - Interpolation values (beatIndex, fileName, etc.) are passed as `data` for
|
|
34
|
+
* translations.
|
|
35
|
+
*
|
|
36
|
+
* ## Notes
|
|
37
|
+
* - If `target` is not required, omit it.
|
|
38
|
+
* - When adding new actions/types/targets, check for consistency with existing ones.
|
|
39
|
+
* - Use descriptive but concise names to avoid translation conflicts.
|
|
40
|
+
*
|
|
41
|
+
* ## Usage in Error Handling
|
|
42
|
+
* - When throwing manually:
|
|
43
|
+
* `throw new Error("message", { cause });`
|
|
44
|
+
* - When using assertions:
|
|
45
|
+
* `assert(condition, message, false, causeObject);`
|
|
46
|
+
* (set the 3rd parameter to `false` and pass the `cause` object as the 4th)
|
|
47
|
+
*/
|
|
1
48
|
export declare const urlFileNotFoundType = "urlFileNotFound";
|
|
2
49
|
export declare const fileNotExistType = "fileNotExist";
|
|
3
50
|
export declare const unknownMediaType = "unknownMedia";
|
|
4
51
|
export declare const sourceUndefinedType = "undefinedSourceType";
|
|
52
|
+
export declare const apiErrorType = "apiError";
|
|
53
|
+
export declare const apiKeyMissingType = "apiKeyMissing";
|
|
54
|
+
export declare const invalidResponseType = "invalidResponse";
|
|
5
55
|
export declare const movieAction = "movie";
|
|
6
56
|
export declare const imageAction = "images";
|
|
7
57
|
export declare const audioAction = "audio";
|
|
8
58
|
export declare const imageReferenceAction = "imageReference";
|
|
59
|
+
export declare const translateAction = "translate";
|
|
9
60
|
export declare const audioFileTarget = "audioFile";
|
|
10
61
|
export declare const imageFileTarget = "imageFile";
|
|
11
62
|
export declare const movieFileTarget = "movieFile";
|
|
12
63
|
export declare const videoSourceTarget = "videoSource";
|
|
13
64
|
export declare const audioSourceTarget = "audioSource";
|
|
14
65
|
export declare const codeTextTarget = "codeText";
|
|
66
|
+
export declare const agentFileNotExistError: (agentName: string, action: string, target: string, fileName: string, beatIndex?: number) => {
|
|
67
|
+
beatIndex?: number | undefined;
|
|
68
|
+
type: string;
|
|
69
|
+
action: string;
|
|
70
|
+
target: string;
|
|
71
|
+
agentName: string;
|
|
72
|
+
fileName: string;
|
|
73
|
+
};
|
|
15
74
|
export declare const getAudioInputIdsError: (index: number, fileName: string) => {
|
|
75
|
+
beatIndex?: number | undefined;
|
|
16
76
|
type: string;
|
|
17
77
|
action: string;
|
|
18
78
|
target: string;
|
|
19
79
|
agentName: string;
|
|
20
|
-
beatIndex: number;
|
|
21
80
|
fileName: string;
|
|
22
81
|
};
|
|
23
82
|
export declare const audioCheckerError: (index: number, fileName: string) => {
|
|
83
|
+
beatIndex?: number | undefined;
|
|
24
84
|
type: string;
|
|
25
85
|
action: string;
|
|
26
86
|
target: string;
|
|
27
87
|
agentName: string;
|
|
28
|
-
beatIndex: number;
|
|
29
88
|
fileName: string;
|
|
30
89
|
};
|
|
31
90
|
export declare const createVideoFileError: (index: number, fileName: string) => {
|
|
91
|
+
beatIndex?: number | undefined;
|
|
32
92
|
type: string;
|
|
33
93
|
action: string;
|
|
34
94
|
target: string;
|
|
35
95
|
agentName: string;
|
|
36
|
-
beatIndex: number;
|
|
37
96
|
fileName: string;
|
|
38
97
|
};
|
|
39
98
|
export declare const createVideoSourceError: (index: number) => {
|
|
@@ -82,3 +141,32 @@ export declare const imagePluginUnknownMediaError: (imageType: string) => {
|
|
|
82
141
|
action: string;
|
|
83
142
|
target: string;
|
|
84
143
|
};
|
|
144
|
+
export declare const apiKeyMissingError: (agentName: string, action: string, envVarName: string) => {
|
|
145
|
+
type: string;
|
|
146
|
+
action: string;
|
|
147
|
+
agentName: string;
|
|
148
|
+
envVarName: string;
|
|
149
|
+
};
|
|
150
|
+
export declare const agentGenerationError: (agentName: string, action: string, target: string, beatIndex?: number) => {
|
|
151
|
+
beatIndex?: number | undefined;
|
|
152
|
+
type: string;
|
|
153
|
+
action: string;
|
|
154
|
+
target: string;
|
|
155
|
+
agentName: string;
|
|
156
|
+
};
|
|
157
|
+
export declare const agentInvalidResponseError: (agentName: string, action: string, target: string, beatIndex?: number) => {
|
|
158
|
+
beatIndex?: number | undefined;
|
|
159
|
+
type: string;
|
|
160
|
+
action: string;
|
|
161
|
+
target: string;
|
|
162
|
+
agentName: string;
|
|
163
|
+
};
|
|
164
|
+
export declare const translateApiKeyMissingError: () => {
|
|
165
|
+
type: string;
|
|
166
|
+
action: string;
|
|
167
|
+
agentName: string;
|
|
168
|
+
envVarName: string;
|
|
169
|
+
};
|
|
170
|
+
export declare const hasCause: (err: unknown) => err is Error & {
|
|
171
|
+
cause: unknown;
|
|
172
|
+
};
|
package/lib/utils/error_cause.js
CHANGED
|
@@ -1,46 +1,90 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Error Definitions for i18n Notifications
|
|
3
|
+
* ----------------------------------------
|
|
4
|
+
* This file provides a set of standardized error action/type/target constants
|
|
5
|
+
* and factory functions that enrich error objects with structured metadata.
|
|
6
|
+
*
|
|
7
|
+
* ## Purpose
|
|
8
|
+
* - Attach `cause` data to `Error` objects for vue-i18n.
|
|
9
|
+
* - Generate consistent i18n keys in the form:
|
|
10
|
+
* notify.error.{action}.{type}[.{target}]
|
|
11
|
+
* - Provide contextual data (e.g., beatIndex, fileName, agentName) for use in
|
|
12
|
+
* translation strings.
|
|
13
|
+
*
|
|
14
|
+
* ## Constants
|
|
15
|
+
* - `type`: describes the error type (e.g., fileNotExist, urlFileNotFound).
|
|
16
|
+
* - `action`: the operation being performed (e.g., movie, image, audio).
|
|
17
|
+
* - `target`: the resource involved (e.g., audioFile, imageFile).
|
|
18
|
+
*
|
|
19
|
+
* ## Factory Functions
|
|
20
|
+
* Each factory function (e.g., getAudioInputIdsError, audioCheckerError) returns
|
|
21
|
+
* a plain object with:
|
|
22
|
+
* - `type` → error type constant
|
|
23
|
+
* - `action` → action constant
|
|
24
|
+
* - `target` → target constant (optional)
|
|
25
|
+
* - `agentName` → name of the agent/tool that produced the error
|
|
26
|
+
* - `beatIndex` → index of the beat in the current script
|
|
27
|
+
* - `fileName` → file name or identifier (if relevant)
|
|
28
|
+
*
|
|
29
|
+
* ## i18n Integration
|
|
30
|
+
* - The UI consumes the generated `{ action, type, target, ... }` to build the i18n key.
|
|
31
|
+
* - Example:
|
|
32
|
+
* notify.error.movie.fileNotExist.audioFile
|
|
33
|
+
* - Interpolation values (beatIndex, fileName, etc.) are passed as `data` for
|
|
34
|
+
* translations.
|
|
35
|
+
*
|
|
36
|
+
* ## Notes
|
|
37
|
+
* - If `target` is not required, omit it.
|
|
38
|
+
* - When adding new actions/types/targets, check for consistency with existing ones.
|
|
39
|
+
* - Use descriptive but concise names to avoid translation conflicts.
|
|
40
|
+
*
|
|
41
|
+
* ## Usage in Error Handling
|
|
42
|
+
* - When throwing manually:
|
|
43
|
+
* `throw new Error("message", { cause });`
|
|
44
|
+
* - When using assertions:
|
|
45
|
+
* `assert(condition, message, false, causeObject);`
|
|
46
|
+
* (set the 3rd parameter to `false` and pass the `cause` object as the 4th)
|
|
47
|
+
*/
|
|
48
|
+
// Error Types
|
|
1
49
|
export const urlFileNotFoundType = "urlFileNotFound";
|
|
2
50
|
export const fileNotExistType = "fileNotExist";
|
|
3
51
|
export const unknownMediaType = "unknownMedia";
|
|
4
52
|
export const sourceUndefinedType = "undefinedSourceType";
|
|
53
|
+
export const apiErrorType = "apiError";
|
|
54
|
+
export const apiKeyMissingType = "apiKeyMissing";
|
|
55
|
+
export const invalidResponseType = "invalidResponse";
|
|
56
|
+
// Actions
|
|
5
57
|
export const movieAction = "movie";
|
|
6
58
|
export const imageAction = "images";
|
|
7
59
|
export const audioAction = "audio";
|
|
8
60
|
export const imageReferenceAction = "imageReference";
|
|
61
|
+
export const translateAction = "translate";
|
|
62
|
+
// Targets
|
|
9
63
|
export const audioFileTarget = "audioFile";
|
|
10
64
|
export const imageFileTarget = "imageFile";
|
|
11
65
|
export const movieFileTarget = "movieFile";
|
|
12
66
|
export const videoSourceTarget = "videoSource";
|
|
13
67
|
export const audioSourceTarget = "audioSource";
|
|
14
68
|
export const codeTextTarget = "codeText";
|
|
15
|
-
|
|
69
|
+
// Agent File Not Exist Errors
|
|
70
|
+
export const agentFileNotExistError = (agentName, action, target, fileName, beatIndex) => {
|
|
16
71
|
return {
|
|
17
72
|
type: fileNotExistType,
|
|
18
|
-
action
|
|
19
|
-
target
|
|
20
|
-
agentName
|
|
21
|
-
beatIndex: index,
|
|
73
|
+
action,
|
|
74
|
+
target,
|
|
75
|
+
agentName,
|
|
22
76
|
fileName,
|
|
77
|
+
...(beatIndex !== undefined && { beatIndex }),
|
|
23
78
|
};
|
|
24
79
|
};
|
|
80
|
+
export const getAudioInputIdsError = (index, fileName) => {
|
|
81
|
+
return agentFileNotExistError("combineAudioFiles", movieAction, audioFileTarget, fileName, index);
|
|
82
|
+
};
|
|
25
83
|
export const audioCheckerError = (index, fileName) => {
|
|
26
|
-
return
|
|
27
|
-
type: fileNotExistType,
|
|
28
|
-
action: imageAction,
|
|
29
|
-
target: imageFileTarget,
|
|
30
|
-
agentName: "audioChecker",
|
|
31
|
-
beatIndex: index,
|
|
32
|
-
fileName,
|
|
33
|
-
};
|
|
84
|
+
return agentFileNotExistError("audioChecker", imageAction, imageFileTarget, fileName, index);
|
|
34
85
|
};
|
|
35
86
|
export const createVideoFileError = (index, fileName) => {
|
|
36
|
-
return
|
|
37
|
-
type: fileNotExistType,
|
|
38
|
-
action: movieAction,
|
|
39
|
-
target: imageFileTarget,
|
|
40
|
-
agentName: "createVideo",
|
|
41
|
-
beatIndex: index,
|
|
42
|
-
fileName,
|
|
43
|
-
};
|
|
87
|
+
return agentFileNotExistError("createVideo", movieAction, imageFileTarget, fileName, index);
|
|
44
88
|
};
|
|
45
89
|
// undefinedSource
|
|
46
90
|
export const createVideoSourceError = (index) => {
|
|
@@ -105,3 +149,44 @@ export const imagePluginUnknownMediaError = (imageType) => {
|
|
|
105
149
|
target: imageType,
|
|
106
150
|
};
|
|
107
151
|
};
|
|
152
|
+
// Agent API Key Errors
|
|
153
|
+
export const apiKeyMissingError = (agentName, action, envVarName) => {
|
|
154
|
+
return {
|
|
155
|
+
type: apiKeyMissingType,
|
|
156
|
+
action,
|
|
157
|
+
agentName,
|
|
158
|
+
envVarName,
|
|
159
|
+
};
|
|
160
|
+
};
|
|
161
|
+
// Agent API/Generation Errors
|
|
162
|
+
export const agentGenerationError = (agentName, action, target, beatIndex) => {
|
|
163
|
+
return {
|
|
164
|
+
type: apiErrorType,
|
|
165
|
+
action,
|
|
166
|
+
target,
|
|
167
|
+
agentName,
|
|
168
|
+
...(beatIndex !== undefined && { beatIndex }),
|
|
169
|
+
};
|
|
170
|
+
};
|
|
171
|
+
// Agent Invalid Response Errors
|
|
172
|
+
export const agentInvalidResponseError = (agentName, action, target, beatIndex) => {
|
|
173
|
+
return {
|
|
174
|
+
type: invalidResponseType,
|
|
175
|
+
action,
|
|
176
|
+
target,
|
|
177
|
+
agentName,
|
|
178
|
+
...(beatIndex !== undefined && { beatIndex }),
|
|
179
|
+
};
|
|
180
|
+
};
|
|
181
|
+
// Translation Errors
|
|
182
|
+
export const translateApiKeyMissingError = () => {
|
|
183
|
+
return {
|
|
184
|
+
type: apiKeyMissingType,
|
|
185
|
+
action: translateAction,
|
|
186
|
+
agentName: "translate",
|
|
187
|
+
envVarName: "OPENAI_API_KEY",
|
|
188
|
+
};
|
|
189
|
+
};
|
|
190
|
+
export const hasCause = (err) => {
|
|
191
|
+
return err instanceof Error && "cause" in err;
|
|
192
|
+
};
|