feat: disable every rating-driven mechanism on H3 except phrase emphasis and REINS
H3's only validated (or being-validated) rating consumers are h3_phrase_emphasis and h3_repr_steering. Everything else -- value_guidance/absolute taste steering, output_guidance, trajectory_guidance, dynashift, embed_guidance, score_slider, taste_nearest_prompt, and the path planner -- was built and calibrated for LTXAV or for a value function nothing on H3 reads. None of it helps REINS; it only trains value functions that are never consumed and prints status lines for mechanisms that can never fire, which is what actually produced the "80% of the log is useless" bug report this responds to. Forced off at two chokepoints (top of sample() in samplers.py once self._is_h3 is known; top of refine_v2 in conditioning.py once the clip's family is known), not just hidden in Engine Settings, so a project saved before this still behaves correctly. Value-function and Absolute-taste TRAINING (not just application) is skipped too -- per-scene, per-key, and the main path -- since nothing on H3 will ever read what they'd produce.
This commit is contained in:
+21
-8
@@ -4791,10 +4791,14 @@ class FunPackVideoRefinerV2(FunPackVideoRefiner):
|
||||
return seed_memory_status
|
||||
|
||||
def _v2_learn_scene_into_keys(self, scene_keys, project_key, scene_run, profile, iter_num,
|
||||
axis_feedback, seed_output_connected):
|
||||
axis_feedback, seed_output_connected, is_h3=False):
|
||||
"""Train every non-default key that participated in a scene. Each key's own state file
|
||||
gets the full learning suite + its value-function update, then is saved. The project
|
||||
key is skipped here (it already learns into the run's main global_state)."""
|
||||
key is skipped here (it already learns into the run's main global_state).
|
||||
|
||||
`is_h3` skips the value-function update: nothing on H3 reads it (see refine_v2's
|
||||
is_h3 override) -- the rest of the learning suite still runs, it is only this
|
||||
mechanism-specific training that has nowhere to go on H3."""
|
||||
trained = []
|
||||
for key in scene_keys or []:
|
||||
key = str(key or "").strip()
|
||||
@@ -4806,7 +4810,7 @@ class FunPackVideoRefinerV2(FunPackVideoRefiner):
|
||||
self._v2_learn_scene_into_state(
|
||||
key_global, scene_run, profile, iter_num, axis_feedback, seed_output_connected,
|
||||
)
|
||||
if not profile.get("skip_learning") and self._v2_reward_admissible(profile):
|
||||
if not is_h3 and not profile.get("skip_learning") and self._v2_reward_admissible(profile):
|
||||
self._v2_train_value_function(
|
||||
refinement_state_path(key, "value_fn", prefix="refine_v2", extension="pt"),
|
||||
scene_run.get("conditioning"),
|
||||
@@ -4834,6 +4838,11 @@ class FunPackVideoRefinerV2(FunPackVideoRefiner):
|
||||
"""Apply Refiner V2 learning once per rated scene (Movie Editor multi-scene chains)."""
|
||||
if not isinstance(previous_run, dict) or not scene_ratings:
|
||||
return "Movie Editor ratings: none.", None
|
||||
try:
|
||||
from . import minimax_h3 as _h3fam
|
||||
except ImportError:
|
||||
import minimax_h3 as _h3fam
|
||||
is_h3 = _h3fam.is_h3_clip(clip)
|
||||
scene_count = int(previous_run.get("scene_count") or len(previous_run.get("scene_texts") or []) or 1)
|
||||
if scene_count <= 1:
|
||||
return "Movie Editor ratings: previous run was single-scene.", None
|
||||
@@ -4873,9 +4882,10 @@ class FunPackVideoRefinerV2(FunPackVideoRefiner):
|
||||
# so a clip's key never trains the default (and vice versa).
|
||||
trained_keys = self._v2_learn_scene_into_keys(
|
||||
scene_keys, refinement_key, scene_run, profile, iter_num,
|
||||
axis_feedback, seed_output_connected,
|
||||
axis_feedback, seed_output_connected, is_h3=is_h3,
|
||||
)
|
||||
self._v2_learn_absolute(scene_run, profile)
|
||||
if not is_h3:
|
||||
self._v2_learn_absolute(scene_run, profile)
|
||||
profiles.append(profile)
|
||||
lines.append(
|
||||
f"Scene {scene_index + 1} ({label}): trained custom key(s) [{', '.join(trained_keys)}], default skipped"
|
||||
@@ -4886,7 +4896,7 @@ class FunPackVideoRefinerV2(FunPackVideoRefiner):
|
||||
seed_memory_status = self._v2_learn_scene_into_state(
|
||||
global_state, scene_run, profile, iter_num, axis_feedback, seed_output_connected,
|
||||
)
|
||||
if refinement_key and self._v2_reward_admissible(profile):
|
||||
if not is_h3 and refinement_key and self._v2_reward_admissible(profile):
|
||||
with self._v2_stage("value function"):
|
||||
n = self._v2_train_value_function(
|
||||
refinement_state_path(refinement_key, "value_fn", prefix="refine_v2", extension="pt"),
|
||||
@@ -4895,7 +4905,8 @@ class FunPackVideoRefinerV2(FunPackVideoRefiner):
|
||||
)
|
||||
if n is not None:
|
||||
print(f"[FunPackRefiner] Scene {scene_index + 1} value function updated — {n} samples")
|
||||
self._v2_learn_absolute(scene_run, profile)
|
||||
if not is_h3:
|
||||
self._v2_learn_absolute(scene_run, profile)
|
||||
profiles.append(profile)
|
||||
lines.append(
|
||||
f"Scene {scene_index + 1} ({label}): {seed_memory_status.split(': ', 1)[-1]}"
|
||||
@@ -10207,7 +10218,9 @@ class FunPackVideoRefinerV2(FunPackVideoRefiner):
|
||||
# Runs even with no refinement_key (Absolute is global), so standalone runs still build it.
|
||||
# Skipped for Wrong-* repair ratings (skip_value_function): Absolute reads reward as pure
|
||||
# quality, so a 0.0/low repair reward on a good gen would push it into the global bad_dir.
|
||||
if has_previous_run:
|
||||
# NOT on H3 -- nothing reads it (steer_mode/absolute_strength are Studio-side, gated by
|
||||
# the same value_guidance override above), so training it is pure waste.
|
||||
if has_previous_run and not _is_h3:
|
||||
with self._v2_stage("absolute taste"):
|
||||
self._v2_learn_absolute(previous_run, learning_profile)
|
||||
if has_previous_run and not learning_profile.get("skip_learning"):
|
||||
|
||||
+14
@@ -6909,6 +6909,20 @@ class FunPackLTXAVSceneChainSampler:
|
||||
# happened last run is reported again rather than deduped away forever.
|
||||
_log.begin_run()
|
||||
self._is_h3 = self._set_stream_axes(model)
|
||||
if self._is_h3:
|
||||
# H3's rating-driven mechanisms are h3_phrase_emphasis (Studio) and
|
||||
# h3_repr_steering (this file) ONLY -- see project_reward_model_rework.md and the
|
||||
# 2026-09-03 dev session. Every other rating-driven latent/conditioning steerer
|
||||
# was built and validated for LTXAV; none of them has been shown to do anything
|
||||
# useful on H3 (output_guidance/trajectory_guidance/dynashift/score_slider ride
|
||||
# the same last-half gate that was calibrated there, and embed_guidance/
|
||||
# taste_nearest_prompt read the same relative value function that phrase
|
||||
# emphasis's own manipulation check showed produces near-invisible pushes at H3's
|
||||
# real calibration). Forced off HERE, not just hidden in Engine Settings, so a
|
||||
# project saved before this still behaves correctly instead of silently doing
|
||||
# six things nobody asked for.
|
||||
output_guidance = trajectory_guidance = dynashift = False
|
||||
embed_guidance = score_slider = taste_nearest_prompt = False
|
||||
# Read once per run, consumed by _install_h3_final_layer at each scene's sample call.
|
||||
self._h3_video_detail = max(0.0, min(2.0, float(h3_video_detail)))
|
||||
# The gate every rating-driven wrapper shares. On H3 it is read off the schedule's
|
||||
|
||||
@@ -457,6 +457,40 @@ def test_me_scene_ratings_custom_key_skips_default(monkeypatch):
|
||||
assert calls["default_vf"] == 1 # default VF trained ONLY scene 2
|
||||
|
||||
|
||||
def test_h3_scene_ratings_skip_value_function_and_absolute(monkeypatch):
|
||||
"""H3's only rating-driven mechanisms are h3_phrase_emphasis and h3_repr_steering --
|
||||
nothing reads the per-key value function or the Absolute taste store on H3, so training
|
||||
them per rated scene is pure waste (see the 2026-09-03 dev session)."""
|
||||
import minimax_h3
|
||||
monkeypatch.setattr(minimax_h3, "is_h3_clip", lambda c: True)
|
||||
studio = conditioning.FunPackVideoRefinerV2()
|
||||
calls = {"vf": 0, "absolute": 0, "custom_is_h3": []}
|
||||
monkeypatch.setattr(studio, "_v2_build_scene_learning_run",
|
||||
lambda run, idx, *a, **k: {"conditioning": {"x": 1}, "scene_index": idx})
|
||||
monkeypatch.setattr(studio, "_v2_axis_feedback",
|
||||
lambda *a, **k: {"missing_axes": [], "satisfied_axes": []})
|
||||
monkeypatch.setattr(studio, "_v2_learn_scene_into_state", lambda *a, **k: "seed: ok")
|
||||
monkeypatch.setattr(studio, "_v2_train_value_function",
|
||||
lambda *a, **k: (calls.__setitem__("vf", calls["vf"] + 1), 1)[1])
|
||||
monkeypatch.setattr(studio, "_v2_learn_absolute",
|
||||
lambda *a, **k: calls.__setitem__("absolute", calls["absolute"] + 1))
|
||||
|
||||
def _fake_learn_into_keys(keys, *a, is_h3=False, **k):
|
||||
calls["custom_is_h3"].append(is_h3)
|
||||
return list(keys)
|
||||
monkeypatch.setattr(studio, "_v2_learn_scene_into_keys", _fake_learn_into_keys)
|
||||
|
||||
prev = {"scene_count": 2, "scene_texts": ["s1", "s2"],
|
||||
"scene_refinement_keys": [["mykey"], []]} # scene1 custom, scene2 default
|
||||
ratings = [{"index": 0, "rating": "Perfect"}, {"index": 1, "rating": "Perfect"}]
|
||||
studio._v2_apply_movie_editor_scene_ratings(
|
||||
{}, prev, ratings, 1, refinement_key="default", clip=object())
|
||||
|
||||
assert calls["vf"] == 0 # scene 2 (default key) never trains its VF on H3
|
||||
assert calls["absolute"] == 0 # neither scene feeds Absolute on H3
|
||||
assert calls["custom_is_h3"] == [True] # scene 1's custom-key path was told it's H3 too
|
||||
|
||||
|
||||
def test_wrong_ratings_skip_value_function():
|
||||
"""Wrong-* are prompt-repair signals, not quality rewards: they must NOT train the value
|
||||
function (a 0.0/low reward on a visually-good gen poisons the learned quality landscape).
|
||||
|
||||
Reference in New Issue
Block a user