Upgrade Refiner V2 intent learning
This commit is contained in:
+509
-20
@@ -5765,6 +5765,11 @@ class FunPackVideoRefinerV2(FunPackVideoRefiner):
|
||||
"lora_weight_memory": {},
|
||||
"preferred_context_memory": {},
|
||||
"intent_alignment_memory": {},
|
||||
"intent_family_memory": {},
|
||||
"perfect_anchors": {},
|
||||
"variant_evidence": {},
|
||||
"intent_preference_phrases": {},
|
||||
"conditioning_deltas": {},
|
||||
"vision_memory": {},
|
||||
"loss_history": [],
|
||||
},
|
||||
@@ -5802,6 +5807,11 @@ class FunPackVideoRefinerV2(FunPackVideoRefiner):
|
||||
data["global"].setdefault("lora_weight_memory", {})
|
||||
data["global"].setdefault("preferred_context_memory", {})
|
||||
data["global"].setdefault("intent_alignment_memory", {})
|
||||
data["global"].setdefault("intent_family_memory", {})
|
||||
data["global"].setdefault("perfect_anchors", {})
|
||||
data["global"].setdefault("variant_evidence", {})
|
||||
data["global"].setdefault("intent_preference_phrases", {})
|
||||
data["global"].setdefault("conditioning_deltas", {})
|
||||
data["global"].setdefault("vision_memory", {})
|
||||
data["global"].setdefault("loss_history", [])
|
||||
return data, "loaded"
|
||||
@@ -6737,6 +6747,315 @@ class FunPackVideoRefinerV2(FunPackVideoRefiner):
|
||||
clean = self._v2_prompt_key(intent_prompt).lower()
|
||||
return md5(clean.encode("utf-8")).hexdigest()
|
||||
|
||||
def _v2_intent_source_prompt(self, prompt, intent_prompt="", intent_is_vague=False):
|
||||
intent_prompt = self._v2_prompt_key(intent_prompt)
|
||||
if intent_prompt and not intent_is_vague and not self._v2_user_intent_prompt_is_vague(intent_prompt):
|
||||
return intent_prompt
|
||||
return self._v2_prompt_key(prompt)
|
||||
|
||||
def _v2_prompt_body_similarity(self, left, right):
|
||||
left_roots = self._v2_repair_intent_roots(left)
|
||||
right_roots = self._v2_repair_intent_roots(right)
|
||||
if not left_roots or not right_roots:
|
||||
return 0.0
|
||||
overlap = left_roots & right_roots
|
||||
if not overlap:
|
||||
return 0.0
|
||||
jaccard = len(overlap) / float(max(1, len(left_roots | right_roots)))
|
||||
containment = len(overlap) / float(max(1, min(len(left_roots), len(right_roots))))
|
||||
return max(jaccard, containment * 0.88)
|
||||
|
||||
def _v2_intent_family_slot(self, global_state, intent_prompt, create=True):
|
||||
intent_prompt = self._v2_prompt_key(intent_prompt)
|
||||
if not isinstance(global_state, dict) or not intent_prompt:
|
||||
return "", None, 0.0
|
||||
memory = global_state.setdefault("intent_family_memory", {})
|
||||
if not isinstance(memory, dict):
|
||||
memory = {}
|
||||
global_state["intent_family_memory"] = memory
|
||||
best_key = ""
|
||||
best_slot = None
|
||||
best_similarity = 0.0
|
||||
for key, slot in memory.items():
|
||||
if not isinstance(slot, dict):
|
||||
continue
|
||||
candidates = [slot.get("intent_prompt", "")]
|
||||
candidates.extend(slot.get("intent_prompts", []) if isinstance(slot.get("intent_prompts"), list) else [])
|
||||
for candidate in candidates:
|
||||
similarity = self._v2_prompt_body_similarity(intent_prompt, candidate)
|
||||
if similarity > best_similarity:
|
||||
best_key = key
|
||||
best_slot = slot
|
||||
best_similarity = similarity
|
||||
if best_slot is not None and best_similarity >= 0.58:
|
||||
return best_key, best_slot, best_similarity
|
||||
if not create:
|
||||
return "", None, best_similarity
|
||||
family_key = self._v2_intent_alignment_key(intent_prompt)
|
||||
slot = memory.setdefault(family_key, {
|
||||
"family_key": family_key,
|
||||
"intent_prompt": intent_prompt,
|
||||
"intent_prompts": [],
|
||||
"total_seen": 0,
|
||||
"perfect_anchors": {},
|
||||
"loved_variants": {},
|
||||
"variant_evidence": {},
|
||||
"intent_preference_phrases": {},
|
||||
"conditioning_deltas": {},
|
||||
"negative_tags": {},
|
||||
"last_seen_iter": 0,
|
||||
})
|
||||
return family_key, slot, 1.0
|
||||
|
||||
def _v2_sync_intent_family_aliases(self, global_state, family_key, slot):
|
||||
if not family_key or not isinstance(slot, dict) or not isinstance(global_state, dict):
|
||||
return
|
||||
global_state.setdefault("perfect_anchors", {})[family_key] = slot.get("perfect_anchors", {})
|
||||
global_state.setdefault("variant_evidence", {})[family_key] = slot.get("variant_evidence", {})
|
||||
global_state.setdefault("intent_preference_phrases", {})[family_key] = slot.get("intent_preference_phrases", {})
|
||||
global_state.setdefault("conditioning_deltas", {})[family_key] = slot.get("conditioning_deltas", {})
|
||||
|
||||
def _v2_intent_texts_for_family(self, intent_prompt, intent_phrases, family_slot=None):
|
||||
texts = []
|
||||
intent_prompt = self._v2_clean_phrase_text(intent_prompt)
|
||||
if intent_prompt:
|
||||
texts.append(intent_prompt)
|
||||
for phrase in intent_phrases or []:
|
||||
text = self._v2_clean_phrase_text(phrase.get("text", "") if isinstance(phrase, dict) else phrase)
|
||||
if text and text not in texts:
|
||||
texts.append(text)
|
||||
if isinstance(family_slot, dict):
|
||||
for entry in family_slot.get("intent_preference_phrases", {}).values():
|
||||
if not isinstance(entry, dict) or int(entry.get("seen_count", 0)) < 2:
|
||||
continue
|
||||
text = self._v2_clean_phrase_text(entry.get("text", ""))
|
||||
if text and text not in texts:
|
||||
texts.append(text)
|
||||
return texts
|
||||
|
||||
def _v2_text_represented_by_intent(self, text, intent_prompt="", intent_phrases=None, family_slot=None):
|
||||
clean = self._v2_clean_phrase_text(text)
|
||||
if not clean:
|
||||
return False
|
||||
for candidate in self._v2_intent_texts_for_family(intent_prompt, intent_phrases or [], family_slot):
|
||||
if self._v2_phrase_texts_match(clean, candidate):
|
||||
return True
|
||||
return False
|
||||
|
||||
def _v2_mark_intent_locks(self, phrases, intent_prompt="", intent_phrases=None, family_slot=None):
|
||||
if not phrases:
|
||||
return phrases
|
||||
for phrase in phrases:
|
||||
if not isinstance(phrase, dict):
|
||||
continue
|
||||
text = phrase.get("text", "")
|
||||
locked = self._v2_text_represented_by_intent(text, intent_prompt, intent_phrases, None)
|
||||
preference_locked = self._v2_text_represented_by_intent(text, "", [], family_slot) and not locked
|
||||
phrase["intent_locked"] = bool(locked)
|
||||
phrase["preference_locked"] = bool(preference_locked)
|
||||
return phrases
|
||||
|
||||
def _v2_serialized_delta(self, target_payload, source_payload):
|
||||
if not isinstance(target_payload, dict) or not isinstance(source_payload, dict):
|
||||
return None
|
||||
try:
|
||||
target = serializable_to_tensor(target_payload).detach().float()
|
||||
source = serializable_to_tensor(source_payload).detach().float()
|
||||
if list(target.shape) != list(source.shape):
|
||||
return None
|
||||
return tensor_to_serializable((target - source).cpu())
|
||||
except Exception:
|
||||
return None
|
||||
|
||||
def _v2_store_conditioning_delta_average(self, slot, delta_payload):
|
||||
if not isinstance(slot, dict) or not isinstance(delta_payload, dict):
|
||||
return False
|
||||
count = int(slot.get("count", 0))
|
||||
existing = slot.get("delta")
|
||||
if count <= 0 or not isinstance(existing, dict):
|
||||
slot["delta"] = delta_payload
|
||||
slot["count"] = 1
|
||||
return True
|
||||
try:
|
||||
previous = serializable_to_tensor(existing)
|
||||
current = serializable_to_tensor(delta_payload).to(previous.device)
|
||||
if list(previous.shape) != list(current.shape):
|
||||
slot["delta"] = delta_payload
|
||||
slot["count"] = 1
|
||||
return True
|
||||
averaged = (previous * count + current) / float(count + 1)
|
||||
slot["delta"] = tensor_to_serializable(averaged.cpu())
|
||||
slot["count"] = count + 1
|
||||
return True
|
||||
except Exception:
|
||||
slot["delta"] = delta_payload
|
||||
slot["count"] = 1
|
||||
return True
|
||||
|
||||
def _v2_update_intent_family_memory(self, global_state, last_run, rating_profile, iter_num, axis_feedback=None):
|
||||
if not isinstance(global_state, dict) or not isinstance(last_run, dict) or rating_profile.get("skip_learning"):
|
||||
return "Intent family: no learning update.", ""
|
||||
source_intent = self._v2_intent_source_prompt(
|
||||
last_run.get("prompt", ""),
|
||||
last_run.get("intent_prompt", ""),
|
||||
bool(last_run.get("intent_prompt_is_vague")),
|
||||
)
|
||||
if not source_intent:
|
||||
return "Intent family: no prompt body.", ""
|
||||
family_key, slot, similarity = self._v2_intent_family_slot(global_state, source_intent, create=True)
|
||||
if not isinstance(slot, dict):
|
||||
return "Intent family: unavailable.", ""
|
||||
|
||||
slot["family_key"] = family_key
|
||||
slot["intent_prompt"] = slot.get("intent_prompt") or source_intent
|
||||
prompts = list(slot.get("intent_prompts", [])) if isinstance(slot.get("intent_prompts"), list) else []
|
||||
if source_intent not in prompts:
|
||||
prompts.append(source_intent)
|
||||
slot["intent_prompts"] = prompts[-12:]
|
||||
slot["total_seen"] = int(slot.get("total_seen", 0)) + 1
|
||||
slot["last_seen_iter"] = int(iter_num)
|
||||
|
||||
intent_phrases = [phrase for phrase in last_run.get("intent_phrases", []) or [] if isinstance(phrase, dict)]
|
||||
if not intent_phrases:
|
||||
intent_phrases = self._v2_classify_phrases(None, self._ordered_prompt_phrases(source_intent), global_state)
|
||||
positive_phrases = [phrase for phrase in last_run.get("phrases", []) or [] if isinstance(phrase, dict)]
|
||||
self._v2_mark_intent_locks(positive_phrases, source_intent, intent_phrases, slot)
|
||||
|
||||
preferences = slot.setdefault("intent_preference_phrases", {})
|
||||
preference_updates = 0
|
||||
for phrase in intent_phrases:
|
||||
payload = self._v2_intent_alignment_phrase_payload(phrase)
|
||||
if not payload:
|
||||
continue
|
||||
entry = preferences.setdefault(payload["text"], {
|
||||
**payload,
|
||||
"seen_count": 0,
|
||||
"repair_count": 0,
|
||||
"score": 0.0,
|
||||
"last_seen_iter": 0,
|
||||
})
|
||||
entry.update(payload)
|
||||
entry["seen_count"] = int(entry.get("seen_count", 0)) + 1
|
||||
entry["last_seen_iter"] = int(iter_num)
|
||||
if int(entry.get("seen_count", 0)) >= 2:
|
||||
entry["score"] = round(min(4.0, float(entry.get("score", 0.0)) + 0.36), 4)
|
||||
preference_updates += 1
|
||||
else:
|
||||
entry["score"] = round(min(4.0, float(entry.get("score", 0.0)) + 0.12), 4)
|
||||
|
||||
missing_intent = [
|
||||
phrase for phrase in intent_phrases
|
||||
if not self._v2_phrase_represented_by(phrase.get("text", ""), positive_phrases)
|
||||
]
|
||||
extra_positive = [
|
||||
phrase for phrase in positive_phrases
|
||||
if not self._v2_text_represented_by_intent(phrase.get("text", ""), source_intent, intent_phrases, None)
|
||||
]
|
||||
repairs = [
|
||||
item for item in last_run.get("repair_candidates", []) or []
|
||||
if isinstance(item, dict) and self._v2_clean_phrase_text(item.get("text", ""))
|
||||
]
|
||||
|
||||
variant_key = md5(self._v2_prompt_key(last_run.get("prompt", "")).encode("utf-8")).hexdigest()
|
||||
variants = slot.setdefault("variant_evidence", {})
|
||||
variant = variants.setdefault(variant_key, {
|
||||
"positive_prompt": self._v2_prompt_key(last_run.get("prompt", "")),
|
||||
"encoded_prompt": self._v2_prompt_key(last_run.get("encoded_prompt", "")),
|
||||
"rating_count": 0,
|
||||
"avg_reward": 0.0,
|
||||
"accepted_count": 0,
|
||||
"rejected_count": 0,
|
||||
"missing_intent": [],
|
||||
"extra_positive": [],
|
||||
"repair_candidates": [],
|
||||
"last_rating_label": "",
|
||||
"last_seen_iter": 0,
|
||||
})
|
||||
count = int(variant.get("rating_count", 0))
|
||||
reward = float(rating_profile.get("reward", 0.0))
|
||||
variant["avg_reward"] = round((float(variant.get("avg_reward", 0.0)) * count + reward) / float(count + 1), 6)
|
||||
variant["rating_count"] = count + 1
|
||||
variant["last_rating_label"] = rating_profile.get("label", "")
|
||||
variant["last_seen_iter"] = int(iter_num)
|
||||
variant["missing_intent"] = [item.get("text", "") for item in missing_intent]
|
||||
variant["extra_positive"] = [item.get("text", "") for item in extra_positive]
|
||||
variant["repair_candidates"] = [item.get("text", "") for item in repairs]
|
||||
if rating_profile.get("key") == "like":
|
||||
variant["accepted_count"] = int(variant.get("accepted_count", 0)) + 1
|
||||
elif reward < 0.0 or rating_profile.get("wrong_axes") or rating_profile.get("key") == "awful":
|
||||
variant["rejected_count"] = int(variant.get("rejected_count", 0)) + 1
|
||||
|
||||
anchor_count = 0
|
||||
delta_updates = 0
|
||||
if rating_profile.get("key") == "like" and isinstance(last_run.get("conditioning"), dict):
|
||||
anchor_payload = {
|
||||
"intent_prompt": source_intent,
|
||||
"positive_prompt": self._v2_prompt_key(last_run.get("prompt", "")),
|
||||
"encoded_prompt": self._v2_prompt_key(last_run.get("encoded_prompt", "")),
|
||||
"conditioning": last_run.get("conditioning"),
|
||||
"source_conditioning": last_run.get("source_conditioning"),
|
||||
"repair_candidates": [item.get("text", "") for item in repairs],
|
||||
"last_seen_iter": int(iter_num),
|
||||
}
|
||||
anchors = slot.setdefault("perfect_anchors", {})
|
||||
if "base" not in anchors:
|
||||
anchors["base"] = dict(anchor_payload, anchor_role="base")
|
||||
else:
|
||||
loved = slot.setdefault("loved_variants", {})
|
||||
loved[variant_key] = dict(anchor_payload, anchor_role="variant")
|
||||
if len(loved) > 16:
|
||||
slot["loved_variants"] = dict(sorted(
|
||||
loved.items(),
|
||||
key=lambda item: int(item[1].get("last_seen_iter", 0)) if isinstance(item[1], dict) else 0,
|
||||
reverse=True,
|
||||
)[:16])
|
||||
anchor_count = 1 + len(slot.get("loved_variants", {}))
|
||||
delta = self._v2_serialized_delta(last_run.get("conditioning"), last_run.get("source_conditioning"))
|
||||
if isinstance(delta, dict):
|
||||
deltas = slot.setdefault("conditioning_deltas", {})
|
||||
positive_delta = deltas.setdefault("positive", {})
|
||||
if self._v2_store_conditioning_delta_average(positive_delta, delta):
|
||||
positive_delta["last_seen_iter"] = int(iter_num)
|
||||
delta_updates += 1
|
||||
|
||||
if len(variants) > 40:
|
||||
slot["variant_evidence"] = dict(sorted(
|
||||
variants.items(),
|
||||
key=lambda item: (
|
||||
float(item[1].get("avg_reward", 0.0)) if isinstance(item[1], dict) else -99.0,
|
||||
int(item[1].get("rating_count", 0)) if isinstance(item[1], dict) else 0,
|
||||
int(item[1].get("last_seen_iter", 0)) if isinstance(item[1], dict) else 0,
|
||||
),
|
||||
reverse=True,
|
||||
)[:40])
|
||||
|
||||
self._v2_sync_intent_family_aliases(global_state, family_key, slot)
|
||||
return (
|
||||
"Intent family learned: "
|
||||
f"family={family_key[:8]} sim={similarity:.2f}, repeated preference(s)={preference_updates}, "
|
||||
f"anchors={anchor_count or len(slot.get('perfect_anchors', {})) + len(slot.get('loved_variants', {}))}, "
|
||||
f"variant reward {variant['avg_reward']:+.2f}, delta update(s)={delta_updates}."
|
||||
), family_key
|
||||
|
||||
def _v2_apply_intent_family_delta(self, conditioning, family_slot, strength):
|
||||
if not isinstance(conditioning, torch.Tensor) or not isinstance(family_slot, dict):
|
||||
return conditioning, "intent-family idle"
|
||||
positive_delta = family_slot.get("conditioning_deltas", {}).get("positive", {})
|
||||
payload = positive_delta.get("delta") if isinstance(positive_delta, dict) else None
|
||||
if not self._v2_shape_compatible(payload, conditioning):
|
||||
return conditioning, "intent-family idle"
|
||||
try:
|
||||
delta = serializable_to_tensor(payload).to(device=conditioning.device, dtype=conditioning.dtype)
|
||||
if list(delta.shape) != list(conditioning.shape):
|
||||
return conditioning, "intent-family idle"
|
||||
original_norm = conditioning.norm(dim=-1, keepdim=True).clamp_min(1e-8)
|
||||
max_delta = original_norm * min(0.030, max(0.006, float(strength) * 0.35))
|
||||
delta_norm = delta.norm(dim=-1, keepdim=True).clamp_min(1e-8)
|
||||
scale = torch.minimum(torch.ones_like(delta_norm), max_delta / delta_norm)
|
||||
return conditioning + delta * scale, f"intent-family positive delta x{int(positive_delta.get('count', 0))}"
|
||||
except Exception:
|
||||
return conditioning, "intent-family delta failed"
|
||||
|
||||
def _v2_intent_alignment_phrase_payload(self, phrase):
|
||||
text = self._v2_clean_phrase_text(phrase.get("text", "") if isinstance(phrase, dict) else phrase)
|
||||
if not text:
|
||||
@@ -6793,6 +7112,12 @@ class FunPackVideoRefinerV2(FunPackVideoRefiner):
|
||||
})
|
||||
slot["intent_prompt"] = intent_prompt
|
||||
slot["last_seen_iter"] = int(iter_num)
|
||||
_, family_slot, _ = self._v2_intent_family_slot(global_state, intent_prompt, create=False)
|
||||
has_perfect_anchor = bool(
|
||||
isinstance(family_slot, dict) and (
|
||||
family_slot.get("perfect_anchors") or family_slot.get("loved_variants")
|
||||
)
|
||||
)
|
||||
|
||||
reward = float(rating_profile.get("reward", 0.0))
|
||||
rating_key = rating_profile.get("key", "")
|
||||
@@ -6945,13 +7270,17 @@ class FunPackVideoRefinerV2(FunPackVideoRefiner):
|
||||
delta = -0.18
|
||||
entry["forgiven_count"] = int(entry.get("forgiven_count", 0)) + 1
|
||||
elif rating_key == "awful":
|
||||
delta = 0.70
|
||||
delta = 0.70 if has_perfect_anchor else 0.44
|
||||
entry["missing_count"] = int(entry.get("missing_count", 0)) + 1
|
||||
elif missing_axes:
|
||||
delta = 0.95 if axes & missing_axes else 0.28
|
||||
if not has_perfect_anchor:
|
||||
delta = min(delta, 0.58)
|
||||
entry["missing_count"] = int(entry.get("missing_count", 0)) + 1
|
||||
elif wrong_axes:
|
||||
delta = 0.42 if axes & wrong_axes else 0.16
|
||||
if not has_perfect_anchor:
|
||||
delta = min(delta, 0.30)
|
||||
entry["missing_count"] = int(entry.get("missing_count", 0)) + 1
|
||||
else:
|
||||
delta = -0.10 if reward > 0.35 else 0.18 if reward < -0.20 else 0.0
|
||||
@@ -7601,7 +7930,7 @@ class FunPackVideoRefinerV2(FunPackVideoRefiner):
|
||||
except Exception:
|
||||
return mixed
|
||||
|
||||
def _v2_apply_conditioning_memory(self, conditioning, global_state, rating_profile, axis_feedback=None):
|
||||
def _v2_apply_conditioning_memory(self, conditioning, global_state, rating_profile, axis_feedback=None, intent_family_slot=None):
|
||||
if not isinstance(conditioning, torch.Tensor):
|
||||
return conditioning, "Adaptation: unavailable."
|
||||
original = conditioning.clone()
|
||||
@@ -7618,6 +7947,7 @@ class FunPackVideoRefinerV2(FunPackVideoRefiner):
|
||||
mixed = mixed.lerp(liked, strength)
|
||||
except Exception:
|
||||
pass
|
||||
mixed, family_delta_status = self._v2_apply_intent_family_delta(mixed, intent_family_slot, strength)
|
||||
axis_memory = global_state.get("axis_conditioning_memory", {})
|
||||
axis_actions = []
|
||||
missing_axes = set(axis_feedback.get("missing_axes", []))
|
||||
@@ -7658,7 +7988,8 @@ class FunPackVideoRefinerV2(FunPackVideoRefiner):
|
||||
f"good streak {int(global_state.get('good_streak', 0))} | "
|
||||
f"bad streak {int(global_state.get('bad_streak', 0))} | "
|
||||
f"reward ema {float(global_state.get('avg_reward_ema', 0.0)):+.3f} | "
|
||||
f"axis memory {', '.join(axis_actions) if axis_actions else 'idle'}"
|
||||
f"axis memory {', '.join(axis_actions) if axis_actions else 'idle'} | "
|
||||
f"{family_delta_status}"
|
||||
)
|
||||
|
||||
def _v2_emphasized_prompt(self, prompt, phrases, global_state, rating_profile):
|
||||
@@ -7786,6 +8117,7 @@ class FunPackVideoRefinerV2(FunPackVideoRefiner):
|
||||
previous_run,
|
||||
axis_feedback,
|
||||
intent_phrases=None,
|
||||
intent_family_slot=None,
|
||||
):
|
||||
missing_axes = set(axis_feedback.get("missing_axes", [])) if isinstance(axis_feedback, dict) else set()
|
||||
missing_axes = set(self._v2_order_axes(missing_axes))
|
||||
@@ -7856,6 +8188,28 @@ class FunPackVideoRefinerV2(FunPackVideoRefiner):
|
||||
if axes & missing_axes:
|
||||
add_candidate(item.get("text", ""), axes, 3.0, "intent")
|
||||
|
||||
if isinstance(intent_family_slot, dict):
|
||||
for entry in intent_family_slot.get("intent_preference_phrases", {}).values():
|
||||
if not isinstance(entry, dict):
|
||||
continue
|
||||
if int(entry.get("seen_count", 0)) < 2:
|
||||
continue
|
||||
axes = candidate_axes(entry)
|
||||
if not (axes & missing_axes):
|
||||
continue
|
||||
score = (
|
||||
1.35 +
|
||||
min(0.75, int(entry.get("seen_count", 0)) * 0.14) +
|
||||
min(0.60, float(entry.get("score", 0.0)) * 0.16)
|
||||
)
|
||||
add_candidate(
|
||||
entry.get("text", ""),
|
||||
axes,
|
||||
score,
|
||||
"intent_preference",
|
||||
memory_entry=entry,
|
||||
)
|
||||
|
||||
if isinstance(previous_run, dict):
|
||||
for item in self._v2_concept_units_for_run(previous_run):
|
||||
if item.get("kind") == "token":
|
||||
@@ -7918,6 +8272,7 @@ class FunPackVideoRefinerV2(FunPackVideoRefiner):
|
||||
|
||||
source_priority = {
|
||||
"intent": 4,
|
||||
"intent_preference": 3,
|
||||
"previous": 3,
|
||||
"preferred_context": 2,
|
||||
"memory": 1,
|
||||
@@ -8170,26 +8525,47 @@ class FunPackVideoRefinerV2(FunPackVideoRefiner):
|
||||
if not should_store:
|
||||
return "Negative repair memory: no poor-rated tags added."
|
||||
|
||||
raw_intent_prompt = self._v2_prompt_key(previous_run.get("intent_prompt", ""))
|
||||
has_explicit_intent = bool(
|
||||
raw_intent_prompt and
|
||||
not previous_run.get("intent_prompt_is_vague") and
|
||||
not self._v2_user_intent_prompt_is_vague(raw_intent_prompt)
|
||||
)
|
||||
intent_prompt = raw_intent_prompt if has_explicit_intent else ""
|
||||
intent_phrases = (
|
||||
[phrase for phrase in previous_run.get("intent_phrases", []) or [] if isinstance(phrase, dict)]
|
||||
if has_explicit_intent else
|
||||
[]
|
||||
)
|
||||
family_key, family_slot, _ = self._v2_intent_family_slot(global_state, intent_prompt, create=False)
|
||||
added = []
|
||||
for phrase in previous_run.get("phrases", []) or []:
|
||||
if not isinstance(phrase, dict):
|
||||
continue
|
||||
phrase_axes = self._v2_phrase_axes(phrase)
|
||||
if wrong_axes and not (phrase_axes & wrong_axes):
|
||||
continue
|
||||
if not wrong_axes and missing_axes and not (phrase_axes & missing_axes):
|
||||
continue
|
||||
text = self._v2_clean_phrase_text(phrase.get("text", ""))
|
||||
skipped_intent = 0
|
||||
|
||||
def remember_tag(text, phrase_axes, source, intent_locked=False):
|
||||
nonlocal skipped_intent
|
||||
text = self._v2_clean_phrase_text(text)
|
||||
if not text:
|
||||
continue
|
||||
return
|
||||
if intent_locked or self._v2_text_represented_by_intent(text, intent_prompt, intent_phrases, family_slot):
|
||||
skipped_intent += 1
|
||||
return
|
||||
if wrong_axes and not (phrase_axes & wrong_axes):
|
||||
return
|
||||
if not wrong_axes and missing_axes and not (phrase_axes & missing_axes):
|
||||
return
|
||||
entry = tags.setdefault(text, {
|
||||
"text": text,
|
||||
"count": 0,
|
||||
"axes": {},
|
||||
"family_key": family_key,
|
||||
"source": source,
|
||||
"probe_count": 0,
|
||||
"last_rating": "",
|
||||
"last_seen_iter": 0,
|
||||
})
|
||||
entry["count"] = int(entry.get("count", 0)) + (2 if wrong_axes else 1)
|
||||
entry["family_key"] = family_key or entry.get("family_key", "")
|
||||
entry["source"] = source
|
||||
entry["last_rating"] = rating_profile.get("label", "")
|
||||
entry["last_seen_iter"] = int(global_state.get("total_iterations", 0)) + 1
|
||||
axes = entry.setdefault("axes", {})
|
||||
@@ -8197,16 +8573,56 @@ class FunPackVideoRefinerV2(FunPackVideoRefiner):
|
||||
axes[axis] = int(axes.get(axis, 0)) + 1
|
||||
added.append(text)
|
||||
|
||||
for phrase in previous_run.get("phrases", []) or []:
|
||||
if not isinstance(phrase, dict):
|
||||
continue
|
||||
remember_tag(
|
||||
phrase.get("text", ""),
|
||||
self._v2_phrase_axes(phrase),
|
||||
"enhancer_or_prompt",
|
||||
intent_locked=has_explicit_intent and bool(phrase.get("intent_locked")),
|
||||
)
|
||||
|
||||
for candidate in previous_run.get("repair_candidates", []) or []:
|
||||
if not isinstance(candidate, dict):
|
||||
continue
|
||||
remember_tag(
|
||||
candidate.get("text", ""),
|
||||
set(candidate.get("axes", [])),
|
||||
"repair_candidate",
|
||||
intent_locked=False,
|
||||
)
|
||||
|
||||
if not added:
|
||||
return "Negative repair memory: no matching poor-rated tags."
|
||||
suffix = f" Skipped {skipped_intent} intent-locked tag(s)." if skipped_intent else ""
|
||||
return f"Negative repair memory: no matching poor-rated tags.{suffix}"
|
||||
memory["tags"] = dict(sorted(
|
||||
tags.items(),
|
||||
key=lambda item: (int(item[1].get("count", 0)), int(item[1].get("last_seen_iter", 0))),
|
||||
reverse=True,
|
||||
)[:80])
|
||||
return f"Negative repair memory: added {len(added)} poor-rated tag(s): {', '.join(added[:8])}."
|
||||
suffix = f" Skipped {skipped_intent} intent-locked tag(s)." if skipped_intent else ""
|
||||
return f"Negative repair memory: added {len(added)} poor-rated tag(s): {', '.join(added[:8])}.{suffix}"
|
||||
|
||||
def _v2_repair_negative_prompt(self, negative_prompt, global_state, axis_feedback=None):
|
||||
def _v2_negative_probe_due(self, text, entry, iter_num):
|
||||
if not isinstance(entry, dict):
|
||||
return False
|
||||
count = int(entry.get("count", 0))
|
||||
if count <= 1:
|
||||
return False
|
||||
digest = int(md5(str(text or "").encode("utf-8")).hexdigest()[:6], 16)
|
||||
return (int(iter_num or 0) + digest) % 11 == 0
|
||||
|
||||
def _v2_repair_negative_prompt(
|
||||
self,
|
||||
negative_prompt,
|
||||
global_state,
|
||||
axis_feedback=None,
|
||||
current_prompt="",
|
||||
intent_prompt="",
|
||||
intent_phrases=None,
|
||||
intent_family_slot=None,
|
||||
):
|
||||
prompt = self._v2_prompt_key(negative_prompt)
|
||||
memory = global_state.get("negative_prompt_memory", {})
|
||||
tags = memory.get("tags", {}) if isinstance(memory, dict) else {}
|
||||
@@ -8214,12 +8630,32 @@ class FunPackVideoRefinerV2(FunPackVideoRefiner):
|
||||
return prompt, "Negative repair: no stored poor-rated tags."
|
||||
wanted_axes = set(axis_feedback.get("missing_axes", [])) | set(axis_feedback.get("wrong_axes", [])) if isinstance(axis_feedback, dict) else set()
|
||||
ranked = []
|
||||
withheld_intent = 0
|
||||
withheld_family = 0
|
||||
withheld_probe = 0
|
||||
current_family_key = intent_family_slot.get("family_key", "") if isinstance(intent_family_slot, dict) else ""
|
||||
for text, entry in tags.items():
|
||||
if not isinstance(entry, dict):
|
||||
continue
|
||||
clean = self._v2_clean_phrase_text(entry.get("text", text))
|
||||
if not clean or self._v2_prompt_contains_text(prompt, clean):
|
||||
continue
|
||||
if self._v2_prompt_contains_text(current_prompt, clean) or self._v2_text_represented_by_intent(
|
||||
clean,
|
||||
intent_prompt,
|
||||
intent_phrases or [],
|
||||
intent_family_slot,
|
||||
):
|
||||
withheld_intent += 1
|
||||
continue
|
||||
entry_family = str(entry.get("family_key", ""))
|
||||
if entry_family and current_family_key and entry_family != current_family_key:
|
||||
withheld_family += 1
|
||||
continue
|
||||
if self._v2_negative_probe_due(clean, entry, int(global_state.get("total_iterations", 0)) + 1):
|
||||
entry["probe_count"] = int(entry.get("probe_count", 0)) + 1
|
||||
withheld_probe += 1
|
||||
continue
|
||||
axes = set(entry.get("axes", {}).keys()) if isinstance(entry.get("axes"), dict) else set()
|
||||
axis_bonus = 1.0 if wanted_axes and axes & wanted_axes else 0.0
|
||||
score = float(entry.get("count", 0)) + axis_bonus + min(0.5, int(entry.get("last_seen_iter", 0)) * 0.001)
|
||||
@@ -8227,9 +8663,25 @@ class FunPackVideoRefinerV2(FunPackVideoRefiner):
|
||||
ranked.sort(reverse=True)
|
||||
additions = [text for _, text in ranked[:8]]
|
||||
if not additions:
|
||||
return prompt, "Negative repair: stored poor-rated tags already present."
|
||||
withheld = []
|
||||
if withheld_intent:
|
||||
withheld.append(f"intent/current {withheld_intent}")
|
||||
if withheld_family:
|
||||
withheld.append(f"other-family {withheld_family}")
|
||||
if withheld_probe:
|
||||
withheld.append(f"probe {withheld_probe}")
|
||||
detail = f" Withheld: {', '.join(withheld)}." if withheld else ""
|
||||
return prompt, f"Negative repair: stored poor-rated tags already present.{detail}"
|
||||
repaired = f"{prompt}, {', '.join(additions)}" if prompt else ", ".join(additions)
|
||||
return repaired, f"Negative repair: added {len(additions)} persistent poor-rated tag(s): {', '.join(additions)}."
|
||||
withheld = []
|
||||
if withheld_intent:
|
||||
withheld.append(f"intent/current {withheld_intent}")
|
||||
if withheld_family:
|
||||
withheld.append(f"other-family {withheld_family}")
|
||||
if withheld_probe:
|
||||
withheld.append(f"probe {withheld_probe}")
|
||||
detail = f" Withheld: {', '.join(withheld)}." if withheld else ""
|
||||
return repaired, f"Negative repair: added {len(additions)} persistent poor-rated tag(s): {', '.join(additions)}.{detail}"
|
||||
|
||||
def _v2_lora_type(self, lora_type):
|
||||
lora_type = str(lora_type or "general").strip().lower()
|
||||
@@ -8375,6 +8827,11 @@ class FunPackVideoRefinerV2(FunPackVideoRefiner):
|
||||
global_state.setdefault("lora_weight_memory", {})
|
||||
global_state.setdefault("preferred_context_memory", {})
|
||||
global_state.setdefault("intent_alignment_memory", {})
|
||||
global_state.setdefault("intent_family_memory", {})
|
||||
global_state.setdefault("perfect_anchors", {})
|
||||
global_state.setdefault("variant_evidence", {})
|
||||
global_state.setdefault("intent_preference_phrases", {})
|
||||
global_state.setdefault("conditioning_deltas", {})
|
||||
global_state.setdefault("vision_memory", {})
|
||||
global_state.setdefault("loss_history", [])
|
||||
previous_run = state.get("last_run")
|
||||
@@ -8401,6 +8858,13 @@ class FunPackVideoRefinerV2(FunPackVideoRefiner):
|
||||
int(global_state.get("total_iterations", 0)) + 1,
|
||||
axis_feedback,
|
||||
)
|
||||
intent_family_status, _ = self._v2_update_intent_family_memory(
|
||||
global_state,
|
||||
previous_run,
|
||||
learning_profile,
|
||||
int(global_state.get("total_iterations", 0)) + 1,
|
||||
axis_feedback,
|
||||
)
|
||||
negative_memory_status = self._v2_update_negative_prompt_memory(
|
||||
global_state,
|
||||
previous_run,
|
||||
@@ -8414,7 +8878,7 @@ class FunPackVideoRefinerV2(FunPackVideoRefiner):
|
||||
int(global_state.get("total_iterations", 0)) + 1,
|
||||
axis_feedback,
|
||||
)
|
||||
memory_status = f"{memory_status}\n{intent_learning_status}"
|
||||
memory_status = f"{memory_status}\n{intent_family_status}\n{intent_learning_status}"
|
||||
self._v2_update_conditioning_memory(global_state, previous_run, learning_profile, axis_feedback)
|
||||
if has_previous_run and not learning_profile.get("skip_learning"):
|
||||
self._v2_update_streaks(global_state, learning_profile)
|
||||
@@ -8442,6 +8906,14 @@ class FunPackVideoRefinerV2(FunPackVideoRefiner):
|
||||
global_state,
|
||||
encode_cache=encode_cache,
|
||||
)
|
||||
intent_source_prompt = self._v2_intent_source_prompt(analysis_prompt, intent_prompt, intent_prompt_is_vague)
|
||||
current_family_key, current_family_slot, current_family_similarity = self._v2_intent_family_slot(
|
||||
global_state,
|
||||
intent_source_prompt,
|
||||
create=bool(intent_source_prompt and not current_prompt_refusal),
|
||||
)
|
||||
self._v2_mark_intent_locks(phrases, intent_source_prompt, intent_phrases, current_family_slot)
|
||||
self._v2_mark_intent_locks(intent_phrases, intent_source_prompt, intent_phrases, current_family_slot)
|
||||
refusal_status = ""
|
||||
repair_status = "Prompt repair: none."
|
||||
repair_candidates = []
|
||||
@@ -8471,6 +8943,7 @@ class FunPackVideoRefinerV2(FunPackVideoRefiner):
|
||||
previous_run,
|
||||
axis_feedback,
|
||||
intent_phrases=intent_phrases,
|
||||
intent_family_slot=current_family_slot,
|
||||
)
|
||||
if im_feeling_lucky and clip is None:
|
||||
lucky_status = f"Lucky: unavailable without CLIP | connected CONDITIONING accepted. {emphasis_status}"
|
||||
@@ -8490,11 +8963,21 @@ class FunPackVideoRefinerV2(FunPackVideoRefiner):
|
||||
training_info = f"Rating: {rating_label}\n{memory_status}\n{lucky_status}\n{encode_status}"
|
||||
return ([], status, training_info, fallback_graph, [])
|
||||
|
||||
refined, adaptation_status = self._v2_apply_conditioning_memory(cond, global_state, learning_profile, axis_feedback)
|
||||
refined, adaptation_status = self._v2_apply_conditioning_memory(
|
||||
cond,
|
||||
global_state,
|
||||
learning_profile,
|
||||
axis_feedback,
|
||||
intent_family_slot=current_family_slot,
|
||||
)
|
||||
repaired_negative_prompt, negative_repair_status = self._v2_repair_negative_prompt(
|
||||
negative_prompt,
|
||||
global_state,
|
||||
axis_feedback,
|
||||
current_prompt=prompt_to_encode,
|
||||
intent_prompt=intent_source_prompt,
|
||||
intent_phrases=intent_phrases,
|
||||
intent_family_slot=current_family_slot,
|
||||
)
|
||||
negative_conditioning = []
|
||||
negative_encode_status = "negative prompt empty"
|
||||
@@ -8601,10 +9084,14 @@ class FunPackVideoRefinerV2(FunPackVideoRefiner):
|
||||
"prompt": analysis_prompt,
|
||||
"encoded_prompt": prompt_to_encode,
|
||||
"conditioning": tensor_to_serializable(refined.detach().cpu()),
|
||||
"source_conditioning": tensor_to_serializable(cond.detach().cpu()),
|
||||
"phrases": phrases,
|
||||
"intent_prompt": intent_prompt,
|
||||
"intent_phrases": intent_phrases,
|
||||
"intent_prompt_is_vague": bool(intent_prompt_is_vague),
|
||||
"intent_source_prompt": intent_source_prompt,
|
||||
"intent_family_key": current_family_key,
|
||||
"intent_family_similarity": round(float(current_family_similarity), 6),
|
||||
"intent_alignment_adjustments": state_intent_alignment_adjustments,
|
||||
"repair_candidates": state_repair_candidates,
|
||||
"negative_prompt": repaired_negative_prompt,
|
||||
@@ -8636,6 +9123,7 @@ class FunPackVideoRefinerV2(FunPackVideoRefiner):
|
||||
f"{adaptation_status}\n"
|
||||
f"{training_guidance}\n"
|
||||
f"{lucky_status}"
|
||||
f"\nIntent family: {current_family_key[:8] if current_family_key else 'none'} sim={current_family_similarity:.2f}"
|
||||
f"\n{intent_alignment_status}"
|
||||
f"\n{repair_status}"
|
||||
f"\n{negative_repair_status}"
|
||||
@@ -8676,6 +9164,7 @@ class FunPackVideoRefinerV2(FunPackVideoRefiner):
|
||||
"Adaptation\n"
|
||||
f"{adaptation_status}\n"
|
||||
f"{lucky_status}\n"
|
||||
f"Intent family: {current_family_key[:8] if current_family_key else 'none'} sim={current_family_similarity:.2f}\n"
|
||||
f"{intent_alignment_status}\n"
|
||||
f"{repair_status}\n"
|
||||
f"{negative_repair_status}\n"
|
||||
|
||||
@@ -464,6 +464,282 @@ def test_intent_alignment_omits_bad_token_in_new_enhancer_phrase():
|
||||
]
|
||||
|
||||
|
||||
def test_repeated_intent_family_phrase_becomes_conservative_repair_candidate():
|
||||
refiner = FunPackVideoRefinerV2()
|
||||
global_state = {
|
||||
"phrase_memory": {},
|
||||
"intent_family_memory": {},
|
||||
"perfect_anchors": {},
|
||||
"variant_evidence": {},
|
||||
"intent_preference_phrases": {},
|
||||
"conditioning_deltas": {},
|
||||
}
|
||||
intent_prompt = "yellow car riding down the road"
|
||||
enhanced_prompt = "yellow car in a desert"
|
||||
profile = normalize_refiner_v2_rating("Missing action")
|
||||
feedback = refiner._v2_axis_feedback(profile, None)
|
||||
|
||||
for iteration in range(2):
|
||||
refiner._v2_update_intent_family_memory(
|
||||
global_state,
|
||||
{
|
||||
"prompt": enhanced_prompt,
|
||||
"phrases": prompt_phrases(refiner, enhanced_prompt, global_state),
|
||||
"intent_prompt": intent_prompt,
|
||||
"intent_phrases": prompt_phrases(refiner, intent_prompt, global_state),
|
||||
},
|
||||
profile,
|
||||
iteration + 1,
|
||||
feedback,
|
||||
)
|
||||
|
||||
current_intent = "yellow car down the road"
|
||||
_, family_slot, _ = refiner._v2_intent_family_slot(global_state, current_intent, create=False)
|
||||
repaired, status, candidates = refiner._v2_repair_prompt_for_missing_axes(
|
||||
enhanced_prompt,
|
||||
prompt_phrases(refiner, enhanced_prompt, global_state),
|
||||
global_state,
|
||||
None,
|
||||
feedback,
|
||||
intent_phrases=prompt_phrases(refiner, current_intent, global_state),
|
||||
intent_family_slot=family_slot,
|
||||
)
|
||||
|
||||
assert "yellow car riding down the road" in repaired
|
||||
assert "intent_preference" in status
|
||||
assert any(candidate["source"] == "intent_preference" for candidate in candidates)
|
||||
|
||||
|
||||
def test_repeated_intent_preference_does_not_repair_unrelated_family():
|
||||
refiner = FunPackVideoRefinerV2()
|
||||
global_state = {
|
||||
"phrase_memory": {},
|
||||
"intent_family_memory": {},
|
||||
"perfect_anchors": {},
|
||||
"variant_evidence": {},
|
||||
"intent_preference_phrases": {},
|
||||
"conditioning_deltas": {},
|
||||
}
|
||||
yellow_intent = "yellow car riding down the road"
|
||||
dragon_intent = "dragon sleeping in a cave"
|
||||
profile = normalize_refiner_v2_rating("Missing action")
|
||||
feedback = refiner._v2_axis_feedback(profile, None)
|
||||
|
||||
for iteration in range(2):
|
||||
refiner._v2_update_intent_family_memory(
|
||||
global_state,
|
||||
{
|
||||
"prompt": "yellow car in a desert",
|
||||
"phrases": prompt_phrases(refiner, "yellow car in a desert", global_state),
|
||||
"intent_prompt": yellow_intent,
|
||||
"intent_phrases": prompt_phrases(refiner, yellow_intent, global_state),
|
||||
},
|
||||
profile,
|
||||
iteration + 1,
|
||||
feedback,
|
||||
)
|
||||
|
||||
_, dragon_slot, _ = refiner._v2_intent_family_slot(global_state, dragon_intent, create=True)
|
||||
repaired, _, candidates = refiner._v2_repair_prompt_for_missing_axes(
|
||||
"dragon in a cave",
|
||||
prompt_phrases(refiner, "dragon in a cave", global_state),
|
||||
global_state,
|
||||
None,
|
||||
feedback,
|
||||
intent_phrases=prompt_phrases(refiner, dragon_intent, global_state),
|
||||
intent_family_slot=dragon_slot,
|
||||
)
|
||||
|
||||
assert "yellow car riding down the road" not in repaired
|
||||
assert all(candidate["text"] != "yellow car riding down the road" for candidate in candidates)
|
||||
|
||||
|
||||
def test_intent_family_perfect_anchor_keeps_loved_variant():
|
||||
refiner = FunPackVideoRefinerV2()
|
||||
global_state = {
|
||||
"phrase_memory": {},
|
||||
"intent_family_memory": {},
|
||||
"perfect_anchors": {},
|
||||
"variant_evidence": {},
|
||||
"intent_preference_phrases": {},
|
||||
"conditioning_deltas": {},
|
||||
}
|
||||
intent_prompt = "yellow car riding down the road"
|
||||
profile = normalize_refiner_v2_rating("Perfect")
|
||||
feedback = refiner._v2_axis_feedback(profile, None)
|
||||
source = tensor_to_serializable(torch.zeros(1, 3, 2))
|
||||
first = tensor_to_serializable(torch.ones(1, 3, 2))
|
||||
second = tensor_to_serializable(torch.ones(1, 3, 2) * 2.0)
|
||||
|
||||
refiner._v2_update_intent_family_memory(
|
||||
global_state,
|
||||
{
|
||||
"prompt": "yellow car riding down the road",
|
||||
"encoded_prompt": "yellow car riding down the road",
|
||||
"phrases": prompt_phrases(refiner, "yellow car riding down the road", global_state),
|
||||
"intent_prompt": intent_prompt,
|
||||
"intent_phrases": prompt_phrases(refiner, intent_prompt, global_state),
|
||||
"source_conditioning": source,
|
||||
"conditioning": first,
|
||||
},
|
||||
profile,
|
||||
1,
|
||||
feedback,
|
||||
)
|
||||
refiner._v2_update_intent_family_memory(
|
||||
global_state,
|
||||
{
|
||||
"prompt": "yellow car riding down the road, camera focused on wheels",
|
||||
"encoded_prompt": "yellow car riding down the road, camera focused on wheels",
|
||||
"phrases": prompt_phrases(refiner, "yellow car riding down the road, camera focused on wheels", global_state),
|
||||
"intent_prompt": intent_prompt,
|
||||
"intent_phrases": prompt_phrases(refiner, intent_prompt, global_state),
|
||||
"source_conditioning": source,
|
||||
"conditioning": second,
|
||||
},
|
||||
profile,
|
||||
2,
|
||||
feedback,
|
||||
)
|
||||
|
||||
_, slot, _ = refiner._v2_intent_family_slot(global_state, intent_prompt, create=False)
|
||||
assert slot["perfect_anchors"]["base"]["positive_prompt"] == "yellow car riding down the road"
|
||||
assert len(slot["loved_variants"]) == 1
|
||||
assert global_state["perfect_anchors"][slot["family_key"]]["base"]["positive_prompt"] == "yellow car riding down the road"
|
||||
assert slot["conditioning_deltas"]["positive"]["count"] == 2
|
||||
|
||||
|
||||
def test_pre_perfect_missing_intent_learning_is_conservative():
|
||||
refiner = FunPackVideoRefinerV2()
|
||||
global_state = {"phrase_memory": {}, "intent_alignment_memory": {}, "intent_family_memory": {}}
|
||||
intent_prompt = "woman walking through neon rain"
|
||||
profile = normalize_refiner_v2_rating("Missing action")
|
||||
feedback = refiner._v2_axis_feedback(profile, None)
|
||||
|
||||
refiner._v2_update_intent_family_memory(
|
||||
global_state,
|
||||
{
|
||||
"prompt": "cinematic studio portrait",
|
||||
"phrases": prompt_phrases(refiner, "cinematic studio portrait", global_state),
|
||||
"intent_prompt": intent_prompt,
|
||||
"intent_phrases": prompt_phrases(refiner, intent_prompt, global_state),
|
||||
},
|
||||
profile,
|
||||
1,
|
||||
feedback,
|
||||
)
|
||||
refiner._v2_update_intent_alignment_memory(
|
||||
global_state,
|
||||
{
|
||||
"prompt": "cinematic studio portrait",
|
||||
"phrases": prompt_phrases(refiner, "cinematic studio portrait", global_state),
|
||||
"intent_prompt": intent_prompt,
|
||||
"intent_phrases": prompt_phrases(refiner, intent_prompt, global_state),
|
||||
},
|
||||
profile,
|
||||
1,
|
||||
feedback,
|
||||
)
|
||||
|
||||
slot = next(iter(global_state["intent_alignment_memory"].values()))
|
||||
missing = slot["missing_intent_phrases"]["woman walking through neon rain"]
|
||||
assert 0.5 <= missing["score"] < 0.8
|
||||
|
||||
|
||||
def test_negative_repair_never_adds_current_intent_phrase():
|
||||
refiner = FunPackVideoRefinerV2()
|
||||
global_state = {"phrase_memory": {}, "negative_prompt_memory": {}, "intent_family_memory": {}}
|
||||
intent_prompt = "Nicole is shooting"
|
||||
enhanced_prompt = "Nicole from Zenless Zone Zero holding a gun and shooting"
|
||||
profile = normalize_refiner_v2_rating("Wrong action")
|
||||
feedback = refiner._v2_axis_feedback(profile, None)
|
||||
intent_phrases = prompt_phrases(refiner, intent_prompt, global_state)
|
||||
phrases = prompt_phrases(refiner, enhanced_prompt, global_state)
|
||||
|
||||
refiner._v2_update_intent_family_memory(
|
||||
global_state,
|
||||
{
|
||||
"prompt": enhanced_prompt,
|
||||
"phrases": phrases,
|
||||
"intent_prompt": intent_prompt,
|
||||
"intent_phrases": intent_phrases,
|
||||
},
|
||||
profile,
|
||||
1,
|
||||
feedback,
|
||||
)
|
||||
status = refiner._v2_update_negative_prompt_memory(
|
||||
global_state,
|
||||
{
|
||||
"prompt": enhanced_prompt,
|
||||
"phrases": phrases,
|
||||
"intent_prompt": intent_prompt,
|
||||
"intent_phrases": intent_phrases,
|
||||
},
|
||||
profile,
|
||||
feedback,
|
||||
)
|
||||
_, family_slot, _ = refiner._v2_intent_family_slot(global_state, intent_prompt, create=False)
|
||||
repaired, repair_status = refiner._v2_repair_negative_prompt(
|
||||
"",
|
||||
global_state,
|
||||
feedback,
|
||||
current_prompt=enhanced_prompt,
|
||||
intent_prompt=intent_prompt,
|
||||
intent_phrases=intent_phrases,
|
||||
intent_family_slot=family_slot,
|
||||
)
|
||||
|
||||
assert repaired == ""
|
||||
assert "Skipped 1 intent-locked" in status
|
||||
assert "intent/current" in repair_status or "no stored poor-rated tags" in repair_status
|
||||
|
||||
|
||||
def test_rejected_repair_candidate_can_be_penalized_as_negative_memory():
|
||||
refiner = FunPackVideoRefinerV2()
|
||||
global_state = {"phrase_memory": {}, "negative_prompt_memory": {}, "intent_family_memory": {}}
|
||||
profile = normalize_refiner_v2_rating("Wrong details")
|
||||
feedback = refiner._v2_axis_feedback(profile, None)
|
||||
|
||||
refiner._v2_update_negative_prompt_memory(
|
||||
global_state,
|
||||
{
|
||||
"prompt": "person smoking",
|
||||
"phrases": prompt_phrases(refiner, "person smoking", global_state),
|
||||
"intent_prompt": "person smoking",
|
||||
"intent_phrases": prompt_phrases(refiner, "person smoking", global_state),
|
||||
"repair_candidates": [{"text": "tiny smoke curls", "axes": ["details"], "score": 2.0, "source": "memory"}],
|
||||
},
|
||||
profile,
|
||||
feedback,
|
||||
)
|
||||
|
||||
assert "tiny smoke curls" in global_state["negative_prompt_memory"]["tags"]
|
||||
assert global_state["negative_prompt_memory"]["tags"]["tiny smoke curls"]["source"] == "repair_candidate"
|
||||
|
||||
|
||||
def test_intent_family_delta_ignores_incompatible_shapes_and_caps_strength():
|
||||
refiner = FunPackVideoRefinerV2()
|
||||
conditioning = torch.ones(1, 3, 2)
|
||||
slot = {
|
||||
"conditioning_deltas": {
|
||||
"positive": {
|
||||
"count": 1,
|
||||
"delta": tensor_to_serializable(torch.ones(1, 4, 2)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
unchanged, status = refiner._v2_apply_intent_family_delta(conditioning, slot, 0.05)
|
||||
assert torch.equal(unchanged, conditioning)
|
||||
assert status == "intent-family idle"
|
||||
|
||||
slot["conditioning_deltas"]["positive"]["delta"] = tensor_to_serializable(torch.ones(1, 3, 2) * 100.0)
|
||||
changed, status = refiner._v2_apply_intent_family_delta(conditioning, slot, 0.05)
|
||||
assert status.startswith("intent-family positive delta")
|
||||
assert torch.max(torch.abs(changed - conditioning)).item() < 1.0
|
||||
|
||||
|
||||
def test_lucky_skips_appearance_memory_unless_prompt_mentions_it():
|
||||
refiner = FunPackVideoRefinerV2()
|
||||
global_state = {"phrase_memory": {}, "preferred_context_memory": {}}
|
||||
|
||||
Reference in New Issue
Block a user