Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
73 changes: 53 additions & 20 deletions lib/estimate.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,44 +9,71 @@
// Prices MIRROR the live @blockrun/mcp tables (src/tools/{image,video,music,
// speech,realface}.ts). Keep them in sync when the MCP catalog changes.

// Per-image USD by model. Base = 1024x1024; larger renders cost more on the
// models in IMAGE_LARGE (matches image.ts GENERATE_MODEL_COST / LARGE_SIZE_COST).
// Per-image USD by model. Base = 1024x1024 list price; larger renders cost
// more on the models in IMAGE_LARGE (matches image.ts GENERATE_MODEL_COST /
// LARGE_SIZE_COST / LARGE_SIZE_THRESHOLD / LARGE_SIZE_MIN_DIM_MODELS).
// Refreshed against @blockrun/mcp and the live catalog on 2026-10-20.
const IMAGE = {
"zai/cogview-4": 0.015,
"xai/grok-imagine-image": 0.02,
"openai/gpt-image-1": 0.02,
"xai/grok-imagine-image-2.0": 0.04,
"bytedance/seedream-5-pro": 0.045,
"google/nano-banana": 0.05,
"xai/grok-imagine-image-pro": 0.05,
"openai/gpt-image-2": 0.06,
"xai/grok-imagine-image-pro": 0.07,
"google/nano-banana-2": 0.09,
"google/nano-banana-pro": 0.10,
"openai/gpt-image-2.5-flare": 0.28,
"openai/gpt-image-2.5-sunburst": 0.28,
};
const IMAGE_DEFAULT = 0.06; // openai/gpt-image-2 (the MCP default)
const IMAGE_LARGE = {
"openai/gpt-image-1": 0.04,
"openai/gpt-image-2": 0.12,
"openai/gpt-image-2.5-flare": 0.56,
"openai/gpt-image-2.5-sunburst": 0.56,
"google/nano-banana-pro": 0.15, // 4096px tier
"bytedance/seedream-5-pro": 0.09, // both sides > 1024
};
// Where each model's large tier starts, in max(w,h); 1024 when unlisted.
const IMAGE_LARGE_THRESHOLD = {
"google/nano-banana-pro": 2048, // same price through 2048x2048
};
// Seedream's large tier is keyed on the SMALLER side, not the larger one.
const IMAGE_LARGE_MIN_DIM = new Set(["bytedance/seedream-5-pro"]);

