@koda-sl/baker-cli 0.148.0 → 0.149.0-dev.328f015d3
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 +23 -25
- package/dist/{chunk-2F5SCTNS.js → chunk-O3VYLCJK.js} +135 -34
- package/dist/chunk-O3VYLCJK.js.map +1 -0
- package/dist/cli.js +91 -26
- package/dist/cli.js.map +1 -1
- package/dist/engine/index.js +1 -1
- package/package.json +1 -1
- package/dist/chunk-2F5SCTNS.js.map +0 -1
package/README.md
CHANGED
|
@@ -1643,6 +1643,8 @@ baker images stock "office reaction" --people only
|
|
|
1643
1643
|
|
|
1644
1644
|
Free tier exists but watermarks previews — pass `--license freemium` to filter to clean free assets explicitly.
|
|
1645
1645
|
|
|
1646
|
+
An empty result comes back with `hints` naming the filters that narrowed the search plus the fallbacks (`baker images find --sources …`, `baker images generate`); a provider failure returns an `error.fix` pointing at the same exits. `baker images google` does the same, minus the cross-provider fallback (it is the last-resort provider).
|
|
1647
|
+
|
|
1646
1648
|
**Flags:**
|
|
1647
1649
|
|
|
1648
1650
|
| Flag | Description |
|
|
@@ -1724,13 +1726,15 @@ baker images generate "flat geometric mascot, brand palette" \
|
|
|
1724
1726
|
| Model | Best for | Aspect ratios | Sizes |
|
|
1725
1727
|
|---|---|---|---|
|
|
1726
1728
|
| `google/gemini-3.1-flash-image-preview` **(default)** | Fast iteration, reference editing, extreme aspect ratios (Nano Banana flash) | standard **+** `1:4` `4:1` `1:8` `8:1` | `0.5K`–`4K` |
|
|
1727
|
-
| `google/gemini-3.5-flash` | Fast; extreme aspect ratios | standard **+** `1:4` `4:1` `1:8` `8:1` | `0.5K`–`4K` |
|
|
1728
1729
|
| `google/gemini-3-pro-image-preview` | Highest fidelity (Nano Banana Pro) — strong prompt adherence and reference grounding | standard set | `1K` `2K` `4K` |
|
|
1729
|
-
| `openai/gpt-
|
|
1730
|
+
| `openai/gpt-image-2` | Photoreal + cleanest in-image text — ad/landing reproduction | standard set **minus** `4:5` `5:4` | *(none — derived from the ratio)* |
|
|
1731
|
+
| `openai/gpt-5.4-image-2` | **Legacy** — accepted so canvases authored before the gpt-image-2 switch still re-run. Don't pick it for new work. | standard set | `1K` `2K` `4K` (ignored upstream) |
|
|
1730
1732
|
| `recraft/recraft-v4.1-pro-vector` | Vector/flat/SVG-style with palette control | standard set | `1K` `2K` `4K` |
|
|
1731
1733
|
|
|
1732
1734
|
Standard aspect ratios: `1:1` `2:3` `3:2` `3:4` `4:3` `4:5` `5:4` `9:16` `16:9` `21:9`.
|
|
1733
1735
|
|
|
1736
|
+
`openai/gpt-image-2` accepts neither `image_size` nor the `4:5` / `5:4` portrait ratios — OpenAI picks the pixel dimensions from the ratio. To ship a 4:5 Meta feed ad from it, render the hero at `3:4` and adapt with `image_aspect_adapt`; the scaffolders do this for you.
|
|
1737
|
+
|
|
1734
1738
|
**Flags:**
|
|
1735
1739
|
|
|
1736
1740
|
| Flag | Description |
|
|
@@ -2747,7 +2751,7 @@ A re-run with no changes hits the cache for every node — total runtime drops t
|
|
|
2747
2751
|
"type": "text_generate",
|
|
2748
2752
|
"inputs": { "topic": "$ref:topic.text" },
|
|
2749
2753
|
"params": {
|
|
2750
|
-
"model": "google/gemini-3.
|
|
2754
|
+
"model": "google/gemini-3.1-flash-image-preview",
|
|
2751
2755
|
"prompt": "Write a 6-word tagline for {{topic}}."
|
|
2752
2756
|
}
|
|
2753
2757
|
}
|
|
@@ -3046,37 +3050,31 @@ Aspect-ratio sets used below:
|
|
|
3046
3050
|
|
|
3047
3051
|
---
|
|
3048
3052
|
|
|
3049
|
-
###### Model: `openai/gpt-
|
|
3053
|
+
###### Model: `openai/gpt-image-2`
|
|
3050
3054
|
|
|
3051
|
-
Photorealistic generalist. Optional `reference` image.
|
|
3055
|
+
Photorealistic generalist, and the cleanest at rendering text inside the image. Optional `reference` image (up to 16).
|
|
3052
3056
|
|
|
3053
3057
|
| Name | Type | Required | Notes |
|
|
3054
3058
|
|---|---|---|---|
|
|
3055
|
-
| `model` | literal | yes | `"openai/gpt-
|
|
3059
|
+
| `model` | literal | yes | `"openai/gpt-image-2"` |
|
|
3056
3060
|
| `prompt` | string | yes | non-empty |
|
|
3057
|
-
| `aspect_ratio` | enum | no |
|
|
3058
|
-
| `image_size` | enum | no | `1K \| 2K \| 4K` |
|
|
3061
|
+
| `aspect_ratio` | enum | no | GPT AR = `1:1, 2:3, 3:2, 3:4, 4:3, 9:16, 16:9, 21:9` — **no `4:5` / `5:4`** |
|
|
3059
3062
|
| `quality` | enum | no | `auto \| low \| medium \| high` — rendering quality; the scaffolder sets `high` for photoreal frames. (Do **not** pass `input_fidelity`: gpt-image-2 forces high fidelity automatically and the param can fail the request.) |
|
|
3060
3063
|
|
|
3064
|
+
No `image_size`: OpenAI derives the pixel dimensions from the ratio, so a size is ignored. For a portrait ad format, render at `3:4` and adapt.
|
|
3065
|
+
|
|
3061
3066
|
```json
|
|
3062
3067
|
{ "id": "hero", "type": "image_generate",
|
|
3063
|
-
"params": { "model": "openai/gpt-
|
|
3068
|
+
"params": { "model": "openai/gpt-image-2", "prompt": "Photorealistic photo of …", "aspect_ratio": "16:9", "quality": "high" } }
|
|
3064
3069
|
```
|
|
3065
3070
|
|
|
3066
|
-
###### Model: `
|
|
3067
|
-
|
|
3068
|
-
Newest Gemini Flash image model. Always returns an image output. Best for fast iteration; supports extreme aspect ratios and the `0.5K` size for cheap previews.
|
|
3071
|
+
###### Model: `openai/gpt-5.4-image-2` (legacy)
|
|
3069
3072
|
|
|
3070
|
-
|
|
3071
|
-
|---|---|---|---|
|
|
3072
|
-
| `model` | literal | yes | `"google/gemini-3.5-flash"` |
|
|
3073
|
-
| `prompt` | string | yes | non-empty |
|
|
3074
|
-
| `aspect_ratio` | enum | no | EXTREME AR |
|
|
3075
|
-
| `image_size` | enum | no | `0.5K \| 1K \| 2K \| 4K` |
|
|
3073
|
+
Kept only so canvases authored before the gpt-image-2 switch still validate and re-run unchanged — `baker canvas rerun` replays a recorded canvas verbatim. It keeps the full standard ratio set (including `4:5` / `5:4`) and still accepts `image_size`, which the provider ignores. Prefer `openai/gpt-image-2` for anything new.
|
|
3076
3074
|
|
|
3077
3075
|
###### Model: `google/gemini-3.1-flash-image-preview`
|
|
3078
3076
|
|
|
3079
|
-
|
|
3077
|
+
Gemini flash image model — the default. Fast iteration, reference editing, extreme aspect ratios, and the `0.5K` size for cheap previews.
|
|
3080
3078
|
|
|
3081
3079
|
| Name | Type | Required | Notes |
|
|
3082
3080
|
|---|---|---|---|
|
|
@@ -3293,7 +3291,7 @@ There is no `prompt` param — the extraction schema is the node. Use `focus` to
|
|
|
3293
3291
|
"params": { "model": "~google/gemini-pro-latest" } },
|
|
3294
3292
|
{ "id": "remix", "type": "image_generate",
|
|
3295
3293
|
"inputs": { "blueprint": "$ref:blueprint.description" },
|
|
3296
|
-
"params": { "model": "openai/gpt-
|
|
3294
|
+
"params": { "model": "openai/gpt-image-2",
|
|
3297
3295
|
"prompt": "Recreate an image matching this exact blueprint, but swap the product for a ceramic mug:\n{{blueprint}}" } }
|
|
3298
3296
|
],
|
|
3299
3297
|
"output": { "node": "remix", "output": "images" }
|
|
@@ -3374,9 +3372,9 @@ Fail-fast: an invalid model selection (wrong count, duplicate or out-of-range pi
|
|
|
3374
3372
|
{
|
|
3375
3373
|
"schema": "baker-canvas/1",
|
|
3376
3374
|
"nodes": [
|
|
3377
|
-
{ "id": "a", "type": "image_generate", "params": { "model": "google/gemini-3.
|
|
3378
|
-
{ "id": "b", "type": "image_generate", "params": { "model": "google/gemini-3.
|
|
3379
|
-
{ "id": "c", "type": "image_generate", "params": { "model": "google/gemini-3.
|
|
3375
|
+
{ "id": "a", "type": "image_generate", "params": { "model": "google/gemini-3.1-flash-image-preview", "prompt": "hero shot of a ceramic mug, warm morning light" } },
|
|
3376
|
+
{ "id": "b", "type": "image_generate", "params": { "model": "google/gemini-3.1-flash-image-preview", "prompt": "hero shot of a ceramic mug, studio softbox" } },
|
|
3377
|
+
{ "id": "c", "type": "image_generate", "params": { "model": "google/gemini-3.1-flash-image-preview", "prompt": "hero shot of a ceramic mug, dramatic side light" } },
|
|
3380
3378
|
{ "id": "best", "type": "image_select",
|
|
3381
3379
|
"inputs": { "images": ["$ref:a.images#0", "$ref:b.images#0", "$ref:c.images#0"] },
|
|
3382
3380
|
"params": { "model": "~google/gemini-pro-latest", "prompt": "most premium-feeling lighting, no harsh shadows" } }
|
|
@@ -4078,7 +4076,7 @@ baker canvas run ./reference-ad.video.canvas.json
|
|
|
4078
4076
|
| `--focus <text>` | — | Known provenance/emphasis to ground the deconstruct. |
|
|
4079
4077
|
| `--deconstruct-model <id>` | `~google/gemini-pro-latest` | Override the `video_deconstruct` model. |
|
|
4080
4078
|
| `--select-model <id>` | `~google/gemini-flash-latest` | Override the element-selection `text_generate` model. |
|
|
4081
|
-
| `--image-model <id>` | `openai/gpt-
|
|
4079
|
+
| `--image-model <id>` | `openai/gpt-image-2` | Override the per-frame `image_generate` model (defaults to the strongest, matching `scaffold-static-ad`). |
|
|
4082
4080
|
| `--video-model <id>` | scored router | Override the `video_generate` model **and skip the router**. Curated roster: `bytedance/seedance-2.0` (workhorse), `google/veo-3.1` (cine ceiling + real-face), `google/veo-3.1-fast` (cheap Veo), `kwaivgi/kling-v3.0-pro` (motion-transfer). |
|
|
4083
4081
|
| `--real-face` | auto-detected | Force the Veo route for a photoreal human presenter (dodges the ByteDance real-person filter). **Usually unnecessary** — the scaffold auto-detects a photoreal on-camera person/animal cast from the deconstruct and routes it to Veo on its own; set this only to force it when the cast isn't detected. Covers AI-generated photoreal faces too, not just real ones. |
|
|
4084
4082
|
| `--motion-transfer` | off | Motion driven from a reference / hyper-dynamic → the router picks Kling. |
|
|
@@ -4135,7 +4133,7 @@ baker canvas run ./static-ad.canvas.json
|
|
|
4135
4133
|
| `--describe-model <id>` | registry default (`~google/gemini-pro-latest`) | Override the `image_describe` model. |
|
|
4136
4134
|
| `--select-model <id>` | registry default (`~google/gemini-flash-latest`) | Override the element-selection `text_generate` model. |
|
|
4137
4135
|
| `--layout-model <id>` | registry default (`~google/gemini-flash-latest`) | Override the global-layout `text_generate` model. |
|
|
4138
|
-
| `--gen-model <id>` | registry default (`openai/gpt-
|
|
4136
|
+
| `--gen-model <id>` | registry default (`openai/gpt-image-2`) | Override the `image_generate` model. |
|
|
4139
4137
|
| `--platform <channel>` | `meta` | `_definition.md` channel **and** the default placement set the hero fans out to (`meta`\|`google`\|`linkedin`\|`tiktok`\|`youtube`\|`x`\|`other`; `x`/`other` fan out to nothing). |
|
|
4140
4138
|
| `--placements <preset\|none>` | derived from `--platform` | Override the fan-out set: `meta_feed`\|`meta_feed_stories`\|`meta_all`\|`linkedin`\|`google_demandgen`\|`tiktok`\|`youtube`, or `none` for a single base ad. |
|
|
4141
4139
|
| `--aspect <ratio>` | inferred from the image, else `9:16` | Force the hero's aspect ratio (clamped into the placement set so it stays a free pass-through). |
|
|
@@ -7,9 +7,9 @@ import {
|
|
|
7
7
|
__toESM
|
|
8
8
|
} from "./chunk-RK67WL4O.js";
|
|
9
9
|
|
|
10
|
-
//
|
|
10
|
+
// ../../.pnpm-store/v10/links/@/safe-stable-stringify/2.5.0/810146e81bae4e3a061fe487864f2fde80c4b03b886877dc0f1fffbc6480b67e/node_modules/safe-stable-stringify/index.js
|
|
11
11
|
var require_safe_stable_stringify = __commonJS({
|
|
12
|
-
"
|
|
12
|
+
"../../.pnpm-store/v10/links/@/safe-stable-stringify/2.5.0/810146e81bae4e3a061fe487864f2fde80c4b03b886877dc0f1fffbc6480b67e/node_modules/safe-stable-stringify/index.js"(exports, module) {
|
|
13
13
|
"use strict";
|
|
14
14
|
var { hasOwnProperty } = Object.prototype;
|
|
15
15
|
var stringify = configure2();
|
|
@@ -1070,7 +1070,7 @@ function resolveAdaptFormats(params) {
|
|
|
1070
1070
|
return params.formats ?? [];
|
|
1071
1071
|
}
|
|
1072
1072
|
|
|
1073
|
-
//
|
|
1073
|
+
// ../../.pnpm-store/v10/links/@/safe-stable-stringify/2.5.0/810146e81bae4e3a061fe487864f2fde80c4b03b886877dc0f1fffbc6480b67e/node_modules/safe-stable-stringify/esm/wrapper.js
|
|
1074
1074
|
var import__ = __toESM(require_safe_stable_stringify(), 1);
|
|
1075
1075
|
var configure = import__.default.configure;
|
|
1076
1076
|
var wrapper_default = import__.default;
|
|
@@ -1114,10 +1114,12 @@ function normalizeForCanonical(value) {
|
|
|
1114
1114
|
// ../canvas-contract/src/registry.ts
|
|
1115
1115
|
var OPENROUTER_IMAGE_AR = ["1:1", "2:3", "3:2", "3:4", "4:3", "4:5", "5:4", "9:16", "16:9", "21:9"];
|
|
1116
1116
|
var OPENROUTER_IMAGE_AR_EXTREME = [...OPENROUTER_IMAGE_AR, "1:4", "4:1", "1:8", "8:1"];
|
|
1117
|
+
var OPENROUTER_IMAGE_AR_GPT = ["1:1", "2:3", "3:2", "3:4", "4:3", "9:16", "16:9", "21:9"];
|
|
1117
1118
|
var OPENROUTER_IMAGE_SIZE = ["1K", "2K", "4K"];
|
|
1118
1119
|
var OPENROUTER_IMAGE_SIZE_EXTENDED = ["0.5K", ...OPENROUTER_IMAGE_SIZE];
|
|
1119
1120
|
var OPENROUTER_IMAGE_QUALITY = ["auto", "low", "medium", "high"];
|
|
1120
1121
|
var SEEDANCE_DURATIONS = [4, 5, 6, 8, 10, 12, 15];
|
|
1122
|
+
var KLING_DURATIONS = [3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15];
|
|
1121
1123
|
var ELEVENLABS_OUTPUT_FORMATS = [
|
|
1122
1124
|
"mp3_22050_32",
|
|
1123
1125
|
"mp3_44100_32",
|
|
@@ -1134,8 +1136,8 @@ var REPLICATE_VIDEO_MIMES = ["video/mp4", "video/webm", "video/quicktime"];
|
|
|
1134
1136
|
var DECONSTRUCT_VIDEO_MIMES = ["video/mp4", "video/webm", "video/quicktime"];
|
|
1135
1137
|
var REPLICATE_AUDIO_MIMES = ["audio/wav", "audio/mpeg", "audio/mp3"];
|
|
1136
1138
|
var IMAGE_GENERATE_MODELS = [
|
|
1139
|
+
"openai/gpt-image-2",
|
|
1137
1140
|
"openai/gpt-5.4-image-2",
|
|
1138
|
-
"google/gemini-3.5-flash",
|
|
1139
1141
|
"google/gemini-3.1-flash-image-preview",
|
|
1140
1142
|
"google/gemini-3-pro-image-preview",
|
|
1141
1143
|
"recraft/recraft-v4.1-pro-vector"
|
|
@@ -1234,28 +1236,37 @@ var MODEL_REGISTRY = {
|
|
|
1234
1236
|
// `seed` is omitted on every entry — OpenRouter's `image_config` schema
|
|
1235
1237
|
// has no `seed` slot for image-gen (different from video-gen), so passing
|
|
1236
1238
|
// it would be a silent no-op.
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
+
// The direct image model, not the `gpt-5.4-image-2` chat model that emitted
|
|
1240
|
+
// images as a side effect. Notable capability gaps vs the Gemini entries:
|
|
1241
|
+
// no `image_size` (OpenAI picks the pixel dimensions from the ratio, and a
|
|
1242
|
+
// `resolution` is silently ignored) and the narrower `..._AR_GPT` ratio set.
|
|
1243
|
+
"openai/gpt-image-2": {
|
|
1244
|
+
label: "OpenAI GPT Image 2",
|
|
1239
1245
|
inputs: [],
|
|
1240
1246
|
optional_inputs: [{ kind: "image", mimes: OPENROUTER_IMAGE_MIMES }],
|
|
1241
1247
|
required: ["prompt"],
|
|
1242
1248
|
params: {
|
|
1243
1249
|
prompt: { kind: "string" },
|
|
1244
|
-
aspect_ratio: { kind: "string", enum:
|
|
1245
|
-
image_size: { kind: "string", enum: OPENROUTER_IMAGE_SIZE },
|
|
1250
|
+
aspect_ratio: { kind: "string", enum: OPENROUTER_IMAGE_AR_GPT },
|
|
1246
1251
|
quality: { kind: "string", enum: OPENROUTER_IMAGE_QUALITY }
|
|
1247
1252
|
}
|
|
1248
1253
|
},
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
1254
|
+
// LEGACY — kept so canvases authored before the gpt-image-2 switch still
|
|
1255
|
+
// validate and re-run unchanged (`baker canvas rerun` restores a recorded
|
|
1256
|
+
// canvas verbatim, so dropping this model would break every stored creative
|
|
1257
|
+
// that used it). Its param surface is the ORIGINAL one on purpose: unlike
|
|
1258
|
+
// gpt-image-2 it still honors 4:5/5:4, and `image_size` is accepted-then-
|
|
1259
|
+
// ignored upstream rather than rejected. Verified live on /api/v1/images.
|
|
1260
|
+
// Don't reach for it in new work — the scaffolders default to gpt-image-2.
|
|
1261
|
+
"openai/gpt-5.4-image-2": {
|
|
1262
|
+
label: "OpenAI GPT-5.4 Image 2 (legacy)",
|
|
1252
1263
|
inputs: [],
|
|
1253
1264
|
optional_inputs: [{ kind: "image", mimes: OPENROUTER_IMAGE_MIMES }],
|
|
1254
1265
|
required: ["prompt"],
|
|
1255
1266
|
params: {
|
|
1256
1267
|
prompt: { kind: "string" },
|
|
1257
|
-
aspect_ratio: { kind: "string", enum:
|
|
1258
|
-
image_size: { kind: "string", enum:
|
|
1268
|
+
aspect_ratio: { kind: "string", enum: OPENROUTER_IMAGE_AR },
|
|
1269
|
+
image_size: { kind: "string", enum: OPENROUTER_IMAGE_SIZE },
|
|
1259
1270
|
quality: { kind: "string", enum: OPENROUTER_IMAGE_QUALITY }
|
|
1260
1271
|
}
|
|
1261
1272
|
},
|
|
@@ -1402,7 +1413,7 @@ var MODEL_REGISTRY = {
|
|
|
1402
1413
|
prompt: { kind: "string" },
|
|
1403
1414
|
negative_prompt: { kind: "string" },
|
|
1404
1415
|
aspect_ratio: { kind: "string", enum: ["16:9", "9:16"] },
|
|
1405
|
-
resolution: { kind: "string", enum: ["720p", "1080p"] },
|
|
1416
|
+
resolution: { kind: "string", enum: ["720p", "1080p", "4K"] },
|
|
1406
1417
|
duration: { kind: "number", enum: [4, 6, 8] },
|
|
1407
1418
|
seed: { kind: "number" },
|
|
1408
1419
|
generate_audio: { kind: "boolean" },
|
|
@@ -1414,9 +1425,14 @@ var MODEL_REGISTRY = {
|
|
|
1414
1425
|
"kwaivgi/kling-v3.0-pro": {
|
|
1415
1426
|
// Motion-transfer / dynamic multi-shot beats. Reachable through the default
|
|
1416
1427
|
// OpenRouter gateway (generic video body — no google-vertex block), so it
|
|
1417
|
-
// needs no direct-provider exception.
|
|
1418
|
-
//
|
|
1419
|
-
//
|
|
1428
|
+
// needs no direct-provider exception. `cfg_scale` trades prompt adherence
|
|
1429
|
+
// vs motion freedom; higher = closer to prompt.
|
|
1430
|
+
//
|
|
1431
|
+
// Params below are the ones OpenRouter actually advertises for this model
|
|
1432
|
+
// (GET /api/v1/videos/models). Three were wrong before and each was a free
|
|
1433
|
+
// 400 or a silently unsupported knob: it renders 720p ONLY (1080p was
|
|
1434
|
+
// offered and rejected), it takes ANY duration from 3-15s (we allowed just
|
|
1435
|
+
// 5 and 10), and it does not support `seed` at all.
|
|
1420
1436
|
label: "Kling 3.0",
|
|
1421
1437
|
inputs: [],
|
|
1422
1438
|
optional_inputs: [{ kind: "image", mimes: OPENROUTER_IMAGE_MIMES }],
|
|
@@ -1426,10 +1442,9 @@ var MODEL_REGISTRY = {
|
|
|
1426
1442
|
// over-length prompt fails validate (free) not the billed call.
|
|
1427
1443
|
prompt: { kind: "string", maxLength: 2500 },
|
|
1428
1444
|
negative_prompt: { kind: "string" },
|
|
1429
|
-
aspect_ratio: { kind: "string", enum: ["
|
|
1430
|
-
resolution: { kind: "string", enum: ["720p"
|
|
1431
|
-
duration: { kind: "number", enum:
|
|
1432
|
-
seed: { kind: "number" },
|
|
1445
|
+
aspect_ratio: { kind: "string", enum: ["16:9", "9:16", "1:1"] },
|
|
1446
|
+
resolution: { kind: "string", enum: ["720p"] },
|
|
1447
|
+
duration: { kind: "number", enum: KLING_DURATIONS },
|
|
1433
1448
|
generate_audio: { kind: "boolean" },
|
|
1434
1449
|
cfg_scale: { kind: "number", min: 0, max: 1 }
|
|
1435
1450
|
}
|
|
@@ -1445,7 +1460,7 @@ var MODEL_REGISTRY = {
|
|
|
1445
1460
|
prompt: { kind: "string" },
|
|
1446
1461
|
negative_prompt: { kind: "string" },
|
|
1447
1462
|
aspect_ratio: { kind: "string", enum: ["16:9", "9:16"] },
|
|
1448
|
-
resolution: { kind: "string", enum: ["720p", "1080p"] },
|
|
1463
|
+
resolution: { kind: "string", enum: ["720p", "1080p", "4K"] },
|
|
1449
1464
|
duration: { kind: "number", enum: [4, 6, 8] },
|
|
1450
1465
|
seed: { kind: "number" },
|
|
1451
1466
|
generate_audio: { kind: "boolean" },
|
|
@@ -1607,6 +1622,36 @@ var MODEL_REGISTRY = {
|
|
|
1607
1622
|
}
|
|
1608
1623
|
}
|
|
1609
1624
|
};
|
|
1625
|
+
function supportsParam(kind, model, param) {
|
|
1626
|
+
return MODEL_REGISTRY[kind]?.[model]?.params[param] !== void 0;
|
|
1627
|
+
}
|
|
1628
|
+
function supportedAspectRatios(kind, model) {
|
|
1629
|
+
const schema = MODEL_REGISTRY[kind]?.[model]?.params.aspect_ratio;
|
|
1630
|
+
return schema?.kind === "string" ? schema.enum : void 0;
|
|
1631
|
+
}
|
|
1632
|
+
function ratioValue(ratio) {
|
|
1633
|
+
const [w, h] = ratio.split(":").map(Number);
|
|
1634
|
+
if (!w || !h || !Number.isFinite(w) || !Number.isFinite(h)) return null;
|
|
1635
|
+
return w / h;
|
|
1636
|
+
}
|
|
1637
|
+
function nearestSupportedAspectRatio(kind, model, target) {
|
|
1638
|
+
const supported = supportedAspectRatios(kind, model);
|
|
1639
|
+
if (!supported || supported.includes(target)) return target;
|
|
1640
|
+
const wanted = ratioValue(target);
|
|
1641
|
+
if (wanted === null) return target;
|
|
1642
|
+
let best = target;
|
|
1643
|
+
let bestDistance = Number.POSITIVE_INFINITY;
|
|
1644
|
+
for (const candidate of supported) {
|
|
1645
|
+
const value = ratioValue(candidate);
|
|
1646
|
+
if (value === null) continue;
|
|
1647
|
+
const distance2 = Math.abs(Math.log(value / wanted));
|
|
1648
|
+
if (distance2 < bestDistance) {
|
|
1649
|
+
bestDistance = distance2;
|
|
1650
|
+
best = candidate;
|
|
1651
|
+
}
|
|
1652
|
+
}
|
|
1653
|
+
return best;
|
|
1654
|
+
}
|
|
1610
1655
|
|
|
1611
1656
|
// ../canvas-contract/src/validateParams.ts
|
|
1612
1657
|
function isManagedNodeKind(nodeType) {
|
|
@@ -1737,6 +1782,41 @@ function validateValue(key, value, schema, model) {
|
|
|
1737
1782
|
}
|
|
1738
1783
|
}
|
|
1739
1784
|
|
|
1785
|
+
// ../canvas-contract/src/videoCost.ts
|
|
1786
|
+
var CREDITS_PER_USD = 100;
|
|
1787
|
+
var SEEDANCE_USD_PER_SECOND = {
|
|
1788
|
+
"480p": 0.18,
|
|
1789
|
+
"720p": 0.5,
|
|
1790
|
+
"1080p": 0.62,
|
|
1791
|
+
"4k": 0.62
|
|
1792
|
+
};
|
|
1793
|
+
var DEFAULT_VIDEO_RESOLUTION = "720p";
|
|
1794
|
+
var DEFAULT_VIDEO_DURATION_S = 5;
|
|
1795
|
+
function isSeedanceModel(model) {
|
|
1796
|
+
return model.startsWith("bytedance/seedance");
|
|
1797
|
+
}
|
|
1798
|
+
var FALLBACK_USD_PER_SECOND = 0.5;
|
|
1799
|
+
function seedanceUsdPerSecond(resolution) {
|
|
1800
|
+
return SEEDANCE_USD_PER_SECOND[resolution ?? DEFAULT_VIDEO_RESOLUTION] ?? FALLBACK_USD_PER_SECOND;
|
|
1801
|
+
}
|
|
1802
|
+
var USD_PER_SECOND = {
|
|
1803
|
+
"kwaivgi/kling-v3.0-pro": { silent: 0.112, audio: 0.168 },
|
|
1804
|
+
"google/veo-3.1": { silent: 0.2, audio: 0.4 },
|
|
1805
|
+
"google/veo-3.1-fast": { silent: 0.1, audio: 0.12 }
|
|
1806
|
+
};
|
|
1807
|
+
function estimateVideoCostUsd({ model, duration, resolution, generateAudio }) {
|
|
1808
|
+
const seconds = duration ?? DEFAULT_VIDEO_DURATION_S;
|
|
1809
|
+
if (isSeedanceModel(model)) return seedanceUsdPerSecond(resolution) * seconds;
|
|
1810
|
+
const published = USD_PER_SECOND[model];
|
|
1811
|
+
if (published) return (generateAudio ? published.audio : published.silent) * seconds;
|
|
1812
|
+
const dearestKnownRate = Math.max(...Object.values(SEEDANCE_USD_PER_SECOND));
|
|
1813
|
+
return dearestKnownRate * seconds;
|
|
1814
|
+
}
|
|
1815
|
+
function estimateVideoCredits(input) {
|
|
1816
|
+
const credits = estimateVideoCostUsd(input) * CREDITS_PER_USD;
|
|
1817
|
+
return Math.ceil(Number(credits.toFixed(6)));
|
|
1818
|
+
}
|
|
1819
|
+
|
|
1740
1820
|
// src/engine/lib/concurrency.ts
|
|
1741
1821
|
var DEFAULT_CONCURRENCY = 8;
|
|
1742
1822
|
function resolveConcurrency(...candidates) {
|
|
@@ -2533,7 +2613,7 @@ function looksLikeHttpUrl(value) {
|
|
|
2533
2613
|
var VEO_PERSON_GENERATION = "allow_adult";
|
|
2534
2614
|
var VEO_NEGATIVE_PROMPT = "subtitles, captions, on-screen text, watermark, logo, warped face, distorted hands, extra fingers, low quality";
|
|
2535
2615
|
var VEO_DURATIONS = [4, 6, 8];
|
|
2536
|
-
var
|
|
2616
|
+
var KLING_DURATIONS2 = [5, 10];
|
|
2537
2617
|
var KLING_NEGATIVE_PROMPT = "warped face, distorted hands, extra fingers, morphing, flicker, on-screen text, watermark, low quality";
|
|
2538
2618
|
var KLING_CFG_SCALE = 0.7;
|
|
2539
2619
|
var SPEAKS_PROSE = (line) => `The person speaks to camera; lip-sync follows the dialogue verbatim, with delivery and emotion carried in the wording itself (no bracketed cues). Dialogue: "${line}"`;
|
|
@@ -2579,7 +2659,7 @@ var KLING_PROFILE = {
|
|
|
2579
2659
|
// Kling takes a negative_prompt PARAM instead (paramDefaults).
|
|
2580
2660
|
keyframeInstruction: "Preserve the composition and colors of the first frame; animate the motion described.",
|
|
2581
2661
|
wordBudget: 200,
|
|
2582
|
-
durationSet:
|
|
2662
|
+
durationSet: KLING_DURATIONS2,
|
|
2583
2663
|
paramDefaults: { negative_prompt: KLING_NEGATIVE_PROMPT, cfg_scale: KLING_CFG_SCALE }
|
|
2584
2664
|
};
|
|
2585
2665
|
function clipProfileFor(modelId) {
|
|
@@ -6991,8 +7071,9 @@ var dialogueNode = delegated({
|
|
|
6991
7071
|
// src/engine/nodes/remote/image.ts
|
|
6992
7072
|
import { z as z17 } from "zod";
|
|
6993
7073
|
var IMAGE_GENERATE_MODELS2 = [
|
|
7074
|
+
"openai/gpt-image-2",
|
|
7075
|
+
// Legacy — see the registry entry; kept so pre-switch canvases still run.
|
|
6994
7076
|
"openai/gpt-5.4-image-2",
|
|
6995
|
-
"google/gemini-3.5-flash",
|
|
6996
7077
|
"google/gemini-3.1-flash-image-preview",
|
|
6997
7078
|
"google/gemini-3-pro-image-preview",
|
|
6998
7079
|
"recraft/recraft-v4.1-pro-vector"
|
|
@@ -7002,11 +7083,11 @@ var ImageGenerateParams = z17.object({
|
|
|
7002
7083
|
prompt: z17.string().min(1),
|
|
7003
7084
|
aspect_ratio: z17.enum(["1:1", "16:9", "9:16", "4:3", "3:4", "3:2", "2:3", "4:5", "5:4", "21:9", "1:4", "4:1", "1:8", "8:1"]).optional(),
|
|
7004
7085
|
image_size: z17.enum(["0.5K", "1K", "2K", "4K"]).optional(),
|
|
7005
|
-
// Rendering quality
|
|
7006
|
-
//
|
|
7086
|
+
// Rendering quality. OpenRouter models without a quality knob ignore it; the
|
|
7087
|
+
// registry gates which models accept it (gpt-image, Gemini).
|
|
7007
7088
|
quality: z17.enum(["auto", "low", "medium", "high"]).optional(),
|
|
7008
|
-
// Recraft v4 vector controls
|
|
7009
|
-
// rejects them on non-Recraft models.
|
|
7089
|
+
// Recraft v4 vector controls, sent as the provider's own `controls` block.
|
|
7090
|
+
// Registry rejects them on non-Recraft models.
|
|
7010
7091
|
strength: z17.number().min(0).max(1).optional(),
|
|
7011
7092
|
rgb_colors: z17.array(z17.array(z17.number().int().min(0).max(255))).optional(),
|
|
7012
7093
|
background_rgb_color: z17.array(z17.number().int().min(0).max(255)).optional()
|
|
@@ -7015,8 +7096,8 @@ var imageGenerateNode = delegated({
|
|
|
7015
7096
|
id: "image_generate",
|
|
7016
7097
|
version: "2.2.0",
|
|
7017
7098
|
category: "image",
|
|
7018
|
-
summary: "Generate images for ad creatives. Curated model set: GPT
|
|
7019
|
-
when_to_use: "Use for hero shots, product photography, illustrations, and vector logos. `recraft/recraft-v4.1-pro-vector` for crisp vector / logo work; `openai/gpt-
|
|
7099
|
+
summary: "Generate images for ad creatives. Curated model set: GPT Image 2, Gemini 3.1 Flash Image Preview, Gemini 3 Pro Image, Recraft v4.1 Pro Vector. Per-model param support comes from the canvas-engine model registry.",
|
|
7100
|
+
when_to_use: "Use for hero shots, product photography, illustrations, and vector logos. `recraft/recraft-v4.1-pro-vector` for crisp vector / logo work; `openai/gpt-image-2` for photorealistic and in-image text (NOTE: it has no `image_size`, and no 4:5 / 5:4 \u2014 route portrait ad formats through Gemini or `image_aspect_adapt`); Gemini variants for fast iteration, extreme ratios, and editing via the `reference` input. `reference` accepts ONE image or an ARRAY of images \u2014 wire several to combine references in a single generation (e.g. a subject sheet + a font specimen + the original ad). Every reference is forwarded to the model in array order.",
|
|
7020
7101
|
// `reference` is one image or an ordered array of images. The backend forwards
|
|
7021
7102
|
// each as a separate `image_url` to the provider (OpenRouter accepts many).
|
|
7022
7103
|
inputs: z17.object({ reference: z17.union([ImageRef, z17.array(ImageRef).min(1)]).optional() }).loose(),
|
|
@@ -7412,12 +7493,30 @@ var videoGenerateNode = delegated({
|
|
|
7412
7493
|
inputs: z28.object({
|
|
7413
7494
|
first_frame: ImageRef.optional(),
|
|
7414
7495
|
last_frame: ImageRef.optional(),
|
|
7415
|
-
reference: ImageRef.optional()
|
|
7496
|
+
reference: ImageRef.optional(),
|
|
7497
|
+
/**
|
|
7498
|
+
* Reference-to-video: several images the model uses as visual guidance
|
|
7499
|
+
* (this person, this product) instead of an exact opening frame. Mutually
|
|
7500
|
+
* exclusive with `first_frame` — OpenRouter treats a request carrying both
|
|
7501
|
+
* as image-to-video and ignores these, so wire one or the other.
|
|
7502
|
+
*/
|
|
7503
|
+
references: z28.array(ImageRef).optional()
|
|
7416
7504
|
}).loose(),
|
|
7417
7505
|
params: VideoGenerateParams,
|
|
7418
7506
|
outputs: z28.object({ video: VideoRef }).strict(),
|
|
7419
7507
|
outputKinds: { video: "video" },
|
|
7420
|
-
|
|
7508
|
+
// Priced from the shared contract, so `validate`'s quote and the charge the
|
|
7509
|
+
// backend applies come from ONE table. A flat number here under-quoted a
|
|
7510
|
+
// 1080p 8s clip by ~10x, and the agent reads this out to the user as "this
|
|
7511
|
+
// will cost N" before spending it.
|
|
7512
|
+
cost: ({ params }) => ({
|
|
7513
|
+
credits: estimateVideoCredits({
|
|
7514
|
+
model: params.model,
|
|
7515
|
+
duration: params.duration,
|
|
7516
|
+
resolution: params.resolution
|
|
7517
|
+
}),
|
|
7518
|
+
seconds_estimate: 120
|
|
7519
|
+
})
|
|
7421
7520
|
});
|
|
7422
7521
|
|
|
7423
7522
|
// src/engine/nodes/remote/videoBackgroundRemove.ts
|
|
@@ -7872,6 +7971,8 @@ export {
|
|
|
7872
7971
|
IMAGE_GENERATE_MODELS,
|
|
7873
7972
|
DEFAULT_VIDEO_GENERATE_MODEL,
|
|
7874
7973
|
MODEL_REGISTRY,
|
|
7974
|
+
supportsParam,
|
|
7975
|
+
nearestSupportedAspectRatio,
|
|
7875
7976
|
resolveConcurrency,
|
|
7876
7977
|
ulid,
|
|
7877
7978
|
isPersistedAssetRef,
|
|
@@ -7898,4 +7999,4 @@ export {
|
|
|
7898
7999
|
defaultRegistry,
|
|
7899
8000
|
createEngineFromEnv
|
|
7900
8001
|
};
|
|
7901
|
-
//# sourceMappingURL=chunk-
|
|
8002
|
+
//# sourceMappingURL=chunk-O3VYLCJK.js.map
|