@koda-sl/baker-cli 0.146.1 → 0.147.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/README.md
CHANGED
|
@@ -2647,10 +2647,14 @@ baker canvas validate my-canvas.json
|
|
|
2647
2647
|
# Blocking issues exit non-zero. Advisory `warnings[]` (exit 0) flag likely-wrong-but-
|
|
2648
2648
|
# legal config to review before billing — a frame that describes a recurring element
|
|
2649
2649
|
# (a pet/product) but doesn't wire its reference (VIDEO_REFERENCE_MISSING), or a scene
|
|
2650
|
-
# span that exceeds the assigned model's max clip duration (VIDEO_SPAN_EXCEEDS_MODEL)
|
|
2650
|
+
# span that exceeds the assigned model's max clip duration (VIDEO_SPAN_EXCEEDS_MODEL),
|
|
2651
|
+
# or the same ad rendered once per aspect ratio instead of one hero + a terminal
|
|
2652
|
+
# image_aspect_adapt (IMAGE_PARALLEL_FORMAT_RENDERS — separate renders drift in type,
|
|
2653
|
+
# crop, and color, so the sizes stop matching).
|
|
2651
2654
|
|
|
2652
2655
|
# 2. Run (executes nodes, writes files to ./canvas/<run_id>/)
|
|
2653
2656
|
baker canvas run my-canvas.json
|
|
2657
|
+
# Advisory warnings print as `[warn] <CODE>: …` before anything bills.
|
|
2654
2658
|
# Only nodes reachable from the declared output execute (orphaned nodes are skipped,
|
|
2655
2659
|
# not billed). Add --keep-runs N to prune old r_* run dirs, keeping the N newest.
|
|
2656
2660
|
|
|
@@ -2681,7 +2685,7 @@ baker canvas run my-canvas.json --remote-cache off --no-record
|
|
|
2681
2685
|
# restructure the canvas (repointing output / deleting nodes) to trick the cache:
|
|
2682
2686
|
# • One-shot flag — forces the named nodes + everything downstream fresh this
|
|
2683
2687
|
# run, leaving every other node cached (unknown ids fail loudly before billing):
|
|
2684
|
-
baker canvas run my-canvas.json --regenerate
|
|
2688
|
+
baker canvas run my-canvas.json --regenerate gen,adapt
|
|
2685
2689
|
# • Persistent — add/bump a node's `regenerate` field in the canvas JSON
|
|
2686
2690
|
# (e.g. "regenerate": 2) and re-run; the fresh render is reproducible in any
|
|
2687
2691
|
# later session. Bump it again (3, 4, …) for each additional draw.
|
|
@@ -4152,8 +4156,8 @@ Re-run a creative's latest recorded canvas **from run history** — no local fil
|
|
|
4152
4156
|
| `--concurrency <n>` | — | Same as `canvas run --concurrency`. |
|
|
4153
4157
|
|
|
4154
4158
|
```bash
|
|
4155
|
-
baker canvas rerun spring-offer
|
|
4156
|
-
baker canvas rerun spring-offer
|
|
4159
|
+
baker canvas rerun spring-offer
|
|
4160
|
+
baker canvas rerun spring-offer --force-remote --regenerate gen
|
|
4157
4161
|
```
|
|
4158
4162
|
|
|
4159
4163
|
Use it when a creative was built in another conversation (or its sandbox is gone) and you need to continue or re-render it here. Files the snapshot could not include (missing at run time, or oversized) are listed as warnings — supply those locally only if the run actually needs to regenerate the nodes that read them.
|
|
@@ -2762,7 +2762,8 @@ var STAGE_CODES = {
|
|
|
2762
2762
|
REGION_DROPPED: "VIDEO_REGION_DROPPED",
|
|
2763
2763
|
OVERLAY_OUT_OF_BOUNDS: "VIDEO_OVERLAY_OUT_OF_BOUNDS",
|
|
2764
2764
|
ORPHAN_NODE: "ORPHAN_NODE",
|
|
2765
|
-
INGEST_PATH_URL_HEALED: "INGEST_PATH_URL_HEALED"
|
|
2765
|
+
INGEST_PATH_URL_HEALED: "INGEST_PATH_URL_HEALED",
|
|
2766
|
+
PARALLEL_FORMAT_RENDERS: "IMAGE_PARALLEL_FORMAT_RENDERS"
|
|
2766
2767
|
};
|
|
2767
2768
|
var SPAN_MODEL_SLACK_S = 0.25;
|
|
2768
2769
|
var VIDEO_TIME_SLACK_S = 0.75;
|
|
@@ -2828,6 +2829,7 @@ function validateCanvas(input, registry) {
|
|
|
2828
2829
|
const estimatedCredits = estimateCredits(ctx);
|
|
2829
2830
|
checkOutputRef(ctx);
|
|
2830
2831
|
checkOrphanNodes(ctx);
|
|
2832
|
+
checkParallelFormatRenders(ctx);
|
|
2831
2833
|
checkVideoInvariants(ctx);
|
|
2832
2834
|
const hasBlocking = issues.some(isBlocking);
|
|
2833
2835
|
if (hasBlocking) return { ok: false, issues };
|
|
@@ -3719,6 +3721,65 @@ function checkAspectConsistency(ctx) {
|
|
|
3719
3721
|
message: `video_generate nodes disagree on aspect_ratio (${[...ratios].sort().join(", ")}) \u2014 the spine's segments would render at different shapes and the composite silently crops. Pin every clip to the same aspect_ratio`
|
|
3720
3722
|
});
|
|
3721
3723
|
}
|
|
3724
|
+
var SAME_CREATIVE_PROMPT_SIMILARITY = 0.6;
|
|
3725
|
+
function promptWordSet(text) {
|
|
3726
|
+
return new Set(
|
|
3727
|
+
text.toLowerCase().split(/[^a-z0-9']+/).filter((w) => w.length >= 3)
|
|
3728
|
+
);
|
|
3729
|
+
}
|
|
3730
|
+
function jaccard(a, b) {
|
|
3731
|
+
if (a.size === 0 || b.size === 0) return 0;
|
|
3732
|
+
let shared = 0;
|
|
3733
|
+
for (const w of a) if (b.has(w)) shared++;
|
|
3734
|
+
return shared / (a.size + b.size - shared);
|
|
3735
|
+
}
|
|
3736
|
+
function parallelFormatRenderGroups(nodes) {
|
|
3737
|
+
const parent = nodes.map((_, i) => i);
|
|
3738
|
+
const find = (i) => {
|
|
3739
|
+
let root = i;
|
|
3740
|
+
while (parent[root] !== root) root = parent[root];
|
|
3741
|
+
return root;
|
|
3742
|
+
};
|
|
3743
|
+
const words = nodes.map((n) => promptWordSet(n.prompt));
|
|
3744
|
+
for (let i = 0; i < nodes.length; i++) {
|
|
3745
|
+
for (let j = i + 1; j < nodes.length; j++) {
|
|
3746
|
+
const a = nodes[i];
|
|
3747
|
+
const b = nodes[j];
|
|
3748
|
+
if (a.ratio === b.ratio) continue;
|
|
3749
|
+
if (jaccard(words[i], words[j]) < SAME_CREATIVE_PROMPT_SIMILARITY) continue;
|
|
3750
|
+
parent[find(i)] = find(j);
|
|
3751
|
+
}
|
|
3752
|
+
}
|
|
3753
|
+
const groups = /* @__PURE__ */ new Map();
|
|
3754
|
+
for (let i = 0; i < nodes.length; i++) {
|
|
3755
|
+
const root = find(i);
|
|
3756
|
+
const group = groups.get(root) ?? [];
|
|
3757
|
+
group.push(nodes[i]);
|
|
3758
|
+
groups.set(root, group);
|
|
3759
|
+
}
|
|
3760
|
+
return [...groups.values()].filter((g) => g.length > 1);
|
|
3761
|
+
}
|
|
3762
|
+
function checkParallelFormatRenders(ctx) {
|
|
3763
|
+
const candidates = [];
|
|
3764
|
+
for (const n of ctx.canvas.nodes) {
|
|
3765
|
+
if (n.type !== "image_generate") continue;
|
|
3766
|
+
const params = n.params;
|
|
3767
|
+
const prompt = params?.prompt;
|
|
3768
|
+
if (typeof prompt !== "string" || prompt.length === 0) continue;
|
|
3769
|
+
candidates.push({ id: n.id, prompt, ratio: String(params?.aspect_ratio ?? "(unset)") });
|
|
3770
|
+
}
|
|
3771
|
+
if (candidates.length < 2) return;
|
|
3772
|
+
for (const group of parallelFormatRenderGroups(candidates)) {
|
|
3773
|
+
const ids = group.map((g) => g.id);
|
|
3774
|
+
const ratios = [...new Set(group.map((g) => g.ratio))].sort();
|
|
3775
|
+
ctx.issues.push({
|
|
3776
|
+
path: "nodes",
|
|
3777
|
+
code: STAGE_CODES.PARALLEL_FORMAT_RENDERS,
|
|
3778
|
+
severity: "warning",
|
|
3779
|
+
message: `${ids.join(", ")} render the same creative once per aspect ratio (${ratios.join(", ")}) \u2014 each is an independent generation, so the formats come back with different type, crop, and color and stop reading as one ad. Keep ONE hero render and add a terminal image_aspect_adapt node fed from it (params.formats or params.platform) so every ratio is re-framed from the same image`
|
|
3780
|
+
});
|
|
3781
|
+
}
|
|
3782
|
+
}
|
|
3722
3783
|
function checkOrphanNodes(ctx) {
|
|
3723
3784
|
const out = ctx.canvas.output;
|
|
3724
3785
|
if (!out || !ctx.idToIndex.has(out.node)) return;
|
|
@@ -7837,4 +7898,4 @@ export {
|
|
|
7837
7898
|
defaultRegistry,
|
|
7838
7899
|
createEngineFromEnv
|
|
7839
7900
|
};
|
|
7840
|
-
//# sourceMappingURL=chunk-
|
|
7901
|
+
//# sourceMappingURL=chunk-2F5SCTNS.js.map
|