fix: the 'reference did not fit' warning names each reference and why

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CHWxqHtxwWrq4kEyFTaHds
This commit is contained in:
Claude
2026-10-21 02:13:10 +00:00
parent 770962e209
commit 1d4c3a7584
3 changed files with 9 additions and 7 deletions
+3 -2
View File
@@ -5,6 +5,7 @@ import { QUEUED, RUNNING } from "../../shell/run.js";
import { inPlace } from "../../shell/place.js";
import { buildInputs, rolesAt } from "./inputs.js";
import { offer } from "../../shell/actions.js";
import { names } from "../../shell/bin.js";
const MAX_TAKES = 24;
const expand = (body) => fetch("/funpack/api/prompt/expand", { method: "POST", headers: { "Content-Type": "application/json" },
@@ -96,14 +97,14 @@ export default {
if (group.every((s) => s.excluded) || !isGenerative(root)) continue; // all left out, or not made by the model
// The unit is made once at the length of its clips together (each cut adds one shared frame).
const frames = group.reduce((t, s) => t + effFrames(s, snap), 0) - (group.length - 1);
const { inputs, unwired, noPrompt, notes } = await buildInputs({ project: snap, scene: root, slots: frozen, expand: same, frames, hooks: app.inputHooks, ...(fixedSeed ? { seed: null } : {}), prefix: (app.promptPrefix || []).flatMap((f) => { try { return f(root, snap); } catch { return []; } }) });
const { inputs, unwired, left, noPrompt, notes } = await buildInputs({ project: snap, scene: root, slots: frozen, expand: same, frames, hooks: app.inputHooks, ...(fixedSeed ? { seed: null } : {}), prefix: (app.promptPrefix || []).flatMap((f) => { try { return f(root, snap); } catch { return []; } }) });
app.lastRun.typed = (root.text || "").trim(); // what a Chat comment made now would be about
const seeded = rolesAt(frozen, "generation.seed")[0];
if (seeded) app.lastRun.seed = (inputs[seeded.slot.id] || {})[seeded.role.input] ?? (seeded.slot.inputs || {})[seeded.role.input]; // shown in ⏱ Sampler, to keep a liked one
if (stopped) break;
if (noPrompt && !made) tell("This pipeline has no prompt input, so the scene text is not sent.");
notes.forEach(tell);
if (unwired) tell(`${unwired} reference(s) did not fit this pipeline and are not used.`);
if (unwired) tell(`${unwired} reference(s) did not fit this pipeline and are not used: ${(left || []).map((l) => `${names.get(l.id) || l.id} (looked for ${l.at.replace("assets.", "")}: ${l.why})`).join("; ")}.`);
// The page's own guard does not see another tab: ComfyUI's queue does. Asked per shot: between this run's shots it is empty.
if (app.api.projectQueued && (await app.api.projectQueued(pid))) { tell("This project already has a run in ComfyUI's queue (another tab or window?). Wait for it, or Stop it there."); break; }
if (stopped) break;
+2 -2
View File
@@ -31,7 +31,7 @@ export async function buildInputs({ project, scene, slots, expand, frames, hooks
if (off) put(off, like.unsend.input, false);
}
const plan = sceneReferences(scene, project.fixed_references);
const { overrides, unwired } = wireReferences(plan.ids, slots, source ? [source.slot.id] : [], plan.pins, (id) => kinds.get(id));
const { overrides, unwired, left } = wireReferences(plan.ids, slots, source ? [source.slot.id] : [], plan.pins, (id) => kinds.get(id));
for (const [id, fields] of Object.entries(overrides)) raw[id] = { ...raw[id], ...fields };
const prompt = rolesAt(slots, "generation.prompt")[0];
@@ -55,5 +55,5 @@ export async function buildInputs({ project, scene, slots, expand, frames, hooks
// Judged on what is SENT (a carried frame counts; a picture t2v blanks, or one this pipeline has no input for, does not).
const picture = (source && (raw[source.slot.id] || {}).media_id) || Object.keys(overrides).length;
if (prompt && !String((raw[prompt.slot.id] || {})[prompt.role.input] ?? "").trim() && !picture) notes.push("This shot has no text and no start picture, so the model makes whatever it likes.");
return { inputs: raw, unwired, noPrompt: !prompt, notes };
return { inputs: raw, unwired, left, noPrompt: !prompt, notes };
}
+4 -3
View File
@@ -21,6 +21,7 @@ export function wireReferences(mediaIds, slots, reservedSlotIds, pins, kindOf) {
const overrides = {};
const claimed = new Set(reservedSlotIds || []);
let unwired = 0;
const left = []; // what was not wired, and the role it looked for: said by name, not just counted
// Each reference gets its slot number (1-based), so a pinned one keeps its number when a reference
// before it is switched off; the rest take the free numbers in the order they were added.
// Pictures, videos and audio each have inputs of their own on the model, numbered per kind; only pictures are pinnable.
@@ -36,7 +37,7 @@ export function wireReferences(mediaIds, slots, reservedSlotIds, pins, kindOf) {
});
placed.forEach(({ id: mediaId, at }) => {
const slot = pool.find((s) => (s.roles || []).some((r) => r.at === at));
if (!slot) { unwired += 1; return; }
if (!slot) { unwired += 1; left.push({ id: mediaId, at, why: "no slot with that role" }); return; }
// The wire's target is the role's own data (`wireTo`), never a slot id
// this file would otherwise have to know: the preset declares where its
// own reference lands, the same way it declares everything else.
@@ -48,7 +49,7 @@ export function wireReferences(mediaIds, slots, reservedSlotIds, pins, kindOf) {
// the same failure either way: the second silently overwrites the first
// unless it is counted unwired instead.
const dest = role && role.wireTo ? `${role.wireTo.slot}.${role.wireTo.input}` : slot.id;
if (claimed.has(slot.id) || claimed.has(dest)) { unwired += 1; return; }
if (claimed.has(slot.id) || claimed.has(dest)) { unwired += 1; left.push({ id: mediaId, at, why: "its slot is already taken" }); return; }
claimed.add(slot.id);
claimed.add(dest);
overrides[slot.id] = { ...overrides[slot.id], media_id: mediaId };
@@ -57,7 +58,7 @@ export function wireReferences(mediaIds, slots, reservedSlotIds, pins, kindOf) {
[role.wireTo.input]: [slot.id, 0] };
}
});
return { overrides, unwired };
return { overrides, unwired, left };
}
/**