Skip to main content

LoopDetector

Struct LoopDetector 

Source
pub struct LoopDetector { /* private fields */ }
Expand description

Detects repeated action patterns in agent loops.

Three independent signals, each tracking consecutive repetitions:

SignalTracksCatches
ExactIdentical signaturesTrivial loops (same tool, same args)
CategoryNormalized signaturesSemantic loops (same intent, diff syntax)
OutputIdentical tool output (by hash)Stagnation (different tools, same result)

Usage:

let mut detector = LoopDetector::new(6);

// Per step: check action signatures
let sig = "bash:rg -n 'TODO' src/";
let cat = normalize_signature(sig);
match detector.check_with_category(sig, &cat) {
    LoopStatus::Abort(n) => { /* stop */ }
    LoopStatus::Warning(n) => { /* inject system message */ }
    LoopStatus::Ok => { /* proceed */ }
}

// Per action execution: check tool output
match detector.record_output("No matches found") {
    LoopStatus::Warning(n) => { /* nudge model */ }
    _ => {}
}

Implementations§

Source§

impl LoopDetector

Source

pub fn new(abort_threshold: usize) -> Self

Create detector. Warns at ⌈abort_threshold/2⌉, aborts at abort_threshold.

Source

pub fn with_thresholds(warn_threshold: usize, abort_threshold: usize) -> Self

Create detector with explicit warn threshold.

Source

pub fn check(&mut self, signature: &str) -> LoopStatus

Check action signature only (backward-compatible).

Uses signature as both exact match and category. For semantic loop detection, use [check_with_category] instead.

Source

pub fn check_with_category( &mut self, signature: &str, category: &str, ) -> LoopStatus

Check action with separate exact signature and normalized category.

Returns the worst status across exact and category signals.

Source

pub fn record_output(&mut self, output: &str) -> LoopStatus

Record a tool output and check for output stagnation.

Call after each action execution. Returns LoopStatus::Warning or LoopStatus::Abort if the same output has been seen too many consecutive times — the model is retrying a command that keeps giving the same result.

Source

pub fn reset(&mut self)

Reset all detector state.

Source

pub fn repeat_count(&self) -> usize

Current repeat count (max across all signals).

Source

pub fn exact_count(&self) -> usize

Exact signature repeat count.

Source

pub fn category_count(&self) -> usize

Normalized category repeat count.

Source

pub fn output_count(&self) -> usize

Output stagnation repeat count.

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.