Skip to main content

LimitsConfig

Struct LimitsConfig 

Source
pub struct LimitsConfig {
Show 17 fields pub max_concurrent_inferences: Option<usize>, pub max_concurrent_tools: usize, pub default_max_iterations: Option<usize>, pub exact_token_counting: bool, pub script_shell_timeout_secs: u64, pub stall_timeout_secs: u64, pub dead_cycles_before_relief: u32, pub finished_retention_secs: u64, pub mcp_idle_disconnect_secs: u64, pub wedge_timeout_secs: u64, pub provider_failures_before_open: u32, pub provider_circuit_cooldown_secs: u64, pub interaction_timeout_secs: u64, pub inference_retry_attempts: u32, pub inference_retry_base_ms: u64, pub max_tool_call_write_bytes: Option<u64>, pub max_run_write_bytes: Option<u64>,
}
Expand description

Runtime resource limits with safe defaults baked in.

Both fields default to a bounded value so a fresh install can’t accidentally run unbounded inference concurrency or an unbounded agent loop. Set a field explicitly in [limits] to raise or lower it.

Fields§

§max_concurrent_inferences: Option<usize>

Global fallback cap on concurrent inference requests for any model without its own per-model pool entry. Defaults to Some(8); omit or set a large number to effectively unbound it.

One physical bound sits behind this for script providers: each of their in-flight calls occupies a blocking-pool thread, and the daemon’s runtime provisions 2048 of those. Pools above 2048 only run that wide for HTTP providers, whose calls are fully async.

§max_concurrent_tools: usize

Size of the shared tool-execution worker pool - the number of agents whose tool batches may run concurrently across the whole daemon (the tool-lane counterpart of max_concurrent_inferences). Defaults to 8. Clamped to at least 1.

§default_max_iterations: Option<usize>

Fallback max_iterations applied to a stage that does not set its own, so an agent can’t loop forever with no completion signal. Defaults to Some(50). A stage’s explicit max_iterations always wins.

§exact_token_counting: bool

Opt-in exact pre-inference token budgeting. When true, each agent inference is preceded by an exact token count of the assembled request (via the provider’s count_tokens, which uses a remote endpoint for Anthropic/Gemini and a local heuristic otherwise) and is rejected before sending if it would exceed the model’s context window. Off by default: normal budgeting uses cheap local estimates, and this adds a network round-trip per inference for providers with a remote count endpoint.

§script_shell_timeout_secs: u64

Wall-clock timeout (seconds) for a Rhai script tool’s shell() host call, mirroring the built-in shell tool’s own 60-second cap so a script can’t hang an agent on a runaway command. Defaults to 60.

§stall_timeout_secs: u64

How long (seconds) a run may sit ready to work but unable to dispatch before it is failed instead of left running.

This only ever fires for something the runtime cannot resolve on its own - today, a stage whose provider is not configured. Waiting for a busy model’s inference pool is ordinary backpressure and is never failed, no matter how long it takes. Defaults to 60; 0 disables the watchdog and restores the old behaviour of waiting indefinitely.

Read once at daemon start, so a change needs a daemon restart.

§dead_cycles_before_relief: u32

How many consecutive safety re-drives may find the tool lane full and no run moving before the daemon widens the lane to break the jam.

The daemon re-drives itself every 30 seconds, so the default of 10 is five minutes of a full lane going nowhere. Relief only ever adds capacity, never cancels anything, and is capped at one extra lane’s worth over the daemon’s life, so it cannot run away.

0 turns relief off. Detection and reporting stay on either way, so lev ps and the metrics still show the streak.

Read once at daemon start, so a change needs a daemon restart.

§finished_retention_secs: u64

How long (seconds) a run keeps its place in lev ps after the daemon unloads it from memory.

A terminal run used to leave the listing the moment it was unloaded, which made a run that died on its first inference look exactly like a run that had never been spawned. A scheduler polling the listing could only tell the two apart with a stopwatch, and issue #205 is what that cost: forty minutes of spawning work, timing out, and spawning it again.

