Files
olivv-cs-ComfyUI-FunPack/core/suggestions.py
T
digital-garbageandClaude Opus 5.5 e877ad08b7 rework: Build tab drafts a scene from your best-rated shortcuts; learns from rated scenes in projects
Drops the run log, edit-replay swap and Generate capture calls (core no longer depends on the feature).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 17:18:15 +03:00

75 lines
3.0 KiB
Python

"""What a person's own prompting habits say about shortcuts: which ones land in
the same scene (pairs), which shot follows which (follows), and how the scenes
using them were rated (scores, rated_pairs: liked +1, disliked -1). Mined on demand
by reading every saved project's scene text for triggers typed verbatim, so
there is nothing learned to store and nothing to go stale. Keyed by shortcut
name, which is what the picker identifies a shortcut by."""
from __future__ import annotations
import re
from . import projects, shortcuts
def vote(label: str) -> int:
"""A scene's saved rating as +1 / -1 / 0. A bad-image dislike blames the picture, not the prompt: 0."""
v = str(label or "").removesuffix("|loved").strip()
if v.isdigit():
return 1 if int(v) >= 6 else -1
return -1 if v == "Disliked: bad composition" else 0
def stats() -> dict:
pats = []
for sc in shortcuts.listing():
if not sc.enabled:
continue
for trig in sc.triggers:
t = trig.strip().lower()
if t:
pats.append((sc.name, re.compile(r"(?<![a-z0-9_])" + re.escape(t) + r"(?![a-z0-9_])")))
counts: dict[str, int] = {}
pairs: dict[tuple, int] = {}
follows: dict[tuple, int] = {}
scores: dict[str, int] = {}
rated_pairs: dict[tuple, int] = {}
scanned = 0
for meta in projects.listing():
proj = projects.get(meta["id"])
if proj is None:
continue
prev: set[str] | None = None
by_id = {sc.id: sc for sc in proj.scenes}
# Timeline (cut) order, as v4 mined it; the plan order until a clip was moved by hand.
order = [i for i in proj.timeline_order if i in by_id] or [sc.id for sc in proj.scenes]
for sid in order:
sc = by_id[sid]
# Editorial cuts share the root's text: only the root owns the prompt.
if sc.excluded or (sc.gen_unit_id and sc.cut_offset_frames):
continue
text = (sc.text or "").strip().lower()
if not text:
continue
present = {k for k, pat in pats if pat.search(text)}
scanned += 1
v = vote(sc.rating)
for k in present:
counts[k] = counts.get(k, 0) + 1
if v:
scores[k] = scores.get(k, 0) + v
for a in present:
for b in present:
if a < b:
pairs[(a, b)] = pairs.get((a, b), 0) + 1
if v:
rated_pairs[(a, b)] = rated_pairs.get((a, b), 0) + v
for a in prev or ():
for b in present:
follows[(a, b)] = follows.get((a, b), 0) + 1
prev = present
return {"scenes": scanned, "counts": counts,
"pairs": [[a, b, n] for (a, b), n in pairs.items()],
"follows": [[a, b, n] for (a, b), n in follows.items()],
"scores": scores, "rated_pairs": [[a, b, n] for (a, b), n in rated_pairs.items()]}