mulmocast 2.6.23 → 2.7.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +24 -0
- package/lib/actions/bundle.js +2 -1
- package/lib/actions/pdf.js +4 -1
- package/lib/agents/image_genai_agent.js +5 -2
- package/lib/agents/image_openai_agent.js +2 -1
- package/lib/agents/image_replicate_agent.js +2 -1
- package/lib/agents/lipsync_replicate_agent.js +2 -1
- package/lib/agents/media_mock_agent.js +2 -1
- package/lib/agents/movie_genai_agent.js +17 -3
- package/lib/agents/movie_replicate_agent.js +2 -1
- package/lib/agents/sound_effect_replicate_agent.js +6 -2
- package/lib/agents/tts_elevenlabs_agent.js +4 -3
- package/lib/agents/tts_gemini_agent.js +4 -1
- package/lib/agents/tts_kotodama_agent.js +7 -3
- package/lib/cli/commands/audio/builder.d.ts +2 -14
- package/lib/cli/commands/audio/builder.js +2 -2
- package/lib/cli/commands/audio/handler.d.ts +2 -0
- package/lib/cli/commands/audio/handler.js +5 -1
- package/lib/cli/commands/image/builder.d.ts +2 -14
- package/lib/cli/commands/image/builder.js +2 -2
- package/lib/cli/commands/image/handler.d.ts +2 -0
- package/lib/cli/commands/image/handler.js +5 -1
- package/lib/cli/commands/movie/builder.d.ts +2 -14
- package/lib/cli/commands/movie/builder.js +2 -2
- package/lib/cli/commands/movie/handler.d.ts +2 -0
- package/lib/cli/commands/movie/handler.js +5 -1
- package/lib/cli/commands/pdf/builder.d.ts +2 -14
- package/lib/cli/commands/pdf/builder.js +2 -2
- package/lib/cli/commands/pdf/handler.d.ts +2 -0
- package/lib/cli/commands/pdf/handler.js +5 -1
- package/lib/cli/commands/translate/builder.d.ts +3 -15
- package/lib/cli/commands/translate/builder.js +2 -2
- package/lib/cli/commands/translate/handler.d.ts +2 -0
- package/lib/cli/commands/translate/handler.js +5 -1
- package/lib/cli/common.d.ts +5 -0
- package/lib/cli/common.js +13 -0
- package/lib/cli/helpers.d.ts +2 -0
- package/lib/cli/helpers.js +19 -0
- package/lib/index.common.d.ts +2 -0
- package/lib/index.common.js +2 -0
- package/lib/methods/mulmo_media_source.js +9 -13
- package/lib/methods/mulmo_presentation_style.d.ts +2 -0
- package/lib/methods/mulmo_presentation_style.js +9 -6
- package/lib/types/index.d.ts +1 -0
- package/lib/types/index.js +1 -0
- package/lib/types/provider2agent.d.ts +17 -0
- package/lib/types/provider2agent.js +79 -0
- package/lib/types/usage.d.ts +21 -0
- package/lib/utils/estimate_usage.d.ts +11 -0
- package/lib/utils/estimate_usage.js +332 -0
- package/lib/utils/estimate_usage_format.d.ts +2 -0
- package/lib/utils/estimate_usage_format.js +64 -0
- package/lib/utils/fetch.d.ts +28 -0
- package/lib/utils/fetch.js +52 -0
- package/lib/utils/file.js +2 -1
- package/lib/utils/image_plugins/bg_image_util.js +7 -20
- package/lib/utils/openai_client.js +6 -0
- package/lib/utils/replicate_usage.d.ts +4 -1
- package/lib/utils/replicate_usage.js +25 -5
- package/lib/utils/sdk_timeout.d.ts +3 -0
- package/lib/utils/sdk_timeout.js +7 -0
- package/lib/utils/zip.js +2 -2
- package/package.json +13 -12
|
@@ -1,10 +1,14 @@
|
|
|
1
1
|
import { translate } from "../../../actions/index.js";
|
|
2
|
-
import { dumpUsageIfRequested, initializeContext } from "../../helpers.js";
|
|
2
|
+
import { dumpUsageIfRequested, initializeContext, printUsageEstimate } from "../../helpers.js";
|
|
3
3
|
export const handler = async (argv) => {
|
|
4
4
|
const context = await initializeContext(argv);
|
|
5
5
|
if (!context) {
|
|
6
6
|
process.exit(1);
|
|
7
7
|
}
|
|
8
|
+
if (argv.estimate) {
|
|
9
|
+
printUsageEstimate(context, "translate", argv.json);
|
|
10
|
+
return;
|
|
11
|
+
}
|
|
8
12
|
await translate(context);
|
|
9
13
|
dumpUsageIfRequested(context);
|
|
10
14
|
};
|
package/lib/cli/common.d.ts
CHANGED
package/lib/cli/common.js
CHANGED
|
@@ -1,4 +1,17 @@
|
|
|
1
1
|
import { languages } from "../types/const.js";
|
|
2
|
+
export const estimateOptions = (yargs) => {
|
|
3
|
+
return yargs
|
|
4
|
+
.option("estimate", {
|
|
5
|
+
describe: "Estimate API usage (tokens / characters / seconds / cost) and exit without generating",
|
|
6
|
+
type: "boolean",
|
|
7
|
+
default: false,
|
|
8
|
+
})
|
|
9
|
+
.option("json", {
|
|
10
|
+
describe: "With --estimate, print the raw JSON records instead of a table",
|
|
11
|
+
type: "boolean",
|
|
12
|
+
default: false,
|
|
13
|
+
});
|
|
14
|
+
};
|
|
2
15
|
export const commonOptions = (yargs) => {
|
|
3
16
|
return yargs
|
|
4
17
|
.option("o", {
|
package/lib/cli/helpers.d.ts
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
|
+
import { type EstimateAction } from "../utils/estimate_usage.js";
|
|
1
2
|
import type { CliArgs } from "../types/cli_types.js";
|
|
2
3
|
import { FileObject, InitOptions, MulmoStudioContext } from "../types/index.js";
|
|
3
4
|
export declare const runTranslateIfNeeded: (context: MulmoStudioContext, includeCaption?: boolean) => Promise<void>;
|
|
4
5
|
export declare const dumpUsageIfRequested: (context: MulmoStudioContext) => void;
|
|
6
|
+
export declare const printUsageEstimate: (context: MulmoStudioContext, action: EstimateAction, asJson?: boolean) => void;
|
|
5
7
|
export declare const setGraphAILogger: (verbose: boolean | undefined, logValues?: Record<string, unknown>) => void;
|
|
6
8
|
export declare const getFileObject: (args: {
|
|
7
9
|
basedir?: string;
|
package/lib/cli/helpers.js
CHANGED
|
@@ -8,6 +8,8 @@ import { outDirName, imageDirName, audioDirName } from "../types/const.js";
|
|
|
8
8
|
import { MulmoStudioContextMethods } from "../methods/mulmo_studio_context.js";
|
|
9
9
|
import { translate } from "../actions/translate.js";
|
|
10
10
|
import { initializeContextFromFiles } from "../utils/context.js";
|
|
11
|
+
import { estimateUsage, actionEstimateProcesses } from "../utils/estimate_usage.js";
|
|
12
|
+
import { formatUsageEstimates } from "../utils/estimate_usage_format.js";
|
|
11
13
|
export const runTranslateIfNeeded = async (context, includeCaption = false) => {
|
|
12
14
|
if (MulmoStudioContextMethods.needTranslate(context, includeCaption)) {
|
|
13
15
|
GraphAILogger.log("run translate");
|
|
@@ -60,6 +62,23 @@ export const dumpUsageIfRequested = (context) => {
|
|
|
60
62
|
fs.writeFileSync(setting, json, "utf-8");
|
|
61
63
|
GraphAILogger.info(`usage written to ${setting}`);
|
|
62
64
|
};
|
|
65
|
+
// Pre-run estimate for --estimate: scoped to what the invoked action would consume,
|
|
66
|
+
// resolved with the same context (script, presentation style, -l, caption lang) as a real run.
|
|
67
|
+
export const printUsageEstimate = (context, action, asJson = false) => {
|
|
68
|
+
const script = context.studio.script;
|
|
69
|
+
const lang = context.lang;
|
|
70
|
+
// The caption language triggers translation only where the real run does:
|
|
71
|
+
// movie (runTranslateIfNeeded with includeCaption) and the translate action itself.
|
|
72
|
+
const captionLang = action === "movie" || action === "translate" ? script.captionParams?.lang : undefined;
|
|
73
|
+
const targetLangs = [...new Set([lang, captionLang].filter((l) => !!l))];
|
|
74
|
+
const records = estimateUsage(script, {
|
|
75
|
+
presentationStyle: context.presentationStyle,
|
|
76
|
+
langs: lang ? [lang] : undefined,
|
|
77
|
+
targetLangs,
|
|
78
|
+
processes: actionEstimateProcesses[action],
|
|
79
|
+
});
|
|
80
|
+
GraphAILogger.info(asJson ? JSON.stringify(records, null, 2) : formatUsageEstimates(records));
|
|
81
|
+
};
|
|
63
82
|
export const setGraphAILogger = (verbose, logValues) => {
|
|
64
83
|
if (verbose) {
|
|
65
84
|
if (logValues) {
|
package/lib/index.common.d.ts
CHANGED
|
@@ -5,6 +5,8 @@ export * from "./utils/string.js";
|
|
|
5
5
|
export * from "./utils/utils.js";
|
|
6
6
|
export * from "./utils/prompt.js";
|
|
7
7
|
export * from "./utils/error_cause.js";
|
|
8
|
+
export * from "./utils/estimate_usage.js";
|
|
9
|
+
export * from "./utils/estimate_usage_format.js";
|
|
8
10
|
export * from "./methods/mulmo_presentation_style.js";
|
|
9
11
|
export * from "./methods/mulmo_script.js";
|
|
10
12
|
export * from "./methods/mulmo_studio_context.js";
|
package/lib/index.common.js
CHANGED
|
@@ -6,6 +6,8 @@ export * from "./utils/string.js";
|
|
|
6
6
|
export * from "./utils/utils.js";
|
|
7
7
|
export * from "./utils/prompt.js";
|
|
8
8
|
export * from "./utils/error_cause.js";
|
|
9
|
+
export * from "./utils/estimate_usage.js";
|
|
10
|
+
export * from "./utils/estimate_usage_format.js";
|
|
9
11
|
export * from "./methods/mulmo_presentation_style.js";
|
|
10
12
|
export * from "./methods/mulmo_script.js";
|
|
11
13
|
export * from "./methods/mulmo_studio_context.js";
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import fs from "fs";
|
|
2
2
|
import { GraphAILogger, assert } from "graphai";
|
|
3
3
|
import { getFullPath, getReferenceImagePath, resolveAssetPath } from "../utils/file.js";
|
|
4
|
+
import { safeFetch, DEFAULT_FETCH_TIMEOUT_MS } from "../utils/fetch.js";
|
|
4
5
|
import { downLoadReferenceImageError, getTextError, imageReferenceUnknownMediaError, downloadImagePluginError, imagePluginUnknownMediaError, mediaSourceToDataUrlError, mediaSourceFileNotFoundError, mediaSourceUnknownKindError, } from "../utils/error_cause.js";
|
|
5
6
|
// for image reference
|
|
6
7
|
export const getExtention = (contentType, url) => {
|
|
@@ -18,7 +19,7 @@ export const getExtention = (contentType, url) => {
|
|
|
18
19
|
return "png"; // default
|
|
19
20
|
};
|
|
20
21
|
const downLoadReferenceImage = async (context, key, url) => {
|
|
21
|
-
const response = await
|
|
22
|
+
const response = await safeFetch(url);
|
|
22
23
|
assert(response.ok, `Failed to download reference image: ${url}`, false, downLoadReferenceImageError(key, url));
|
|
23
24
|
const buffer = Buffer.from(await response.arrayBuffer());
|
|
24
25
|
// Detect file extension from Content-Type header or URL
|
|
@@ -38,26 +39,21 @@ function pluginSourceFixExtention(path, imageType) {
|
|
|
38
39
|
return path;
|
|
39
40
|
}
|
|
40
41
|
// end of util
|
|
41
|
-
const DEFAULT_FETCH_TIMEOUT_MS = 30000;
|
|
42
42
|
// Convert URL to data URL (base64 encoded)
|
|
43
43
|
const urlToDataUrl = async (url, timeoutMs = DEFAULT_FETCH_TIMEOUT_MS) => {
|
|
44
|
-
const controller = new AbortController();
|
|
45
|
-
const timeoutId = setTimeout(() => controller.abort(), timeoutMs);
|
|
46
44
|
try {
|
|
47
|
-
const response = await
|
|
45
|
+
const response = await safeFetch(url, {}, timeoutMs);
|
|
48
46
|
assert(response.ok, `Failed to fetch: ${url}`, false, mediaSourceToDataUrlError(url));
|
|
49
47
|
const buffer = Buffer.from(await response.arrayBuffer());
|
|
50
48
|
const contentType = response.headers.get("content-type") || "image/png";
|
|
51
49
|
return `data:${contentType};base64,${buffer.toString("base64")}`;
|
|
52
50
|
}
|
|
53
51
|
catch (error) {
|
|
54
|
-
|
|
55
|
-
|
|
52
|
+
// assert() failures already carry the structured cause — keep it as-is.
|
|
53
|
+
if (error instanceof Error && error.cause) {
|
|
54
|
+
throw error;
|
|
56
55
|
}
|
|
57
|
-
throw new Error(`Fetch failed: ${url}`, { cause: mediaSourceToDataUrlError(url) });
|
|
58
|
-
}
|
|
59
|
-
finally {
|
|
60
|
-
clearTimeout(timeoutId);
|
|
56
|
+
throw new Error(`Fetch failed: ${url}: ${error instanceof Error ? error.message : String(error)}`, { cause: mediaSourceToDataUrlError(url) });
|
|
61
57
|
}
|
|
62
58
|
};
|
|
63
59
|
/** Map file extension to MIME type for data URLs */
|
|
@@ -92,7 +88,7 @@ export const MulmoMediaSourceMethods = {
|
|
|
92
88
|
return mediaSource.text;
|
|
93
89
|
}
|
|
94
90
|
if (mediaSource.kind === "url") {
|
|
95
|
-
const response = await
|
|
91
|
+
const response = await safeFetch(mediaSource.url);
|
|
96
92
|
assert(response.ok, `Failed to download mermaid code text: ${mediaSource.url}`, false, getTextError(mediaSource.url)); // TODO: index
|
|
97
93
|
return await response.text();
|
|
98
94
|
}
|
|
@@ -126,7 +122,7 @@ export const MulmoMediaSourceMethods = {
|
|
|
126
122
|
},
|
|
127
123
|
async imagePluginSource(mediaSource, context, expectImagePath, imageType) {
|
|
128
124
|
if (mediaSource.kind === "url") {
|
|
129
|
-
const response = await
|
|
125
|
+
const response = await safeFetch(mediaSource.url);
|
|
130
126
|
assert(response.ok, `Failed to download image plugin: ${imageType} ${mediaSource.url}`, false, downloadImagePluginError(mediaSource.url, imageType)); // TODO: key, id, index
|
|
131
127
|
const buffer = Buffer.from(await response.arrayBuffer());
|
|
132
128
|
// Detect file extension from Content-Type header or URL
|
|
@@ -27,6 +27,7 @@ export declare const MulmoPresentationStyleMethods: {
|
|
|
27
27
|
*/
|
|
28
28
|
getResolvedSlideTheme(presentationStyle: MulmoPresentationStyle, beat: MulmoBeat): SlideTheme;
|
|
29
29
|
getDefaultSpeaker(presentationStyle: MulmoPresentationStyle): string;
|
|
30
|
+
getSpeakerData(presentationStyle: MulmoPresentationStyle, beat: MulmoBeat, lang: string | undefined): SpeakerData;
|
|
30
31
|
getSpeaker(context: MulmoStudioContext, beat: MulmoBeat, targetLang: string | undefined): SpeakerData;
|
|
31
32
|
getMovieTransition(context: MulmoStudioContext, beat: MulmoBeat): MulmoTransition | null;
|
|
32
33
|
getText2ImageProvider(provider: Text2ImageProvider | undefined): Text2ImageProvider;
|
|
@@ -222,6 +223,7 @@ export declare const MulmoPresentationStyleMethods: {
|
|
|
222
223
|
video?: string;
|
|
223
224
|
audio: string;
|
|
224
225
|
image?: string;
|
|
226
|
+
price_per_sec?: number;
|
|
225
227
|
}>;
|
|
226
228
|
};
|
|
227
229
|
/** Concurrency for image/movie generation graph (uses min of imageParams/movieParams) */
|
|
@@ -73,19 +73,22 @@ export const MulmoPresentationStyleMethods = {
|
|
|
73
73
|
}
|
|
74
74
|
return keys[0];
|
|
75
75
|
},
|
|
76
|
-
|
|
77
|
-
userAssert(!!
|
|
78
|
-
const speakerId = beat?.speaker ?? MulmoPresentationStyleMethods.getDefaultSpeaker(
|
|
79
|
-
const speaker =
|
|
76
|
+
getSpeakerData(presentationStyle, beat, lang) {
|
|
77
|
+
userAssert(!!presentationStyle?.speechParams?.speakers, "presentationStyle.speechParams.speakers is not set!!");
|
|
78
|
+
const speakerId = beat?.speaker ?? MulmoPresentationStyleMethods.getDefaultSpeaker(presentationStyle);
|
|
79
|
+
const speaker = presentationStyle.speechParams.speakers[speakerId];
|
|
80
80
|
userAssert(!!speaker, `speaker is not set: speaker "${speakerId}"`);
|
|
81
81
|
// Check if the speaker has a language-specific version.
|
|
82
|
-
// Normally, lang is determined by the context, but lang may be specified when using the API.
|
|
83
|
-
const lang = targetLang ?? context.lang ?? context?.studio?.script?.lang;
|
|
84
82
|
if (speaker.lang && lang && speaker.lang[lang]) {
|
|
85
83
|
return speaker.lang[lang];
|
|
86
84
|
}
|
|
87
85
|
return speaker;
|
|
88
86
|
},
|
|
87
|
+
getSpeaker(context, beat, targetLang) {
|
|
88
|
+
// Normally, lang is determined by the context, but lang may be specified when using the API.
|
|
89
|
+
const lang = targetLang ?? context.lang ?? context?.studio?.script?.lang;
|
|
90
|
+
return MulmoPresentationStyleMethods.getSpeakerData(context.presentationStyle, beat, lang);
|
|
91
|
+
},
|
|
89
92
|
getMovieTransition(context, beat) {
|
|
90
93
|
const transitionData = beat.movieParams?.transition ?? context.presentationStyle.movieParams?.transition;
|
|
91
94
|
if (!transitionData)
|
package/lib/types/index.d.ts
CHANGED
package/lib/types/index.js
CHANGED
|
@@ -158,6 +158,7 @@ export declare const provider2LipSyncAgent: {
|
|
|
158
158
|
video?: string;
|
|
159
159
|
audio: string;
|
|
160
160
|
image?: string;
|
|
161
|
+
price_per_sec?: number;
|
|
161
162
|
}>;
|
|
162
163
|
};
|
|
163
164
|
};
|
|
@@ -213,4 +214,20 @@ export declare const llm: (keyof typeof provider2LLMAgent)[];
|
|
|
213
214
|
export type LLM = keyof typeof provider2LLMAgent;
|
|
214
215
|
export declare const htmlLLMProvider: string[];
|
|
215
216
|
export declare const getModelDuration: (provider: keyof typeof provider2MovieAgent, model: string, movieDuration?: number) => number | undefined;
|
|
217
|
+
export type ModelPricing = {
|
|
218
|
+
unit: "tokens" | "chars" | "seconds" | "images";
|
|
219
|
+
inputPerMTokensUSD?: number;
|
|
220
|
+
outputPerMTokensUSD?: number;
|
|
221
|
+
perMCharsUSD?: number;
|
|
222
|
+
perSecUSD?: number;
|
|
223
|
+
perImageUSD?: number;
|
|
224
|
+
asOf: string;
|
|
225
|
+
};
|
|
226
|
+
export declare const gptImageOutputTokens: Record<string, {
|
|
227
|
+
low: number;
|
|
228
|
+
medium: number;
|
|
229
|
+
high: number;
|
|
230
|
+
}>;
|
|
231
|
+
export declare const modelPricing: Record<string, Record<string, ModelPricing>>;
|
|
232
|
+
export declare const getModelPricing: (provider: string, model: string) => ModelPricing | undefined;
|
|
216
233
|
export {};
|
|
@@ -517,3 +517,82 @@ export const getModelDuration = (provider, model, movieDuration) => {
|
|
|
517
517
|
}
|
|
518
518
|
return durations?.[0];
|
|
519
519
|
};
|
|
520
|
+
// gpt-image-1 / gpt-image-1-mini emit a fixed number of image output tokens per size × quality.
|
|
521
|
+
// Later gpt-image models (1.5 / 2) use variable resolutions, so this table is only an approximation for them.
|
|
522
|
+
// Source: https://developers.openai.com/api/docs/guides/image-generation (verified 2026-07-03)
|
|
523
|
+
export const gptImageOutputTokens = {
|
|
524
|
+
"1024x1024": { low: 272, medium: 1056, high: 4160 },
|
|
525
|
+
"1024x1536": { low: 408, medium: 1584, high: 6240 },
|
|
526
|
+
"1536x1024": { low: 400, medium: 1568, high: 6208 },
|
|
527
|
+
};
|
|
528
|
+
// Spot-checked against replicate.com model pages on this date (seedance-1-lite, kling-v2.1, omni-human).
|
|
529
|
+
// Some replicate prices vary by resolution/variant; price_per_sec holds the rate for the typical configuration
|
|
530
|
+
// (e.g. seedance-1-lite at 720p, kling-v2.1 standard).
|
|
531
|
+
const REPLICATE_PRICING_AS_OF = "2026-07-03";
|
|
532
|
+
const replicateMoviePricing = Object.fromEntries(Object.entries(provider2MovieAgent.replicate.modelParams).map(([model, params]) => [
|
|
533
|
+
model,
|
|
534
|
+
{ unit: "seconds", perSecUSD: params.price_per_sec, asOf: REPLICATE_PRICING_AS_OF },
|
|
535
|
+
]));
|
|
536
|
+
const replicateLipSyncPricing = Object.fromEntries(Object.entries(provider2LipSyncAgent.replicate.modelParams)
|
|
537
|
+
.filter(([, params]) => params.price_per_sec !== undefined)
|
|
538
|
+
.map(([model, params]) => [model, { unit: "seconds", perSecUSD: params.price_per_sec, asOf: REPLICATE_PRICING_AS_OF }]));
|
|
539
|
+
export const modelPricing = {
|
|
540
|
+
openai: {
|
|
541
|
+
// https://developers.openai.com/api/docs/models/gpt-4o-mini-tts
|
|
542
|
+
"gpt-4o-mini-tts": { unit: "tokens", inputPerMTokensUSD: 0.6, outputPerMTokensUSD: 12, asOf: "2026-07-03" },
|
|
543
|
+
"tts-1": { unit: "chars", perMCharsUSD: 15, asOf: "2026-07-03" },
|
|
544
|
+
"tts-1-hd": { unit: "chars", perMCharsUSD: 30, asOf: "2026-07-03" },
|
|
545
|
+
// https://developers.openai.com/api/docs/models/gpt-image-1 (image input tokens, $10/1M, are not modeled)
|
|
546
|
+
"gpt-image-1": { unit: "tokens", inputPerMTokensUSD: 5, outputPerMTokensUSD: 40, asOf: "2026-07-03" },
|
|
547
|
+
"gpt-image-1.5": { unit: "tokens", inputPerMTokensUSD: 5, outputPerMTokensUSD: 32, asOf: "2026-07-03" },
|
|
548
|
+
"gpt-image-2": { unit: "tokens", inputPerMTokensUSD: 5, outputPerMTokensUSD: 30, asOf: "2026-07-03" },
|
|
549
|
+
"gpt-image-1-mini": { unit: "tokens", inputPerMTokensUSD: 2, outputPerMTokensUSD: 8, asOf: "2026-07-03" },
|
|
550
|
+
// https://developers.openai.com/api/docs/models/gpt-5 etc.
|
|
551
|
+
"gpt-5": { unit: "tokens", inputPerMTokensUSD: 1.25, outputPerMTokensUSD: 10, asOf: "2026-07-03" },
|
|
552
|
+
"gpt-5-mini": { unit: "tokens", inputPerMTokensUSD: 0.25, outputPerMTokensUSD: 2, asOf: "2026-07-03" },
|
|
553
|
+
"gpt-4o": { unit: "tokens", inputPerMTokensUSD: 2.5, outputPerMTokensUSD: 10, asOf: "2026-07-03" },
|
|
554
|
+
},
|
|
555
|
+
gemini: {
|
|
556
|
+
// https://ai.google.dev/gemini-api/docs/pricing
|
|
557
|
+
"gemini-2.5-flash-preview-tts": { unit: "tokens", inputPerMTokensUSD: 0.5, outputPerMTokensUSD: 10, asOf: "2026-07-03" },
|
|
558
|
+
"gemini-2.5-pro-preview-tts": { unit: "tokens", inputPerMTokensUSD: 1, outputPerMTokensUSD: 20, asOf: "2026-07-03" },
|
|
559
|
+
},
|
|
560
|
+
google: {
|
|
561
|
+
// https://ai.google.dev/gemini-api/docs/pricing ($0.039/image ≒ 1290 output tokens at $30/1M)
|
|
562
|
+
"gemini-2.5-flash-image": { unit: "images", inputPerMTokensUSD: 0.3, perImageUSD: 0.039, asOf: "2026-07-03" },
|
|
563
|
+
// Veo per second of generated video (720p). veo-2.0-generate-001 and veo-3.0-generate-001
|
|
564
|
+
// were shut down on 2026-06-30, so they intentionally have no price.
|
|
565
|
+
"veo-3.1-generate-preview": { unit: "seconds", perSecUSD: 0.4, asOf: "2026-07-03" },
|
|
566
|
+
"veo-3.1-lite-generate-preview": { unit: "seconds", perSecUSD: 0.05, asOf: "2026-07-03" },
|
|
567
|
+
// Google Cloud Text-to-Speech per 1M characters, keyed by voice tier.
|
|
568
|
+
// https://cloud.google.com/text-to-speech/pricing
|
|
569
|
+
"tts-standard": { unit: "chars", perMCharsUSD: 4, asOf: "2026-07-03" },
|
|
570
|
+
"tts-wavenet": { unit: "chars", perMCharsUSD: 4, asOf: "2026-07-03" },
|
|
571
|
+
"tts-neural2": { unit: "chars", perMCharsUSD: 16, asOf: "2026-07-03" },
|
|
572
|
+
"tts-chirp3-hd": { unit: "chars", perMCharsUSD: 30, asOf: "2026-07-03" },
|
|
573
|
+
"tts-studio": { unit: "chars", perMCharsUSD: 160, asOf: "2026-07-03" },
|
|
574
|
+
},
|
|
575
|
+
anthropic: {
|
|
576
|
+
// https://platform.claude.com/docs/en/docs/about-claude/pricing
|
|
577
|
+
"claude-sonnet-4-5-20250929": { unit: "tokens", inputPerMTokensUSD: 3, outputPerMTokensUSD: 15, asOf: "2026-07-03" },
|
|
578
|
+
},
|
|
579
|
+
elevenlabs: {
|
|
580
|
+
// https://elevenlabs.io/pricing/api ($0.10 per 1k chars for multilingual/v3, $0.05 for flash/turbo)
|
|
581
|
+
eleven_v3: { unit: "chars", perMCharsUSD: 100, asOf: "2026-07-03" },
|
|
582
|
+
eleven_multilingual_v2: { unit: "chars", perMCharsUSD: 100, asOf: "2026-07-03" },
|
|
583
|
+
eleven_turbo_v2_5: { unit: "chars", perMCharsUSD: 50, asOf: "2026-07-03" },
|
|
584
|
+
eleven_turbo_v2: { unit: "chars", perMCharsUSD: 50, asOf: "2026-07-03" },
|
|
585
|
+
eleven_flash_v2_5: { unit: "chars", perMCharsUSD: 50, asOf: "2026-07-03" },
|
|
586
|
+
eleven_flash_v2: { unit: "chars", perMCharsUSD: 50, asOf: "2026-07-03" },
|
|
587
|
+
},
|
|
588
|
+
replicate: {
|
|
589
|
+
...replicateMoviePricing,
|
|
590
|
+
...replicateLipSyncPricing,
|
|
591
|
+
// https://replicate.com/bytedance/seedream-4 ($0.03 per output image)
|
|
592
|
+
// zsxkib/mmaudio is intentionally unpriced: it bills by GPU time (~$0.005 per run), not by clip duration.
|
|
593
|
+
"bytedance/seedream-4": { unit: "images", perImageUSD: 0.03, asOf: "2026-07-03" },
|
|
594
|
+
},
|
|
595
|
+
};
|
|
596
|
+
export const getModelPricing = (provider, model) => {
|
|
597
|
+
return modelPricing[provider]?.[model];
|
|
598
|
+
};
|
package/lib/types/usage.d.ts
CHANGED
|
@@ -30,3 +30,24 @@ export type UsageCollectorAPI = {
|
|
|
30
30
|
clear(): void;
|
|
31
31
|
readonly size: number;
|
|
32
32
|
};
|
|
33
|
+
export type EstimatePrecision = "exact" | "estimated";
|
|
34
|
+
export type EstimatedMetric = {
|
|
35
|
+
value: number;
|
|
36
|
+
precision: EstimatePrecision;
|
|
37
|
+
};
|
|
38
|
+
export type UsageEstimateProcess = "tts" | "image" | "htmlImage" | "movie" | "soundEffect" | "lipSync" | "translate" | "imageReference" | "movieReference";
|
|
39
|
+
export type UsageEstimate = {
|
|
40
|
+
process: UsageEstimateProcess;
|
|
41
|
+
beatIndex?: number;
|
|
42
|
+
refKey?: string;
|
|
43
|
+
lang?: string;
|
|
44
|
+
provider: string;
|
|
45
|
+
model: string;
|
|
46
|
+
inputTokens?: EstimatedMetric;
|
|
47
|
+
outputTokens?: EstimatedMetric;
|
|
48
|
+
inputChars?: EstimatedMetric;
|
|
49
|
+
predictSec?: EstimatedMetric;
|
|
50
|
+
imageCount?: EstimatedMetric;
|
|
51
|
+
costUSD?: number;
|
|
52
|
+
pricingAsOf?: string;
|
|
53
|
+
};
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { MulmoScript, MulmoPresentationStyle } from "../types/index.js";
|
|
2
|
+
import type { UsageEstimate, UsageEstimateProcess } from "../types/usage.js";
|
|
3
|
+
export type EstimateUsageOptions = {
|
|
4
|
+
langs?: string[];
|
|
5
|
+
targetLangs?: string[];
|
|
6
|
+
presentationStyle?: MulmoPresentationStyle;
|
|
7
|
+
processes?: UsageEstimateProcess[];
|
|
8
|
+
};
|
|
9
|
+
export type EstimateAction = "audio" | "images" | "pdf" | "movie" | "translate";
|
|
10
|
+
export declare const actionEstimateProcesses: Record<EstimateAction, UsageEstimateProcess[]>;
|
|
11
|
+
export declare const estimateUsage: (script: MulmoScript, options?: EstimateUsageOptions) => UsageEstimate[];
|