Defaults to 300. 0 drops a run as soon as it finishes, which is the old behaviour. The record lives in memory, so a restart clears it whatever this is set to.

Read once at daemon start, so a change needs a daemon restart.

§mcp_idle_disconnect_secs: u64

How long (seconds) a per-agent MCP server may sit with zero live runs leasing it before the daemon disconnects it (ending a stdio server’s child process). Long enough that back-to-back runs of a blueprint reuse the warm connection; the next run that declares the server reconnects lazily. 0 keeps every server connected for the daemon’s life, which was the old behaviour. Global [[mcp_servers]] from config.toml are never disconnected regardless.

Read once at daemon start, so a change needs a daemon restart.

§wedge_timeout_secs: u64

How long (seconds) a run may sit in a state no part of the engine can reach before it is failed instead of left reported as running.

Not a general “this run looks slow” timeout, and never fires on one. An agent waiting on the model, on a tool, on its sub-agents, or on a person is holding the marker that says so, and is exempt however long it takes. This only catches an agent holding no marker at all, which the engine’s own invariants say cannot happen and which nothing will ever look at again. Such a run stays running in meta.json for the life of the daemon and keeps whatever capacity an external scheduler assigned it, which is issue #202.

Defaults to 0, which is off: this fails runs, and an upgrade that starts killing work nobody asked it to kill is worse than the leak. 300 is a reasonable value to set. Turning it on is also a way to find out whether it is happening to you, since it says so in the log and in the run’s error.

Read once at daemon start, so a change needs a daemon restart.

§provider_failures_before_open: u32

How many consecutive provider-fatal failures (out of credits, rejected key) take a provider out of service for every run.

Defaults to 3. One 402 can just be a request asking for more output tokens than the balance covers; three in a row is the account. While a provider is out, runs move to their next candidate, and runs with none left are failed by the stall watchdog rather than left “running”.

0 disables the breaker, leaving per-run failover on its own.

Read once at daemon start, so a change needs a daemon restart.

§provider_circuit_cooldown_secs: u64

How long a provider stays out of service before one request is let through to see whether it recovered.

Defaults to 300 (five minutes). That probe either succeeds, which puts the provider straight back into service, or fails and restarts the wait, so topping up an account brings the factory back with no restart.

Read once at daemon start, so a change needs a daemon restart.

§interaction_timeout_secs: u64

How long (seconds) a prompt may go unanswered before the daemon resolves it itself and lets the run carry on.

