Skip to content

Commit 5d969e9

Browse files
committed
use NonZeroUsize for max_iterations
number of iterations must be at least 1. Use NonZeroUsize for this. Signed-off-by: Aurabindo Pillai <mail@aurabindo.in>
1 parent 1d8078b commit 5d969e9

File tree

3 files changed

+52
-25
lines changed

3 files changed

+52
-25
lines changed

crates/agents-runtime/src/agent/builder.rs

Lines changed: 19 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ use agents_core::llm::LanguageModel;
1818
use agents_core::persistence::Checkpointer;
1919
use agents_core::tools::ToolBox;
2020
use std::collections::{HashMap, HashSet};
21+
use std::num::NonZeroUsize;
2122
use std::sync::Arc;
2223

2324
/// Builder API to assemble a DeepAgent in a single fluent flow, mirroring the Python
@@ -36,7 +37,7 @@ pub struct ConfigurableAgentBuilder {
3637
event_dispatcher: Option<Arc<agents_core::events::EventDispatcher>>,
3738
enable_pii_sanitization: bool,
3839
token_tracking_config: Option<TokenTrackingConfig>,
39-
max_iterations: usize,
40+
max_iterations: NonZeroUsize,
4041
}
4142

4243
impl ConfigurableAgentBuilder {
@@ -55,7 +56,7 @@ impl ConfigurableAgentBuilder {
5556
event_dispatcher: None,
5657
enable_pii_sanitization: true, // Enabled by default for security
5758
token_tracking_config: None,
58-
max_iterations: 10,
59+
max_iterations: NonZeroUsize::new(10).unwrap(),
5960
}
6061
}
6162

