
    sjM                    >    d Z ddlmZ ddlZdZdZdZdZddZddZ	dS )u  Cheap content-sanity checks for the truncated-response continuation path.

Issue #86581: a model in a degenerate repetition loop can spend its ENTIRE
output budget echoing one fragment.  The ``finish_reason=length``
continuation path in ``conversation_loop.py`` would then retry with a
"continue, don't repeat" nudge — stitching a pathological fragment into the
final response with no content-sanity check.  In the incident behind #86581
a single turn produced a 60,698-char response delivered as 31 Discord
messages.

These helpers detect repetition-dominated fragments BEFORE the continuation
nudge is appended so the turn can abort with a clear user-facing error
(mirroring the existing ``_thinking_exhausted`` guard) instead of flooding.

The detection is deliberately conservative: only LONG verbatim repeats
(60+ chars) whose occurrences cover a majority of the fragment trip the
guard, so ordinary truncated responses (a sentence cut mid-word, a heading
repeated, code with similar-looking lines) are never blocked.
    )annotationsNi  <      g      ?textstrreturnboolc                   t          | t                    sdS t          |           }|t          k     rdS t	          | |          rdS t
          }t          t          t          j	        |t          z  |z                      }i }t          ||z
  dz             D ]6}| |||z            }|                    |d          dz   }||k    r dS |||<   7dS )uS  True when ``text`` is dominated by verbatim repeated fragments.

    A truncated response is "repetition-dominated" when a single 60+ char
    substring appears often enough that its occurrences cover at least half
    of the fragment.  That shape is the signature of a model repetition
    loop (issue #86581), and continuing such a fragment is pointless — the
    continuation nudge would just stitch more repeated text into the final
    response.

    Returns False for non-string / empty / short inputs (fail-open: never
    blocks a continuation the guard cannot confidently judge).
    FT   r   )
isinstancer   lenMIN_FRAGMENT_LENGTH_line_repetition_dominated_REPEAT_WINDOWmax_MIN_REPEAT_COUNTmathceil_DOMINANCE_RATIOrangeget)r   nwindowneededcountsikeycs           :/home/agent/.hermes/hermes-agent/agent/repetition_guard.pyis_repetition_dominatedr    +   s     dC   uD		Au
 "$** t F "DIa2B.BV.K$L$LMMFF1v:>""  1q6z>"JJsA";;44s5    r   intc                *   i }|                                  D ]5}|                                }|s|                    |d          dz   ||<   6|                                D ]1\  }}|t          k    r!|t          |          z  |t          z  k    r dS 2dS )zHTrue when a single normalized line covers half the fragment via repeats.r   r   TF)
splitlinesstripr   itemsr   r   r   )r   r   r   linenormr   s         r   r   r   T   s    F!! / /zz|| 	zz$**Q.t<<>>  a!!!a#d))mq;K7K&K&K445r!   )r   r   r   r	   )r   r   r   r"   r   r	   )
__doc__
__future__r   r   r   r   r   r   r    r    r!   r   <module>r,      s~    ( # " " " " " 
       & & & &R     r!   