Covers every prompt that waits on a person: an agent’s ask_user_* / present_for_review call, a tool-approval prompt, a taint gate, and a blueprint interaction point. Before this existed, a run whose operator had walked away sat in WaitingInput holding its slot until the daemon restarted - hours, in the report that prompted it (issue #204).

Expiry resolves the prompt exactly as cancelling it would: a tool approval and a taint gate deny, an ask_user_* call is told nobody answered, and an interaction point proceeds with no user text. Nothing is approved on the strength of a timeout.

Defaults to 3600 (one hour); 0 waits indefinitely.

Read once at daemon start, so a change needs a daemon restart.

§inference_retry_attempts: u32

How many times an inference is attempted, the first try included, before the agent is failed with whatever the provider last said.

Only a transient failure is retried at all - a reset connection, a timeout, a 429, a 5xx. An authentication error or an over-long request fails on the first answer, since the second would be identical.

Defaults to 4, which is one try and three retries. 1 turns retrying off. The wait between retries is inference_retry_base_ms, doubling each time, so the default schedule is 1s, 2s, 4s.

Raising it lengthens how long a run rides out a provider overload (an Anthropic 529, or a 429), which is retried on its own much slower schedule of 15s, 30s, then 60s per further attempt - that case is why the key exists (issue #417). Whatever this is set to, the retries of one request may sleep at most five minutes in total.

Read once at daemon start, so a change needs a daemon restart.

§inference_retry_base_ms: u64

The wait before the first inference retry, in milliseconds, doubling for each retry after it. Defaults to 1000, so the schedule is 1s, 2s, 4s.

This is the blip schedule and is meant to stay short: a reset connection or a 500 is usually gone by the next attempt. A provider overload does not use it - see inference_retry_attempts - so raising this to wait out an outage is the wrong lever and only delays ordinary failures.

Read once at daemon start, so a change needs a daemon restart.

§max_tool_call_write_bytes: Option<u64>

Most bytes one tool call may write to disk. Unset is unlimited.

Unset in code, set by lev setup. How much an agent should write is a judgement about what you are doing with it, so nothing is imposed on a user who never opened this file - but a fresh install gets a concrete number written here, where it is visible and can be deleted outright.

The incident behind it (issue #252) was a single shell call appending in a loop until the 60-second timeout: about 14 GB, from one call that looked ordinary.

A shell redirect is measured after the call, since the bytes go from the shell to the file without passing through Leviath. So this stops the call after the one that overran, not the one that did. write_file is measured before, and is stopped outright.

Running out of disk is checked separately and is never configurable: see leviath_core::write_limits::MIN_FREE_BYTES.

§max_run_write_bytes: Option<u64>

Most bytes a whole run may write to disk. Unset is unlimited.

The companion to max_tool_call_write_bytes, and the one that catches what a per-call ceiling cannot: three calls of 12-14 GB each are individually plausible and collectively a full disk. Same defaulting - unset in code, written by lev setup.

Implementations§

Source§

impl LimitsConfig

Source

pub fn write_limits(&self) -> WriteLimits

The write ceilings in effect, for the engine.

Trait Implementations§

Source§

impl Clone for LimitsConfig

Source§

fn clone(&self) -> LimitsConfig

Returns a duplicate of the value. Read more
1.0.0 (const: unstable) · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for LimitsConfig

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Default for LimitsConfig

Source§

fn default() -> Self

Returns the “default value” for a type. Read more
Source§

impl<'de> Deserialize<'de> for LimitsConfig

Source§

fn deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>
where __D: Deserializer<'de>,

Deserialize this value from the given Serde deserializer. Read more
Source§

impl Serialize for LimitsConfig

Source§

fn serialize<__S>(&self, __serializer: __S) -> Result<__S::Ok, __S::Error>
where __S: Serializer,

Serialize this value into the given Serde serializer. Read more

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<ST, DT> CastableFrom<ST, Initialized, Initialized> for DT
where ST: ?Sized, DT: ?Sized,

Source§

impl<ST, DT> CastableFrom<ST, Uninit, Uninit> for DT
where ST: ?Sized, DT: ?Sized,

Source§

impl<T> CloneToUninit for T
where T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> ConditionalSend for T
where T: Send,

Source§

impl<T> DeserializeOwned for T
where T: for<'de> Deserialize<'de>,

Source§

impl<T> Downcast for T
where T: Any,

Source§

fn into_any(self: Box<T>) -> Box<dyn Any>

Converts Box<dyn Trait> (where Trait: Downcast) to Box<dyn Any>, which can then be downcast into Box<dyn ConcreteType> where ConcreteType implements Trait.
Source§

fn into_any_rc(self: Rc<T>) -> Rc<dyn Any>

Converts Rc<Trait> (where Trait: Downcast) to Rc<Any>, which can then be further downcast into Rc<ConcreteType> where ConcreteType implements Trait.
Source§

fn as_any(&self) -> &(dyn Any + 'static)

Converts &Trait (where Trait: Downcast) to &Any. This is needed since Rust cannot generate &Any’s vtable from &Trait’s.
Source§

fn as_any_mut(&mut self) -> &mut (dyn Any + 'static)

Converts &mut Trait (where Trait: Downcast) to &Any. This is needed since Rust cannot generate &mut Any’s vtable from &mut Trait’s.
Source§

impl<T> DowncastSend for T
where T: Any + Send,

Source§

fn into_any_send(self: Box<T>) -> Box<dyn Any + Send>

Converts Box<Trait> (where Trait: DowncastSend) to Box<dyn Any + Send>, which can then be downcast into Box<ConcreteType> where ConcreteType implements Trait.
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T> FromRef<T> for T
where T: Clone,

Source§

fn from_ref(input: &T) -> T

Converts to this type from a reference to the input type.
Source§

impl<T> FromTemplate for T
where T: Clone + Default + Unpin,

Source§

type Template = T

The Template for this type.
Source§

impl<T> FromWorld for T
where T: Default,

Source§

fn from_world(_world: &mut World) -> T

Creates Self using default().

Source§

impl<T> FutureExt for T

Source§

fn with_context(self, otel_cx: Context) -> WithContext<Self>

Attaches the provided Context to this type, returning a WithContext wrapper. Read more
Source§

fn with_current_context(self) -> WithContext<Self>

Attaches the current Context to this type, returning a WithContext wrapper. Read more
Source§

impl<T> Instrument for T

Source§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided Span, returning an Instrumented wrapper. Read more
Source§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an Instrumented wrapper. Read more
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> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts 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 more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where F: FnOnce(&Self) -> bool,

Converts 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 more
Source§

impl<T> IntoResult<T> for T

Source§

fn into_result(self) -> Result<T, RunSystemError>

Converts this type into the system output type.
Source§

impl<A> Is for A
where A: Any,

Source§

fn is<T>() -> bool
where T: Any,

Checks if the current type “is” another type, using a TypeId equality comparison. This is most useful in the context of generic logic. Read more
Source§

impl<T> NoneValue for T
where T: Default,

Source§

type NoneType = T

Source§

fn null_value() -> T

The none-equivalent value.
Source§

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

Source§

fn and<P, B, E>(self, other: P) -> And<T, P>
where T: Sized + Policy<B, E>, P: Policy<B, E>,

Create a new Policy that returns Action::Follow only if self and other return Action::Follow. Read more
Source§

fn or<P, B, E>(self, other: P) -> Or<T, P>
where T: Sized + Policy<B, E>, P: Policy<B, E>,

Create a new Policy that returns Action::Follow if either self or other returns Action::Follow. Read more
Source§

impl<T> Read<Exclusive, BecauseExclusive> for T
where T: ?Sized,

Source§

impl<T> Same for T

Source§

type Output = T

Should always be Self
Source§

impl<T> Serialize for T
where T: Serialize + ?Sized,

Source§

fn erased_serialize(&self, serializer: &mut dyn Serializer) -> Result<(), Error>

Source§

fn do_erased_serialize( &self, serializer: &mut dyn Serializer, ) -> Result<(), ErrorImpl>

Source§

impl<T> Template for T
where T: Clone + Default + Unpin,

Source§

type Output = T

The type of value produced by this Template.
Source§

fn build_template( &self, _context: &mut TemplateContext<'_, '_>, ) -> Result<<T as Template>::Output, BevyError>

Uses this template and the given entity context to produce a Template::Output.
Source§

fn clone_template(&self) -> T

Clones this template. See Clone.
Source§

impl<T> ToOwned for T
where T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
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.
Source§

impl<T> TypeData for T
where T: 'static + Send + Sync + Clone,

Source§

fn clone_type_data(&self) -> Box<dyn TypeData>

Creates a type-erased clone of this value.
Source§

impl<V, T> VZip<V> for T
where V: MultiLane<T>,

Source§

fn vzip(self) -> V

Source§

impl<T> WithSubscriber for T

Source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a WithDispatch wrapper. Read more
Source§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a WithDispatch wrapper. Read more