-
Notifications
You must be signed in to change notification settings - Fork 2.9k
fix: detect and abort repetitive reasoning loops (#11337) #11338
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
…tput Adds a ReasoningRepetitionDetector that monitors streaming reasoning/thinking content for repetitive patterns. When a line is repeated more than a threshold number of times (default 5), the stream is aborted early to save tokens and the model receives guidance to try a different approach. This addresses an issue where some models (particularly Gemini) get stuck in reasoning loops, repeating the same lines like "I'll use attempt_completion" or "I'll mention that I verified with tests" indefinitely. Changes: - New ReasoningRepetitionDetector class with streaming chunk support - Integration into Task.ts reasoning streaming handler - New reasoningRepetitionDetected response format for model guidance - 18 unit tests covering detection, normalization, reset, and edge cases Closes #11337
Reviewing your PR now. Feedback coming soon! |
Reviewed the
Mention @roomote in a comment to request specific changes to this pull request or fix all unresolved issues. |
| public isRepetitive(): boolean { | ||
| // Also process any remaining buffer content | ||
| if (this.buffer.length > 0) { | ||
| const line = this.normalizeLine(this.buffer) | ||
| if (line.length >= this.minLineLength) { | ||
| const count = (this.lineCounts.get(line) ?? 0) + 1 | ||
| this.lineCounts.set(line, count) | ||
| if (count >= this.repetitionThreshold) { | ||
| return true | ||
| } | ||
| } | ||
| } | ||
|
|
||
| for (const count of this.lineCounts.values()) { | ||
| if (count >= this.repetitionThreshold) { | ||
| return true | ||
| } | ||
| } | ||
|
|
||
| return false | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
isRepetitive() mutates lineCounts as a side effect: each call increments the count for whatever is sitting in the buffer. If this method is called more than once without an intervening addChunk() or reset(), the buffered line's count keeps climbing, and can cross the threshold even though the line only appeared once in the actual stream. Although the current Task.ts integration only uses addChunk(), this is a public query method whose semantics suggest it should be idempotent.
One fix: process the buffer into a local variable instead of writing back to lineCounts, or clear the buffer after processing it.
| public isRepetitive(): boolean { | |
| // Also process any remaining buffer content | |
| if (this.buffer.length > 0) { | |
| const line = this.normalizeLine(this.buffer) | |
| if (line.length >= this.minLineLength) { | |
| const count = (this.lineCounts.get(line) ?? 0) + 1 | |
| this.lineCounts.set(line, count) | |
| if (count >= this.repetitionThreshold) { | |
| return true | |
| } | |
| } | |
| } | |
| for (const count of this.lineCounts.values()) { | |
| if (count >= this.repetitionThreshold) { | |
| return true | |
| } | |
| } | |
| return false | |
| } | |
| public isRepetitive(): boolean { | |
| // Also process any remaining buffer content | |
| if (this.buffer.length > 0) { | |
| const line = this.normalizeLine(this.buffer) | |
| if (line.length >= this.minLineLength) { | |
| const count = (this.lineCounts.get(line) ?? 0) + 1 | |
| if (count >= this.repetitionThreshold) { | |
| return true | |
| } | |
| } | |
| } | |
| for (const count of this.lineCounts.values()) { | |
| if (count >= this.repetitionThreshold) { | |
| return true | |
| } | |
| } | |
| return false | |
| } |
Fix it with Roo Code or mention @roomote and request a fix.
Related GitHub Issue
Closes: #11337
Description
This PR attempts to address the infinite looping issue reported in #11337, where some models (particularly Google Gemini) get stuck in a reasoning/thinking loop, repeating the same lines over and over (e.g., "I'll mention that I verified with tests" or "I'll use attempt_completion" repeated indefinitely).
The existing
ToolRepetitionDetectoronly catches repeated tool calls, but does not detect when the model's reasoning/thinking output is looping. This PR fills that gap with a newReasoningRepetitionDetectorthat monitors the reasoning stream in real-time.How it works:
ReasoningRepetitionDetectorclass tracks lines as they stream in during the"reasoning"chunk handler inTask.ts.cancelCurrentRequest()to save tokens.consecutiveMistakeCountis incremented so if the model keeps looping, the existing mistake limit will eventually pause and ask the user for guidance.Key design decisions:
reasoningRepetitionAbortedflag) to avoid the normal retry logic.Feedback and guidance are welcome.
Test Procedure
ReasoningRepetitionDetectorcovering:isRepetitive()method,getMostRepeatedLine()diagnosticsreset()behavior, default threshold, alternating A-B-A-B patternsToolRepetitionDetector,Task.spec.ts, andgrace-retry-errorstests -- all pass.Run tests:
cd src && npx vitest run core/tools/__tests__/ReasoningRepetitionDetector.spec.tsPre-Submission Checklist
Documentation Updates
No documentation updates needed. This is an internal behavior improvement that is transparent to users -- the only visible change is that infinite reasoning loops will now be automatically detected and interrupted with a helpful error message.
Important
Introduces
ReasoningRepetitionDetectorto abort repetitive reasoning loops in models, integrated intoTaskclass with comprehensive testing.ReasoningRepetitionDetectorto detect repetitive reasoning inTask.ts, aborting streams when a line repeats 5+ times.cancelCurrentRequest()and continues task with guidance message.consecutiveMistakeCountto eventually pause and ask for user guidance.ReasoningRepetitionDetectortracks lines, normalizes them, and checks repetition threshold.Taskclass to monitor reasoning output in real-time.ReasoningRepetitionDetectorinReasoningRepetitionDetector.spec.ts.ToolRepetitionDetectorandTask.spec.ts.This description was created by
for c9b1d5e. You can customize this summary. It will automatically update as commits are pushed.