mirror of
https://github.com/pewdiepie-archdaemon/odysseus.git
synced 2026-10-31 10:12:21 +01:00
Merge pull request #6546 from harshit-ojha0324/fix/degenerate-guard-numeric-data
fix(llm): stop the repeat guard from cutting off numeric data
This commit is contained in:
+17
-4
@@ -432,12 +432,15 @@ class _DegenerateStreamGuard:
|
||||
"Summer Summer ..."). This is not a useful response and can burn context,
|
||||
browser memory, and GPU time. Keep the guard conservative: only fire on long
|
||||
same-token runs or a very dominant repeated token in the recent window.
|
||||
Repetition made only of numbers is data (a zero or identity matrix, a
|
||||
padded buffer), so it only stops at a long all-numeric backstop.
|
||||
"""
|
||||
|
||||
def __init__(self, model: str):
|
||||
self.model = model or "model"
|
||||
self.last_token = ""
|
||||
self.same_run = 0
|
||||
self.numeric_run = 0
|
||||
self.recent_tokens: List[str] = []
|
||||
self.periodic_tokens: List[str] = []
|
||||
self.total_chars = 0
|
||||
@@ -458,6 +461,7 @@ class _DegenerateStreamGuard:
|
||||
else:
|
||||
self.last_token = token
|
||||
self.same_run = 1
|
||||
self.numeric_run = self.numeric_run + 1 if token.isdigit() else 0
|
||||
self.recent_tokens.append(token)
|
||||
self.periodic_tokens.append(token)
|
||||
if len(self.recent_tokens) > 96:
|
||||
@@ -466,14 +470,20 @@ class _DegenerateStreamGuard:
|
||||
self.periodic_tokens = self.periodic_tokens[-2048:]
|
||||
|
||||
reason = None
|
||||
if self.same_run >= 28 and self.total_chars >= 100:
|
||||
if self.same_run >= 28 and self.total_chars >= 100 and not self.last_token.isdigit():
|
||||
reason = f"repeated '{self.last_token}' {self.same_run} times"
|
||||
elif len(self.recent_tokens) >= 72:
|
||||
top = max(set(self.recent_tokens), key=self.recent_tokens.count)
|
||||
count = self.recent_tokens.count(top)
|
||||
if count >= 60 and count / max(len(self.recent_tokens), 1) >= 0.78:
|
||||
if count >= 60 and count / max(len(self.recent_tokens), 1) >= 0.78 and not top.isdigit():
|
||||
reason = f"repeated '{top}' {count}/{len(self.recent_tokens)} recent tokens"
|
||||
if not reason and len(self.recent_tokens) >= 48:
|
||||
if (
|
||||
not reason
|
||||
and len(self.recent_tokens) >= 48
|
||||
# A window that is mostly numbers is matrix/table data; its
|
||||
# periodicity is real. Mixed loops still hit the exact-block check.
|
||||
and sum(t.isdigit() for t in self.recent_tokens) * 2 < len(self.recent_tokens)
|
||||
):
|
||||
# Detect a periodic suffix instead of counting repeated n-grams.
|
||||
# Reused property names are normal in CSS/SVG/JSON; a collapsed
|
||||
# generation repeats nearly every token at a short fixed period.
|
||||
@@ -510,10 +520,13 @@ class _DegenerateStreamGuard:
|
||||
period = current_anchor - previous
|
||||
if period < 24 or len(history) < period * 2:
|
||||
continue
|
||||
if history[-2 * period:-period] == history[-period:]:
|
||||
if history[-2 * period:-period] == history[-period:] and not all(t.isdigit() for t in history[-period:]):
|
||||
reason = f"repeated an exact {period}-token block twice"
|
||||
break
|
||||
|
||||
if not reason and self.numeric_run >= 2048:
|
||||
reason = f"emitted {self.numeric_run} numbers in a row"
|
||||
|
||||
if not reason:
|
||||
return None
|
||||
|
||||
|
||||
@@ -1091,6 +1091,43 @@ def test_degenerate_stream_guard_allows_long_rows_with_changing_values():
|
||||
assert guard.check(rows) is None
|
||||
|
||||
|
||||
def _latex_matrix(cell):
|
||||
rows = (" & ".join(cell(i, j) for j in range(8)) for i in range(8))
|
||||
return "$$\\begin{pmatrix}\n" + " \\\\\n".join(rows) + "\n\\end{pmatrix}$$"
|
||||
|
||||
|
||||
@pytest.mark.parametrize("text", [
|
||||
_latex_matrix(lambda i, j: "0"),
|
||||
_latex_matrix(lambda i, j: "1" if i == j else "0"),
|
||||
"buf = [" + ", ".join("0" for _ in range(64)) + "]",
|
||||
])
|
||||
@pytest.mark.parametrize("chunk", [1, 6, 64])
|
||||
def test_degenerate_stream_guard_allows_numeric_data(text, chunk):
|
||||
"""#5560: zero/identity matrices and padded buffers repeat numbers by
|
||||
design; they are not token collapse."""
|
||||
guard = llm_core._DegenerateStreamGuard("math-model")
|
||||
|
||||
assert all(guard.check(text[i:i + chunk]) is None for i in range(0, len(text), chunk))
|
||||
|
||||
|
||||
def test_degenerate_stream_guard_still_stops_an_endless_number_run():
|
||||
guard = llm_core._DegenerateStreamGuard("looping-model")
|
||||
|
||||
chunk = guard.check("0 " * 3000)
|
||||
|
||||
assert chunk is not None
|
||||
assert "numbers in a row" in chunk
|
||||
|
||||
|
||||
def test_degenerate_stream_guard_still_stops_a_mixed_word_number_loop():
|
||||
guard = llm_core._DegenerateStreamGuard("looping-model")
|
||||
|
||||
chunk = guard.check("row 0 0 0 0 " * 300)
|
||||
|
||||
assert chunk is not None
|
||||
assert "repeated" in chunk
|
||||
|
||||
|
||||
def test_terminal_stream_retries_degenerate_generation_before_emitting(monkeypatch):
|
||||
calls = 0
|
||||
requests = []
|
||||
|
||||
Reference in New Issue
Block a user