Skip to main content

adk_runner/
builder.rs

1//! Typestate builder for [`RunnerConfig`] / [`Runner`].
2//!
3//! The builder enforces at compile time that the three required fields
4//! (`app_name`, `agent`, `session_service`) are set before `build()` is
5//! callable.
6//!
7//! # Example
8//!
9//! ```rust,ignore
10//! let runner = Runner::builder()
11//!     .app_name("my-app")
12//!     .agent(agent)
13//!     .session_service(session_service)
14//!     .memory_service(memory)
15//!     .build()?;
16//! ```
17
18use std::marker::PhantomData;
19use std::sync::Arc;
20
21#[cfg(feature = "artifacts")]
22use adk_artifact::ArtifactService;
23use adk_core::{Agent, CacheCapable, ContextCacheConfig, Memory, Result, RunConfig};
24#[cfg(feature = "plugins")]
25use adk_plugin::PluginManager;
26use adk_session::SessionService;
27use tokio_util::sync::CancellationToken;
28
29use crate::runner::{Runner, RunnerConfig};
30
31// ---------------------------------------------------------------------------
32// Typestate marker types
33// ---------------------------------------------------------------------------
34
35/// Marker: `app_name` has not been set.
36pub struct NoAppName;
37/// Marker: `app_name` has been set.
38pub struct HasAppName;
39/// Marker: `agent` has not been set.
40pub struct NoAgent;
41/// Marker: `agent` has been set.
42pub struct HasAgent;
43/// Marker: `session_service` has not been set.
44pub struct NoSessionService;
45/// Marker: `session_service` has been set.
46pub struct HasSessionService;
47
48// ---------------------------------------------------------------------------
49// Builder
50// ---------------------------------------------------------------------------
51
52/// A typestate builder for constructing a [`Runner`].
53///
54/// The three type parameters track whether the required fields have been
55/// provided. `build()` is only available when all three are `Has*`.
56pub struct RunnerConfigBuilder<A, G, S> {
57    app_name: Option<String>,
58    agent: Option<Arc<dyn Agent>>,
59    session_service: Option<Arc<dyn SessionService>>,
60    #[cfg(feature = "artifacts")]
61    artifact_service: Option<Arc<dyn ArtifactService>>,
62    memory_service: Option<Arc<dyn Memory>>,
63    #[cfg(feature = "plugins")]
64    plugin_manager: Option<Arc<PluginManager>>,
65    run_config: Option<RunConfig>,
66    compaction_config: Option<adk_core::EventsCompactionConfig>,
67    context_cache_config: Option<ContextCacheConfig>,
68    cache_capable: Option<Arc<dyn CacheCapable>>,
69    request_context: Option<adk_core::RequestContext>,
70    cancellation_token: Option<CancellationToken>,
71    intra_compaction_config: Option<adk_core::IntraCompactionConfig>,
72    intra_compaction_summarizer: Option<Arc<dyn adk_core::BaseEventsSummarizer>>,
73    #[cfg(feature = "context-compaction")]
74    context_compaction: Option<crate::compaction::CompactionConfig>,
75    _marker: PhantomData<(A, G, S)>,
76}
77
78impl RunnerConfigBuilder<NoAppName, NoAgent, NoSessionService> {
79    /// Create a new builder with all fields unset and defaults applied.
80    pub fn new() -> Self {
81        Self {
82            app_name: None,
83            agent: None,
84            session_service: None,
85            #[cfg(feature = "artifacts")]
86            artifact_service: None,
87            memory_service: None,
88            #[cfg(feature = "plugins")]
89            plugin_manager: None,
90            run_config: None,
91            compaction_config: None,
92            context_cache_config: None,
93            cache_capable: None,
94            request_context: None,
95            cancellation_token: None,
96            intra_compaction_config: None,
97            intra_compaction_summarizer: None,
98            #[cfg(feature = "context-compaction")]
99            context_compaction: None,
100            _marker: PhantomData,
101        }
102    }
103}
104
105impl Default for RunnerConfigBuilder<NoAppName, NoAgent, NoSessionService> {
106    fn default() -> Self {
107        Self::new()
108    }
109}
110
111// ---------------------------------------------------------------------------
112// Required-field setters (transition type state)
113// ---------------------------------------------------------------------------
114
115impl<A, G, S> RunnerConfigBuilder<A, G, S> {
116    /// Set the application name (required).
117    pub fn app_name(self, name: impl Into<String>) -> RunnerConfigBuilder<HasAppName, G, S> {
118        RunnerConfigBuilder {
119            app_name: Some(name.into()),
120            agent: self.agent,
121            session_service: self.session_service,
122            #[cfg(feature = "artifacts")]
123            artifact_service: self.artifact_service,
124            memory_service: self.memory_service,
125            #[cfg(feature = "plugins")]
126            plugin_manager: self.plugin_manager,
127            run_config: self.run_config,
128            compaction_config: self.compaction_config,
129            context_cache_config: self.context_cache_config,
130            cache_capable: self.cache_capable,
131            request_context: self.request_context,
132            cancellation_token: self.cancellation_token,
133            intra_compaction_config: self.intra_compaction_config,
134            intra_compaction_summarizer: self.intra_compaction_summarizer,
135            #[cfg(feature = "context-compaction")]
136            context_compaction: self.context_compaction,
137            _marker: PhantomData,
138        }
139    }
140
141    /// Set the root agent (required).
142    pub fn agent(self, agent: Arc<dyn Agent>) -> RunnerConfigBuilder<A, HasAgent, S> {
143        RunnerConfigBuilder {
144            app_name: self.app_name,
145            agent: Some(agent),
146            session_service: self.session_service,
147            #[cfg(feature = "artifacts")]
148            artifact_service: self.artifact_service,
149            memory_service: self.memory_service,
150            #[cfg(feature = "plugins")]
151            plugin_manager: self.plugin_manager,
152            run_config: self.run_config,
153            compaction_config: self.compaction_config,
154            context_cache_config: self.context_cache_config,
155            cache_capable: self.cache_capable,
156            request_context: self.request_context,
157            cancellation_token: self.cancellation_token,
158            intra_compaction_config: self.intra_compaction_config,
159            intra_compaction_summarizer: self.intra_compaction_summarizer,
160            #[cfg(feature = "context-compaction")]
161            context_compaction: self.context_compaction,
162            _marker: PhantomData,
163        }
164    }
165
166    /// Set the session service (required).
167    pub fn session_service(
168        self,
169        service: Arc<dyn SessionService>,
170    ) -> RunnerConfigBuilder<A, G, HasSessionService> {
171        RunnerConfigBuilder {
172            app_name: self.app_name,
173            agent: self.agent,
174            session_service: Some(service),
175            #[cfg(feature = "artifacts")]
176            artifact_service: self.artifact_service,
177            memory_service: self.memory_service,
178            #[cfg(feature = "plugins")]
179            plugin_manager: self.plugin_manager,
180            run_config: self.run_config,
181            compaction_config: self.compaction_config,
182            context_cache_config: self.context_cache_config,
183            cache_capable: self.cache_capable,
184            request_context: self.request_context,
185            cancellation_token: self.cancellation_token,
186            intra_compaction_config: self.intra_compaction_config,
187            intra_compaction_summarizer: self.intra_compaction_summarizer,
188            #[cfg(feature = "context-compaction")]
189            context_compaction: self.context_compaction,
190            _marker: PhantomData,
191        }
192    }
193}
194
195// ---------------------------------------------------------------------------
196// Optional-field setters (no type-state change)
197// ---------------------------------------------------------------------------
198
199impl<A, G, S> RunnerConfigBuilder<A, G, S> {
200    /// Set the artifact service (optional).
201    #[cfg(feature = "artifacts")]
202    pub fn artifact_service(mut self, service: Arc<dyn ArtifactService>) -> Self {
203        self.artifact_service = Some(service);
204        self
205    }
206
207    /// Set the memory service (optional).
208    pub fn memory_service(mut self, service: Arc<dyn Memory>) -> Self {
209        self.memory_service = Some(service);
210        self
211    }
212
213    /// Set the plugin manager (optional).
214    #[cfg(feature = "plugins")]
215    pub fn plugin_manager(mut self, manager: Arc<PluginManager>) -> Self {
216        self.plugin_manager = Some(manager);
217        self
218    }
219
220    /// Set the run configuration (optional).
221    pub fn run_config(mut self, config: RunConfig) -> Self {
222        self.run_config = Some(config);
223        self
224    }
225
226    /// Set the events compaction configuration (optional).
227    pub fn compaction_config(mut self, config: adk_core::EventsCompactionConfig) -> Self {
228        self.compaction_config = Some(config);
229        self
230    }
231
232    /// Set the context cache configuration (optional).
233    pub fn context_cache_config(mut self, config: ContextCacheConfig) -> Self {
234        self.context_cache_config = Some(config);
235        self
236    }
237
238    /// Set the cache-capable model reference (optional).
239    pub fn cache_capable(mut self, model: Arc<dyn CacheCapable>) -> Self {
240        self.cache_capable = Some(model);
241        self
242    }
243
244    /// Set the request context from auth middleware (optional).
245    pub fn request_context(mut self, ctx: adk_core::RequestContext) -> Self {
246        self.request_context = Some(ctx);
247        self
248    }
249
250    /// Set a cooperative cancellation token (optional).
251    pub fn cancellation_token(mut self, token: CancellationToken) -> Self {
252        self.cancellation_token = Some(token);
253        self
254    }
255
256    /// Set the intra-invocation compaction configuration (optional).
257    pub fn intra_compaction_config(mut self, config: adk_core::IntraCompactionConfig) -> Self {
258        self.intra_compaction_config = Some(config);
259        self
260    }
261
262    /// Set the summarizer for intra-invocation compaction (optional).
263    pub fn intra_compaction_summarizer(
264        mut self,
265        summarizer: Arc<dyn adk_core::BaseEventsSummarizer>,
266    ) -> Self {
267        self.intra_compaction_summarizer = Some(summarizer);
268        self
269    }
270
271    /// Set the context compaction configuration for token-budget overflow handling (optional).
272    ///
273    /// When configured, the runner applies the given [`CompactionStrategy`](crate::compaction::CompactionStrategy)
274    /// to shrink the event history when the context exceeds the token budget.
275    #[cfg(feature = "context-compaction")]
276    pub fn context_compaction(mut self, config: crate::compaction::CompactionConfig) -> Self {
277        self.context_compaction = Some(config);
278        self
279    }
280}
281
282// ---------------------------------------------------------------------------
283// build() — only available when all required fields are set
284// ---------------------------------------------------------------------------
285
286impl RunnerConfigBuilder<HasAppName, HasAgent, HasSessionService> {
287    /// Consume the builder and produce a [`RunnerConfig`] without creating a [`Runner`].
288    ///
289    /// Use this when you need the raw config (e.g. to wrap in `Arc` for A2A handlers).
290    pub fn build_config(self) -> RunnerConfig {
291        RunnerConfig {
292            app_name: self.app_name.expect("typestate guarantees app_name is set"),
293            agent: self.agent.expect("typestate guarantees agent is set"),
294            session_service: self
295                .session_service
296                .expect("typestate guarantees session_service is set"),
297            #[cfg(feature = "artifacts")]
298            artifact_service: self.artifact_service,
299            memory_service: self.memory_service,
300            #[cfg(feature = "plugins")]
301            plugin_manager: self.plugin_manager,
302            run_config: self.run_config,
303            compaction_config: self.compaction_config,
304            context_cache_config: self.context_cache_config,
305            cache_capable: self.cache_capable,
306            request_context: self.request_context,
307            cancellation_token: self.cancellation_token,
308            intra_compaction_config: self.intra_compaction_config,
309            intra_compaction_summarizer: self.intra_compaction_summarizer,
310            #[cfg(feature = "context-compaction")]
311            context_compaction: self.context_compaction,
312        }
313    }
314
315    /// Consume the builder and create a [`Runner`].
316    ///
317    /// Delegates to [`Runner::new()`] internally.
318    ///
319    /// # Errors
320    ///
321    /// Returns an error if `Runner::new()` fails (e.g. invalid `app_name`).
322    pub fn build(self) -> Result<Runner> {
323        let config = RunnerConfig {
324            // SAFETY: typestate guarantees these are `Some`.
325            app_name: self.app_name.expect("typestate guarantees app_name is set"),
326            agent: self.agent.expect("typestate guarantees agent is set"),
327            session_service: self
328                .session_service
329                .expect("typestate guarantees session_service is set"),
330            #[cfg(feature = "artifacts")]
331            artifact_service: self.artifact_service,
332            memory_service: self.memory_service,
333            #[cfg(feature = "plugins")]
334            plugin_manager: self.plugin_manager,
335            run_config: self.run_config,
336            compaction_config: self.compaction_config,
337            context_cache_config: self.context_cache_config,
338            cache_capable: self.cache_capable,
339            request_context: self.request_context,
340            cancellation_token: self.cancellation_token,
341            intra_compaction_config: self.intra_compaction_config,
342            intra_compaction_summarizer: self.intra_compaction_summarizer,
343            #[cfg(feature = "context-compaction")]
344            context_compaction: self.context_compaction,
345        };
346        Runner::new(config)
347    }
348}