@@ -308,10 +309,16 @@ impl ConfigurableAgentBuilder {
308309
/// user requests, calling tools and reasoning about the results. This setting
309310
/// controls how many iterations the agent can perform before it stops.
310311
///
312+
/// **Note**: `max_iterations` must be greater than 0. Passing 0 will result in a panic.
313+
///
311314
/// # Default
312315
///
313316
/// Defaults to 10 iterations if not specified.
314317
///
318+
/// # Panics
319+
///
320+
/// Panics if `max_iterations` is 0.
321+
///
315322
/// # Example
316323
///
317324
/// ```ignore
@@ -321,7 +328,8 @@ impl ConfigurableAgentBuilder {
321328
/// .build()?;
322329
/// ```
323330
pub fn with_max_iterations(mut self, max_iterations: usize) -> Self {
324-
self.max_iterations = max_iterations;
331+
self.max_iterations =
332+
NonZeroUsize::new(max_iterations).expect("max_iterations must be greater than 0");
325333
self
326334
}
327335

@@ -386,7 +394,7 @@ impl ConfigurableAgentBuilder {
386394
.with_auto_general_purpose(auto_general_purpose)
387395
.with_prompt_caching(enable_prompt_caching)
388396
.with_pii_sanitization(enable_pii_sanitization)
389-
.with_max_iterations(max_iterations);
397+
.with_max_iterations(max_iterations.get());
390398

391399
if let Some(ckpt) = checkpointer {
392400
cfg = cfg.with_checkpointer(ckpt);
@@ -421,25 +429,25 @@ mod tests {
421429
#[test]
422430
fn test_builder_default_max_iterations() {
423431
let builder = ConfigurableAgentBuilder::new("test instructions");
424-
assert_eq!(builder.max_iterations, 10);
432+
assert_eq!(builder.max_iterations.get(), 10);
425433
}
426434

427435
#[test]
428436
fn test_builder_custom_max_iterations() {
429437
let builder = ConfigurableAgentBuilder::new("test instructions").with_max_iterations(20);
430-
assert_eq!(builder.max_iterations, 20);
438+
assert_eq!(builder.max_iterations.get(), 20);
431439
}
432440

433441
#[test]
434-
fn test_builder_zero_max_iterations() {
435-
let builder = ConfigurableAgentBuilder::new("test instructions").with_max_iterations(0);
436-
assert_eq!(builder.max_iterations, 0);
442+
#[should_panic(expected = "max_iterations must be greater than 0")]
443+
fn test_builder_zero_max_iterations_panics() {
444+
let _builder = ConfigurableAgentBuilder::new("test instructions").with_max_iterations(0);
437445
}
438446

439447
#[test]
440448
fn test_builder_large_max_iterations() {
441449
let builder = ConfigurableAgentBuilder::new("test instructions").with_max_iterations(1000);
442-
assert_eq!(builder.max_iterations, 1000);
450+
assert_eq!(builder.max_iterations.get(), 1000);
443451
}
444452

445453
#[test]
@@ -450,7 +458,7 @@ mod tests {
450458
.with_prompt_caching(true)
451459
.with_pii_sanitization(false);
452460

453-
assert_eq!(builder.max_iterations, 15);
461+
assert_eq!(builder.max_iterations.get(), 15);
454462
assert_eq!(builder.auto_general_purpose, false);
455463
assert_eq!(builder.enable_prompt_caching, true);
456464
assert_eq!(builder.enable_pii_sanitization, false);

crates/agents-runtime/src/agent/config.rs

Lines changed: 28 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ use agents_core::agent::PlannerHandle;
88
use agents_core::persistence::Checkpointer;
99
use agents_core::tools::ToolBox;
1010
use std::collections::{HashMap, HashSet};
11+
use std::num::NonZeroUsize;
1112
use std::sync::Arc;
1213

1314
/// Parameters for create_deep_agent() that mirror the Python API exactly
@@ -54,7 +55,7 @@ pub struct DeepAgentConfig {
5455
pub event_dispatcher: Option<Arc<agents_core::events::EventDispatcher>>,
5556
pub enable_pii_sanitization: bool,
5657
pub token_tracking_config: Option<TokenTrackingConfig>,
57-
pub max_iterations: usize,
58+
pub max_iterations: NonZeroUsize,
5859
}
5960

6061
impl DeepAgentConfig {
@@ -73,7 +74,7 @@ impl DeepAgentConfig {
7374
event_dispatcher: None,
7475
enable_pii_sanitization: true, // Enabled by default for security
7576
token_tracking_config: None,
76-
max_iterations: 10,
77+
max_iterations: NonZeroUsize::new(10).unwrap(),
7778
}
7879
}
7980

@@ -183,9 +184,19 @@ impl DeepAgentConfig {
183184
}
184185

185186
/// Set the maximum number of ReAct loop iterations before stopping.
187+
///
188+
/// **Note**: `max_iterations` must be greater than 0. Passing 0 will result in a panic.
189+
///
190+
/// # Panics
191+
///
192+
/// Panics if `max_iterations` is 0.
193+
///
194+
/// # Default
195+
///
186196
/// Defaults to 10 if not specified.
187197
pub fn with_max_iterations(mut self, max_iterations: usize) -> Self {
188-
self.max_iterations = max_iterations;
198+
self.max_iterations =
199+
NonZeroUsize::new(max_iterations).expect("max_iterations must be greater than 0");
189200
self
190201
}
191202
}
@@ -310,14 +321,14 @@ mod tests {
310321
fn test_config_default_max_iterations() {
311322
let planner = create_mock_planner();
312323
let config = DeepAgentConfig::new("test instructions", planner);
313-
assert_eq!(config.max_iterations, 10);
324+
assert_eq!(config.max_iterations.get(), 10);
314325
}
315326

316327
#[test]
317328
fn test_config_custom_max_iterations() {
318329
let planner = create_mock_planner();
319330
let config = DeepAgentConfig::new("test instructions", planner).with_max_iterations(25);
320-
assert_eq!(config.max_iterations, 25);
331+
assert_eq!(config.max_iterations.get(), 25);
321332
}
322333

323334
#[test]
@@ -329,7 +340,7 @@ mod tests {
329340
.with_prompt_caching(true)
330341
.with_pii_sanitization(false);
331342

332-
assert_eq!(config.max_iterations, 30);
343+
assert_eq!(config.max_iterations.get(), 30);
333344
assert_eq!(config.auto_general_purpose, false);
334345
assert_eq!(config.enable_prompt_caching, true);
335346
assert_eq!(config.enable_pii_sanitization, false);
@@ -341,7 +352,14 @@ mod tests {
341352
let config = DeepAgentConfig::new("test instructions", planner).with_max_iterations(42);
342353

343354
// Verify the value is actually stored
344-
assert_eq!(config.max_iterations, 42);
355+
assert_eq!(config.max_iterations.get(), 42);
356+
}
357+
358+
#[test]
359+
#[should_panic(expected = "max_iterations must be greater than 0")]
360+
fn test_config_zero_max_iterations_panics() {
361+
let planner = create_mock_planner();
362+
let _config = DeepAgentConfig::new("test instructions", planner).with_max_iterations(0);
345363
}
346364

347365
#[test]
@@ -351,19 +369,19 @@ mod tests {
351369
// Test that max_iterations works with various combinations
352370
let config =
353371
DeepAgentConfig::new("test instructions", planner.clone()).with_max_iterations(5);
354-
assert_eq!(config.max_iterations, 5);
372+
assert_eq!(config.max_iterations.get(), 5);
355373

356374
let config2 = DeepAgentConfig::new("test instructions", planner.clone())
357375
.with_prompt_caching(true)
358376
.with_max_iterations(15);
359-
assert_eq!(config2.max_iterations, 15);
377+
assert_eq!(config2.max_iterations.get(), 15);
360378
assert_eq!(config2.enable_prompt_caching, true);
361379

362380
let config3 = DeepAgentConfig::new("test instructions", planner)
363381
.with_auto_general_purpose(false)
364382
.with_max_iterations(100)
365383
.with_pii_sanitization(true);
366-
assert_eq!(config3.max_iterations, 100);
384+
assert_eq!(config3.max_iterations.get(), 100);
367385
assert_eq!(config3.auto_general_purpose, false);
368386
assert_eq!(config3.enable_pii_sanitization, true);
369387
}

crates/agents-runtime/src/agent/runtime.rs

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ use agents_core::tools::{ToolBox, ToolContext, ToolResult};
2222
use async_trait::async_trait;
2323
use serde_json::Value;
2424
use std::collections::{HashMap, HashSet};
25+
use std::num::NonZeroUsize;
2526
use std::sync::{Arc, RwLock};
2627

2728
// Built-in tool names exposed by middlewares. The `task` tool for subagents is not gated.
@@ -64,7 +65,7 @@ pub struct DeepAgent {
6465
checkpointer: Option<Arc<dyn Checkpointer>>,
6566
event_dispatcher: Option<Arc<agents_core::events::EventDispatcher>>,
6667
enable_pii_sanitization: bool,
67-
max_iterations: usize,
68+
max_iterations: NonZeroUsize,
6869
}
6970

7071
impl DeepAgent {
@@ -509,7 +510,7 @@ impl DeepAgent {
509510
self.append_history(input.clone());
510511

511512
// ReAct loop: continue until LLM responds with text (not tool calls)
512-
let max_iterations = self.max_iterations;
513+
let max_iterations = self.max_iterations.get();
513514
let mut iteration = 0;
514515

515516
loop {
@@ -946,7 +947,7 @@ pub fn create_deep_agent_from_config(config: DeepAgentConfig) -> DeepAgent {
946947
let mut sub_cfg = DeepAgentConfig::new(subagent_config.instructions.clone(), sub_planner);
947948

948949
// Inherit max_iterations from parent
949-
sub_cfg = sub_cfg.with_max_iterations(config.max_iterations);
950+
sub_cfg = sub_cfg.with_max_iterations(config.max_iterations.get());
950951

951952
// Configure tools
952953
if let Some(ref tools) = subagent_config.tools {
@@ -1003,7 +1004,7 @@ pub fn create_deep_agent_from_config(config: DeepAgentConfig) -> DeepAgent {
10031004
.with_auto_general_purpose(false)
10041005
.with_prompt_caching(config.enable_prompt_caching)
10051006
.with_pii_sanitization(config.enable_pii_sanitization)
1006-
.with_max_iterations(config.max_iterations);
1007+
.with_max_iterations(config.max_iterations.get());
10071008
if let Some(ref selected) = config.builtin_tools {
10081009
sub_cfg = sub_cfg.with_builtin_tools(selected.iter().cloned());
10091010
}

0 commit comments

Comments
 (0)