feat: dislikes name what went wrong (bad image / bad composition); composition learners and DynaShift read the other axis as neutral; picker labels reach the taste store
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Sonnet 5.5
parent
a9ef7d5c44
commit
28339e9fec
+1
-1
@@ -315,7 +315,7 @@
|
||||
previewSegmentUrl: unsupportedUrl,
|
||||
|
||||
// --- taste keys: modules/system/taste --------------------------------------
|
||||
rateTaste: (prompt_id, rating) => j("POST", API("/api/m/taste/rate"), { prompt_id, rating }),
|
||||
rateTaste: (prompt_id, rating, axis) => j("POST", API("/api/m/taste/rate"), { prompt_id, rating, axis: axis || null }),
|
||||
tasteKeys: () => j("GET", API("/api/m/taste/keys")),
|
||||
|
||||
// --- refinement keys / absolute taste store (c) -- not built in v5 -------
|
||||
|
||||
@@ -57,6 +57,8 @@
|
||||
const H3_SCALE = [
|
||||
{ label: "10", display: "Liked", reward: "+", hint: "This generation was good." },
|
||||
{ label: "1", display: "Disliked", reward: "−", hint: "This generation was not." },
|
||||
{ label: "Disliked: bad image", display: "Disliked — bad image", reward: "−", hint: "Composition was fine; the picture itself was ruined." },
|
||||
{ label: "Disliked: bad composition", display: "Disliked — bad composition", reward: "−", hint: "Well drawn, but the shots/layout/movement were wrong." },
|
||||
];
|
||||
|
||||
// canonical label -> display name, for rendering a stored value (scene button, chips).
|
||||
@@ -66,7 +68,7 @@
|
||||
const displayName = (label) => DISPLAY_NAMES[label] || label;
|
||||
|
||||
const NO_LOVED = new Set([
|
||||
"Perfect", "Nailed it", "Awful", FORGET_LABEL,
|
||||
"Perfect", "Nailed it", "Awful", FORGET_LABEL, "Disliked: bad image", "Disliked: bad composition",
|
||||
"Missing quality", "Missing details + quality", "Missing action + quality", "Wrong action + quality",
|
||||
// H3 scale (1-10): a bare number never reaches the label branch that applies the loved
|
||||
// boost (normalize_refiner_v2_rating), so the heart would silently do nothing. 10 already
|
||||
@@ -251,5 +253,22 @@
|
||||
setTimeout(() => document.addEventListener("mousedown", onOutside, true), 0);
|
||||
}
|
||||
|
||||
window.MovieRatingPicker = { open, close: closePicker, formatLabel, buttonLabel, FORGET_LABEL };
|
||||
// What a picked label teaches the taste key. v5's ratings are liked / disliked (a dislike may
|
||||
// name the axis); the sign of a category's reward decides, as v4's learners only ever read it.
|
||||
// null = teaches nothing (a neutral label); clear = forget what this clip taught.
|
||||
const ALL = [...CATEGORIES.flatMap((c) => c.ratings), ...NUCLEAR, ...H3_SCALE];
|
||||
function tasteOf(value) {
|
||||
const v = String(value || "").replace(/\|loved$/, "").trim();
|
||||
if (!v) return "clear";
|
||||
if (v === FORGET_LABEL) return "clear";
|
||||
if (v === "Disliked: bad image") return { rating: "disliked", axis: "image" };
|
||||
if (v === "Disliked: bad composition") return { rating: "disliked", axis: "composition" };
|
||||
if (/^\d+$/.test(v)) return { rating: Number(v) >= 6 ? "liked" : "disliked", axis: null };
|
||||
const hit = ALL.find((r) => r.label === v);
|
||||
const n = hit && parseFloat(String(hit.reward).replace("−", "-"));
|
||||
if (!n || Number.isNaN(n)) return null;
|
||||
return { rating: n > 0 ? "liked" : "disliked", axis: null };
|
||||
}
|
||||
|
||||
window.MovieRatingPicker = { open, close: closePicker, formatLabel, buttonLabel, tasteOf, FORGET_LABEL };
|
||||
})();
|
||||
|
||||
@@ -31,3 +31,15 @@ test("forget and empty still render as nothing / the prompt", () => {
|
||||
assert.strictEqual(Picker.formatLabel(Picker.FORGET_LABEL), "");
|
||||
assert.strictEqual(Picker.buttonLabel(""), "Rate scene…");
|
||||
});
|
||||
|
||||
test("what a label teaches the taste key: sign decides, a dislike may name its axis", () => {
|
||||
assert.deepStrictEqual(Picker.tasteOf("10"), { rating: "liked", axis: null });
|
||||
assert.deepStrictEqual(Picker.tasteOf("1"), { rating: "disliked", axis: null });
|
||||
assert.deepStrictEqual(Picker.tasteOf("Disliked: bad image"), { rating: "disliked", axis: "image" });
|
||||
assert.deepStrictEqual(Picker.tasteOf("Disliked: bad composition"), { rating: "disliked", axis: "composition" });
|
||||
assert.deepStrictEqual(Picker.tasteOf("Perfect|loved"), { rating: "liked", axis: null });
|
||||
assert.deepStrictEqual(Picker.tasteOf("Awful"), { rating: "disliked", axis: null });
|
||||
assert.strictEqual(Picker.tasteOf("Wrong appearance"), null);
|
||||
assert.strictEqual(Picker.tasteOf(""), "clear");
|
||||
assert.strictEqual(Picker.tasteOf(Picker.FORGET_LABEL), "clear");
|
||||
});
|
||||
|
||||
+4
-2
@@ -4968,8 +4968,10 @@
|
||||
patchScene(id, { rating: v || "" });
|
||||
if (window.Timeline?.syncClipRatings) window.Timeline.syncClipRatings(get());
|
||||
const promptId = state.sceneRenders[id]?.promptId;
|
||||
if (promptId) {
|
||||
API.rateTaste(promptId, v || null).then((r) => {
|
||||
const taste = window.MovieRatingPicker?.tasteOf(v); // null: a label that teaches nothing
|
||||
if (promptId && taste !== null) {
|
||||
const t = taste === "clear" || taste === undefined ? { rating: null, axis: null } : taste;
|
||||
API.rateTaste(promptId, t.rating, t.axis).then((r) => {
|
||||
if (r?.why) set({ notice: `Taste key not taught: ${r.why}.` });
|
||||
}).catch(() => {}); // no taste module: rating stays a label
|
||||
}
|
||||
|
||||
@@ -126,7 +126,7 @@ def install(patcher, values, key):
|
||||
steer.attach(patcher, key)
|
||||
|
||||
def fresh():
|
||||
live.update(checked=False, acts=False, steps=0, rows=None if manual else taste.rows(KIND))
|
||||
live.update(checked=False, acts=False, steps=0, rows=None if manual else taste.rows(KIND, blind_to="image"))
|
||||
live["k"] = float(values.get("k", 1.0)) if manual else 1.0
|
||||
if manual:
|
||||
log.once(f"{ID}:state", log.INFO, "FunPack Decisiveness", f"manual k {live['k']:.3f}")
|
||||
|
||||
@@ -172,7 +172,7 @@ def install(patcher, values, key):
|
||||
live = {"bank": Bank([])}
|
||||
|
||||
def fresh():
|
||||
bank = live["bank"] = Bank(taste.rows(KIND))
|
||||
bank = live["bank"] = Bank(taste.rows(KIND, blind_to="composition"))
|
||||
if not bank.negatives:
|
||||
summary = "learning: nothing disliked banked yet"
|
||||
else:
|
||||
|
||||
@@ -102,7 +102,7 @@ def install(patcher, values, key):
|
||||
if manual:
|
||||
return log.once(f"{ID}:state", log.INFO, "FunPack Late-branch guidance",
|
||||
f"manual strength {live['w']:.2f}, branch at block {branch}")
|
||||
live["w"], centre, held = DIAL.pick(rated_dial.history(taste.rows(KIND), {"b": branch}))
|
||||
live["w"], centre, held = DIAL.pick(rated_dial.history(taste.rows(KIND, blind_to="image"), {"b": branch}))
|
||||
log.once(f"{ID}:state", log.INFO, "FunPack Late-branch guidance",
|
||||
f"key {taste.key!r}: learned {centre:.2f} from {held} rating(s), trying "
|
||||
f"{live['w']:.2f}, branch at block {branch}")
|
||||
|
||||
@@ -210,7 +210,7 @@ def install(patcher, values, key):
|
||||
live = {"rows": [], "plan": None}
|
||||
|
||||
def fresh():
|
||||
live["rows"], live["plan"] = shots(taste.rows(KIND)), None
|
||||
live["rows"], live["plan"] = shots(taste.rows(KIND, blind_to="image")), None
|
||||
liked = sum(1 for r in live["rows"] if r["reward"] > 0)
|
||||
log.once(f"{ID}:state", log.INFO, "FunPack Shot memory",
|
||||
f"key {taste.key!r}: {liked} liked shot(s) of {len(live['rows'])} rated")
|
||||
|
||||
@@ -137,7 +137,7 @@ def install(patcher, values, key):
|
||||
if manual:
|
||||
return log.once(f"{ID}:state", log.INFO, "FunPack STAS",
|
||||
f"manual alpha {live['alpha']:.2f} at block {block}")
|
||||
live["alpha"], centre, held = DIAL.pick(rated_dial.history(taste.rows(KIND), {"b": block}))
|
||||
live["alpha"], centre, held = DIAL.pick(rated_dial.history(taste.rows(KIND, blind_to="image"), {"b": block}))
|
||||
log.once(f"{ID}:state", log.INFO, "FunPack STAS",
|
||||
f"key {taste.key!r}: learned alpha {centre:.2f} from {held} rating(s), trying "
|
||||
f"{live['alpha']:.2f} at block {block}")
|
||||
|
||||
Reference in New Issue
Block a user