+45
@@ -0,0 +1,45 @@
|
||||
/**
|
||||
* Detect and collapse degenerate generation loops. No Bare imports.
|
||||
*
|
||||
* Small Qwen runs with no predict cap will repeat a clause until the idle
|
||||
* timeout. Catch the repeating tail and stop the stream.
|
||||
*/
|
||||
|
||||
function repeatingUnit(text, minLen, copies) {
|
||||
const s = String(text || '');
|
||||
const min = minLen > 0 ? minLen : 24;
|
||||
const need = copies > 0 ? copies : 3;
|
||||
if (s.length < min * need) return '';
|
||||
const window = s.length > 2400 ? s.slice(-2400) : s;
|
||||
const maxLen = Math.min(180, Math.floor(window.length / need));
|
||||
for (let len = min; len <= maxLen; len++) {
|
||||
const unit = window.slice(-len);
|
||||
if (unit.replace(/\s+/g, '').length < 12) continue;
|
||||
let matched = true;
|
||||
for (let i = 2; i <= need; i++) {
|
||||
if (window.slice(-len * i, -len * (i - 1)) !== unit) {
|
||||
matched = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (matched) return unit;
|
||||
}
|
||||
return '';
|
||||
}
|
||||
|
||||
function isRepeating(text) {
|
||||
return !!repeatingUnit(text);
|
||||
}
|
||||
|
||||
function escapeRe(value) {
|
||||
return String(value).replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
||||
}
|
||||
|
||||
function collapseRepeats(text) {
|
||||
const s = String(text || '');
|
||||
const unit = repeatingUnit(s);
|
||||
if (!unit) return s.trim();
|
||||
return s.replace(new RegExp('(?:' + escapeRe(unit) + '){3,}$'), unit).trim();
|
||||
}
|
||||
|
||||
module.exports = { repeatingUnit, isRepeating, collapseRepeats };
|
||||
Reference in New Issue
Block a user