maint: remove the v1 LoRA relevance scorer and suggestion writer
291 lines: _lora_words, _ensure_lora_memory, _score_lora_prompt_relation and _update_lora_weight_suggestions. The scorer matched prompt words against each LoRA's filename; the writer was the v1 half of the suggestion pair, superseded by _v2_update_lora_suggestions, and unreachable since the v1 refiner entry point was removed on this branch. _lora_state_id stays — the live v2 writer still uses it. Checked: no refs anywhere (grep -a, all file types, node_modules excluded), not a route handler, not in NODE_CLASS_MAPPINGS, not an INPUT_TYPES widget, no getattr dispatch. Tests: 1379 passed, baseline plus this branch's two new ones. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
e9ba41f62a
commit
8739fa8d2e
-291
@@ -3399,297 +3399,6 @@ class FunPackVideoRefiner:
|
||||
def _lora_state_id(self, lora_name: str, lora_type: str):
|
||||
return md5(f"{lora_name}::{lora_type}".encode("utf-8")).hexdigest()[:16]
|
||||
|
||||
def _lora_words(self, text: str):
|
||||
return {
|
||||
word.strip().lower()
|
||||
for word in re.split(r"[\s,;:_\\/\-().\[\]{}]+", text or "")
|
||||
if self._is_valuable_token(word.strip())
|
||||
}
|
||||
|
||||
def _ensure_lora_memory(self, memory: dict, lora_entry: dict):
|
||||
lora_type = lora_entry.get("type", "general")
|
||||
lora_id = lora_entry.get("id") or self._lora_state_id(lora_entry.get("name", ""), lora_type)
|
||||
state = memory.setdefault(
|
||||
lora_id,
|
||||
{
|
||||
"name": lora_entry.get("name", ""),
|
||||
"type": lora_type,
|
||||
"offset_ratio": 0.0,
|
||||
"stable_offset_ratio": None,
|
||||
"reward_ema": 0.0,
|
||||
"culprit_score": 0.0,
|
||||
"good_streak": 0,
|
||||
"bad_streak": 0,
|
||||
"culprit_hits": 0,
|
||||
"iterations": 0,
|
||||
},
|
||||
)
|
||||
state["name"] = lora_entry.get("name", "")
|
||||
state["type"] = lora_type
|
||||
state.setdefault("offset_ratio", 0.0)
|
||||
state.setdefault("stable_offset_ratio", None)
|
||||
state.setdefault("reward_ema", 0.0)
|
||||
state.setdefault("culprit_score", 0.0)
|
||||
state.setdefault("good_streak", 0)
|
||||
state.setdefault("bad_streak", 0)
|
||||
state.setdefault("culprit_hits", 0)
|
||||
state.setdefault("iterations", 0)
|
||||
return lora_id, state
|
||||
|
||||
def _score_lora_prompt_relation(self, lora_entry, ordered_concept_ids, concept_clusters, current_concept_labels):
|
||||
lora_type = lora_entry.get("type", "general")
|
||||
lora_words = self._lora_words(lora_entry.get("name", ""))
|
||||
|
||||
best_score = 0.35 if lora_type == "general" else 0.0
|
||||
best_labels = []
|
||||
best_importance = 1.0
|
||||
|
||||
for cid in ordered_concept_ids:
|
||||
cluster = concept_clusters.get(cid)
|
||||
if not cluster:
|
||||
continue
|
||||
cluster = self._ensure_concept_cluster_defaults(cluster)
|
||||
label = current_concept_labels.get(cid, cluster.get("label", cid))
|
||||
label_words = self._lora_words(label)
|
||||
anchor_words = set(cluster.get("anchor_words", []))
|
||||
prompt_words = label_words | anchor_words
|
||||
category = cluster.get("category", "general")
|
||||
|
||||
overlap = len(lora_words & prompt_words) / max(1, len(lora_words)) if lora_words else 0.0
|
||||
category_score = 0.62 if lora_type == category else 0.0
|
||||
if lora_type in {"action", "concept"} and category in {"concept", "details", "subject", "appearance", "action", "environment"}:
|
||||
category_score = max(category_score, 0.58)
|
||||
if lora_type == "character" and category in {"character", "subject", "appearance"}:
|
||||
category_score = max(category_score, 0.72)
|
||||
if lora_type == "quality" and category == "quality":
|
||||
category_score = 0.78
|
||||
if lora_type == "style" and category in {"style", "camera"}:
|
||||
category_score = max(category_score, 0.58)
|
||||
|
||||
score = max(category_score, overlap)
|
||||
if score > best_score:
|
||||
best_score = score
|
||||
best_labels = [label]
|
||||
best_importance = self._get_concept_mean_importance(cluster)
|
||||
elif score > 0 and abs(score - best_score) < 1e-6:
|
||||
best_labels.append(label)
|
||||
|
||||
return min(1.0, best_score), best_labels[:4], best_importance
|
||||
|
||||
def _update_lora_weight_suggestions(self, lora_stack, active, global_adaptive, ordered_concept_ids,
|
||||
concept_clusters, current_concept_labels, rating, reward,
|
||||
rating_profile: Optional[dict] = None):
|
||||
if not isinstance(lora_stack, dict) or not lora_stack.get("loras"):
|
||||
return "LoRA suggestions: no FunPack LoRA stack connected."
|
||||
|
||||
rating_profile = rating_profile or normalize_refiner_rating(rating)
|
||||
rating_key = rating_profile.get("key", "")
|
||||
missing_axes = set(rating_profile.get("missing_axes", []))
|
||||
missing_count = len(missing_axes)
|
||||
memory = global_adaptive.setdefault("lora_weight_memory", {})
|
||||
suggestions = {}
|
||||
status_parts = []
|
||||
relation_cache = {}
|
||||
top_concept_relation = 0.0
|
||||
concept_lora_count = 0
|
||||
|
||||
for entry in lora_stack.get("loras", []):
|
||||
lora_type = entry.get("type", "general")
|
||||
lora_id = entry.get("id") or self._lora_state_id(entry.get("name", ""), lora_type)
|
||||
relation, matched_labels, concept_importance = self._score_lora_prompt_relation(
|
||||
entry,
|
||||
ordered_concept_ids,
|
||||
concept_clusters,
|
||||
current_concept_labels,
|
||||
)
|
||||
relation_cache[lora_id] = (relation, matched_labels, concept_importance)
|
||||
if lora_type in {"action", "concept", "character"}:
|
||||
concept_lora_count += 1
|
||||
top_concept_relation = max(top_concept_relation, relation)
|
||||
|
||||
for entry in lora_stack.get("loras", []):
|
||||
lora_type = entry.get("type", "general")
|
||||
profile = LORA_REFINER_TYPE_PROFILES.get(lora_type, LORA_REFINER_TYPE_PROFILES["general"])
|
||||
lora_id, state = self._ensure_lora_memory(memory, entry)
|
||||
relation, matched_labels, concept_importance = relation_cache.get(lora_id, (0.0, [], 1.0))
|
||||
|
||||
state["reward_ema"] = 0.84 * float(state.get("reward_ema", 0.0)) + 0.16 * reward
|
||||
if rating_key == "like":
|
||||
state["good_streak"] = int(state.get("good_streak", 0)) + 1
|
||||
state["bad_streak"] = 0
|
||||
elif rating_key == "dislike" or {"concept", "quality"}.issubset(missing_axes):
|
||||
state["bad_streak"] = int(state.get("bad_streak", 0)) + 1
|
||||
state["good_streak"] = 0
|
||||
else:
|
||||
state["good_streak"] = 0
|
||||
state["bad_streak"] = 0
|
||||
|
||||
offset = float(state.get("offset_ratio", 0.0))
|
||||
stable_offset = state.get("stable_offset_ratio")
|
||||
culprit_score = float(state.get("culprit_score", 0.0))
|
||||
culprit_hits = int(state.get("culprit_hits", 0))
|
||||
if stable_offset is not None and (rating_key == "like" or missing_axes == {"details"}):
|
||||
offset = 0.72 * offset + 0.28 * float(stable_offset)
|
||||
|
||||
step = profile["step"] * max(0.15, relation)
|
||||
max_offset = profile["max_offset"]
|
||||
min_offset = profile["min_offset"]
|
||||
effective_relation = max(relation, profile.get("culprit_bias", 0.0))
|
||||
base_model = float(entry.get("base_model_weight", entry.get("model_weight", 1.0)))
|
||||
base_abs = abs(base_model)
|
||||
is_concept_lora = lora_type in {"action", "concept"}
|
||||
concept_match_strength = 1.0
|
||||
if is_concept_lora:
|
||||
concept_match_strength += 0.30 if matched_labels else 0.0
|
||||
concept_match_strength += max(0.0, relation - 0.30) * 1.35
|
||||
|
||||
is_primary_concept_lora = (
|
||||
lora_type in {"action", "concept", "character"} and
|
||||
(
|
||||
concept_lora_count == 1 or
|
||||
(top_concept_relation > 0.0 and relation >= max(0.30, top_concept_relation - 1e-6))
|
||||
)
|
||||
)
|
||||
|
||||
if rating_key == "like":
|
||||
value_mult = 0.75 + min(1.4, max(0.5, concept_importance)) * 0.25
|
||||
offset += step * (0.45 + max(0.0, reward)) * value_mult
|
||||
culprit_score *= 0.72
|
||||
if state["good_streak"] >= 3:
|
||||
if stable_offset is None:
|
||||
stable_offset = offset
|
||||
else:
|
||||
stable_offset = 0.78 * float(stable_offset) + 0.22 * offset
|
||||
state["stable_offset_ratio"] = _clamp(stable_offset, min_offset, max_offset)
|
||||
offset = state["stable_offset_ratio"]
|
||||
elif missing_axes:
|
||||
if "concept" in missing_axes or "quality" in missing_axes:
|
||||
state["stable_offset_ratio"] = None
|
||||
|
||||
culprit_score *= 0.74 if rating_key == "awful" else 0.82
|
||||
culprit_hits = max(0, culprit_hits - 1)
|
||||
axis_boost = 0.0
|
||||
|
||||
if "details" in missing_axes:
|
||||
if relation >= 0.12 and lora_type in {"action", "concept", "character", "general", "style"}:
|
||||
value_mult = 0.85 + min(1.2, max(0.5, concept_importance)) * 0.20
|
||||
axis_boost += (0.26 + relation * 0.34) * value_mult
|
||||
elif lora_type == "quality":
|
||||
axis_boost += 0.06
|
||||
|
||||
if "concept" in missing_axes:
|
||||
if lora_type in {"action", "concept", "character"}:
|
||||
if is_primary_concept_lora:
|
||||
axis_boost += (0.95 + abs(reward) * 0.45) * concept_match_strength
|
||||
elif relation > 0.08:
|
||||
axis_boost += 0.34 + relation * 0.40
|
||||
elif lora_type == "general" and relation >= 0.25:
|
||||
axis_boost += 0.25 + relation * 0.25
|
||||
|
||||
if "quality" in missing_axes:
|
||||
if lora_type == "quality":
|
||||
axis_boost += 0.90 + abs(reward) * 0.35
|
||||
elif lora_type == "style" and relation >= 0.25:
|
||||
axis_boost += 0.20 + relation * 0.20
|
||||
elif lora_type == "general" and relation >= 0.20:
|
||||
axis_boost += 0.16 + relation * 0.15
|
||||
|
||||
if axis_boost > 0.0:
|
||||
if rating_key == "awful":
|
||||
axis_boost *= 1.25
|
||||
max_offset = max(max_offset, profile["bad_max_offset"] * (0.62 + 0.10 * missing_count))
|
||||
relation_floor = 0.35 if missing_count >= 2 else 0.22
|
||||
boost_step = profile["step"] * max(relation_floor, effective_relation)
|
||||
offset += boost_step * axis_boost
|
||||
elif relation <= 0.05:
|
||||
offset *= 0.97
|
||||
else:
|
||||
offset *= 0.94
|
||||
elif rating_key == "dislike":
|
||||
severity = _clamp((5.0 - float(rating)) / 4.0, 0.0, 1.0)
|
||||
culprit_signal = max(0.20, effective_relation) * (0.70 + base_abs * 0.30)
|
||||
if is_concept_lora:
|
||||
culprit_signal *= concept_match_strength
|
||||
culprit_score = _clamp(culprit_score * 0.72 + severity * culprit_signal, 0.0, 2.5)
|
||||
culprit_hits = culprit_hits + 1 if culprit_score >= 0.45 else max(0, culprit_hits - 1)
|
||||
max_offset = profile["bad_max_offset"] if rating <= 2 else max(max_offset, profile["bad_max_offset"] * 0.72)
|
||||
bad_floor_strength = min(1.0, 0.45 + 0.35 * culprit_score + 0.14 * state["bad_streak"])
|
||||
if is_concept_lora:
|
||||
bad_floor_strength = min(1.0, bad_floor_strength + 0.18 * concept_match_strength)
|
||||
min_offset = min(min_offset, profile["bad_min_offset"] * bad_floor_strength)
|
||||
offset -= step * (1.0 + severity * 3.0) * max(0.4, effective_relation) * max(0.8, 0.85 + culprit_score)
|
||||
if state["bad_streak"] >= 2:
|
||||
offset -= step * (0.65 + severity * 1.8) * max(0.25, effective_relation)
|
||||
if is_concept_lora and state["bad_streak"] >= 2:
|
||||
offset -= step * (0.85 + severity * 2.4) * max(0.50, effective_relation) * concept_match_strength
|
||||
if state["bad_streak"] >= 2:
|
||||
state["stable_offset_ratio"] = None
|
||||
if is_concept_lora and matched_labels and state["bad_streak"] >= 2 and culprit_score >= 0.70:
|
||||
offset = min(offset, -1.0)
|
||||
if state["bad_streak"] >= 3 and culprit_score >= 0.85 and abs(1.0 + offset) < 0.08:
|
||||
offset = -1.0
|
||||
else:
|
||||
culprit_score *= 0.92
|
||||
culprit_hits = max(0, culprit_hits - 1)
|
||||
if relation <= 0.05:
|
||||
offset *= 0.94
|
||||
elif stable_offset is not None:
|
||||
offset = 0.65 * offset + 0.35 * float(stable_offset)
|
||||
else:
|
||||
offset *= 0.90
|
||||
|
||||
offset = _clamp(offset, min_offset, max_offset)
|
||||
state["offset_ratio"] = offset
|
||||
state["culprit_score"] = culprit_score
|
||||
state["culprit_hits"] = culprit_hits
|
||||
state["iterations"] = int(state.get("iterations", 0)) + 1
|
||||
|
||||
model_weight = base_model * (1.0 + offset)
|
||||
suspect = culprit_score >= 0.65 or model_weight <= 0.0 or culprit_hits >= 2
|
||||
action = (
|
||||
"invert" if model_weight < 0.0 else
|
||||
"mute" if model_weight == 0.0 else
|
||||
"hold" if abs(model_weight - base_model) < 1e-6 else
|
||||
"reduce" if abs(model_weight) < abs(base_model) else
|
||||
"boost"
|
||||
)
|
||||
suggestions[lora_id] = {
|
||||
"name": entry.get("name", ""),
|
||||
"type": lora_type,
|
||||
"model_weight": model_weight,
|
||||
"base_model_weight": base_model,
|
||||
"offset_ratio": offset,
|
||||
"culprit_score": culprit_score,
|
||||
"culprit_hits": culprit_hits,
|
||||
"suspect": suspect,
|
||||
"action": action,
|
||||
"relation": relation,
|
||||
"matched_concepts": matched_labels,
|
||||
"rating": int(rating),
|
||||
"rating_label": rating_profile.get("label", str(rating)),
|
||||
"rating_range": rating_profile.get("legacy_range", ""),
|
||||
"good_streak": state.get("good_streak", 0),
|
||||
"bad_streak": state.get("bad_streak", 0),
|
||||
"stable": state.get("stable_offset_ratio") is not None,
|
||||
}
|
||||
|
||||
match_text = ",".join(matched_labels) if matched_labels else "none"
|
||||
status_parts.append(
|
||||
f"{entry.get('name', '?')}[{lora_type}] rel={relation:.2f} "
|
||||
f"offset={offset:+.3f} next={model_weight:+.3f} "
|
||||
f"sus={culprit_score:.2f}{' !' if suspect else ''} "
|
||||
f"match={match_text}"
|
||||
)
|
||||
|
||||
active["lora_weight_suggestions"] = suggestions
|
||||
active["last_lora_stack"] = lora_stack
|
||||
if not status_parts:
|
||||
return "LoRA suggestions: stack empty."
|
||||
return f"LoRA suggestions ({rating_profile.get('label', str(rating))}): " + " | ".join(status_parts)
|
||||
|
||||
# =========================================================================
|
||||
# MAIN REFINE
|
||||
# =========================================================================
|
||||
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user