Session Reset wipes all prompt keys; warn with the key list
Per-scene multi-key learning trains every refinement key whose shortcut fired in the prompt, so a reset must clear them all — not just the project/default key, which left stale per-key state behind. - conditioning.py: _v2_reset_prompt_keys() — on reset_session, union the non-default keys from the raw positive + intent prompts and reset/save each (primary key excluded; main flow already handles it). Reported in the state status string. - store.js: "Reset Studio session" now confirms before arming, listing every key it will wipe (default + non-default keys in the prompt, from the preview's scene_refinement_keys). Disarming does not re-prompt. - tests: union/primary-exclusion + no-op cases. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.8
parent
b78de23ccd
commit
c8ec0e3d55
@@ -8,6 +8,8 @@
|
||||
|
||||
**Per-scene refinement-key preview.** The timeline preview now shows, on each scene, the refinement key(s) it will actually steer with before you generate — explicit keys (with `(avg)` when more than one), or the project default key (greyed, `(default)`) when no shortcut key fired in that scene. The preview reuses the exact generation-time resolver (`resolve_scene_refinement_keys`), so what you see matches what runs, including the safe-union fallback.
|
||||
|
||||
**Session Reset now wipes every key a run trains, not just the default.** Because per-scene multi-key learning trains *every* refinement key whose shortcut fired in the prompt, a Studio Session Reset now clears all of those keys too (project/default key + each non-default key activated by a shortcut), so no stale per-key state survives a reset (`FunPackVideoRefinerV2._v2_reset_prompt_keys`). The Movie Editor's "Reset Studio session" now confirms first, listing exactly which keys will be wiped: *"This action will reset Studio learning for keys: default, key1, key2… To avoid resetting a non-default key, remove the shortcut that activates it from the prompt. Proceed?"* — disarming a mis-armed reset does not re-prompt.
|
||||
|
||||
## [3.0.0] - 2026-06-11
|
||||
|
||||
### Added
|
||||
|
||||
@@ -6461,6 +6461,32 @@ class FunPackVideoRefinerV2(FunPackVideoRefiner):
|
||||
except (json.JSONDecodeError, OSError, ValueError):
|
||||
return self._v2_empty_state(refinement_key), "reset unreadable"
|
||||
|
||||
def _v2_reset_prompt_keys(self, primary_key, *raw_prompts):
|
||||
"""On Session Reset, also wipe every NON-default refinement key whose shortcut fired in
|
||||
the prompt(s) — not just the project/default key. Any key a run can TRAIN (per-scene
|
||||
multi-key learning) must also be resettable by that run, otherwise stale state for those
|
||||
keys survives a reset. primary_key is excluded (the main flow already reset + will resave
|
||||
it). Returns the extra keys reset, for the status string."""
|
||||
keys = set()
|
||||
for text in raw_prompts:
|
||||
try:
|
||||
_, _, all_keys = prompt_scene_shortcut_keys(text)
|
||||
except Exception as error:
|
||||
print(f"[FunPackVideoRefinerV2] Reset key scan failed: {error}")
|
||||
all_keys = set()
|
||||
keys |= all_keys
|
||||
keys.discard(str(primary_key or "default"))
|
||||
keys.discard("")
|
||||
done = []
|
||||
for key in sorted(keys):
|
||||
try:
|
||||
fresh_state, _ = self._v2_load_state(key, reset_session=True)
|
||||
self._v2_save_state(fresh_state, key)
|
||||
done.append(key)
|
||||
except Exception as error:
|
||||
print(f"[FunPackVideoRefinerV2] Reset of refinement key '{key}' failed: {error}")
|
||||
return done
|
||||
|
||||
def _v2_save_state(self, data, refinement_key):
|
||||
path = self._v2_state_path(refinement_key)
|
||||
os.makedirs(os.path.dirname(path), exist_ok=True)
|
||||
@@ -11984,6 +12010,7 @@ class FunPackVideoRefinerV2(FunPackVideoRefiner):
|
||||
# Pre-shortcut prompt, kept so Batch Training can re-resolve shortcuts with different
|
||||
# seeds to build N variant conditionings (Studio owns conditioning production).
|
||||
_raw_positive_prompt = str(positive_prompt or "")
|
||||
_raw_intent_prompt = str(user_intent_prompt or "")
|
||||
positive_prompt, shortcut_status, shortcut_applied = self._v2_apply_global_shortcuts(positive_prompt, seed=seed)
|
||||
user_intent_prompt, intent_shortcut_status, intent_shortcut_applied = self._v2_apply_global_shortcuts(user_intent_prompt, seed=seed)
|
||||
if intent_shortcut_applied:
|
||||
@@ -12006,6 +12033,10 @@ class FunPackVideoRefinerV2(FunPackVideoRefiner):
|
||||
advisor_clip = advisor_clip if advisor_clip is not None else clip
|
||||
prompt_only_mode = execution_mode == "Prompt only"
|
||||
state, state_status = self._v2_load_state(refinement_key, reset_session=reset_session)
|
||||
if reset_session:
|
||||
extra_reset = self._v2_reset_prompt_keys(refinement_key, _raw_positive_prompt, _raw_intent_prompt)
|
||||
if extra_reset:
|
||||
state_status = f"{state_status} (also reset keys: {', '.join(extra_reset)})"
|
||||
scene_db, scene_builder_status = self._v2_scene_builder_db(state)
|
||||
global_state = state.setdefault("global", {})
|
||||
global_state.setdefault("phrase_memory", {})
|
||||
|
||||
@@ -2786,10 +2786,31 @@
|
||||
);
|
||||
}
|
||||
|
||||
// Every refinement key a reset will wipe: the project/default key plus every non-default
|
||||
// key whose shortcut fires in the current prompt (those keys also get TRAINED per scene, so
|
||||
// a reset must clear them too). Mirrors the backend _v2_reset_prompt_keys union.
|
||||
function sessionResetKeys() {
|
||||
const pv = state.preview || {};
|
||||
const defKey = (state.project?.refinement_key || "default").trim() || "default";
|
||||
const out = new Set([defKey]);
|
||||
(pv.scene_refinement_keys || []).forEach((rk) => {
|
||||
if (rk && !rk.uses_default) (rk.keys || []).forEach((k) => { if (k) out.add(k); });
|
||||
});
|
||||
return [...out];
|
||||
}
|
||||
|
||||
// Toggle a pending Studio session reset — applied to the FIRST run of the next
|
||||
// generation. Clicking again disarms it (in case of a mis-click).
|
||||
// generation. Clicking again disarms it (in case of a mis-click). Arming asks for
|
||||
// confirmation, listing every key it will wipe so non-default keys aren't lost silently.
|
||||
let _resetSessionPending = false;
|
||||
function resetStudioSession() {
|
||||
if (!_resetSessionPending) {
|
||||
const keys = sessionResetKeys();
|
||||
const msg = `This action will reset Studio learning for keys: ${keys.join(", ")}.\n\n`
|
||||
+ "It is applied on the next generation. To avoid resetting a non-default key, "
|
||||
+ "remove the shortcut that activates it from the prompt.\n\nProceed?";
|
||||
if (!confirm(msg)) return;
|
||||
}
|
||||
_resetSessionPending = !_resetSessionPending;
|
||||
set({ resetSessionArmed: _resetSessionPending });
|
||||
}
|
||||
|
||||
@@ -133,6 +133,38 @@ def test_resolver_matches_studio(monkeypatch):
|
||||
assert conditioning.resolve_scene_refinement_keys(raw, n) == studio._v2_scene_refinement_keys(raw, n)
|
||||
|
||||
|
||||
def test_reset_prompt_keys_union_excludes_primary(monkeypatch):
|
||||
"""Session reset wipes the project key (caller) PLUS every non-default key in the prompt."""
|
||||
db = _db({"a": (["alpha"], ["AAA"], "keyA"), "b": (["beta"], ["BBB"], "keyB"),
|
||||
"c": (["gamma"], ["CCC"], "keyC")})
|
||||
monkeypatch.setattr(templates, "load_shortcut_db", lambda: db)
|
||||
monkeypatch.setattr(templates, "load_custom_transition_triggers", lambda: {})
|
||||
studio = conditioning.FunPackVideoRefinerV2()
|
||||
reset_calls, saved = [], []
|
||||
monkeypatch.setattr(studio, "_v2_load_state",
|
||||
lambda key, reset_session=False: (reset_calls.append((key, reset_session)) or ({}, "fresh")))
|
||||
monkeypatch.setattr(studio, "_v2_save_state", lambda data, key: saved.append(key))
|
||||
# keyA fires in positive, keyC fires in intent; primary "default" is excluded.
|
||||
done = studio._v2_reset_prompt_keys("default", "alpha shot", "gamma intent")
|
||||
assert done == ["keyA", "keyC"]
|
||||
assert all(rs is True for _, rs in reset_calls)
|
||||
assert sorted(k for k, _ in reset_calls) == ["keyA", "keyC"]
|
||||
assert sorted(saved) == ["keyA", "keyC"]
|
||||
|
||||
|
||||
def test_reset_prompt_keys_skips_when_primary_only(monkeypatch):
|
||||
db = _db({"a": (["alpha"], ["AAA"], "keyA")})
|
||||
monkeypatch.setattr(templates, "load_shortcut_db", lambda: db)
|
||||
monkeypatch.setattr(templates, "load_custom_transition_triggers", lambda: {})
|
||||
studio = conditioning.FunPackVideoRefinerV2()
|
||||
monkeypatch.setattr(studio, "_v2_load_state", lambda *a, **k: ({}, "fresh"))
|
||||
monkeypatch.setattr(studio, "_v2_save_state", lambda *a, **k: None)
|
||||
# primary key is keyA itself -> nothing extra to reset
|
||||
assert studio._v2_reset_prompt_keys("keyA", "alpha shot") == []
|
||||
# no keyed shortcuts at all -> nothing extra
|
||||
assert studio._v2_reset_prompt_keys("default", "plain prompt") == []
|
||||
|
||||
|
||||
def test_bridge_scene_refinement_keys_preview(monkeypatch):
|
||||
"""Preview payload: explicit keys vs project-default fallback per scene."""
|
||||
from movie_editor.backend import bridge
|
||||
|
||||
Reference in New Issue
Block a user