1use std::path::PathBuf;
2use std::sync::Arc;
3use std::time::Duration;
4
5use serde_json::Value;
6
7use crate::approval::{ApprovalBroker, ApprovalProvider};
8use crate::budget::{HostCostMeter, RunBudgetLimits};
9use crate::checkpoint::{CheckpointConfig, CheckpointExtension, ReconciliationProvider};
10use crate::context_providers::ContextProvider;
11use crate::event_store::RunEventStore;
12use crate::execution_mode::ExecutionMode;
13use crate::memory::{MemoryProvider, MicrocompactionPolicy};
14use crate::model::{ModelProvider, ModelRef};
15use crate::model_settings::ModelSettings;
16use crate::runtime::backends::RuntimeExecutionBackend;
17use crate::runtime::{
18 AfterCycleHook, BeforeCycleMessageProvider, CancellationToken, InterruptionMessageProvider,
19 RunEventHandler, RuntimeHook, SubTaskManager,
20};
21use crate::sessions::Session;
22use crate::tools::{ToolPolicy, ToolRegistry};
23use crate::tracing::TraceSink;
24use crate::types::{Message, Metadata, NoToolPolicy};
25use crate::workspace::WorkspaceBackend;
26
27pub type ToolRegistryFactory = Arc<dyn Fn() -> ToolRegistry + Send + Sync + 'static>;
28
29pub(crate) const INITIAL_BUDGET_USAGE_METADATA_KEY: &str = "_vv_agent_initial_budget_usage";
30
31pub(crate) const MAX_CYCLES_RANGE_ERROR: &str = "max_cycles must be between 1 and 4294967295";
32
33pub(crate) fn validate_max_cycles(max_cycles: u32) -> Result<u32, String> {
34 if max_cycles == 0 {
35 return Err(MAX_CYCLES_RANGE_ERROR.to_string());
36 }
37 Ok(max_cycles)
38}
39
40#[derive(Clone, Default)]
41pub struct RunConfig {
42 pub model: Option<ModelRef>,
43 pub model_provider: Option<Arc<dyn ModelProvider>>,
44 pub model_settings: Option<ModelSettings>,
45 pub workspace: Option<PathBuf>,
46 pub workspace_backend: Option<Arc<dyn WorkspaceBackend>>,
47 pub session: Option<Arc<dyn Session>>,
48 pub session_memory_enabled: bool,
49 pub initial_messages: Option<Vec<Message>>,
50 pub max_cycles: Option<u32>,
51 pub max_handoffs: Option<u32>,
52 pub microcompaction_policy: Option<MicrocompactionPolicy>,
53 pub no_tool_policy: Option<NoToolPolicy>,
54 pub tool_policy: ToolPolicy,
55 pub execution_backend: Option<RuntimeExecutionBackend>,
56 pub cancellation_token: Option<CancellationToken>,
57 pub hooks: Vec<Arc<dyn RuntimeHook>>,
58 pub after_cycle_hooks: Vec<Arc<dyn AfterCycleHook>>,
59 pub trace_sink: Option<Arc<dyn TraceSink>>,
60 pub trace_id: Option<String>,
61 pub workflow_name: Option<String>,
62 pub event_store: Option<Arc<dyn RunEventStore>>,
63 pub event_store_fail_closed: bool,
64 pub approval_provider: Option<Arc<dyn ApprovalProvider>>,
65 pub approval_timeout: Option<Duration>,
66 pub approval_broker: Option<ApprovalBroker>,
67 pub context_providers: Vec<Arc<dyn ContextProvider>>,
68 pub max_context_chars: Option<usize>,
69 pub memory_providers: Vec<Arc<dyn MemoryProvider>>,
70 pub app_state: Option<Arc<dyn std::any::Any + Send + Sync>>,
71 pub initial_shared_state: Metadata,
72 pub tool_registry_factory: Option<ToolRegistryFactory>,
73 pub log_preview_chars: Option<usize>,
74 pub debug_dump_dir: Option<PathBuf>,
75 pub before_cycle_messages: Option<BeforeCycleMessageProvider>,
76 pub interruption_messages: Option<InterruptionMessageProvider>,
77 pub sub_task_manager: Option<SubTaskManager>,
78 pub stream: Option<RunEventHandler>,
79 pub budget_limits: Option<RunBudgetLimits>,
80 pub host_cost_meter: Option<Arc<dyn HostCostMeter>>,
81 pub checkpoint_config: Option<CheckpointConfig>,
82 pub checkpoint_extensions: Vec<Arc<dyn CheckpointExtension>>,
83 pub reconciliation_provider: Option<Arc<dyn ReconciliationProvider>>,
84 pub metadata: Metadata,
85}
86
87impl RunConfig {
88 pub fn builder() -> RunConfigBuilder {
89 RunConfigBuilder::default()
90 }
91
92 pub(crate) fn for_background_child(&self, shared_state: Metadata) -> Self {
93 let mut child = self.clone();
94 child.model = None;
95 child.model_settings = None;
96 child.session = None;
97 child.initial_messages = None;
98 child.cancellation_token = self
99 .cancellation_token
100 .as_ref()
101 .map(CancellationToken::child);
102 child.initial_shared_state = shared_state;
103 child.before_cycle_messages = None;
104 child.interruption_messages = None;
105 child.sub_task_manager = None;
106 child.stream = None;
107 child.host_cost_meter = None;
108 child.checkpoint_config = None;
109 child.checkpoint_extensions.clear();
110 child.reconciliation_provider = None;
111 child.metadata.remove(INITIAL_BUDGET_USAGE_METADATA_KEY);
112 child
113 }
114}
115
116#[derive(Default)]
117pub struct RunConfigBuilder {
118 config: RunConfig,
119}
120
121impl RunConfigBuilder {
122 pub fn model(mut self, model: ModelRef) -> Self {
123 self.config.model = Some(model);
124 self
125 }
126
127 pub fn model_provider(mut self, provider: impl ModelProvider + 'static) -> Self {
128 self.config.model_provider = Some(Arc::new(provider));
129 self
130 }
131
132 pub fn model_provider_arc(mut self, provider: Arc<dyn ModelProvider>) -> Self {
133 self.config.model_provider = Some(provider);
134 self
135 }
136
137 pub fn model_settings(mut self, settings: ModelSettings) -> Self {
138 self.config.model_settings = Some(settings);
139 self
140 }
141
142 pub fn workspace(mut self, workspace: impl Into<PathBuf>) -> Self {
143 self.config.workspace = Some(workspace.into());
144 self
145 }
146
147 pub fn workspace_backend(mut self, backend: Arc<dyn WorkspaceBackend>) -> Self {
148 self.config.workspace_backend = Some(backend);
149 self
150 }
151
152 pub fn session(mut self, session: impl Session + 'static) -> Self {
153 self.config.session = Some(Arc::new(session));
154 self
155 }
156
157 pub fn session_arc(mut self, session: Arc<dyn Session>) -> Self {
158 self.config.session = Some(session);
159 self
160 }
161
162 pub fn session_memory_enabled(mut self, enabled: bool) -> Self {
163 self.config.session_memory_enabled = enabled;
164 self
165 }
166
167 pub fn initial_messages(mut self, messages: Vec<Message>) -> Self {
168 self.config.initial_messages = Some(messages);
169 self
170 }
171
172 pub fn max_cycles(mut self, max_cycles: u32) -> Self {
173 self.config.max_cycles = Some(max_cycles);
174 self
175 }
176
177 pub fn max_handoffs(mut self, max_handoffs: u32) -> Self {
178 self.config.max_handoffs = Some(max_handoffs);
179 self
180 }
181
182 pub fn microcompaction_policy(mut self, policy: MicrocompactionPolicy) -> Self {
183 self.config.microcompaction_policy = Some(policy);
184 self
185 }
186
187 pub fn no_tool_policy(mut self, policy: NoToolPolicy) -> Self {
188 self.config.no_tool_policy = Some(policy);
189 self
190 }
191
192 pub fn tool_policy(mut self, tool_policy: ToolPolicy) -> Self {
193 self.config.tool_policy = tool_policy;
194 self
195 }
196
197 pub fn execution_backend(mut self, execution_backend: RuntimeExecutionBackend) -> Self {
198 self.config.execution_backend = Some(execution_backend);
199 self
200 }
201
202 pub fn execution_mode(mut self, execution_mode: ExecutionMode) -> Self {
203 self.config.execution_backend = Some(execution_mode.into());
204 self
205 }
206
207 pub fn cancellation_token(mut self, cancellation_token: CancellationToken) -> Self {
208 self.config.cancellation_token = Some(cancellation_token);
209 self
210 }
211
212 pub fn hook(mut self, hook: Arc<dyn RuntimeHook>) -> Self {
213 self.config.hooks.push(hook);
214 self
215 }
216
217 pub fn after_cycle_hook(mut self, hook: impl AfterCycleHook + 'static) -> Self {
218 self.config.after_cycle_hooks.push(Arc::new(hook));
219 self
220 }
221
222 pub fn after_cycle_hook_arc(mut self, hook: Arc<dyn AfterCycleHook>) -> Self {
223 self.config.after_cycle_hooks.push(hook);
224 self
225 }
226
227 pub fn trace_sink(mut self, sink: Arc<dyn TraceSink>) -> Self {
228 self.config.trace_sink = Some(sink);
229 self
230 }
231
232 pub fn trace_id(mut self, trace_id: impl Into<String>) -> Self {
233 self.config.trace_id = Some(trace_id.into());
234 self
235 }
236
237 pub fn workflow_name(mut self, workflow_name: impl Into<String>) -> Self {
238 self.config.workflow_name = Some(workflow_name.into());
239 self
240 }
241
242 pub fn event_store(mut self, store: Arc<dyn RunEventStore>) -> Self {
243 self.config.event_store = Some(store);
244 self
245 }
246
247 pub fn event_store_fail_closed(mut self, fail_closed: bool) -> Self {
248 self.config.event_store_fail_closed = fail_closed;
249 self
250 }
251
252 pub fn approval_provider(mut self, provider: Arc<dyn ApprovalProvider>) -> Self {
253 self.config.approval_provider = Some(provider);
254 self
255 }
256
257 pub fn approval_timeout(mut self, timeout: Duration) -> Self {
258 self.config.approval_timeout = Some(timeout);
259 self
260 }
261
262 pub fn approval_broker(mut self, broker: ApprovalBroker) -> Self {
263 self.config.approval_broker = Some(broker);
264 self
265 }
266
267 pub fn context_provider(mut self, provider: Arc<dyn ContextProvider>) -> Self {
268 self.config.context_providers.push(provider);
269 self
270 }
271
272 pub fn max_context_chars(mut self, max_chars: usize) -> Self {
273 self.config.max_context_chars = Some(max_chars);
274 self
275 }
276
277 pub fn memory_provider(mut self, provider: Arc<dyn MemoryProvider>) -> Self {
278 self.config.memory_providers.push(provider);
279 self
280 }
281
282 pub fn app_state<T>(mut self, app_state: T) -> Self
283 where
284 T: Send + Sync + 'static,
285 {
286 self.config.app_state = Some(Arc::new(app_state));
287 self
288 }
289
290 pub fn app_state_arc(mut self, app_state: Arc<dyn std::any::Any + Send + Sync>) -> Self {
291 self.config.app_state = Some(app_state);
292 self
293 }
294
295 pub fn initial_shared_state(mut self, shared_state: Metadata) -> Self {
296 self.config.initial_shared_state = shared_state;
297 self
298 }
299
300 pub fn shared_state(self, shared_state: Metadata) -> Self {
301 self.initial_shared_state(shared_state)
302 }
303
304 pub fn tool_registry_factory(
305 mut self,
306 factory: impl Fn() -> ToolRegistry + Send + Sync + 'static,
307 ) -> Self {
308 self.config.tool_registry_factory = Some(Arc::new(factory));
309 self
310 }
311
312 pub fn tool_registry_factory_arc(mut self, factory: ToolRegistryFactory) -> Self {
313 self.config.tool_registry_factory = Some(factory);
314 self
315 }
316
317 pub fn log_preview_chars(mut self, max_chars: usize) -> Self {
318 self.config.log_preview_chars = Some(max_chars);
319 self
320 }
321
322 pub fn debug_dump_dir(mut self, path: impl Into<PathBuf>) -> Self {
323 self.config.debug_dump_dir = Some(path.into());
324 self
325 }
326
327 pub fn before_cycle_messages(
328 mut self,
329 provider: impl Fn(u32, &[Message], &Metadata) -> Vec<Message> + Send + Sync + 'static,
330 ) -> Self {
331 self.config.before_cycle_messages = Some(Arc::new(provider));
332 self
333 }
334
335 pub fn before_cycle_messages_arc(mut self, provider: BeforeCycleMessageProvider) -> Self {
336 self.config.before_cycle_messages = Some(provider);
337 self
338 }
339
340 pub fn interruption_messages(
341 mut self,
342 provider: impl Fn() -> Vec<Message> + Send + Sync + 'static,
343 ) -> Self {
344 self.config.interruption_messages = Some(Arc::new(provider));
345 self
346 }
347
348 pub fn interruption_messages_arc(mut self, provider: InterruptionMessageProvider) -> Self {
349 self.config.interruption_messages = Some(provider);
350 self
351 }
352
353 pub fn sub_task_manager(mut self, manager: SubTaskManager) -> Self {
354 self.config.sub_task_manager = Some(manager);
355 self
356 }
357
358 pub fn stream(
359 mut self,
360 observer: impl Fn(&crate::events::RunEvent) + Send + Sync + 'static,
361 ) -> Self {
362 self.config.stream = Some(Arc::new(observer));
363 self
364 }
365
366 pub fn stream_arc(mut self, observer: RunEventHandler) -> Self {
367 self.config.stream = Some(observer);
368 self
369 }
370
371 pub fn budget_limits(mut self, limits: RunBudgetLimits) -> Self {
372 self.config.budget_limits = Some(limits);
373 self
374 }
375
376 pub fn host_cost_meter(mut self, meter: impl HostCostMeter + 'static) -> Self {
377 self.config.host_cost_meter = Some(Arc::new(meter));
378 self
379 }
380
381 pub fn host_cost_meter_arc(mut self, meter: Arc<dyn HostCostMeter>) -> Self {
382 self.config.host_cost_meter = Some(meter);
383 self
384 }
385
386 pub fn checkpoint_config(mut self, checkpoint_config: CheckpointConfig) -> Self {
387 self.config.checkpoint_config = Some(checkpoint_config);
388 self
389 }
390
391 pub fn checkpoint_extension(mut self, extension: impl CheckpointExtension + 'static) -> Self {
392 self.config.checkpoint_extensions.push(Arc::new(extension));
393 self
394 }
395
396 pub fn checkpoint_extension_arc(mut self, extension: Arc<dyn CheckpointExtension>) -> Self {
397 self.config.checkpoint_extensions.push(extension);
398 self
399 }
400
401 pub fn reconciliation_provider(
402 mut self,
403 provider: impl ReconciliationProvider + 'static,
404 ) -> Self {
405 self.config.reconciliation_provider = Some(Arc::new(provider));
406 self
407 }
408
409 pub fn reconciliation_provider_arc(
410 mut self,
411 provider: Arc<dyn ReconciliationProvider>,
412 ) -> Self {
413 self.config.reconciliation_provider = Some(provider);
414 self
415 }
416
417 pub fn metadata(mut self, key: impl Into<String>, value: Value) -> Self {
418 self.config.metadata.insert(key.into(), value);
419 self
420 }
421
422 pub fn build(self) -> RunConfig {
423 self.config
424 }
425}