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}