pub struct LoopControlAgent { /* private fields */ }Expand description
Agent that guards LLM tool-call cycles against runaway iteration.
Insert this agent between a chat agent’s message output and the
tool-execution node. It forwards traffic transparently while counting,
per flow, the final assistant messages that request tool calls. Once the
count would exceed max_iterations, the triggering message is not
forwarded — severing the cycle — and a synthesized assistant message
explaining the stop is emitted on limit_exceeded instead.
A message is counted only when all of the following hold: role is
“assistant”, tool_calls is present and non-empty, streaming is false,
and its id differs from the last counted id for the flow. Streaming turns
re-deliver the same tool_calls-bearing message under one id (partials plus
a possibly duplicated final), so counting every delivery would exhaust the
limit within a few turns. Messages without an id cannot be deduplicated
and are always counted. Everything else — non-message values, user / tool /
system messages, streaming partials, and assistant messages without tool
calls — passes through unchanged.
Setting max_iterations to zero or a negative value disables the limit.
Counters are kept per flow (ctx_key), capped at 1024 flows with
oldest-first eviction, and cleared when the agent stops.
§Configuration
max_iterations- Maximum tool-call iterations per flow;<= 0disables the limit (default: 25)
§Ports
- Input
message- Messages flowing through the tool-call cycle - Output
message- The forwarded input while within the limit - Output
limit_exceeded- Synthesized assistant message emitted when the limit is exceeded
Implementations§
Source§impl LoopControlAgent
impl LoopControlAgent
pub const DEF_NAME: &'static str = "modular_agent_core::tool::LoopControlAgent"
pub fn def_name() -> &'static str
pub fn agent_definition() -> AgentDefinition
pub fn register(ma: &ModularAgent)
Trait Implementations§
Source§impl AsAgent for LoopControlAgent
impl AsAgent for LoopControlAgent
Source§fn new(
ma: ModularAgent,
id: String,
spec: AgentSpec,
) -> Result<Self, AgentError>
fn new( ma: ModularAgent, id: String, spec: AgentSpec, ) -> Result<Self, AgentError>
Source§fn stop<'life0, 'async_trait>(
&'life0 mut self,
) -> Pin<Box<dyn Future<Output = Result<(), AgentError>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
fn stop<'life0, 'async_trait>(
&'life0 mut self,
) -> Pin<Box<dyn Future<Output = Result<(), AgentError>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
Source§fn process<'life0, 'async_trait>(
&'life0 mut self,
ctx: AgentContext,
_port: String,
value: AgentValue,
) -> Pin<Box<dyn Future<Output = Result<(), AgentError>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
fn process<'life0, 'async_trait>(
&'life0 mut self,
ctx: AgentContext,
_port: String,
value: AgentValue,
) -> Pin<Box<dyn Future<Output = Result<(), AgentError>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
Source§fn configs_changed(&mut self) -> Result<(), AgentError>
fn configs_changed(&mut self) -> Result<(), AgentError>
Auto Trait Implementations§
impl Freeze for LoopControlAgent
impl RefUnwindSafe for LoopControlAgent
impl Send for LoopControlAgent
impl Sync for LoopControlAgent
impl Unpin for LoopControlAgent
impl UnsafeUnpin for LoopControlAgent
impl UnwindSafe for LoopControlAgent
Blanket Implementations§
Source§impl<S, D, Swp, Dwp, T> AdaptInto<D, Swp, Dwp, T> for S
impl<S, D, Swp, Dwp, T> AdaptInto<D, Swp, Dwp, T> for S
Source§fn adapt_into_using<M>(self, method: M) -> Dwhere
M: TransformMatrix<Swp, Dwp, T>,
fn adapt_into_using<M>(self, method: M) -> Dwhere
M: TransformMatrix<Swp, Dwp, T>,
Source§fn adapt_into(self) -> D
fn adapt_into(self) -> D
Source§impl<T> Agent for Twhere
T: AsAgent,
impl<T> Agent for Twhere
T: AsAgent,
Source§fn new(ma: ModularAgent, id: String, spec: AgentSpec) -> Result<T, AgentError>
fn new(ma: ModularAgent, id: String, spec: AgentSpec) -> Result<T, AgentError>
Source§fn ma(&self) -> &ModularAgent
fn ma(&self) -> &ModularAgent
ModularAgent.Source§fn update_spec(&mut self, value: &Value) -> Result<(), AgentError>
fn update_spec(&mut self, value: &Value) -> Result<(), AgentError>
Source§fn status(&self) -> &AgentStatus
fn status(&self) -> &AgentStatus
Source§fn configs(&self) -> Result<&AgentConfigs, AgentError>
fn configs(&self) -> Result<&AgentConfigs, AgentError>
Source§fn set_config(
&mut self,
key: String,
value: AgentValue,
) -> Result<(), AgentError>
fn set_config( &mut self, key: String, value: AgentValue, ) -> Result<(), AgentError>
Source§fn set_configs(&mut self, configs: AgentConfigs) -> Result<(), AgentError>
fn set_configs(&mut self, configs: AgentConfigs) -> Result<(), AgentError>
Source§fn set_preset_id(&mut self, preset_id: String)
fn set_preset_id(&mut self, preset_id: String)
Source§fn start<'life0, 'async_trait>(
&'life0 mut self,
) -> Pin<Box<dyn Future<Output = Result<(), AgentError>> + Send + 'async_trait>>where
'life0: 'async_trait,
T: 'async_trait,
fn start<'life0, 'async_trait>(
&'life0 mut self,
) -> Pin<Box<dyn Future<Output = Result<(), AgentError>> + Send + 'async_trait>>where
'life0: 'async_trait,
T: 'async_trait,
Source§fn stop<'life0, 'async_trait>(
&'life0 mut self,
) -> Pin<Box<dyn Future<Output = Result<(), AgentError>> + Send + 'async_trait>>where
'life0: 'async_trait,
T: 'async_trait,
fn stop<'life0, 'async_trait>(
&'life0 mut self,
) -> Pin<Box<dyn Future<Output = Result<(), AgentError>> + Send + 'async_trait>>where
'life0: 'async_trait,
T: 'async_trait,
Source§fn process<'life0, 'async_trait>(
&'life0 mut self,
ctx: AgentContext,
port: String,
value: AgentValue,
) -> Pin<Box<dyn Future<Output = Result<(), AgentError>> + Send + 'async_trait>>where
'life0: 'async_trait,
T: 'async_trait,
fn process<'life0, 'async_trait>(
&'life0 mut self,
ctx: AgentContext,
port: String,
value: AgentValue,
) -> Pin<Box<dyn Future<Output = Result<(), AgentError>> + Send + 'async_trait>>where
'life0: 'async_trait,
T: 'async_trait,
Source§fn get_global_configs(&self) -> Option<AgentConfigs>
fn get_global_configs(&self) -> Option<AgentConfigs>
fn as_any(&self) -> &(dyn Any + 'static)
fn as_any_mut(&mut self) -> &mut (dyn Any + 'static)
Source§impl<T> AgentOutput for Twhere
T: Agent,
impl<T> AgentOutput for Twhere
T: Agent,
Source§fn output_raw(
&self,
ctx: AgentContext,
port: String,
value: AgentValue,
) -> Pin<Box<dyn Future<Output = Result<(), AgentError>> + Send + '_>>
fn output_raw( &self, ctx: AgentContext, port: String, value: AgentValue, ) -> Pin<Box<dyn Future<Output = Result<(), AgentError>> + Send + '_>>
Source§fn try_output_raw(
&self,
ctx: AgentContext,
port: String,
value: AgentValue,
) -> Result<(), AgentError>
fn try_output_raw( &self, ctx: AgentContext, port: String, value: AgentValue, ) -> Result<(), AgentError>
Source§fn emit_config_updated_raw(&self, key: String, value: AgentValue)
fn emit_config_updated_raw(&self, key: String, value: AgentValue)
Source§fn emit_agent_spec_updated_raw(&self)
fn emit_agent_spec_updated_raw(&self)
Source§fn emit_error_raw(&self, message: String)
fn emit_error_raw(&self, message: String)
Source§fn output<S: Into<String>>(
&self,
ctx: AgentContext,
port: S,
value: AgentValue,
) -> Pin<Box<dyn Future<Output = Result<(), AgentError>> + Send + '_>>
fn output<S: Into<String>>( &self, ctx: AgentContext, port: S, value: AgentValue, ) -> Pin<Box<dyn Future<Output = Result<(), AgentError>> + Send + '_>>
Source§fn try_output<S: Into<String>>(
&self,
ctx: AgentContext,
port: S,
value: AgentValue,
) -> Result<(), AgentError>
fn try_output<S: Into<String>>( &self, ctx: AgentContext, port: S, value: AgentValue, ) -> Result<(), AgentError>
Source§fn emit_config_updated<S: Into<String>>(&self, key: S, value: AgentValue)
fn emit_config_updated<S: Into<String>>(&self, key: S, value: AgentValue)
Source§fn emit_agent_spec_updated(&self)
fn emit_agent_spec_updated(&self)
Source§impl<Src, Scheme> ApproxFrom<Src, Scheme> for Srcwhere
Scheme: ApproxScheme,
impl<Src, Scheme> ApproxFrom<Src, Scheme> for Srcwhere
Scheme: ApproxScheme,
Source§fn approx_from(src: Src) -> Result<Src, <Src as ApproxFrom<Src, Scheme>>::Err>
fn approx_from(src: Src) -> Result<Src, <Src as ApproxFrom<Src, Scheme>>::Err>
Source§impl<Dst, Src, Scheme> ApproxInto<Dst, Scheme> for Srcwhere
Dst: ApproxFrom<Src, Scheme>,
Scheme: ApproxScheme,
impl<Dst, Src, Scheme> ApproxInto<Dst, Scheme> for Srcwhere
Dst: ApproxFrom<Src, Scheme>,
Scheme: ApproxScheme,
Source§type Err = <Dst as ApproxFrom<Src, Scheme>>::Err
type Err = <Dst as ApproxFrom<Src, Scheme>>::Err
Source§fn approx_into(self) -> Result<Dst, <Src as ApproxInto<Dst, Scheme>>::Err>
fn approx_into(self) -> Result<Dst, <Src as ApproxInto<Dst, Scheme>>::Err>
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
Source§impl<T, Dst> ConvAsUtil<Dst> for T
impl<T, Dst> ConvAsUtil<Dst> for T
Source§impl<T> ConvUtil for T
impl<T> ConvUtil for T
Source§fn approx_as<Dst>(self) -> Result<Dst, Self::Err>where
Self: Sized + ApproxInto<Dst>,
fn approx_as<Dst>(self) -> Result<Dst, Self::Err>where
Self: Sized + ApproxInto<Dst>,
Source§fn approx_as_by<Dst, Scheme>(self) -> Result<Dst, Self::Err>
fn approx_as_by<Dst, Scheme>(self) -> Result<Dst, Self::Err>
Source§impl<T> Instrument for T
impl<T> Instrument for T
Source§fn instrument(self, span: Span) -> Instrumented<Self>
fn instrument(self, span: Span) -> Instrumented<Self>
Source§fn in_current_span(self) -> Instrumented<Self>
fn in_current_span(self) -> Instrumented<Self>
Source§impl<T, U> IntoColor<U> for Twhere
U: FromColor<T>,
impl<T, U> IntoColor<U> for Twhere
U: FromColor<T>,
Source§fn into_color(self) -> U
fn into_color(self) -> U
Source§impl<T, U> IntoColorUnclamped<U> for Twhere
U: FromColorUnclamped<T>,
impl<T, U> IntoColorUnclamped<U> for Twhere
U: FromColorUnclamped<T>,
Source§fn into_color_unclamped(self) -> U
fn into_color_unclamped(self) -> U
Source§impl<T> IntoEither for T
impl<T> IntoEither for T
Source§fn into_either(self, into_left: bool) -> Either<Self, Self>
fn into_either(self, into_left: bool) -> Either<Self, Self>
self into a Left variant of Either<Self, Self>
if into_left is true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read moreSource§fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
self into a Left variant of Either<Self, Self>
if into_left(&self) returns true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read moreSource§impl<SS, SP> SupersetOf<SS> for SPwhere
SS: SubsetOf<SP>,
impl<SS, SP> SupersetOf<SS> for SPwhere
SS: SubsetOf<SP>,
Source§fn to_subset(&self) -> Option<SS>
fn to_subset(&self) -> Option<SS>
self from the equivalent element of its
superset. Read moreSource§fn is_in_subset(&self) -> bool
fn is_in_subset(&self) -> bool
self is actually part of its subset T (and can be converted to it).Source§fn to_subset_unchecked(&self) -> SS
fn to_subset_unchecked(&self) -> SS
self.to_subset but without any property checks. Always succeeds.Source§fn from_subset(element: &SS) -> SP
fn from_subset(element: &SS) -> SP
self to the equivalent element of its superset.Source§impl<T, U> TryIntoColor<U> for Twhere
U: TryFromColor<T>,
impl<T, U> TryIntoColor<U> for Twhere
U: TryFromColor<T>,
Source§fn try_into_color(self) -> Result<U, OutOfBounds<U>>
fn try_into_color(self) -> Result<U, OutOfBounds<U>>
OutOfBounds error is returned which contains
the unclamped color. Read more