fix(studio): trim the enhancer by repetition, not by an invented length cap

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.
This commit is contained in:
digital-garbage
2026-08-25 23:03:22 +03:00
parent 599e6a7fa8
commit 5e9de5223c
2 changed files with 92 additions and 25 deletions
+42 -13
View File
@@ -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]
+50 -12
View File
@@ -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 ────────────────────────────────────────────────────────────