From 5e9de5223c980fa7a7b2dc3d99ffcaf6933b69e0 Mon Sep 17 00:00:00 2001 From: digital-garbage <133010426+digital-garbage@users.noreply.github.com> Date: Tue, 25 Aug 2026 23:03:22 +0300 Subject: [PATCH] fix(studio): trim the enhancer by repetition, not by an invented length cap MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The 2000-character cap was wrong. H3 has no padding, so every prompt token is a real sequence row and a long prompt is a legitimate thing to want — an enhanced one especially. Cutting at a fixed length contradicted the token limit the user had just set. Length cannot tell "long and detailed" from "stuck". What distinguishes them is that a model which has stopped writing starts repeating itself, so that is what is detected now: three occurrences of the same sentence and the prompt is cut at the first repeat. A long prompt that never repeats is never touched. The only remaining length bound is the one the user's own token limit already implies (~4 chars/token), so there is one control rather than two fighting. Short recurring lines are exempt — "She smiles." legitimately recurs in a shot list. Tests: 1690 passed. --- conditioning.py | 55 +++++++++++++++++++++++-------- tests/test_prompt_enhancer.py | 62 ++++++++++++++++++++++++++++------- 2 files changed, 92 insertions(+), 25 deletions(-) diff --git a/conditioning.py b/conditioning.py index 66fb175d..e1a3c96e 100644 --- a/conditioning.py +++ b/conditioning.py @@ -9211,10 +9211,11 @@ class FunPackVideoRefinerV2(FunPackVideoRefiner): max_length=max_length, temperature=temperature, top_p=top_p, min_floor=32, label="Prompt enhancer", ) - enhanced, overran = self._v2_trim_runaway_prompt(self._v2_clean_enhanced_prompt(raw)) + enhanced, overran = self._v2_trim_runaway_prompt( + self._v2_clean_enhanced_prompt(raw), max_length=max_length) if overran: - status += (" The model did not stop on its own and was cut at a sentence — lower " - "the length limit, or use a model with a trained chat head.") + status += (" The model repeated itself rather than stopping, and was cut at a " + "sentence — a model with a trained chat head will not do this.") if not enhanced: return original, (status if "failed" in status or "unavailable" in status else "Prompt enhancer: model returned nothing; prompt unchanged.") @@ -9243,20 +9244,48 @@ class FunPackVideoRefinerV2(FunPackVideoRefiner): return str(probe) return "an unreported device" - #: A model with no trained decoder head may never emit a stop token, so `max_length` stops - #: being a ceiling and becomes the actual length. A prompt is a paragraph; anything past - #: this many characters is the model failing to stop, not detail worth keeping. - V2_ENHANCED_PROMPT_CHAR_CAP = 2000 + #: Rough chars per token, to turn the user's TOKEN limit into a character bound. There is + #: no fixed character cap: H3 has no padding, so every prompt token is a real sequence row + #: and a long prompt is a legitimate thing to want. The length the user asked for is the + #: length they get — this only stops output that overshoots the tokenizer's own ceiling. + V2_ENHANCED_PROMPT_CHARS_PER_TOKEN = 4 + #: How many times a phrase may repeat before it is a stuck model rather than a style. + V2_ENHANCED_PROMPT_REPEAT_LIMIT = 3 @classmethod - def _v2_trim_runaway_prompt(cls, text, cap=None): - """Cut a prompt that never ended back to its last COMPLETE sentence. + def _v2_trim_runaway_prompt(cls, text, max_length=None): + """Cut a prompt that never ended — by REPETITION first, length second. - Cutting mid-word would hand the encoder a fragment; cutting at a sentence keeps the - prompt readable and keeps whatever the model got right before it started rambling. + A model with no trained decoder head may never emit a stop token, so it fills whatever + budget it is given. But length alone cannot tell "long and detailed" from "stuck": on + H3 a long prompt is often exactly what is wanted. What distinguishes the two is that a + stuck model repeats itself, so that is what is detected — and the length bound is + derived from the user's own token limit rather than a number invented here. + + Cutting lands on a complete sentence; a fragment handed to the encoder is worse than a + shorter prompt. """ - cap = int(cap or cls.V2_ENHANCED_PROMPT_CHAR_CAP) - text = str(text or "") + text = str(text or "").strip() + if not text: + return text, False + + # 1. repetition: the honest signal that the model stopped writing and started looping + sentences = re.split(r"(?<=[.!?])\s+", text) + seen = {} + for index, sentence in enumerate(sentences): + key = " ".join(sentence.lower().split()) + if len(key) < 12: # too short to judge; "She smiles." recurs + continue + seen[key] = seen.get(key, 0) + 1 + if seen[key] >= cls.V2_ENHANCED_PROMPT_REPEAT_LIMIT: + kept = " ".join(sentences[:index]).strip() + if kept: + return kept, True + + # 2. length: only the ceiling the user's own token limit implies + if max_length is None: + return text, False + cap = max(64, int(max_length) * cls.V2_ENHANCED_PROMPT_CHARS_PER_TOKEN) if len(text) <= cap: return text, False head = text[:cap] diff --git a/tests/test_prompt_enhancer.py b/tests/test_prompt_enhancer.py index 97ff6ecb..60c7c467 100644 --- a/tests/test_prompt_enhancer.py +++ b/tests/test_prompt_enhancer.py @@ -233,36 +233,74 @@ def test_the_built_in_instructions_forbid_inventing_speech(): # being a ceiling and becomes the actual length. Nothing downstream would notice: it would # just encode 8000 tokens of rambling as the prompt. -def test_a_prompt_that_never_ended_is_cut(studio): - long = "A cat sits. " * 900 - out, cut = C.FunPackVideoRefinerV2._v2_trim_runaway_prompt(long) +def _trim(text, **kw): + return C.FunPackVideoRefinerV2._v2_trim_runaway_prompt(text, **kw) + + +def test_a_looping_model_is_cut_at_the_repeat(studio): + """Repetition, not length, is what distinguishes a stuck model from a detailed one.""" + out, cut = _trim("A ginger cat sits on the sunlit sill. " * 40) assert cut is True - assert len(out) <= C.FunPackVideoRefinerV2.V2_ENHANCED_PROMPT_CHAR_CAP + assert out.count("A ginger cat sits on the sunlit sill.") < 3 + + +def test_a_long_prompt_that_never_repeats_survives(studio): + """H3 has no padding — every prompt token is a real sequence row, so a long prompt is a + legitimate thing to want and must not be cut for being long.""" + text = " ".join(f"Shot {i}: a different thing happens in this room." for i in range(200)) + out, cut = _trim(text) + assert cut is False and out == text + + +def test_length_alone_never_cuts_without_a_token_limit(studio): + out, cut = _trim("Unique sentence number %d here. " % 0 + "".join( + f"Sentence {i} describes something new. " for i in range(500))) + assert cut is False + + +def test_the_length_bound_comes_from_the_users_token_limit(studio): + """The limit the user set IS the control; no character cap is invented next to it.""" + text = "".join(f"Sentence {i} describes something new and distinct. " for i in range(500)) + out, cut = _trim(text, max_length=64) + assert cut is True + assert len(out) <= 64 * C.FunPackVideoRefinerV2.V2_ENHANCED_PROMPT_CHARS_PER_TOKEN + + +def test_a_generous_token_limit_keeps_a_generous_prompt(studio): + text = "".join(f"Sentence {i} describes something new and distinct. " for i in range(200)) + _out, cut = _trim(text, max_length=8192) + assert cut is False def test_the_cut_lands_on_a_finished_sentence(studio): - long = "A cat sits on the sill. " * 900 - out, _cut = C.FunPackVideoRefinerV2._v2_trim_runaway_prompt(long) + out, _cut = _trim("A ginger cat sits on the sunlit sill. " * 40) assert out.endswith(".") def test_a_normal_prompt_is_not_cut(studio): text = "A ginger cat sits on a sunlit sill." - out, cut = C.FunPackVideoRefinerV2._v2_trim_runaway_prompt(text) + out, cut = _trim(text, max_length=400) assert out == text and cut is False +def test_a_short_recurring_line_is_not_treated_as_a_loop(studio): + """"She smiles." legitimately recurs in a long shot list.""" + text = " ".join(f"The {w} moves across the room. She smiles." for w in + ("cat", "dog", "bird", "fox", "owl")) + _out, cut = _trim(text) + assert cut is False + + def test_a_runaway_never_ends_mid_word(studio): - """A fragment handed to the encoder is worse than a shorter prompt.""" - out, _cut = C.FunPackVideoRefinerV2._v2_trim_runaway_prompt("supercalifragilistic " * 500) + out, _cut = _trim("supercalifragilistic " * 500, max_length=64) assert not out.endswith("supercalifragilis") assert out == out.strip() -def test_the_user_is_told_the_model_did_not_stop(studio): - clip = FakeClip(reply="A cat sits. " * 900) +def test_the_user_is_told_the_model_repeated_itself(studio): + clip = FakeClip(reply="A ginger cat sits on the sunlit sill. " * 40) _out, status = studio._v2_enhance_prompt(clip, "a cat", "sys") - assert "did not stop" in status + assert "repeated itself" in status # ── where it ran ────────────────────────────────────────────────────────────