mulmocast 2.8.1 → 2.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/actions/image_agents.d.ts +20 -0
- package/lib/actions/image_agents.js +83 -15
- package/lib/actions/image_references.d.ts +3 -0
- package/lib/actions/image_references.js +55 -33
- package/lib/actions/images.d.ts +30 -0
- package/lib/actions/images.js +16 -4
- package/lib/methods/index.d.ts +1 -0
- package/lib/methods/index.js +1 -0
- package/lib/methods/mulmo_image_prompt_media.d.ts +14 -0
- package/lib/methods/mulmo_image_prompt_media.js +45 -0
- package/lib/types/provider2agent.d.ts +15 -0
- package/lib/types/provider2agent.js +31 -0
- package/lib/types/schema.d.ts +192 -0
- package/lib/types/schema.js +42 -12
- package/lib/types/type.d.ts +2 -1
- package/lib/utils/context.d.ts +70 -0
- package/lib/utils/error_cause.d.ts +12 -0
- package/lib/utils/error_cause.js +15 -0
- package/package.json +3 -3
|
@@ -24,6 +24,9 @@ type ImagePreprocessAgentReturnValue = {
|
|
|
24
24
|
};
|
|
25
25
|
firstFrameImagePath?: string;
|
|
26
26
|
lastFrameImagePath?: string;
|
|
27
|
+
firstFrameIsBeatImage?: boolean;
|
|
28
|
+
lastFrameIsBeatImage?: boolean;
|
|
29
|
+
frameFillColor?: string;
|
|
27
30
|
movieReferenceImages?: {
|
|
28
31
|
imagePath: string;
|
|
29
32
|
referenceType: "ASSET" | "STYLE";
|
|
@@ -62,6 +65,23 @@ export declare const imagePreprocessAgent: (namedInputs: {
|
|
|
62
65
|
index: number;
|
|
63
66
|
imageRefs?: Record<string, string>;
|
|
64
67
|
}) => Promise<ImagePreprocessAgentResponse>;
|
|
68
|
+
export type BeatFramePreprocessorInput = {
|
|
69
|
+
imagePath?: string;
|
|
70
|
+
lastFrameImagePath?: string;
|
|
71
|
+
firstFrameIsBeatImage?: boolean;
|
|
72
|
+
lastFrameIsBeatImage?: boolean;
|
|
73
|
+
frameFillColor?: string;
|
|
74
|
+
referenceImageForMovie?: string;
|
|
75
|
+
};
|
|
76
|
+
export declare const beatFrameResolverAgent: (namedInputs: {
|
|
77
|
+
context: MulmoStudioContext;
|
|
78
|
+
beat: MulmoBeat;
|
|
79
|
+
index: number;
|
|
80
|
+
preprocessor?: BeatFramePreprocessorInput;
|
|
81
|
+
}) => Promise<{
|
|
82
|
+
lastFrameImagePath: string | undefined;
|
|
83
|
+
referenceImageForMovie: string | undefined;
|
|
84
|
+
}>;
|
|
65
85
|
export declare const imagePluginAgent: (namedInputs: {
|
|
66
86
|
context: MulmoStudioContext;
|
|
67
87
|
beat: MulmoBeat;
|
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
import fs from "fs";
|
|
2
2
|
import { createHash } from "crypto";
|
|
3
3
|
import { GraphAILogger } from "graphai";
|
|
4
|
+
import { BEAT_IMAGE_SENTINEL, ImageMediaType, beatImageTypesWithoutOwnStill } from "../types/schema.js";
|
|
4
5
|
import { MulmoPresentationStyleMethods, MulmoStudioContextMethods, MulmoBeatMethods, MulmoMediaSourceMethods } from "../methods/index.js";
|
|
5
6
|
import { getBeatPngImagePath, getBeatMoviePaths, getBeatAnimatedVideoPath, getAudioFilePath, getGroupedAudioFilePath, getReferenceImagePath, } from "../utils/file.js";
|
|
6
7
|
import { ffmpegGetImageDimensions, padImageToCanvas } from "../utils/ffmpeg_utils.js";
|
|
8
|
+
import { beatImageSentinelError, bothFramesTarget, missingBeatImageTarget, unsupportedImageTypeTarget, missingFirstFrameTarget } from "../utils/error_cause.js";
|
|
7
9
|
import { imagePrompt, htmlImageSystemPrompt } from "../utils/prompt.js";
|
|
8
10
|
import { renderHTMLToImage } from "../utils/html_render.js";
|
|
9
11
|
import { beatId } from "../utils/utils.js";
|
|
@@ -71,6 +73,64 @@ export const conformFrameImageToCanvas = async (context, imageName, imagePath, f
|
|
|
71
73
|
conformingInFlight.delete(destPath);
|
|
72
74
|
}
|
|
73
75
|
};
|
|
76
|
+
const validateBeatImageSentinel = (movieParams, beat, index) => {
|
|
77
|
+
if (movieParams.firstFrameImageName === BEAT_IMAGE_SENTINEL && movieParams.lastFrameImageName === BEAT_IMAGE_SENTINEL) {
|
|
78
|
+
throw new Error(`${BEAT_IMAGE_SENTINEL} cannot be used for both firstFrameImageName and lastFrameImageName. beat: ${index}`, {
|
|
79
|
+
cause: beatImageSentinelError(index, bothFramesTarget),
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
if (!beat.imagePrompt && !beat.image) {
|
|
83
|
+
throw new Error(`${BEAT_IMAGE_SENTINEL} requires the beat to have an imagePrompt or image. beat: ${index}`, {
|
|
84
|
+
cause: beatImageSentinelError(index, missingBeatImageTarget),
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
if (beat.image && beatImageTypesWithoutOwnStill.includes(beat.image.type)) {
|
|
88
|
+
throw new Error(`${BEAT_IMAGE_SENTINEL} cannot be used with image type "${beat.image.type}" (the beat image is not its own still). beat: ${index}`, {
|
|
89
|
+
cause: beatImageSentinelError(index, unsupportedImageTypeTarget),
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
// The implicit first frame (the raw beat image) is not conformed to the canvas, so pairing
|
|
93
|
+
// it with a conformed $beatImage last frame would feed mismatched frame sizes to strict
|
|
94
|
+
// image-to-video models (e.g. wan-2.2-i2v-fast). Require an explicit, conformed first frame.
|
|
95
|
+
if (movieParams.lastFrameImageName === BEAT_IMAGE_SENTINEL && !movieParams.firstFrameImageName) {
|
|
96
|
+
throw new Error(`${BEAT_IMAGE_SENTINEL} as lastFrameImageName requires firstFrameImageName. beat: ${index}`, {
|
|
97
|
+
cause: beatImageSentinelError(index, missingFirstFrameTarget),
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
};
|
|
101
|
+
// Resolve firstFrameImageName/lastFrameImageName. Named refs conform to the canvas here;
|
|
102
|
+
// the $beatImage sentinel only sets flags (the beat image does not exist yet at preprocess
|
|
103
|
+
// time) and is resolved by beatFrameResolverAgent after image generation.
|
|
104
|
+
const applyFrameImageParams = async (returnValue, context, beat, index, movieParams, imageRefs) => {
|
|
105
|
+
const frameFillColor = movieParams.frameFillColor ?? "black";
|
|
106
|
+
// A style-level $beatImage merges into every beat; beats that generate no movie
|
|
107
|
+
// (no moviePrompt) ignore the sentinel instead of failing validation.
|
|
108
|
+
const sentinelActive = Boolean(beat.moviePrompt);
|
|
109
|
+
const firstFrameImageName = !sentinelActive && movieParams.firstFrameImageName === BEAT_IMAGE_SENTINEL ? undefined : movieParams.firstFrameImageName;
|
|
110
|
+
const lastFrameImageName = !sentinelActive && movieParams.lastFrameImageName === BEAT_IMAGE_SENTINEL ? undefined : movieParams.lastFrameImageName;
|
|
111
|
+
if (firstFrameImageName === BEAT_IMAGE_SENTINEL || lastFrameImageName === BEAT_IMAGE_SENTINEL) {
|
|
112
|
+
validateBeatImageSentinel(movieParams, beat, index);
|
|
113
|
+
returnValue.frameFillColor = frameFillColor;
|
|
114
|
+
}
|
|
115
|
+
if (firstFrameImageName === BEAT_IMAGE_SENTINEL) {
|
|
116
|
+
returnValue.firstFrameIsBeatImage = true;
|
|
117
|
+
}
|
|
118
|
+
else if (firstFrameImageName && imageRefs) {
|
|
119
|
+
const firstFramePath = imageRefs[firstFrameImageName];
|
|
120
|
+
if (firstFramePath) {
|
|
121
|
+
returnValue.firstFrameImagePath = await conformFrameImageToCanvas(context, firstFrameImageName, firstFramePath, frameFillColor);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
if (lastFrameImageName === BEAT_IMAGE_SENTINEL) {
|
|
125
|
+
returnValue.lastFrameIsBeatImage = true;
|
|
126
|
+
}
|
|
127
|
+
else if (lastFrameImageName && imageRefs) {
|
|
128
|
+
const lastFramePath = imageRefs[lastFrameImageName];
|
|
129
|
+
if (lastFramePath) {
|
|
130
|
+
returnValue.lastFrameImagePath = await conformFrameImageToCanvas(context, lastFrameImageName, lastFramePath, frameFillColor);
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
};
|
|
74
134
|
export const imagePreprocessAgent = async (namedInputs) => {
|
|
75
135
|
const { context, beat, index, imageRefs } = namedInputs;
|
|
76
136
|
const studioBeat = context.studio.beats[index];
|
|
@@ -88,7 +148,7 @@ export const imagePreprocessAgent = async (namedInputs) => {
|
|
|
88
148
|
movieFile: beat.moviePrompt ? moviePaths.movieFile : undefined,
|
|
89
149
|
beatDuration: beat.duration ?? studioBeat?.duration,
|
|
90
150
|
};
|
|
91
|
-
const isMovie = Boolean(beat.moviePrompt || beat?.image?.type ===
|
|
151
|
+
const isMovie = Boolean(beat.moviePrompt || beat?.image?.type === ImageMediaType.Movie);
|
|
92
152
|
if (beat.soundEffectPrompt) {
|
|
93
153
|
if (isMovie) {
|
|
94
154
|
returnValue.soundEffectAgentInfo = MulmoPresentationStyleMethods.getSoundEffectAgentInfo(context.presentationStyle, beat);
|
|
@@ -130,19 +190,7 @@ export const imagePreprocessAgent = async (namedInputs) => {
|
|
|
130
190
|
// Shallow-merge like getMovieAgentInfo: beat-level fields override style-level fields
|
|
131
191
|
// individually, so a beat setting only lastFrameImageName keeps the global frameFillColor.
|
|
132
192
|
const movieParams = { ...context.presentationStyle.movieParams, ...beat.movieParams };
|
|
133
|
-
|
|
134
|
-
if (movieParams.firstFrameImageName && imageRefs) {
|
|
135
|
-
const firstFramePath = imageRefs[movieParams.firstFrameImageName];
|
|
136
|
-
if (firstFramePath) {
|
|
137
|
-
returnValue.firstFrameImagePath = await conformFrameImageToCanvas(context, movieParams.firstFrameImageName, firstFramePath, frameFillColor);
|
|
138
|
-
}
|
|
139
|
-
}
|
|
140
|
-
if (movieParams.lastFrameImageName && imageRefs) {
|
|
141
|
-
const lastFramePath = imageRefs[movieParams.lastFrameImageName];
|
|
142
|
-
if (lastFramePath) {
|
|
143
|
-
returnValue.lastFrameImagePath = await conformFrameImageToCanvas(context, movieParams.lastFrameImageName, lastFramePath, frameFillColor);
|
|
144
|
-
}
|
|
145
|
-
}
|
|
193
|
+
await applyFrameImageParams(returnValue, context, beat, index, movieParams, imageRefs);
|
|
146
194
|
if (movieParams.referenceImages && imageRefs) {
|
|
147
195
|
returnValue.movieReferenceImages = movieParams.referenceImages
|
|
148
196
|
.map((ref) => {
|
|
@@ -156,7 +204,7 @@ export const imagePreprocessAgent = async (namedInputs) => {
|
|
|
156
204
|
const pluginPath = plugin.path({ beat, context, imagePath, ...htmlStyle(context, beat) });
|
|
157
205
|
const markdown = plugin.markdown ? plugin.markdown({ beat, context, imagePath, ...htmlStyle(context, beat) }) : undefined;
|
|
158
206
|
const html = plugin.html ? await plugin.html({ beat, context, imagePath, ...htmlStyle(context, beat) }) : undefined;
|
|
159
|
-
const isTypeMovie = beat.image.type ===
|
|
207
|
+
const isTypeMovie = beat.image.type === ImageMediaType.Movie;
|
|
160
208
|
const isAnimatedHtml = MulmoBeatMethods.isAnimatedHtmlTailwind(beat);
|
|
161
209
|
// animation and moviePrompt cannot be used together
|
|
162
210
|
if (isAnimatedHtml && beat.moviePrompt) {
|
|
@@ -203,6 +251,26 @@ export const imagePreprocessAgent = async (namedInputs) => {
|
|
|
203
251
|
const movieFirstFramePath = returnValue.firstFrameImagePath ?? imagePath;
|
|
204
252
|
return { ...returnValue, imagePath, referenceImageForMovie: movieFirstFramePath, imageAgentInfo, prompt, referenceImages };
|
|
205
253
|
};
|
|
254
|
+
// Resolves $beatImage frame references after the beat's image exists. imagePreprocessAgent runs
|
|
255
|
+
// before image generation, so it can only flag the sentinel; this agent runs after
|
|
256
|
+
// imageGenerator/imagePlugin and conforms the now-existing beat image to the canvas.
|
|
257
|
+
// movieGenerator consumes only referenceImageForMovie (the movie's first frame / i2v input)
|
|
258
|
+
// and lastFrameImagePath.
|
|
259
|
+
export const beatFrameResolverAgent = async (namedInputs) => {
|
|
260
|
+
const { context, beat, index, preprocessor } = namedInputs;
|
|
261
|
+
if (preprocessor?.imagePath && (preprocessor.firstFrameIsBeatImage || preprocessor.lastFrameIsBeatImage)) {
|
|
262
|
+
const name = `${beatId(beat.id, index)}_beatImage`;
|
|
263
|
+
const conformed = await conformFrameImageToCanvas(context, name, preprocessor.imagePath, preprocessor.frameFillColor ?? "black");
|
|
264
|
+
return {
|
|
265
|
+
lastFrameImagePath: preprocessor.lastFrameIsBeatImage ? conformed : preprocessor.lastFrameImagePath,
|
|
266
|
+
referenceImageForMovie: preprocessor.firstFrameIsBeatImage ? conformed : preprocessor.referenceImageForMovie,
|
|
267
|
+
};
|
|
268
|
+
}
|
|
269
|
+
return {
|
|
270
|
+
lastFrameImagePath: preprocessor?.lastFrameImagePath,
|
|
271
|
+
referenceImageForMovie: preprocessor?.referenceImageForMovie,
|
|
272
|
+
};
|
|
273
|
+
};
|
|
206
274
|
export const imagePluginAgent = async (namedInputs) => {
|
|
207
275
|
const { context, beat, index, imageRefs, movieRefs } = namedInputs;
|
|
208
276
|
const { imagePath } = getBeatPngImagePath(context, index);
|
|
@@ -1,10 +1,13 @@
|
|
|
1
1
|
import { MulmoStudioContext, MulmoBeat, MulmoImagePromptMedia } from "../types/index.js";
|
|
2
|
+
import type { ResolvedImageReference } from "../methods/mulmo_image_prompt_media.js";
|
|
3
|
+
export type { ResolvedImageReference } from "../methods/mulmo_image_prompt_media.js";
|
|
2
4
|
export declare const generateReferenceImage: (inputs: {
|
|
3
5
|
context: MulmoStudioContext;
|
|
4
6
|
key: string;
|
|
5
7
|
index: number;
|
|
6
8
|
image: MulmoImagePromptMedia;
|
|
7
9
|
referenceImagePath?: string;
|
|
10
|
+
references?: ResolvedImageReference[];
|
|
8
11
|
force?: boolean;
|
|
9
12
|
}) => Promise<string>;
|
|
10
13
|
export type MediaRefs = {
|
|
@@ -1,10 +1,36 @@
|
|
|
1
|
-
import { GraphAI, GraphAILogger } from "graphai";
|
|
1
|
+
import { GraphAI, GraphAILogger, graphDataLatestVersion } from "graphai";
|
|
2
2
|
import { getReferenceImagePath } from "../utils/file.js";
|
|
3
3
|
import { imageGraphOption } from "./graph_option.js";
|
|
4
4
|
import { createUsageCallback } from "../utils/usage_callback.js";
|
|
5
|
-
import { MulmoPresentationStyleMethods, MulmoMediaSourceMethods } from "../methods/index.js";
|
|
5
|
+
import { MulmoPresentationStyleMethods, MulmoMediaSourceMethods, MulmoImagePromptMediaMethods } from "../methods/index.js";
|
|
6
|
+
import { ImageMediaType, } from "../types/index.js";
|
|
6
7
|
import { imageOpenaiAgent, mediaMockAgent, imageGenAIAgent, imageReplicateAgent, movieGenAIAgent, movieReplicateAgent } from "../agents/index.js";
|
|
7
8
|
import { agentGenerationError, imageReferenceAction, imageFileTarget, movieFileTarget } from "../utils/error_cause.js";
|
|
9
|
+
// Resolve the normalized reference list to local file paths (with labels), in reference order.
|
|
10
|
+
// imageKey is the imageRefs key of the image being generated (used for warnings and url
|
|
11
|
+
// download file naming). A named reference that is missing from imageRefs warns and is
|
|
12
|
+
// skipped (existing behavior).
|
|
13
|
+
const resolveImageReferences = async (context, imageKey, image, imageRefs) => {
|
|
14
|
+
const imageReferences = MulmoImagePromptMediaMethods.getReferences(image);
|
|
15
|
+
const resolved = await Promise.all(imageReferences.map(async (ref, i) => {
|
|
16
|
+
if (ref.name !== undefined) {
|
|
17
|
+
const path = imageRefs?.[ref.name];
|
|
18
|
+
if (!path) {
|
|
19
|
+
GraphAILogger.warn(`imagePrompt "${imageKey}": reference "${ref.name}" not found in imageRefs — generating without this reference`);
|
|
20
|
+
return undefined;
|
|
21
|
+
}
|
|
22
|
+
return { path, label: ref.label };
|
|
23
|
+
}
|
|
24
|
+
if (ref.source !== undefined) {
|
|
25
|
+
// The legacy referenceImage keeps the bare imageKey so url downloads reuse their historical cache file name.
|
|
26
|
+
const sourceKey = ref.legacy ? imageKey : `${imageKey}_ref${i}`;
|
|
27
|
+
return { path: await MulmoMediaSourceMethods.imageReference(ref.source, context, sourceKey), label: ref.label };
|
|
28
|
+
}
|
|
29
|
+
return undefined;
|
|
30
|
+
}));
|
|
31
|
+
const references = resolved.filter((ref) => ref !== undefined);
|
|
32
|
+
return references.length > 0 ? references : undefined;
|
|
33
|
+
};
|
|
8
34
|
// public api
|
|
9
35
|
// Application may call this function directly to generate reference image.
|
|
10
36
|
export const generateReferenceImage = async (inputs) => {
|
|
@@ -12,11 +38,13 @@ export const generateReferenceImage = async (inputs) => {
|
|
|
12
38
|
const imagePath = getReferenceImagePath(context, key, "png");
|
|
13
39
|
// generate image
|
|
14
40
|
const imageAgentInfo = MulmoPresentationStyleMethods.getImageAgentInfo(context.presentationStyle);
|
|
15
|
-
const
|
|
41
|
+
const references = MulmoImagePromptMediaMethods.limitReferences(inputs.references ?? (referenceImagePath ? [{ path: referenceImagePath }] : undefined), imageAgentInfo, key);
|
|
42
|
+
const preamble = MulmoImagePromptMediaMethods.buildReferencePreamble((references ?? []).map((ref) => ref.label));
|
|
43
|
+
const prompt = `${preamble}${image.prompt}\n${imageAgentInfo.imageParams.style || ""}`;
|
|
16
44
|
GraphAILogger.info(`Generating reference image for ${key}: ${prompt}`);
|
|
17
|
-
const referenceImages =
|
|
45
|
+
const referenceImages = references ? references.map((ref) => ref.path) : undefined;
|
|
18
46
|
const image_graph_data = {
|
|
19
|
-
version:
|
|
47
|
+
version: graphDataLatestVersion,
|
|
20
48
|
nodes: {
|
|
21
49
|
imageGenerator: {
|
|
22
50
|
agent: imageAgentInfo.agent,
|
|
@@ -62,33 +90,30 @@ export const getMediaRefs = async (context) => {
|
|
|
62
90
|
}
|
|
63
91
|
const imageRefs = {};
|
|
64
92
|
const movieRefs = {};
|
|
65
|
-
// Stage 1: resolve non-referencing entries (image, imagePrompt without
|
|
93
|
+
// Stage 1: resolve non-referencing entries (image, imagePrompt without named references, movie)
|
|
66
94
|
await Promise.all(Object.keys(images)
|
|
67
95
|
.sort()
|
|
68
96
|
.map(async (key, index) => {
|
|
69
97
|
const image = images[key];
|
|
70
|
-
if (image.type ===
|
|
71
|
-
const
|
|
72
|
-
imageRefs[key] = await generateReferenceImage({ context, key, index, image,
|
|
98
|
+
if (image.type === ImageMediaType.ImagePrompt && !MulmoImagePromptMediaMethods.hasNamedReference(image)) {
|
|
99
|
+
const references = await resolveImageReferences(context, key, image);
|
|
100
|
+
imageRefs[key] = await generateReferenceImage({ context, key, index, image, references, force: false });
|
|
73
101
|
}
|
|
74
|
-
else if (image.type ===
|
|
102
|
+
else if (image.type === ImageMediaType.Image) {
|
|
75
103
|
imageRefs[key] = await MulmoMediaSourceMethods.imageReference(image.source, context, key);
|
|
76
104
|
}
|
|
77
|
-
else if (image.type ===
|
|
105
|
+
else if (image.type === ImageMediaType.Movie) {
|
|
78
106
|
movieRefs[key] = await resolveMovieReference(image, context, key);
|
|
79
107
|
}
|
|
80
108
|
}));
|
|
81
|
-
// Stage 2: resolve imagePrompt with
|
|
109
|
+
// Stage 2: resolve imagePrompt with named references (depends on Stage 1 results)
|
|
82
110
|
await Promise.all(Object.keys(images)
|
|
83
111
|
.sort()
|
|
84
112
|
.map(async (key, index) => {
|
|
85
113
|
const image = images[key];
|
|
86
|
-
if (image.type ===
|
|
87
|
-
const
|
|
88
|
-
|
|
89
|
-
GraphAILogger.warn(`imagePrompt "${key}": referenceImageName "${image.referenceImageName}" not found in imageRefs — generating without reference`);
|
|
90
|
-
}
|
|
91
|
-
imageRefs[key] = await generateReferenceImage({ context, key, index, image, referenceImagePath: refPath, force: false });
|
|
114
|
+
if (image.type === ImageMediaType.ImagePrompt && MulmoImagePromptMediaMethods.hasNamedReference(image)) {
|
|
115
|
+
const references = await resolveImageReferences(context, key, image, imageRefs);
|
|
116
|
+
imageRefs[key] = await generateReferenceImage({ context, key, index, image, references, force: false });
|
|
92
117
|
}
|
|
93
118
|
}));
|
|
94
119
|
return { imageRefs, movieRefs };
|
|
@@ -102,7 +127,7 @@ const generateReferenceMovie = async (inputs) => {
|
|
|
102
127
|
const movieAgentInfo = MulmoPresentationStyleMethods.getMovieAgentInfo(context.presentationStyle);
|
|
103
128
|
GraphAILogger.info(`Generating reference movie for ${key}: ${moviePrompt.prompt}`);
|
|
104
129
|
const movie_graph_data = {
|
|
105
|
-
version:
|
|
130
|
+
version: graphDataLatestVersion,
|
|
106
131
|
nodes: {
|
|
107
132
|
movieGenerator: {
|
|
108
133
|
agent: movieAgentInfo.agent,
|
|
@@ -145,45 +170,42 @@ const generateReferenceMovie = async (inputs) => {
|
|
|
145
170
|
const resolveLocalRefs = async (context, images, beatIndex, globalImageRefs) => {
|
|
146
171
|
const localImageRefs = {};
|
|
147
172
|
const localMovieRefs = {};
|
|
148
|
-
// Stage 1: image, imagePrompt (without
|
|
173
|
+
// Stage 1: image, imagePrompt (without named references), movie (parallel)
|
|
149
174
|
await Promise.all(Object.keys(images)
|
|
150
175
|
.sort()
|
|
151
176
|
.map(async (key, i) => {
|
|
152
177
|
const entry = images[key];
|
|
153
|
-
if (entry.type ===
|
|
154
|
-
const
|
|
178
|
+
if (entry.type === ImageMediaType.ImagePrompt && !MulmoImagePromptMediaMethods.hasNamedReference(entry)) {
|
|
179
|
+
const references = await resolveImageReferences(context, key, entry);
|
|
155
180
|
localImageRefs[key] = await generateReferenceImage({
|
|
156
181
|
context,
|
|
157
182
|
key,
|
|
158
183
|
index: beatIndex * 100 + i,
|
|
159
184
|
image: entry,
|
|
160
|
-
|
|
185
|
+
references,
|
|
161
186
|
});
|
|
162
187
|
}
|
|
163
|
-
else if (entry.type ===
|
|
188
|
+
else if (entry.type === ImageMediaType.Image) {
|
|
164
189
|
localImageRefs[key] = await MulmoMediaSourceMethods.imageReference(entry.source, context, key);
|
|
165
190
|
}
|
|
166
|
-
else if (entry.type ===
|
|
191
|
+
else if (entry.type === ImageMediaType.Movie) {
|
|
167
192
|
localMovieRefs[key] = await resolveMovieReference(entry, context, key);
|
|
168
193
|
}
|
|
169
194
|
}));
|
|
170
|
-
// Stage 2: imagePrompt with
|
|
195
|
+
// Stage 2: imagePrompt with named references (depends on Stage 1)
|
|
171
196
|
const combinedImageRefsForImagePrompt = { ...globalImageRefs, ...localImageRefs };
|
|
172
197
|
await Promise.all(Object.keys(images)
|
|
173
198
|
.sort()
|
|
174
199
|
.map(async (key, i) => {
|
|
175
200
|
const entry = images[key];
|
|
176
|
-
if (entry.type ===
|
|
177
|
-
const
|
|
178
|
-
if (!refPath) {
|
|
179
|
-
GraphAILogger.warn(`imagePrompt "${key}": referenceImageName "${entry.referenceImageName}" not found — generating without reference`);
|
|
180
|
-
}
|
|
201
|
+
if (entry.type === ImageMediaType.ImagePrompt && MulmoImagePromptMediaMethods.hasNamedReference(entry)) {
|
|
202
|
+
const references = await resolveImageReferences(context, key, entry, combinedImageRefsForImagePrompt);
|
|
181
203
|
localImageRefs[key] = await generateReferenceImage({
|
|
182
204
|
context,
|
|
183
205
|
key,
|
|
184
206
|
index: beatIndex * 100 + i,
|
|
185
207
|
image: entry,
|
|
186
|
-
|
|
208
|
+
references,
|
|
187
209
|
});
|
|
188
210
|
}
|
|
189
211
|
}));
|
|
@@ -193,7 +215,7 @@ const resolveLocalRefs = async (context, images, beatIndex, globalImageRefs) =>
|
|
|
193
215
|
.sort()
|
|
194
216
|
.map(async (key, i) => {
|
|
195
217
|
const entry = images[key];
|
|
196
|
-
if (entry.type ===
|
|
218
|
+
if (entry.type === ImageMediaType.MoviePrompt) {
|
|
197
219
|
const refImagePath = entry.imageName ? combinedImageRefs[entry.imageName] : undefined;
|
|
198
220
|
localMovieRefs[key] = await generateReferenceMovie({
|
|
199
221
|
context,
|
package/lib/actions/images.d.ts
CHANGED
|
@@ -72,6 +72,9 @@ export declare const beat_graph_data: {
|
|
|
72
72
|
};
|
|
73
73
|
firstFrameImagePath?: string;
|
|
74
74
|
lastFrameImagePath?: string;
|
|
75
|
+
firstFrameIsBeatImage?: boolean;
|
|
76
|
+
lastFrameIsBeatImage?: boolean;
|
|
77
|
+
frameFillColor?: string;
|
|
75
78
|
movieReferenceImages?: {
|
|
76
79
|
imagePath: string;
|
|
77
80
|
referenceType: "ASSET" | "STYLE";
|
|
@@ -103,6 +106,9 @@ export declare const beat_graph_data: {
|
|
|
103
106
|
};
|
|
104
107
|
firstFrameImagePath?: string;
|
|
105
108
|
lastFrameImagePath?: string;
|
|
109
|
+
firstFrameIsBeatImage?: boolean;
|
|
110
|
+
lastFrameIsBeatImage?: boolean;
|
|
111
|
+
frameFillColor?: string;
|
|
106
112
|
movieReferenceImages?: {
|
|
107
113
|
imagePath: string;
|
|
108
114
|
referenceType: "ASSET" | "STYLE";
|
|
@@ -145,6 +151,9 @@ export declare const beat_graph_data: {
|
|
|
145
151
|
};
|
|
146
152
|
firstFrameImagePath?: string;
|
|
147
153
|
lastFrameImagePath?: string;
|
|
154
|
+
firstFrameIsBeatImage?: boolean;
|
|
155
|
+
lastFrameIsBeatImage?: boolean;
|
|
156
|
+
frameFillColor?: string;
|
|
148
157
|
movieReferenceImages?: {
|
|
149
158
|
imagePath: string;
|
|
150
159
|
referenceType: "ASSET" | "STYLE";
|
|
@@ -179,6 +188,9 @@ export declare const beat_graph_data: {
|
|
|
179
188
|
};
|
|
180
189
|
firstFrameImagePath?: string;
|
|
181
190
|
lastFrameImagePath?: string;
|
|
191
|
+
firstFrameIsBeatImage?: boolean;
|
|
192
|
+
lastFrameIsBeatImage?: boolean;
|
|
193
|
+
frameFillColor?: string;
|
|
182
194
|
movieReferenceImages?: {
|
|
183
195
|
imagePath: string;
|
|
184
196
|
referenceType: "ASSET" | "STYLE";
|
|
@@ -298,6 +310,24 @@ export declare const beat_graph_data: {
|
|
|
298
310
|
};
|
|
299
311
|
defaultValue: {};
|
|
300
312
|
};
|
|
313
|
+
frameResolver: {
|
|
314
|
+
agent: (namedInputs: {
|
|
315
|
+
context: MulmoStudioContext;
|
|
316
|
+
beat: import("../types/type.js").MulmoBeat;
|
|
317
|
+
index: number;
|
|
318
|
+
preprocessor?: import("./image_agents.js").BeatFramePreprocessorInput;
|
|
319
|
+
}) => Promise<{
|
|
320
|
+
lastFrameImagePath: string | undefined;
|
|
321
|
+
referenceImageForMovie: string | undefined;
|
|
322
|
+
}>;
|
|
323
|
+
inputs: {
|
|
324
|
+
onComplete: string[];
|
|
325
|
+
context: string;
|
|
326
|
+
beat: string;
|
|
327
|
+
index: string;
|
|
328
|
+
preprocessor: string;
|
|
329
|
+
};
|
|
330
|
+
};
|
|
301
331
|
movieGenerator: {
|
|
302
332
|
if: string;
|
|
303
333
|
agent: string;
|
package/lib/actions/images.js
CHANGED
|
@@ -13,7 +13,7 @@ import { getOutputStudioFilePath, mkdir } from "../utils/file.js";
|
|
|
13
13
|
import { audioCheckerError } from "../utils/error_cause.js";
|
|
14
14
|
import { extractImageFromMovie, ffmpegGetMediaDuration, trimMusic } from "../utils/ffmpeg_utils.js";
|
|
15
15
|
import { getMediaRefs, resolveBeatLocalRefs } from "./image_references.js";
|
|
16
|
-
import { imagePreprocessAgent, imagePluginAgent, htmlImageGeneratorAgent } from "./image_agents.js";
|
|
16
|
+
import { imagePreprocessAgent, imagePluginAgent, htmlImageGeneratorAgent, beatFrameResolverAgent } from "./image_agents.js";
|
|
17
17
|
import { imageGraphOption } from "./graph_option.js";
|
|
18
18
|
import { createUsageCallback } from "../utils/usage_callback.js";
|
|
19
19
|
const vanillaAgents = vanilla.default ?? vanilla;
|
|
@@ -169,15 +169,27 @@ export const beat_graph_data = {
|
|
|
169
169
|
},
|
|
170
170
|
defaultValue: {},
|
|
171
171
|
},
|
|
172
|
+
// Resolves $beatImage frame references once the beat image exists (after imageGenerator/imagePlugin);
|
|
173
|
+
// passes the preprocessor's frame paths through unchanged for non-sentinel beats.
|
|
174
|
+
frameResolver: {
|
|
175
|
+
agent: beatFrameResolverAgent,
|
|
176
|
+
inputs: {
|
|
177
|
+
onComplete: [":imageGenerator", ":imagePlugin"],
|
|
178
|
+
context: ":context",
|
|
179
|
+
beat: ":beat",
|
|
180
|
+
index: ":__mapIndex",
|
|
181
|
+
preprocessor: ":preprocessor",
|
|
182
|
+
},
|
|
183
|
+
},
|
|
172
184
|
movieGenerator: {
|
|
173
185
|
if: ":beat.moviePrompt",
|
|
174
186
|
agent: ":preprocessor.movieAgentInfo.agent",
|
|
175
187
|
inputs: {
|
|
176
188
|
media: "movie",
|
|
177
|
-
onComplete: [":
|
|
189
|
+
onComplete: [":frameResolver"], // frameResolver itself waits for imageGenerator/imagePlugin
|
|
178
190
|
prompt: ":beat.moviePrompt",
|
|
179
|
-
imagePath: ":
|
|
180
|
-
lastFrameImagePath: ":
|
|
191
|
+
imagePath: ":frameResolver.referenceImageForMovie",
|
|
192
|
+
lastFrameImagePath: ":frameResolver.lastFrameImagePath",
|
|
181
193
|
referenceImages: ":preprocessor.movieReferenceImages",
|
|
182
194
|
movieFile: ":preprocessor.movieFile", // for google genai agent
|
|
183
195
|
cache: {
|
package/lib/methods/index.d.ts
CHANGED
package/lib/methods/index.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
export * from "./mulmo_presentation_style.js";
|
|
2
2
|
export * from "./mulmo_studio_context.js";
|
|
3
3
|
export * from "./mulmo_media_source.js";
|
|
4
|
+
export * from "./mulmo_image_prompt_media.js";
|
|
4
5
|
// Node-only MulmoBeatMethods (includes getPlugin). The browser-safe subset lives in
|
|
5
6
|
// ./mulmo_beat.js and is re-exported by ../index.common.js for mulmocast/browser.
|
|
6
7
|
export * from "./mulmo_beat_node.js";
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { MulmoImagePromptMedia, MulmoImageReference, Text2ImageAgentInfo } from "../types/index.js";
|
|
2
|
+
export type MulmoNormalizedImageReference = MulmoImageReference & {
|
|
3
|
+
legacy?: boolean;
|
|
4
|
+
};
|
|
5
|
+
export type ResolvedImageReference = {
|
|
6
|
+
path: string;
|
|
7
|
+
label?: string;
|
|
8
|
+
};
|
|
9
|
+
export declare const MulmoImagePromptMediaMethods: {
|
|
10
|
+
getReferences(image: MulmoImagePromptMedia): MulmoNormalizedImageReference[];
|
|
11
|
+
hasNamedReference(image: MulmoImagePromptMedia): boolean;
|
|
12
|
+
buildReferencePreamble(labels: (string | undefined)[]): string;
|
|
13
|
+
limitReferences(references: ResolvedImageReference[] | undefined, imageAgentInfo: Text2ImageAgentInfo, imageKey: string): ResolvedImageReference[] | undefined;
|
|
14
|
+
};
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { GraphAILogger } from "graphai";
|
|
2
|
+
import { getMaxImageReferenceImages } from "../types/provider2agent.js";
|
|
3
|
+
export const MulmoImagePromptMediaMethods = {
|
|
4
|
+
// Normalized reference list: legacy single-ref fields first, then the references array.
|
|
5
|
+
// When both legacy fields are set, referenceImageName wins and referenceImage is ignored,
|
|
6
|
+
// matching the pre-references resolution behavior.
|
|
7
|
+
getReferences(image) {
|
|
8
|
+
const references = [];
|
|
9
|
+
if (image.referenceImageName) {
|
|
10
|
+
references.push({ name: image.referenceImageName, legacy: true });
|
|
11
|
+
}
|
|
12
|
+
else if (image.referenceImage) {
|
|
13
|
+
references.push({ source: image.referenceImage, legacy: true });
|
|
14
|
+
}
|
|
15
|
+
references.push(...(image.references ?? []));
|
|
16
|
+
return references;
|
|
17
|
+
},
|
|
18
|
+
// Entries with a named reference must resolve after stage 1 (which generates/loads the named images).
|
|
19
|
+
hasNamedReference(image) {
|
|
20
|
+
return this.getReferences(image).some((ref) => ref.name !== undefined);
|
|
21
|
+
},
|
|
22
|
+
// "Reference image 1 shows <label>. ... Use these exact designs.\n"
|
|
23
|
+
// Empty when no reference has a label, keeping label-free prompts byte-identical to before.
|
|
24
|
+
buildReferencePreamble(labels) {
|
|
25
|
+
const sentences = labels.map((label, index) => (label ? `Reference image ${index + 1} shows ${label}.` : undefined)).filter((s) => s !== undefined);
|
|
26
|
+
if (sentences.length === 0) {
|
|
27
|
+
return "";
|
|
28
|
+
}
|
|
29
|
+
return `${sentences.join(" ")} Use these exact designs.\n`;
|
|
30
|
+
},
|
|
31
|
+
// Truncate to the provider/model limit, keeping the first N deterministically.
|
|
32
|
+
// imageKey is the imageRefs key of the image being generated (used for the warning).
|
|
33
|
+
limitReferences(references, imageAgentInfo, imageKey) {
|
|
34
|
+
if (!references) {
|
|
35
|
+
return undefined;
|
|
36
|
+
}
|
|
37
|
+
const { provider, model } = imageAgentInfo.imageParams;
|
|
38
|
+
const max = getMaxImageReferenceImages(provider, model ?? "");
|
|
39
|
+
if (max !== undefined && references.length > max) {
|
|
40
|
+
GraphAILogger.warn(`imagePrompt "${imageKey}": ${references.length} reference images exceed the ${provider} limit of ${max} — keeping the first ${max}`);
|
|
41
|
+
return references.slice(0, max);
|
|
42
|
+
}
|
|
43
|
+
return references;
|
|
44
|
+
},
|
|
45
|
+
};
|
|
@@ -64,6 +64,7 @@ export declare const provider2ImageAgent: {
|
|
|
64
64
|
models: string[];
|
|
65
65
|
keyName: string;
|
|
66
66
|
baseURLKeyName: string;
|
|
67
|
+
maxReferenceImages: number;
|
|
67
68
|
};
|
|
68
69
|
google: {
|
|
69
70
|
agentName: string;
|
|
@@ -74,6 +75,9 @@ export declare const provider2ImageAgent: {
|
|
|
74
75
|
replicate: {
|
|
75
76
|
agentName: string;
|
|
76
77
|
defaultModel: string;
|
|
78
|
+
imageModelParams: Record<string, {
|
|
79
|
+
maxReferenceImages?: number;
|
|
80
|
+
}>;
|
|
77
81
|
models: string[];
|
|
78
82
|
keyName: string;
|
|
79
83
|
};
|
|
@@ -213,6 +217,17 @@ export declare const defaultProviders: {
|
|
|
213
217
|
export declare const llm: (keyof typeof provider2LLMAgent)[];
|
|
214
218
|
export type LLM = keyof typeof provider2LLMAgent;
|
|
215
219
|
export declare const htmlLLMProvider: string[];
|
|
220
|
+
export declare const ProviderName: {
|
|
221
|
+
readonly OpenAI: "openai";
|
|
222
|
+
readonly Google: "google";
|
|
223
|
+
readonly Gemini: "gemini";
|
|
224
|
+
readonly Anthropic: "anthropic";
|
|
225
|
+
readonly Replicate: "replicate";
|
|
226
|
+
readonly ElevenLabs: "elevenlabs";
|
|
227
|
+
readonly Mock: "mock";
|
|
228
|
+
};
|
|
229
|
+
export declare const isImageProvider: (provider: string | undefined) => provider is keyof typeof provider2ImageAgent;
|
|
230
|
+
export declare const getMaxImageReferenceImages: (provider: string | undefined, model: string) => number | undefined;
|
|
216
231
|
export declare const getModelAudio: (provider: keyof typeof provider2MovieAgent, model: string) => MovieAudioSpec | undefined;
|
|
217
232
|
export declare const getModelDuration: (provider: keyof typeof provider2MovieAgent, model: string, movieDuration?: number) => number | undefined;
|
|
218
233
|
export type ModelPricing = {
|
|
@@ -64,6 +64,10 @@ export const deprecatedGoogleImageModelHints = {
|
|
|
64
64
|
// See https://cloud.google.com/vertex-ai/generative-ai/docs/models/gemini/3-pro-image
|
|
65
65
|
// and https://cloud.google.com/vertex-ai/generative-ai/docs/models/gemini/3-1-flash-image
|
|
66
66
|
export const vertexAIGlobalOnlyImageModels = new Set(["gemini-3-pro-image-preview", "gemini-3.1-flash-image-preview"]);
|
|
67
|
+
// Per-model reference image limits (image_input array). Only verified entries; unlisted models are not truncated.
|
|
68
|
+
const replicateImageModelParams = {
|
|
69
|
+
"bytedance/seedream-4": { maxReferenceImages: 10 },
|
|
70
|
+
};
|
|
67
71
|
export const provider2ImageAgent = {
|
|
68
72
|
openai: {
|
|
69
73
|
agentName: "imageOpenaiAgent",
|
|
@@ -71,6 +75,7 @@ export const provider2ImageAgent = {
|
|
|
71
75
|
models: [...gptImages],
|
|
72
76
|
keyName: "OPENAI_API_KEY",
|
|
73
77
|
baseURLKeyName: "OPENAI_BASE_URL",
|
|
78
|
+
maxReferenceImages: 16, // images.edit accepts up to 16 input images
|
|
74
79
|
},
|
|
75
80
|
google: {
|
|
76
81
|
agentName: "imageGenAIAgent",
|
|
@@ -81,6 +86,7 @@ export const provider2ImageAgent = {
|
|
|
81
86
|
replicate: {
|
|
82
87
|
agentName: "imageReplicateAgent",
|
|
83
88
|
defaultModel: "bytedance/seedream-4",
|
|
89
|
+
imageModelParams: replicateImageModelParams,
|
|
84
90
|
models: [
|
|
85
91
|
"bytedance/seedream-4",
|
|
86
92
|
"qwen/qwen-image",
|
|
@@ -527,6 +533,31 @@ export const defaultProviders = {
|
|
|
527
533
|
};
|
|
528
534
|
export const llm = Object.keys(provider2LLMAgent);
|
|
529
535
|
export const htmlLLMProvider = ["openai", "anthropic", "mock"];
|
|
536
|
+
// Provider name literals. Compare via these identifiers (e.g. provider === ProviderName.Replicate)
|
|
537
|
+
// instead of raw strings.
|
|
538
|
+
export const ProviderName = {
|
|
539
|
+
OpenAI: "openai",
|
|
540
|
+
Google: "google",
|
|
541
|
+
Gemini: "gemini",
|
|
542
|
+
Anthropic: "anthropic",
|
|
543
|
+
Replicate: "replicate",
|
|
544
|
+
ElevenLabs: "elevenlabs",
|
|
545
|
+
Mock: "mock",
|
|
546
|
+
};
|
|
547
|
+
export const isImageProvider = (provider) => {
|
|
548
|
+
return provider !== undefined && provider in provider2ImageAgent;
|
|
549
|
+
};
|
|
550
|
+
// Max reference images an image model accepts as input; undefined means no known limit (no truncation).
|
|
551
|
+
export const getMaxImageReferenceImages = (provider, model) => {
|
|
552
|
+
if (!isImageProvider(provider)) {
|
|
553
|
+
return undefined;
|
|
554
|
+
}
|
|
555
|
+
if (provider === ProviderName.Replicate) {
|
|
556
|
+
return provider2ImageAgent.replicate.imageModelParams[model]?.maxReferenceImages;
|
|
557
|
+
}
|
|
558
|
+
const agentInfo = provider2ImageAgent[provider];
|
|
559
|
+
return "maxReferenceImages" in agentInfo ? agentInfo.maxReferenceImages : undefined;
|
|
560
|
+
};
|
|
530
561
|
export const getModelAudio = (provider, model) => {
|
|
531
562
|
const modelParams = provider2MovieAgent[provider]?.modelParams;
|
|
532
563
|
return modelParams?.[model]?.audio;
|
package/lib/types/schema.d.ts
CHANGED
|
@@ -263,6 +263,15 @@ export declare const mulmoMarkdownMediaSchema: z.ZodObject<{
|
|
|
263
263
|
opacity: z.ZodOptional<z.ZodNumber>;
|
|
264
264
|
}, z.core.$strip>]>>>;
|
|
265
265
|
}, z.core.$strict>;
|
|
266
|
+
export declare const ImageMediaType: {
|
|
267
|
+
readonly Image: "image";
|
|
268
|
+
readonly ImagePrompt: "imagePrompt";
|
|
269
|
+
readonly Movie: "movie";
|
|
270
|
+
readonly MoviePrompt: "moviePrompt";
|
|
271
|
+
readonly Beat: "beat";
|
|
272
|
+
readonly VoiceOver: "voice_over";
|
|
273
|
+
};
|
|
274
|
+
export declare const beatImageTypesWithoutOwnStill: readonly string[];
|
|
266
275
|
export declare const mulmoImageMediaSchema: z.ZodObject<{
|
|
267
276
|
type: z.ZodLiteral<"image">;
|
|
268
277
|
source: z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
@@ -4135,6 +4144,21 @@ export declare const mulmoAudioAssetSchema: z.ZodUnion<readonly [z.ZodObject<{
|
|
|
4135
4144
|
type: z.ZodLiteral<"midi">;
|
|
4136
4145
|
source: z.ZodString;
|
|
4137
4146
|
}, z.core.$strict>]>;
|
|
4147
|
+
export declare const BEAT_IMAGE_SENTINEL = "$beatImage";
|
|
4148
|
+
export declare const mulmoImageReferenceSchema: z.ZodObject<{
|
|
4149
|
+
name: z.ZodOptional<z.ZodString>;
|
|
4150
|
+
source: z.ZodOptional<z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
4151
|
+
kind: z.ZodLiteral<"url">;
|
|
4152
|
+
url: z.ZodURL;
|
|
4153
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
4154
|
+
kind: z.ZodLiteral<"base64">;
|
|
4155
|
+
data: z.ZodString;
|
|
4156
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
4157
|
+
kind: z.ZodLiteral<"path">;
|
|
4158
|
+
path: z.ZodString;
|
|
4159
|
+
}, z.core.$strict>], "kind">>;
|
|
4160
|
+
label: z.ZodOptional<z.ZodString>;
|
|
4161
|
+
}, z.core.$strict>;
|
|
4138
4162
|
export declare const mulmoImagePromptMediaSchema: z.ZodObject<{
|
|
4139
4163
|
type: z.ZodLiteral<"imagePrompt">;
|
|
4140
4164
|
prompt: z.ZodString;
|
|
@@ -4153,6 +4177,20 @@ export declare const mulmoImagePromptMediaSchema: z.ZodObject<{
|
|
|
4153
4177
|
kind: z.ZodLiteral<"path">;
|
|
4154
4178
|
path: z.ZodString;
|
|
4155
4179
|
}, z.core.$strict>], "kind">>;
|
|
4180
|
+
references: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
4181
|
+
name: z.ZodOptional<z.ZodString>;
|
|
4182
|
+
source: z.ZodOptional<z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
4183
|
+
kind: z.ZodLiteral<"url">;
|
|
4184
|
+
url: z.ZodURL;
|
|
4185
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
4186
|
+
kind: z.ZodLiteral<"base64">;
|
|
4187
|
+
data: z.ZodString;
|
|
4188
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
4189
|
+
kind: z.ZodLiteral<"path">;
|
|
4190
|
+
path: z.ZodString;
|
|
4191
|
+
}, z.core.$strict>], "kind">>;
|
|
4192
|
+
label: z.ZodOptional<z.ZodString>;
|
|
4193
|
+
}, z.core.$strict>>>;
|
|
4156
4194
|
}, z.core.$strict>;
|
|
4157
4195
|
export declare const mulmoImageParamsImagesValueSchema: z.ZodUnion<readonly [z.ZodObject<{
|
|
4158
4196
|
type: z.ZodLiteral<"image">;
|
|
@@ -4184,6 +4222,20 @@ export declare const mulmoImageParamsImagesValueSchema: z.ZodUnion<readonly [z.Z
|
|
|
4184
4222
|
kind: z.ZodLiteral<"path">;
|
|
4185
4223
|
path: z.ZodString;
|
|
4186
4224
|
}, z.core.$strict>], "kind">>;
|
|
4225
|
+
references: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
4226
|
+
name: z.ZodOptional<z.ZodString>;
|
|
4227
|
+
source: z.ZodOptional<z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
4228
|
+
kind: z.ZodLiteral<"url">;
|
|
4229
|
+
url: z.ZodURL;
|
|
4230
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
4231
|
+
kind: z.ZodLiteral<"base64">;
|
|
4232
|
+
data: z.ZodString;
|
|
4233
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
4234
|
+
kind: z.ZodLiteral<"path">;
|
|
4235
|
+
path: z.ZodString;
|
|
4236
|
+
}, z.core.$strict>], "kind">>;
|
|
4237
|
+
label: z.ZodOptional<z.ZodString>;
|
|
4238
|
+
}, z.core.$strict>>>;
|
|
4187
4239
|
}, z.core.$strict>, z.ZodObject<{
|
|
4188
4240
|
type: z.ZodLiteral<"movie">;
|
|
4189
4241
|
source: z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
@@ -4231,6 +4283,20 @@ export declare const mulmoImageParamsImagesSchema: z.ZodRecord<z.ZodString, z.Zo
|
|
|
4231
4283
|
kind: z.ZodLiteral<"path">;
|
|
4232
4284
|
path: z.ZodString;
|
|
4233
4285
|
}, z.core.$strict>], "kind">>;
|
|
4286
|
+
references: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
4287
|
+
name: z.ZodOptional<z.ZodString>;
|
|
4288
|
+
source: z.ZodOptional<z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
4289
|
+
kind: z.ZodLiteral<"url">;
|
|
4290
|
+
url: z.ZodURL;
|
|
4291
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
4292
|
+
kind: z.ZodLiteral<"base64">;
|
|
4293
|
+
data: z.ZodString;
|
|
4294
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
4295
|
+
kind: z.ZodLiteral<"path">;
|
|
4296
|
+
path: z.ZodString;
|
|
4297
|
+
}, z.core.$strict>], "kind">>;
|
|
4298
|
+
label: z.ZodOptional<z.ZodString>;
|
|
4299
|
+
}, z.core.$strict>>>;
|
|
4234
4300
|
}, z.core.$strict>, z.ZodObject<{
|
|
4235
4301
|
type: z.ZodLiteral<"movie">;
|
|
4236
4302
|
source: z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
@@ -4330,6 +4396,20 @@ export declare const mulmoImageParamsSchema: z.ZodObject<{
|
|
|
4330
4396
|
kind: z.ZodLiteral<"path">;
|
|
4331
4397
|
path: z.ZodString;
|
|
4332
4398
|
}, z.core.$strict>], "kind">>;
|
|
4399
|
+
references: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
4400
|
+
name: z.ZodOptional<z.ZodString>;
|
|
4401
|
+
source: z.ZodOptional<z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
4402
|
+
kind: z.ZodLiteral<"url">;
|
|
4403
|
+
url: z.ZodURL;
|
|
4404
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
4405
|
+
kind: z.ZodLiteral<"base64">;
|
|
4406
|
+
data: z.ZodString;
|
|
4407
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
4408
|
+
kind: z.ZodLiteral<"path">;
|
|
4409
|
+
path: z.ZodString;
|
|
4410
|
+
}, z.core.$strict>], "kind">>;
|
|
4411
|
+
label: z.ZodOptional<z.ZodString>;
|
|
4412
|
+
}, z.core.$strict>>>;
|
|
4333
4413
|
}, z.core.$strict>, z.ZodObject<{
|
|
4334
4414
|
type: z.ZodLiteral<"movie">;
|
|
4335
4415
|
source: z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
@@ -8800,6 +8880,20 @@ export declare const mulmoBeatSchema: z.ZodObject<{
|
|
|
8800
8880
|
kind: z.ZodLiteral<"path">;
|
|
8801
8881
|
path: z.ZodString;
|
|
8802
8882
|
}, z.core.$strict>], "kind">>;
|
|
8883
|
+
references: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
8884
|
+
name: z.ZodOptional<z.ZodString>;
|
|
8885
|
+
source: z.ZodOptional<z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
8886
|
+
kind: z.ZodLiteral<"url">;
|
|
8887
|
+
url: z.ZodURL;
|
|
8888
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
8889
|
+
kind: z.ZodLiteral<"base64">;
|
|
8890
|
+
data: z.ZodString;
|
|
8891
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
8892
|
+
kind: z.ZodLiteral<"path">;
|
|
8893
|
+
path: z.ZodString;
|
|
8894
|
+
}, z.core.$strict>], "kind">>;
|
|
8895
|
+
label: z.ZodOptional<z.ZodString>;
|
|
8896
|
+
}, z.core.$strict>>>;
|
|
8803
8897
|
}, z.core.$strict>, z.ZodObject<{
|
|
8804
8898
|
type: z.ZodLiteral<"movie">;
|
|
8805
8899
|
source: z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
@@ -8942,6 +9036,20 @@ export declare const mulmoPresentationStyleSchema: z.ZodObject<{
|
|
|
8942
9036
|
kind: z.ZodLiteral<"path">;
|
|
8943
9037
|
path: z.ZodString;
|
|
8944
9038
|
}, z.core.$strict>], "kind">>;
|
|
9039
|
+
references: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
9040
|
+
name: z.ZodOptional<z.ZodString>;
|
|
9041
|
+
source: z.ZodOptional<z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
9042
|
+
kind: z.ZodLiteral<"url">;
|
|
9043
|
+
url: z.ZodURL;
|
|
9044
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
9045
|
+
kind: z.ZodLiteral<"base64">;
|
|
9046
|
+
data: z.ZodString;
|
|
9047
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
9048
|
+
kind: z.ZodLiteral<"path">;
|
|
9049
|
+
path: z.ZodString;
|
|
9050
|
+
}, z.core.$strict>], "kind">>;
|
|
9051
|
+
label: z.ZodOptional<z.ZodString>;
|
|
9052
|
+
}, z.core.$strict>>>;
|
|
8945
9053
|
}, z.core.$strict>, z.ZodObject<{
|
|
8946
9054
|
type: z.ZodLiteral<"movie">;
|
|
8947
9055
|
source: z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
@@ -9455,6 +9563,20 @@ export declare const mulmoScriptSchema: z.ZodObject<{
|
|
|
9455
9563
|
kind: z.ZodLiteral<"path">;
|
|
9456
9564
|
path: z.ZodString;
|
|
9457
9565
|
}, z.core.$strict>], "kind">>;
|
|
9566
|
+
references: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
9567
|
+
name: z.ZodOptional<z.ZodString>;
|
|
9568
|
+
source: z.ZodOptional<z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
9569
|
+
kind: z.ZodLiteral<"url">;
|
|
9570
|
+
url: z.ZodURL;
|
|
9571
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
9572
|
+
kind: z.ZodLiteral<"base64">;
|
|
9573
|
+
data: z.ZodString;
|
|
9574
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
9575
|
+
kind: z.ZodLiteral<"path">;
|
|
9576
|
+
path: z.ZodString;
|
|
9577
|
+
}, z.core.$strict>], "kind">>;
|
|
9578
|
+
label: z.ZodOptional<z.ZodString>;
|
|
9579
|
+
}, z.core.$strict>>>;
|
|
9458
9580
|
}, z.core.$strict>, z.ZodObject<{
|
|
9459
9581
|
type: z.ZodLiteral<"movie">;
|
|
9460
9582
|
source: z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
@@ -13913,6 +14035,20 @@ export declare const mulmoScriptSchema: z.ZodObject<{
|
|
|
13913
14035
|
kind: z.ZodLiteral<"path">;
|
|
13914
14036
|
path: z.ZodString;
|
|
13915
14037
|
}, z.core.$strict>], "kind">>;
|
|
14038
|
+
references: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
14039
|
+
name: z.ZodOptional<z.ZodString>;
|
|
14040
|
+
source: z.ZodOptional<z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
14041
|
+
kind: z.ZodLiteral<"url">;
|
|
14042
|
+
url: z.ZodURL;
|
|
14043
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
14044
|
+
kind: z.ZodLiteral<"base64">;
|
|
14045
|
+
data: z.ZodString;
|
|
14046
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
14047
|
+
kind: z.ZodLiteral<"path">;
|
|
14048
|
+
path: z.ZodString;
|
|
14049
|
+
}, z.core.$strict>], "kind">>;
|
|
14050
|
+
label: z.ZodOptional<z.ZodString>;
|
|
14051
|
+
}, z.core.$strict>>>;
|
|
13916
14052
|
}, z.core.$strict>, z.ZodObject<{
|
|
13917
14053
|
type: z.ZodLiteral<"movie">;
|
|
13918
14054
|
source: z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
@@ -14131,6 +14267,20 @@ export declare const mulmoStudioSchema: z.ZodObject<{
|
|
|
14131
14267
|
kind: z.ZodLiteral<"path">;
|
|
14132
14268
|
path: z.ZodString;
|
|
14133
14269
|
}, z.core.$strict>], "kind">>;
|
|
14270
|
+
references: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
14271
|
+
name: z.ZodOptional<z.ZodString>;
|
|
14272
|
+
source: z.ZodOptional<z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
14273
|
+
kind: z.ZodLiteral<"url">;
|
|
14274
|
+
url: z.ZodURL;
|
|
14275
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
14276
|
+
kind: z.ZodLiteral<"base64">;
|
|
14277
|
+
data: z.ZodString;
|
|
14278
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
14279
|
+
kind: z.ZodLiteral<"path">;
|
|
14280
|
+
path: z.ZodString;
|
|
14281
|
+
}, z.core.$strict>], "kind">>;
|
|
14282
|
+
label: z.ZodOptional<z.ZodString>;
|
|
14283
|
+
}, z.core.$strict>>>;
|
|
14134
14284
|
}, z.core.$strict>, z.ZodObject<{
|
|
14135
14285
|
type: z.ZodLiteral<"movie">;
|
|
14136
14286
|
source: z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
@@ -18589,6 +18739,20 @@ export declare const mulmoStudioSchema: z.ZodObject<{
|
|
|
18589
18739
|
kind: z.ZodLiteral<"path">;
|
|
18590
18740
|
path: z.ZodString;
|
|
18591
18741
|
}, z.core.$strict>], "kind">>;
|
|
18742
|
+
references: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
18743
|
+
name: z.ZodOptional<z.ZodString>;
|
|
18744
|
+
source: z.ZodOptional<z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
18745
|
+
kind: z.ZodLiteral<"url">;
|
|
18746
|
+
url: z.ZodURL;
|
|
18747
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
18748
|
+
kind: z.ZodLiteral<"base64">;
|
|
18749
|
+
data: z.ZodString;
|
|
18750
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
18751
|
+
kind: z.ZodLiteral<"path">;
|
|
18752
|
+
path: z.ZodString;
|
|
18753
|
+
}, z.core.$strict>], "kind">>;
|
|
18754
|
+
label: z.ZodOptional<z.ZodString>;
|
|
18755
|
+
}, z.core.$strict>>>;
|
|
18592
18756
|
}, z.core.$strict>, z.ZodObject<{
|
|
18593
18757
|
type: z.ZodLiteral<"movie">;
|
|
18594
18758
|
source: z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
@@ -18742,6 +18906,20 @@ export declare const mulmoPromptTemplateSchema: z.ZodObject<{
|
|
|
18742
18906
|
kind: z.ZodLiteral<"path">;
|
|
18743
18907
|
path: z.ZodString;
|
|
18744
18908
|
}, z.core.$strict>], "kind">>;
|
|
18909
|
+
references: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
18910
|
+
name: z.ZodOptional<z.ZodString>;
|
|
18911
|
+
source: z.ZodOptional<z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
18912
|
+
kind: z.ZodLiteral<"url">;
|
|
18913
|
+
url: z.ZodURL;
|
|
18914
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
18915
|
+
kind: z.ZodLiteral<"base64">;
|
|
18916
|
+
data: z.ZodString;
|
|
18917
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
18918
|
+
kind: z.ZodLiteral<"path">;
|
|
18919
|
+
path: z.ZodString;
|
|
18920
|
+
}, z.core.$strict>], "kind">>;
|
|
18921
|
+
label: z.ZodOptional<z.ZodString>;
|
|
18922
|
+
}, z.core.$strict>>>;
|
|
18745
18923
|
}, z.core.$strict>, z.ZodObject<{
|
|
18746
18924
|
type: z.ZodLiteral<"movie">;
|
|
18747
18925
|
source: z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
@@ -19249,6 +19427,20 @@ export declare const mulmoPromptTemplateFileSchema: z.ZodObject<{
|
|
|
19249
19427
|
kind: z.ZodLiteral<"path">;
|
|
19250
19428
|
path: z.ZodString;
|
|
19251
19429
|
}, z.core.$strict>], "kind">>;
|
|
19430
|
+
references: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
19431
|
+
name: z.ZodOptional<z.ZodString>;
|
|
19432
|
+
source: z.ZodOptional<z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
19433
|
+
kind: z.ZodLiteral<"url">;
|
|
19434
|
+
url: z.ZodURL;
|
|
19435
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
19436
|
+
kind: z.ZodLiteral<"base64">;
|
|
19437
|
+
data: z.ZodString;
|
|
19438
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
19439
|
+
kind: z.ZodLiteral<"path">;
|
|
19440
|
+
path: z.ZodString;
|
|
19441
|
+
}, z.core.$strict>], "kind">>;
|
|
19442
|
+
label: z.ZodOptional<z.ZodString>;
|
|
19443
|
+
}, z.core.$strict>>>;
|
|
19252
19444
|
}, z.core.$strict>, z.ZodObject<{
|
|
19253
19445
|
type: z.ZodLiteral<"movie">;
|
|
19254
19446
|
source: z.ZodDiscriminatedUnion<[z.ZodObject<{
|
package/lib/types/schema.js
CHANGED
|
@@ -130,9 +130,22 @@ const mulmoPdfMediaSchema = z
|
|
|
130
130
|
source: mediaSourceSchema,
|
|
131
131
|
})
|
|
132
132
|
.strict();
|
|
133
|
+
// Media type literals shared by imageParams.images entries and beat images. Compare via
|
|
134
|
+
// these identifiers (e.g. entry.type === ImageMediaType.ImagePrompt) instead of raw strings.
|
|
135
|
+
export const ImageMediaType = {
|
|
136
|
+
Image: "image",
|
|
137
|
+
ImagePrompt: "imagePrompt",
|
|
138
|
+
Movie: "movie",
|
|
139
|
+
MoviePrompt: "moviePrompt",
|
|
140
|
+
Beat: "beat",
|
|
141
|
+
VoiceOver: "voice_over",
|
|
142
|
+
};
|
|
143
|
+
// Beat image types whose still derives from the movie or another beat — they cannot supply
|
|
144
|
+
// their own still for the $beatImage frame sentinel.
|
|
145
|
+
export const beatImageTypesWithoutOwnStill = [ImageMediaType.Movie, ImageMediaType.Beat, ImageMediaType.VoiceOver];
|
|
133
146
|
export const mulmoImageMediaSchema = z
|
|
134
147
|
.object({
|
|
135
|
-
type: z.literal(
|
|
148
|
+
type: z.literal(ImageMediaType.Image),
|
|
136
149
|
source: mediaSourceSchema,
|
|
137
150
|
})
|
|
138
151
|
.strict();
|
|
@@ -144,13 +157,13 @@ const mulmoSvgMediaSchema = z
|
|
|
144
157
|
.strict();
|
|
145
158
|
export const mulmoMovieMediaSchema = z
|
|
146
159
|
.object({
|
|
147
|
-
type: z.literal(
|
|
160
|
+
type: z.literal(ImageMediaType.Movie),
|
|
148
161
|
source: mediaSourceSchema,
|
|
149
162
|
})
|
|
150
163
|
.strict();
|
|
151
164
|
export const mulmoMoviePromptMediaSchema = z
|
|
152
165
|
.object({
|
|
153
|
-
type: z.literal(
|
|
166
|
+
type: z.literal(ImageMediaType.MoviePrompt),
|
|
154
167
|
prompt: z.string().min(1),
|
|
155
168
|
imageName: z.string().optional().describe("Reference an imageRefs key to use as image-to-video input"),
|
|
156
169
|
})
|
|
@@ -292,13 +305,13 @@ export const mulmoHtmlTailwindMediaSchema = z
|
|
|
292
305
|
.strict();
|
|
293
306
|
export const mulmoBeatReferenceMediaSchema = z
|
|
294
307
|
.object({
|
|
295
|
-
type: z.literal(
|
|
308
|
+
type: z.literal(ImageMediaType.Beat),
|
|
296
309
|
id: z.string().optional().describe("Specifies the beat to reference."),
|
|
297
310
|
})
|
|
298
311
|
.strict();
|
|
299
312
|
export const mulmoVoiceOverMediaSchema = z
|
|
300
313
|
.object({
|
|
301
|
-
type: z.literal(
|
|
314
|
+
type: z.literal(ImageMediaType.VoiceOver),
|
|
302
315
|
startAt: z.number().optional().describe("The time to start the voice over the video in seconds."),
|
|
303
316
|
})
|
|
304
317
|
.strict();
|
|
@@ -339,13 +352,26 @@ const mulmoMidiMediaSchema = z
|
|
|
339
352
|
.strict();
|
|
340
353
|
export const mulmoAudioAssetSchema = z.union([mulmoAudioMediaSchema, mulmoMidiMediaSchema]);
|
|
341
354
|
const imageIdSchema = z.string();
|
|
355
|
+
// Sentinel for movieParams.firstFrameImageName / lastFrameImageName: "this beat's own generated image".
|
|
356
|
+
export const BEAT_IMAGE_SENTINEL = "$beatImage";
|
|
357
|
+
// imageRefs keys may not start with "$": that prefix is reserved for sentinels like $beatImage.
|
|
358
|
+
const imageRefKeySchema = imageIdSchema.refine((key) => !key.startsWith("$"), { message: 'image keys must not start with the reserved prefix "$"' });
|
|
359
|
+
export const mulmoImageReferenceSchema = z
|
|
360
|
+
.object({
|
|
361
|
+
name: imageIdSchema.optional().describe("An imageRefs key to use as a reference image"),
|
|
362
|
+
source: mediaSourceSchema.optional().describe("Direct source (path/url) to use as a reference image"),
|
|
363
|
+
label: z.string().optional().describe("Role of this reference (e.g. 'the firefighter Travis Kane'), injected into the prompt as a preamble"),
|
|
364
|
+
})
|
|
365
|
+
.strict()
|
|
366
|
+
.refine((ref) => (ref.name === undefined) !== (ref.source === undefined), { message: "specify exactly one of name or source" });
|
|
342
367
|
export const mulmoImagePromptMediaSchema = z
|
|
343
368
|
.object({
|
|
344
|
-
type: z.literal(
|
|
369
|
+
type: z.literal(ImageMediaType.ImagePrompt),
|
|
345
370
|
prompt: z.string().min(1),
|
|
346
371
|
canvasSize: z.object({ width: z.number(), height: z.number() }).strict().optional(),
|
|
347
|
-
referenceImageName: imageIdSchema.optional().describe("Reference another imageRefs key as input for image generation"),
|
|
348
|
-
referenceImage: mediaSourceSchema.optional().describe("Direct source (path/url/base64) as reference image for generation"),
|
|
372
|
+
referenceImageName: imageIdSchema.optional().describe("Reference another imageRefs key as input for image generation. Sugar for references[0].name"),
|
|
373
|
+
referenceImage: mediaSourceSchema.optional().describe("Direct source (path/url/base64) as reference image for generation. Sugar for references[0].source"),
|
|
374
|
+
references: z.array(mulmoImageReferenceSchema).optional().describe("Reference images (by imageRefs key or direct source) with optional role labels"),
|
|
349
375
|
})
|
|
350
376
|
.strict();
|
|
351
377
|
export const mulmoImageParamsImagesValueSchema = z.union([
|
|
@@ -354,7 +380,7 @@ export const mulmoImageParamsImagesValueSchema = z.union([
|
|
|
354
380
|
mulmoMovieMediaSchema,
|
|
355
381
|
mulmoMoviePromptMediaSchema,
|
|
356
382
|
]);
|
|
357
|
-
export const mulmoImageParamsImagesSchema = z.record(
|
|
383
|
+
export const mulmoImageParamsImagesSchema = z.record(imageRefKeySchema, mulmoImageParamsImagesValueSchema);
|
|
358
384
|
export const mulmoFillOptionSchema = z
|
|
359
385
|
.object({
|
|
360
386
|
style: z.enum(["aspectFit", "aspectFill"]).optional().default("aspectFit"),
|
|
@@ -500,8 +526,12 @@ export const mulmoMovieParamsSchema = z.object({
|
|
|
500
526
|
filters: z.array(mulmoVideoFilterSchema).optional(), // for movie.ts
|
|
501
527
|
vertexai_project: z.string().optional(), // Google Cloud Project ID for Vertex AI
|
|
502
528
|
vertexai_location: z.string().optional(), // Vertex AI location (default: us-central1)
|
|
503
|
-
firstFrameImageName: imageIdSchema
|
|
504
|
-
|
|
529
|
+
firstFrameImageName: imageIdSchema
|
|
530
|
+
.optional()
|
|
531
|
+
.describe("Reference an imageRefs key for the first frame (image-to-video input), or '$beatImage' for the beat's own generated image"),
|
|
532
|
+
lastFrameImageName: imageIdSchema
|
|
533
|
+
.optional()
|
|
534
|
+
.describe("Reference an imageRefs key for the last frame (image-to-video interpolation), or '$beatImage' for the beat's own generated image"),
|
|
505
535
|
frameFillColor: z
|
|
506
536
|
.string()
|
|
507
537
|
.regex(/^(#|0x)?[0-9a-fA-F]{6}([0-9a-fA-F]{2})?$|^[a-zA-Z]+$/, "must be a hex color ('#RRGGBB[AA]', '0x' or no prefix) or a color name")
|
|
@@ -540,7 +570,7 @@ export const mulmoBeatSchema = z
|
|
|
540
570
|
images: mulmoImageParamsImagesSchema
|
|
541
571
|
.optional()
|
|
542
572
|
.describe("Beat-local media references. Same schema as imageParams.images. Merged with global refs (local takes precedence)."),
|
|
543
|
-
imageNames: z.array(
|
|
573
|
+
imageNames: z.array(imageRefKeySchema).optional(), // list of image names to use for image generation. The default is all images in the imageParams.images.
|
|
544
574
|
imagePrompt: z.string().optional(),
|
|
545
575
|
moviePrompt: z.string().optional(),
|
|
546
576
|
soundEffectPrompt: z.string().optional(),
|
package/lib/types/type.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { type CallbackFunction } from "graphai";
|
|
2
2
|
import type { UsageCollectorAPI } from "./usage.js";
|
|
3
|
-
import { langSchema, localizedTextSchema, mulmoBeatSchema, mulmoScriptSchema, mulmoStudioSchema, mulmoStudioBeatSchema, mulmoStoryboardSchema, mulmoStoryboardSceneSchema, mulmoStudioMultiLingualSchema, mulmoStudioMultiLingualArraySchema, mulmoStudioMultiLingualDataSchema, mulmoStudioMultiLingualFileSchema, speakerDictionarySchema, speakerSchema, mulmoSpeechParamsSchema, mulmoImageParamsSchema, mulmoImageParamsImagesValueSchema, mulmoImageParamsImagesSchema, mulmoFillOptionSchema, mulmoTransitionSchema, mulmoVideoFilterSchema, mulmoMovieParamsSchema, mulmoSoundEffectParamsSchema, mulmoLipSyncParamsSchema, textSlideParamsSchema, speechOptionsSchema, speakerDataSchema, mulmoCanvasDimensionSchema, mulmoPromptTemplateSchema, mulmoPromptTemplateFileSchema, text2ImageProviderSchema, text2HtmlImageProviderSchema, text2MovieProviderSchema, text2SpeechProviderSchema, mulmoPresentationStyleSchema, multiLingualTextsSchema, mulmoImageAssetSchema, mulmoMermaidMediaSchema, mulmoTextSlideMediaSchema, mulmoMarkdownMediaSchema, mulmoImageMediaSchema, mulmoChartMediaSchema, mediaSourceSchema, mediaSourceMermaidSchema, backgroundImageSchema, backgroundImageSourceSchema, mulmoSessionStateSchema, mulmoOpenAIImageModelSchema, mulmoGoogleImageModelSchema, mulmoGoogleMovieModelSchema, mulmoReplicateMovieModelSchema, mulmoImagePromptMediaSchema, mulmoMovieMediaSchema, mulmoMoviePromptMediaSchema, markdownLayoutSchema, row2Schema, grid2x2Schema } from "./schema.js";
|
|
3
|
+
import { langSchema, localizedTextSchema, mulmoBeatSchema, mulmoScriptSchema, mulmoStudioSchema, mulmoStudioBeatSchema, mulmoStoryboardSchema, mulmoStoryboardSceneSchema, mulmoStudioMultiLingualSchema, mulmoStudioMultiLingualArraySchema, mulmoStudioMultiLingualDataSchema, mulmoStudioMultiLingualFileSchema, speakerDictionarySchema, speakerSchema, mulmoSpeechParamsSchema, mulmoImageParamsSchema, mulmoImageParamsImagesValueSchema, mulmoImageParamsImagesSchema, mulmoFillOptionSchema, mulmoTransitionSchema, mulmoVideoFilterSchema, mulmoMovieParamsSchema, mulmoSoundEffectParamsSchema, mulmoLipSyncParamsSchema, textSlideParamsSchema, speechOptionsSchema, speakerDataSchema, mulmoCanvasDimensionSchema, mulmoPromptTemplateSchema, mulmoPromptTemplateFileSchema, text2ImageProviderSchema, text2HtmlImageProviderSchema, text2MovieProviderSchema, text2SpeechProviderSchema, mulmoPresentationStyleSchema, multiLingualTextsSchema, mulmoImageAssetSchema, mulmoMermaidMediaSchema, mulmoTextSlideMediaSchema, mulmoMarkdownMediaSchema, mulmoImageMediaSchema, mulmoChartMediaSchema, mediaSourceSchema, mediaSourceMermaidSchema, backgroundImageSchema, backgroundImageSourceSchema, mulmoSessionStateSchema, mulmoOpenAIImageModelSchema, mulmoGoogleImageModelSchema, mulmoGoogleMovieModelSchema, mulmoReplicateMovieModelSchema, mulmoImagePromptMediaSchema, mulmoImageReferenceSchema, mulmoMovieMediaSchema, mulmoMoviePromptMediaSchema, markdownLayoutSchema, row2Schema, grid2x2Schema } from "./schema.js";
|
|
4
4
|
import { pdf_modes, pdf_sizes, storyToScriptGenerateMode } from "./const.js";
|
|
5
5
|
import type { LLM } from "./provider2agent.js";
|
|
6
6
|
import { z } from "zod";
|
|
@@ -49,6 +49,7 @@ export type MulmoGoogleImageModel = z.infer<typeof mulmoGoogleImageModelSchema>;
|
|
|
49
49
|
export type MulmoGoogleMovieModel = z.infer<typeof mulmoGoogleMovieModelSchema>;
|
|
50
50
|
export type MulmoReplicateMovieModel = z.infer<typeof mulmoReplicateMovieModelSchema>;
|
|
51
51
|
export type MulmoImagePromptMedia = z.infer<typeof mulmoImagePromptMediaSchema>;
|
|
52
|
+
export type MulmoImageReference = z.infer<typeof mulmoImageReferenceSchema>;
|
|
52
53
|
export type MulmoMoviePromptMedia = z.infer<typeof mulmoMoviePromptMediaSchema>;
|
|
53
54
|
export type MulmoMarkdownLayout = z.infer<typeof markdownLayoutSchema>;
|
|
54
55
|
export type MulmoRow2 = z.infer<typeof row2Schema>;
|
package/lib/utils/context.d.ts
CHANGED
|
@@ -103,6 +103,20 @@ export declare const createStudioData: (_mulmoScript: MulmoScript, fileName: str
|
|
|
103
103
|
kind: "path";
|
|
104
104
|
path: string;
|
|
105
105
|
} | undefined;
|
|
106
|
+
references?: {
|
|
107
|
+
name?: string | undefined;
|
|
108
|
+
source?: {
|
|
109
|
+
kind: "url";
|
|
110
|
+
url: string;
|
|
111
|
+
} | {
|
|
112
|
+
kind: "base64";
|
|
113
|
+
data: string;
|
|
114
|
+
} | {
|
|
115
|
+
kind: "path";
|
|
116
|
+
path: string;
|
|
117
|
+
} | undefined;
|
|
118
|
+
label?: string | undefined;
|
|
119
|
+
}[] | undefined;
|
|
106
120
|
}> | undefined;
|
|
107
121
|
backgroundImage?: string | {
|
|
108
122
|
source: {
|
|
@@ -2404,6 +2418,20 @@ export declare const createStudioData: (_mulmoScript: MulmoScript, fileName: str
|
|
|
2404
2418
|
kind: "path";
|
|
2405
2419
|
path: string;
|
|
2406
2420
|
} | undefined;
|
|
2421
|
+
references?: {
|
|
2422
|
+
name?: string | undefined;
|
|
2423
|
+
source?: {
|
|
2424
|
+
kind: "url";
|
|
2425
|
+
url: string;
|
|
2426
|
+
} | {
|
|
2427
|
+
kind: "base64";
|
|
2428
|
+
data: string;
|
|
2429
|
+
} | {
|
|
2430
|
+
kind: "path";
|
|
2431
|
+
path: string;
|
|
2432
|
+
} | undefined;
|
|
2433
|
+
label?: string | undefined;
|
|
2434
|
+
}[] | undefined;
|
|
2407
2435
|
}> | undefined;
|
|
2408
2436
|
imageNames?: string[] | undefined;
|
|
2409
2437
|
imagePrompt?: string | undefined;
|
|
@@ -2641,6 +2669,20 @@ export declare const initializeContextFromFiles: (files: FileObject, raiseError:
|
|
|
2641
2669
|
kind: "path";
|
|
2642
2670
|
path: string;
|
|
2643
2671
|
} | undefined;
|
|
2672
|
+
references?: {
|
|
2673
|
+
name?: string | undefined;
|
|
2674
|
+
source?: {
|
|
2675
|
+
kind: "url";
|
|
2676
|
+
url: string;
|
|
2677
|
+
} | {
|
|
2678
|
+
kind: "base64";
|
|
2679
|
+
data: string;
|
|
2680
|
+
} | {
|
|
2681
|
+
kind: "path";
|
|
2682
|
+
path: string;
|
|
2683
|
+
} | undefined;
|
|
2684
|
+
label?: string | undefined;
|
|
2685
|
+
}[] | undefined;
|
|
2644
2686
|
}> | undefined;
|
|
2645
2687
|
backgroundImage?: string | {
|
|
2646
2688
|
source: {
|
|
@@ -4942,6 +4984,20 @@ export declare const initializeContextFromFiles: (files: FileObject, raiseError:
|
|
|
4942
4984
|
kind: "path";
|
|
4943
4985
|
path: string;
|
|
4944
4986
|
} | undefined;
|
|
4987
|
+
references?: {
|
|
4988
|
+
name?: string | undefined;
|
|
4989
|
+
source?: {
|
|
4990
|
+
kind: "url";
|
|
4991
|
+
url: string;
|
|
4992
|
+
} | {
|
|
4993
|
+
kind: "base64";
|
|
4994
|
+
data: string;
|
|
4995
|
+
} | {
|
|
4996
|
+
kind: "path";
|
|
4997
|
+
path: string;
|
|
4998
|
+
} | undefined;
|
|
4999
|
+
label?: string | undefined;
|
|
5000
|
+
}[] | undefined;
|
|
4945
5001
|
}> | undefined;
|
|
4946
5002
|
imageNames?: string[] | undefined;
|
|
4947
5003
|
imagePrompt?: string | undefined;
|
|
@@ -5186,6 +5242,20 @@ export declare const initializeContextFromFiles: (files: FileObject, raiseError:
|
|
|
5186
5242
|
kind: "path";
|
|
5187
5243
|
path: string;
|
|
5188
5244
|
} | undefined;
|
|
5245
|
+
references?: {
|
|
5246
|
+
name?: string | undefined;
|
|
5247
|
+
source?: {
|
|
5248
|
+
kind: "url";
|
|
5249
|
+
url: string;
|
|
5250
|
+
} | {
|
|
5251
|
+
kind: "base64";
|
|
5252
|
+
data: string;
|
|
5253
|
+
} | {
|
|
5254
|
+
kind: "path";
|
|
5255
|
+
path: string;
|
|
5256
|
+
} | undefined;
|
|
5257
|
+
label?: string | undefined;
|
|
5258
|
+
}[] | undefined;
|
|
5189
5259
|
}> | undefined;
|
|
5190
5260
|
backgroundImage?: string | {
|
|
5191
5261
|
source: {
|
|
@@ -55,6 +55,7 @@ export declare const apiRateLimitErrorType = "apiRateLimit";
|
|
|
55
55
|
export declare const apiKeyMissingType = "apiKeyMissing";
|
|
56
56
|
export declare const invalidResponseType = "invalidResponse";
|
|
57
57
|
export declare const voiceLimitReachedType = "voice_limit_reached";
|
|
58
|
+
export declare const invalidFrameSentinelType = "invalidFrameSentinel";
|
|
58
59
|
export declare const movieAction = "movie";
|
|
59
60
|
export declare const imageAction = "images";
|
|
60
61
|
export declare const audioAction = "audio";
|
|
@@ -69,6 +70,10 @@ export declare const multiLingualFileTarget = "multiLingualFile";
|
|
|
69
70
|
export declare const videoSourceTarget = "videoSource";
|
|
70
71
|
export declare const audioSourceTarget = "audioSource";
|
|
71
72
|
export declare const codeTextTarget = "codeText";
|
|
73
|
+
export declare const bothFramesTarget = "bothFrames";
|
|
74
|
+
export declare const missingBeatImageTarget = "missingBeatImage";
|
|
75
|
+
export declare const unsupportedImageTypeTarget = "unsupportedImageType";
|
|
76
|
+
export declare const missingFirstFrameTarget = "missingFirstFrame";
|
|
72
77
|
export declare const agentFileNotExistError: (agentName: string, action: string, target: string, fileName: string, beatIndex?: number) => {
|
|
73
78
|
beatIndex?: number | undefined;
|
|
74
79
|
type: string;
|
|
@@ -115,6 +120,13 @@ export declare const invalidAudioSourceError: (beatIndex: number) => {
|
|
|
115
120
|
agentName: string;
|
|
116
121
|
beatIndex: number;
|
|
117
122
|
};
|
|
123
|
+
export declare const beatImageSentinelError: (beatIndex: number, target: string) => {
|
|
124
|
+
type: string;
|
|
125
|
+
action: string;
|
|
126
|
+
target: string;
|
|
127
|
+
agentName: string;
|
|
128
|
+
beatIndex: number;
|
|
129
|
+
};
|
|
118
130
|
export declare const downLoadReferenceImageError: (key: string, url: string) => {
|
|
119
131
|
type: string;
|
|
120
132
|
action: string;
|
package/lib/utils/error_cause.js
CHANGED
|
@@ -56,6 +56,7 @@ export const apiRateLimitErrorType = "apiRateLimit";
|
|
|
56
56
|
export const apiKeyMissingType = "apiKeyMissing";
|
|
57
57
|
export const invalidResponseType = "invalidResponse";
|
|
58
58
|
export const voiceLimitReachedType = "voice_limit_reached";
|
|
59
|
+
export const invalidFrameSentinelType = "invalidFrameSentinel";
|
|
59
60
|
// Actions
|
|
60
61
|
export const movieAction = "movie";
|
|
61
62
|
export const imageAction = "images";
|
|
@@ -72,6 +73,10 @@ export const multiLingualFileTarget = "multiLingualFile";
|
|
|
72
73
|
export const videoSourceTarget = "videoSource";
|
|
73
74
|
export const audioSourceTarget = "audioSource";
|
|
74
75
|
export const codeTextTarget = "codeText";
|
|
76
|
+
export const bothFramesTarget = "bothFrames";
|
|
77
|
+
export const missingBeatImageTarget = "missingBeatImage";
|
|
78
|
+
export const unsupportedImageTypeTarget = "unsupportedImageType";
|
|
79
|
+
export const missingFirstFrameTarget = "missingFirstFrame";
|
|
75
80
|
// Agent File Not Exist Errors
|
|
76
81
|
export const agentFileNotExistError = (agentName, action, target, fileName, beatIndex) => {
|
|
77
82
|
return {
|
|
@@ -111,6 +116,16 @@ export const invalidAudioSourceError = (beatIndex) => {
|
|
|
111
116
|
beatIndex,
|
|
112
117
|
};
|
|
113
118
|
};
|
|
119
|
+
// $beatImage frame sentinel misuse (invalid combination or beat cannot supply its own image)
|
|
120
|
+
export const beatImageSentinelError = (beatIndex, target) => {
|
|
121
|
+
return {
|
|
122
|
+
type: invalidFrameSentinelType,
|
|
123
|
+
action: imageAction,
|
|
124
|
+
target,
|
|
125
|
+
agentName: "imagePreprocessAgent",
|
|
126
|
+
beatIndex,
|
|
127
|
+
};
|
|
128
|
+
};
|
|
114
129
|
// 404
|
|
115
130
|
export const downLoadReferenceImageError = (key, url) => {
|
|
116
131
|
return {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "mulmocast",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.9.0",
|
|
4
4
|
"description": "",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "lib/index.node.js",
|
|
@@ -89,7 +89,7 @@
|
|
|
89
89
|
"homepage": "https://github.com/receptron/mulmocast-cli#readme",
|
|
90
90
|
"dependencies": {
|
|
91
91
|
"@google-cloud/text-to-speech": "^6.4.1",
|
|
92
|
-
"@google/genai": "^2.
|
|
92
|
+
"@google/genai": "^2.12.0",
|
|
93
93
|
"@graphai/anthropic_agent": "^2.0.12",
|
|
94
94
|
"@graphai/browserless_agent": "^2.0.3",
|
|
95
95
|
"@graphai/gemini_agent": "^2.0.5",
|
|
@@ -102,7 +102,7 @@
|
|
|
102
102
|
"@inquirer/input": "^5.1.2",
|
|
103
103
|
"@inquirer/select": "^5.2.1",
|
|
104
104
|
"@modelcontextprotocol/sdk": "^1.29.0",
|
|
105
|
-
"@modernized/fluent-ffmpeg": "^1.0.
|
|
105
|
+
"@modernized/fluent-ffmpeg": "^1.0.2",
|
|
106
106
|
"@mozilla/readability": "^0.6.0",
|
|
107
107
|
"@mulmocast/deck": "^1.1.0",
|
|
108
108
|
"@tavily/core": "^0.7.6",
|