Skip to main content

ailoop_core/
config.rs

1//! Per-run configuration: see [`RunConfig`].
2
3use std::sync::Arc;
4use std::time::Duration;
5
6use tokio_util::sync::CancellationToken;
7
8use crate::ids::RunId;
9use crate::message::SystemPrompt;
10use crate::middleware::ChatMiddleware;
11
12/// Per-run configuration consumed by the engine entry point.
13///
14/// The defaults shipped via [`RunConfig::default`] are tuned for
15/// short interactive turns (10 iterations, 4096 max output tokens, no
16/// timeout). Use struct-update syntax to override what you need:
17/// `RunConfig { max_iterations: 20, ..Default::default() }`. The
18/// struct is `#[non_exhaustive]`, so external callers must always go
19/// through `Default` (or [`RunConfig::new`]) to construct it.
20#[non_exhaustive]
21pub struct RunConfig {
22    /// System prompt prepended to the conversation. `None` lets the
23    /// provider use its own default behaviour. Use
24    /// [`SystemPrompt::Blocks`] to opt in to per-block cache breakpoints.
25    pub system_prompt: Option<SystemPrompt>,
26    /// Maximum number of provider turns before the engine aborts the
27    /// run with [`crate::FinishReason::Aborted`]. One iteration covers a
28    /// `chat_stream` call plus the tool calls it triggers. The cap
29    /// prevents runaway tool-use loops; pair with an [`crate::ChatMiddleware`]
30    /// such as `AntiLoop` for content-aware loop detection.
31    pub max_iterations: usize,
32    /// Default `max_tokens` for every per-turn [`crate::ChatRequest`]
33    /// the engine builds. User-supplied middlewares can override this
34    /// per request via [`crate::ChatMiddleware::on_chat_request`].
35    pub max_tokens: u32,
36    /// Middlewares the engine invokes in registration order. The
37    /// façade prepends an internal middleware that injects per-request
38    /// defaults; entries supplied here run after it and can override
39    /// any field.
40    pub middlewares: Vec<Arc<dyn ChatMiddleware>>,
41    /// Caller-supplied id for the run. When `None`, the engine mints a
42    /// fresh UUID v4. Set this when an outer system needs to correlate
43    /// the run with its own trace id.
44    pub run_id: Option<RunId>,
45    /// Wall-clock deadline for the entire run, including tool calls and
46    /// any retry backoff inside `RetryingModel`. `None` disables the
47    /// timeout. The engine checks this at await boundaries (HTTP setup,
48    /// SSE chunks, tool execution, approval middleware) — synchronous
49    /// work is not preempted. Sleeps inside `RetryingModel`'s backoff
50    /// race against this deadline because they run under the engine's
51    /// `select!`, so retry attempts are interruptible without the
52    /// decorator knowing about cancellation.
53    pub timeout: Option<Duration>,
54    /// External cancellation handle. Calling `cancel()` from another
55    /// task aborts the in-flight run at the next await boundary, with
56    /// the same persistence discipline as the timeout (partial
57    /// `tools_result` preserved, `on_run_finished` fired). Pass
58    /// `parent.child_token()` if you want to cancel this run without
59    /// affecting siblings sharing the parent.
60    pub cancellation: Option<CancellationToken>,
61}
62
63impl Default for RunConfig {
64    fn default() -> Self {
65        Self {
66            system_prompt: None,
67            max_iterations: 10,
68            max_tokens: 4096,
69            middlewares: vec![],
70            run_id: None,
71            timeout: None,
72            cancellation: None,
73        }
74    }
75}
76
77impl RunConfig {
78    /// Build a config with the given iteration cap and otherwise
79    /// default values. Equivalent to
80    /// `RunConfig { max_iterations, ..Default::default() }`; provided
81    /// because capping iterations is the most common single-field
82    /// override.
83    pub fn new(max_iterations: usize) -> Self {
84        Self {
85            max_iterations,
86            ..Default::default()
87        }
88    }
89}