// Per-second USD by video model — text-to-video rate, and the cheaper
// image-to-video tier when a seed image / RealFace asset is supplied
// (matches video.ts VIDEO_PRICE_PER_SECOND / _IMAGE).
// Per-second USD by video model, as CHARGED (margin included) at the model's
// default resolution, and per resolution where the model prices it. Seedance
// is token-priced upstream; these are the per-second figures GET /v1/models
// publishes at the 720p baseline, rounded UP to 4dp. Image-to-video costs
// the same as text-to-video on every model (video.ts).
const VIDEO_RATE = {
"xai/grok-imagine-video": 0.05,
"bytedance/seedance-1.5-pro": 0.092,
"bytedance/seedance-2.0-fast": 0.238,
"bytedance/seedance-2.0": 0.298,
"azure/sora-2": 0.10,
"xai/grok-imagine-video-1.5": 0.08,
"bytedance/seedance-1.5-pro": 0.0708,
"bytedance/seedance-2.0-mini": 0.0798,
"bytedance/seedance-2.0-fast": 0.1652,
"bytedance/seedance-2.0": 0.2271,
"bytedance/seedance-2.5": 0.3156,
"azure/sora-2": 0.105,
};
const VIDEO_RATE_IMAGE = {
"bytedance/seedance-2.0-fast": 0.140,
"bytedance/seedance-2.0": 0.183,
// Grok bills per resolution, no margin (video.ts GROK_PRICE_PER_SECOND).
const VIDEO_RATE_BY_RES = {
"xai/grok-imagine-video": { "480p": 0.05, "720p": 0.07 },
"xai/grok-imagine-video-1.5": { "480p": 0.08, "720p": 0.14, "1080p": 0.25 },
};
// Seedance token cost scales with resolution (video.ts RESOLUTION_TOKEN_FACTOR).
const SEEDANCE_RES_FACTOR = { "480p": 0.5, "720p": 1, "1080p": 2.25, "4K": 9 };
const VIDEO_DEFAULT_RATE = 0.05; // xai/grok-imagine-video (the MCP default)
const VIDEO_DEFAULT_SECS = { // per-model default duration
"xai/grok-imagine-video": 8,
"xai/grok-imagine-video-1.5": 8,
"bytedance/seedance-1.5-pro": 5,
"bytedance/seedance-2.0-mini": 5,
"bytedance/seedance-2.0-fast": 5,
"bytedance/seedance-2.0": 5,
"bytedance/seedance-2.5": 5,
"azure/sora-2": 4,
};

Expand All @@ -58,15 +85,19 @@ const SPEECH_RATE = {
"elevenlabs/turbo-v2.5": 0.05,
"elevenlabs/multilingual-v2": 0.10,
"elevenlabs/v3": 0.10,
"bytedance/seed-audio-1.0": 0.30, // display rate; bills per output second
};
const SPEECH_DEFAULT_RATE = 0.05; // elevenlabs/flash-v2.5 (the MCP default)
const SOUND_EFFECT_COST = 0.0525;

// A dimension >1024 pushes gpt-image-* / nano-banana-pro into the large tier.
function isLargerThanBase(size) {
// Whether `size` is in `model`'s large tier (see IMAGE_LARGE_THRESHOLD /
// IMAGE_LARGE_MIN_DIM). Unparseable sizes price at the base.
function isLargerThanBase(model, size) {
const m = /^\s*(\d+)\s*[x×]\s*(\d+)\s*$/i.exec(size || "");
if (!m) return false;
return Math.max(Number(m[1]), Number(m[2])) > 1024;
const threshold = IMAGE_LARGE_THRESHOLD[model] ?? 1024;
const pick = IMAGE_LARGE_MIN_DIM.has(model) ? Math.min : Math.max;
return pick(Number(m[1]), Number(m[2])) > threshold;
}

// Map a full MCP tool name (mcp__<server>__blockrun_<tool>) to the bare tool.
Expand All @@ -81,16 +112,18 @@ function estimate(toolName, input = {}) {
case "image": {
const model = input.model || "openai/gpt-image-2";
let usd = IMAGE[model] ?? IMAGE_DEFAULT;
if (IMAGE_LARGE[model] && isLargerThanBase(input.size)) usd = IMAGE_LARGE[model];
if (IMAGE_LARGE[model] && isLargerThanBase(model, input.size)) usd = IMAGE_LARGE[model];
return { usd, label: `image · ${model}` };
}
case "video": {
const model = input.model || "xai/grok-imagine-video";
const hasImage = Boolean(input.image_url || input.real_face_asset_id);
const rate = (hasImage ? VIDEO_RATE_IMAGE[model] : undefined)
?? VIDEO_RATE[model] ?? VIDEO_DEFAULT_RATE;
const res = input.resolution;
let rate = VIDEO_RATE[model] ?? VIDEO_DEFAULT_RATE;
if (res && VIDEO_RATE_BY_RES[model]?.[res]) rate = VIDEO_RATE_BY_RES[model][res];
else if (res && model.startsWith("bytedance/seedance-") && SEEDANCE_RES_FACTOR[res]) rate *= SEEDANCE_RES_FACTOR[res];
const secs = Number(input.duration_seconds) || VIDEO_DEFAULT_SECS[model] || 8;
return { usd: rate * secs, label: `video · ${model} · ${secs}s${hasImage ? " (i2v)" : ""}` };
return { usd: rate * secs, label: `video · ${model} · ${secs}s${res ? ` · ${res}` : ""}${hasImage ? " (i2v)" : ""}` };
}
case "music":
return { usd: MUSIC_COST, label: `music · ${input.model || "minimax/music-2.5+"}` };
Expand Down
36 changes: 22 additions & 14 deletions skills/announce-cost/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,32 +24,40 @@ If the user asked for several images/clips at once, announce the **total**

## Estimated pricing (USD)

**Image** (`blockrun_image`, 1024² base; `hd`/larger sizes cost more):
**Image** (`blockrun_image`, 1024² base list price; larger sizes cost more where a range is shown):

| Model | ~Cost |
|-------|-------|
| `zai/cogview-4` | $0.015 |
| `xai/grok-imagine-image` | $0.02 |
| `openai/gpt-image-1` | $0.02–0.04 |
| `xai/grok-imagine-image-2.0` | $0.04 |
| `bytedance/seedream-5-pro` | $0.045–0.09 |
| `google/nano-banana` | $0.05 |
| `xai/grok-imagine-image-pro` | $0.07 |
| `xai/grok-imagine-image-pro` | $0.05 |
| `openai/gpt-image-2` (default) | $0.06–0.12 |
| `google/nano-banana-2` | $0.09 |
| `google/nano-banana-pro` | $0.10–0.15 |
| `openai/gpt-image-2.5-flare` | $0.28–0.56 |
| `openai/gpt-image-2.5-sunburst` | $0.28–0.56 |

**Video** (`blockrun_video`, per second × duration):
**Video** (`blockrun_video`, per second × duration, at the model's default resolution):

| Model | ~Cost/sec |
|-------|-----------|
| `bytedance/seedance-2.0-fast` | $0.05 |
| `bytedance/seedance-1.5-pro` | $0.09 |
| `bytedance/seedance-2.0` | $0.12 |
| `xai/grok-imagine-video` (default) | $0.10 |
| `azure/sora-2` | $0.10 |

e.g. `grok-imagine-video` 8s ≈ $0.80; `seedance-2.0-fast` 5s ≈ $0.25.

**Other:** `blockrun_music` ≈ $0.02/track · `blockrun_speech` TTS ≈ $0.07/1k chars,
sound effect ≈ $0.0525 · `blockrun_realface` enroll/portrait ≈ $0.01 · paid stock
`blockrun_price` ≈ $0.001.
| `xai/grok-imagine-video` (default) | $0.05 (480p) · $0.07 (720p) |
| `xai/grok-imagine-video-1.5` | $0.08 (480p) · $0.14 (720p) · $0.25 (1080p) |
| `bytedance/seedance-1.5-pro` | $0.071 |
| `bytedance/seedance-2.0-mini` | $0.080 |
| `bytedance/seedance-2.0-fast` | $0.165 |
| `bytedance/seedance-2.0` | $0.227 (720p; ~2.25× at 1080p, ~9× at 4K) |
| `bytedance/seedance-2.5` | $0.316 |
| `azure/sora-2` | $0.105 |

e.g. `grok-imagine-video` 8s ≈ $0.40; `seedance-2.0-fast` 5s ≈ $0.83.

**Other:** `blockrun_music` ≈ $0.16/track · `blockrun_speech` TTS ≈ $0.05/1k chars
(flash/turbo) or $0.10/1k (multilingual-v2, v3), sound effect ≈ $0.0525 ·
`blockrun_realface` enroll/portrait ≈ $0.01 · paid stock `blockrun_price` ≈ $0.001.

These are estimates to inform the user; the gateway settles the exact amount.