mirror of
https://github.com/pchuan98/codex.git
synced 2026-07-01 00:31:56 +08:00
Make multi_agent_v2 wait_agent timeouts configurable (#22528)
## Why `multi_agent_v2` already allowed configuring the minimum `wait_agent` timeout, but the default timeout and upper bound were still hard-coded. That made it hard to tune waits for subagent mailbox activity in sessions that need either faster wakeups or longer waits, and it meant the model-visible `wait_agent` schema could not fully reflect the resolved runtime limits. ## What Changed - Added `features.multi_agent_v2.max_wait_timeout_ms` and `features.multi_agent_v2.default_wait_timeout_ms` alongside the existing `min_wait_timeout_ms` setting. - Validated all three timeouts in config as `0..=3_600_000`, with `min_wait_timeout_ms <= default_wait_timeout_ms <= max_wait_timeout_ms`. - Thread and review session tool config now passes the resolved min/default/max values into the `wait_agent` tool schema. - `wait_agent` now uses the configured default when `timeout_ms` is omitted and rejects explicit values outside the configured min/max range instead of silently clamping them. - Updated the generated config schema and config-lock test coverage for the new fields.
This commit is contained in:
committed by
GitHub
Unverified
parent
8ae0c837f0
commit
7c57a59f51
@@ -1465,6 +1465,12 @@
|
||||
"MultiAgentV2ConfigToml": {
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"default_wait_timeout_ms": {
|
||||
"format": "int64",
|
||||
"maximum": 3600000.0,
|
||||
"minimum": 0.0,
|
||||
"type": "integer"
|
||||
},
|
||||
"enabled": {
|
||||
"type": "boolean"
|
||||
},
|
||||
@@ -1476,10 +1482,16 @@
|
||||
"minimum": 1.0,
|
||||
"type": "integer"
|
||||
},
|
||||
"max_wait_timeout_ms": {
|
||||
"format": "int64",
|
||||
"maximum": 3600000.0,
|
||||
"minimum": 0.0,
|
||||
"type": "integer"
|
||||
},
|
||||
"min_wait_timeout_ms": {
|
||||
"format": "int64",
|
||||
"maximum": 3600000.0,
|
||||
"minimum": 1.0,
|
||||
"minimum": 0.0,
|
||||
"type": "integer"
|
||||
},
|
||||
"non_code_mode_only": {
|
||||
|
||||
@@ -9687,6 +9687,8 @@ async fn multi_agent_v2_config_from_feature_table() -> std::io::Result<()> {
|
||||
enabled = true
|
||||
max_concurrent_threads_per_session = 5
|
||||
min_wait_timeout_ms = 2500
|
||||
max_wait_timeout_ms = 120000
|
||||
default_wait_timeout_ms = 30000
|
||||
usage_hint_enabled = false
|
||||
usage_hint_text = "Custom delegation guidance."
|
||||
root_agent_usage_hint_text = "Root guidance."
|
||||
@@ -9705,6 +9707,8 @@ non_code_mode_only = true
|
||||
assert!(config.features.enabled(Feature::MultiAgentV2));
|
||||
assert_eq!(config.multi_agent_v2.max_concurrent_threads_per_session, 5);
|
||||
assert_eq!(config.multi_agent_v2.min_wait_timeout_ms, 2500);
|
||||
assert_eq!(config.multi_agent_v2.max_wait_timeout_ms, 120000);
|
||||
assert_eq!(config.multi_agent_v2.default_wait_timeout_ms, 30000);
|
||||
assert_eq!(config.agent_max_threads, Some(4));
|
||||
assert!(!config.multi_agent_v2.usage_hint_enabled);
|
||||
assert_eq!(
|
||||
@@ -9735,6 +9739,8 @@ async fn profile_multi_agent_v2_config_overrides_base() -> std::io::Result<()> {
|
||||
[features.multi_agent_v2]
|
||||
max_concurrent_threads_per_session = 4
|
||||
min_wait_timeout_ms = 3000
|
||||
max_wait_timeout_ms = 120000
|
||||
default_wait_timeout_ms = 30000
|
||||
usage_hint_enabled = true
|
||||
usage_hint_text = "base hint"
|
||||
root_agent_usage_hint_text = "base root hint"
|
||||
@@ -9745,6 +9751,8 @@ non_code_mode_only = false
|
||||
[profiles.no_hint.features.multi_agent_v2]
|
||||
max_concurrent_threads_per_session = 6
|
||||
min_wait_timeout_ms = 1500
|
||||
max_wait_timeout_ms = 90000
|
||||
default_wait_timeout_ms = 15000
|
||||
usage_hint_enabled = false
|
||||
usage_hint_text = "profile hint"
|
||||
root_agent_usage_hint_text = "profile root hint"
|
||||
@@ -9762,6 +9770,8 @@ non_code_mode_only = true
|
||||
|
||||
assert_eq!(config.multi_agent_v2.max_concurrent_threads_per_session, 6);
|
||||
assert_eq!(config.multi_agent_v2.min_wait_timeout_ms, 1500);
|
||||
assert_eq!(config.multi_agent_v2.max_wait_timeout_ms, 90000);
|
||||
assert_eq!(config.multi_agent_v2.default_wait_timeout_ms, 15000);
|
||||
assert!(!config.multi_agent_v2.usage_hint_enabled);
|
||||
assert_eq!(
|
||||
config.multi_agent_v2.usage_hint_text.as_deref(),
|
||||
@@ -9799,6 +9809,8 @@ enabled = true
|
||||
|
||||
assert_eq!(config.multi_agent_v2.max_concurrent_threads_per_session, 4);
|
||||
assert_eq!(config.multi_agent_v2.min_wait_timeout_ms, 10_000);
|
||||
assert_eq!(config.multi_agent_v2.max_wait_timeout_ms, 3_600_000);
|
||||
assert_eq!(config.multi_agent_v2.default_wait_timeout_ms, 30_000);
|
||||
assert_eq!(config.agent_max_threads, Some(3));
|
||||
assert!(!config.multi_agent_v2.non_code_mode_only);
|
||||
|
||||
@@ -9835,13 +9847,33 @@ max_threads = 3
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn multi_agent_v2_rejects_invalid_min_wait_timeout() -> std::io::Result<()> {
|
||||
async fn multi_agent_v2_rejects_invalid_wait_timeouts() -> std::io::Result<()> {
|
||||
let codex_home = TempDir::new()?;
|
||||
std::fs::write(
|
||||
codex_home.path().join(CONFIG_TOML_FILE),
|
||||
r#"[features.multi_agent_v2]
|
||||
enabled = true
|
||||
min_wait_timeout_ms = 0
|
||||
max_wait_timeout_ms = 0
|
||||
default_wait_timeout_ms = 0
|
||||
"#,
|
||||
)?;
|
||||
|
||||
let config = ConfigBuilder::without_managed_config_for_tests()
|
||||
.codex_home(codex_home.path().to_path_buf())
|
||||
.fallback_cwd(Some(codex_home.path().to_path_buf()))
|
||||
.build()
|
||||
.await?;
|
||||
|
||||
assert_eq!(config.multi_agent_v2.min_wait_timeout_ms, 0);
|
||||
assert_eq!(config.multi_agent_v2.max_wait_timeout_ms, 0);
|
||||
assert_eq!(config.multi_agent_v2.default_wait_timeout_ms, 0);
|
||||
|
||||
std::fs::write(
|
||||
codex_home.path().join(CONFIG_TOML_FILE),
|
||||
r#"[features.multi_agent_v2]
|
||||
enabled = true
|
||||
min_wait_timeout_ms = -1
|
||||
"#,
|
||||
)?;
|
||||
|
||||
@@ -9850,12 +9882,12 @@ min_wait_timeout_ms = 0
|
||||
.fallback_cwd(Some(codex_home.path().to_path_buf()))
|
||||
.build()
|
||||
.await
|
||||
.expect_err("zero min_wait_timeout_ms should be rejected");
|
||||
.expect_err("negative min_wait_timeout_ms should be rejected");
|
||||
|
||||
assert_eq!(err.kind(), std::io::ErrorKind::InvalidInput);
|
||||
assert_eq!(
|
||||
err.to_string(),
|
||||
"features.multi_agent_v2.min_wait_timeout_ms must be at least 1"
|
||||
"features.multi_agent_v2.min_wait_timeout_ms must be at least 0"
|
||||
);
|
||||
|
||||
std::fs::write(
|
||||
@@ -9879,6 +9911,137 @@ min_wait_timeout_ms = 3600001
|
||||
"features.multi_agent_v2.min_wait_timeout_ms must be at most 3600000"
|
||||
);
|
||||
|
||||
std::fs::write(
|
||||
codex_home.path().join(CONFIG_TOML_FILE),
|
||||
r#"[features.multi_agent_v2]
|
||||
enabled = true
|
||||
max_wait_timeout_ms = -1
|
||||
"#,
|
||||
)?;
|
||||
|
||||
let err = ConfigBuilder::without_managed_config_for_tests()
|
||||
.codex_home(codex_home.path().to_path_buf())
|
||||
.fallback_cwd(Some(codex_home.path().to_path_buf()))
|
||||
.build()
|
||||
.await
|
||||
.expect_err("negative max_wait_timeout_ms should be rejected");
|
||||
|
||||
assert_eq!(err.kind(), std::io::ErrorKind::InvalidInput);
|
||||
assert_eq!(
|
||||
err.to_string(),
|
||||
"features.multi_agent_v2.max_wait_timeout_ms must be at least 0"
|
||||
);
|
||||
|
||||
std::fs::write(
|
||||
codex_home.path().join(CONFIG_TOML_FILE),
|
||||
r#"[features.multi_agent_v2]
|
||||
enabled = true
|
||||
max_wait_timeout_ms = 3600001
|
||||
"#,
|
||||
)?;
|
||||
|
||||
let err = ConfigBuilder::without_managed_config_for_tests()
|
||||
.codex_home(codex_home.path().to_path_buf())
|
||||
.fallback_cwd(Some(codex_home.path().to_path_buf()))
|
||||
.build()
|
||||
.await
|
||||
.expect_err("too large max_wait_timeout_ms should be rejected");
|
||||
|
||||
assert_eq!(err.kind(), std::io::ErrorKind::InvalidInput);
|
||||
assert_eq!(
|
||||
err.to_string(),
|
||||
"features.multi_agent_v2.max_wait_timeout_ms must be at most 3600000"
|
||||
);
|
||||
|
||||
std::fs::write(
|
||||
codex_home.path().join(CONFIG_TOML_FILE),
|
||||
r#"[features.multi_agent_v2]
|
||||
enabled = true
|
||||
default_wait_timeout_ms = -1
|
||||
"#,
|
||||
)?;
|
||||
|
||||
let err = ConfigBuilder::without_managed_config_for_tests()
|
||||
.codex_home(codex_home.path().to_path_buf())
|
||||
.fallback_cwd(Some(codex_home.path().to_path_buf()))
|
||||
.build()
|
||||
.await
|
||||
.expect_err("negative default_wait_timeout_ms should be rejected");
|
||||
|
||||
assert_eq!(err.kind(), std::io::ErrorKind::InvalidInput);
|
||||
assert_eq!(
|
||||
err.to_string(),
|
||||
"features.multi_agent_v2.default_wait_timeout_ms must be at least 0"
|
||||
);
|
||||
|
||||
std::fs::write(
|
||||
codex_home.path().join(CONFIG_TOML_FILE),
|
||||
r#"[features.multi_agent_v2]
|
||||
enabled = true
|
||||
min_wait_timeout_ms = 1000
|
||||
max_wait_timeout_ms = 500
|
||||
"#,
|
||||
)?;
|
||||
|
||||
let err = ConfigBuilder::without_managed_config_for_tests()
|
||||
.codex_home(codex_home.path().to_path_buf())
|
||||
.fallback_cwd(Some(codex_home.path().to_path_buf()))
|
||||
.build()
|
||||
.await
|
||||
.expect_err("min greater than max should be rejected");
|
||||
|
||||
assert_eq!(err.kind(), std::io::ErrorKind::InvalidInput);
|
||||
assert_eq!(
|
||||
err.to_string(),
|
||||
"features.multi_agent_v2.min_wait_timeout_ms must be at most features.multi_agent_v2.max_wait_timeout_ms"
|
||||
);
|
||||
|
||||
std::fs::write(
|
||||
codex_home.path().join(CONFIG_TOML_FILE),
|
||||
r#"[features.multi_agent_v2]
|
||||
enabled = true
|
||||
min_wait_timeout_ms = 1000
|
||||
max_wait_timeout_ms = 2000
|
||||
default_wait_timeout_ms = 500
|
||||
"#,
|
||||
)?;
|
||||
|
||||
let err = ConfigBuilder::without_managed_config_for_tests()
|
||||
.codex_home(codex_home.path().to_path_buf())
|
||||
.fallback_cwd(Some(codex_home.path().to_path_buf()))
|
||||
.build()
|
||||
.await
|
||||
.expect_err("default less than min should be rejected");
|
||||
|
||||
assert_eq!(err.kind(), std::io::ErrorKind::InvalidInput);
|
||||
assert_eq!(
|
||||
err.to_string(),
|
||||
"features.multi_agent_v2.default_wait_timeout_ms must be at least features.multi_agent_v2.min_wait_timeout_ms"
|
||||
);
|
||||
|
||||
std::fs::write(
|
||||
codex_home.path().join(CONFIG_TOML_FILE),
|
||||
r#"[features.multi_agent_v2]
|
||||
enabled = true
|
||||
min_wait_timeout_ms = 1000
|
||||
max_wait_timeout_ms = 2000
|
||||
default_wait_timeout_ms = 2500
|
||||
"#,
|
||||
)?;
|
||||
|
||||
let err = ConfigBuilder::without_managed_config_for_tests()
|
||||
.codex_home(codex_home.path().to_path_buf())
|
||||
.fallback_cwd(Some(codex_home.path().to_path_buf()))
|
||||
.build()
|
||||
.await
|
||||
.expect_err("default greater than max should be rejected");
|
||||
|
||||
assert_eq!(err.kind(), std::io::ErrorKind::InvalidInput);
|
||||
assert_eq!(
|
||||
err.to_string(),
|
||||
"features.multi_agent_v2.default_wait_timeout_ms must be at most features.multi_agent_v2.max_wait_timeout_ms"
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
||||
@@ -177,7 +177,11 @@ pub(crate) const AGENTS_MD_MAX_BYTES: usize = DEFAULT_PROJECT_DOC_MAX_BYTES; //
|
||||
pub(crate) const DEFAULT_AGENT_MAX_THREADS: Option<usize> = Some(6);
|
||||
pub(crate) const DEFAULT_MULTI_AGENT_V2_MAX_CONCURRENT_THREADS_PER_SESSION: usize = 4;
|
||||
pub(crate) const DEFAULT_MULTI_AGENT_V2_MIN_WAIT_TIMEOUT_MS: i64 = 10_000;
|
||||
pub(crate) const MAX_MULTI_AGENT_V2_WAIT_TIMEOUT_MS: i64 = 3600 * 1000;
|
||||
pub(crate) const DEFAULT_MULTI_AGENT_V2_MAX_WAIT_TIMEOUT_MS: i64 = 3600 * 1000;
|
||||
pub(crate) const DEFAULT_MULTI_AGENT_V2_DEFAULT_WAIT_TIMEOUT_MS: i64 = 30_000;
|
||||
pub(crate) const HARD_MIN_MULTI_AGENT_V2_TIMEOUT_MS: i64 = 0;
|
||||
pub(crate) const HARD_MAX_MULTI_AGENT_V2_TIMEOUT_MS: i64 =
|
||||
DEFAULT_MULTI_AGENT_V2_MAX_WAIT_TIMEOUT_MS;
|
||||
pub(crate) const DEFAULT_AGENT_MAX_DEPTH: i32 = 1;
|
||||
pub(crate) const DEFAULT_AGENT_JOB_MAX_RUNTIME_SECONDS: Option<u64> = None;
|
||||
const LOCAL_DEV_BUILD_VERSION: &str = "0.0.0";
|
||||
@@ -832,6 +836,8 @@ pub struct Config {
|
||||
pub struct MultiAgentV2Config {
|
||||
pub max_concurrent_threads_per_session: usize,
|
||||
pub min_wait_timeout_ms: i64,
|
||||
pub max_wait_timeout_ms: i64,
|
||||
pub default_wait_timeout_ms: i64,
|
||||
pub usage_hint_enabled: bool,
|
||||
pub usage_hint_text: Option<String>,
|
||||
pub root_agent_usage_hint_text: Option<String>,
|
||||
@@ -846,6 +852,8 @@ impl Default for MultiAgentV2Config {
|
||||
max_concurrent_threads_per_session:
|
||||
DEFAULT_MULTI_AGENT_V2_MAX_CONCURRENT_THREADS_PER_SESSION,
|
||||
min_wait_timeout_ms: DEFAULT_MULTI_AGENT_V2_MIN_WAIT_TIMEOUT_MS,
|
||||
max_wait_timeout_ms: DEFAULT_MULTI_AGENT_V2_MAX_WAIT_TIMEOUT_MS,
|
||||
default_wait_timeout_ms: DEFAULT_MULTI_AGENT_V2_DEFAULT_WAIT_TIMEOUT_MS,
|
||||
usage_hint_enabled: true,
|
||||
usage_hint_text: None,
|
||||
root_agent_usage_hint_text: None,
|
||||
@@ -1965,6 +1973,14 @@ fn resolve_multi_agent_v2_config(
|
||||
.and_then(|config| config.min_wait_timeout_ms)
|
||||
.or_else(|| base.and_then(|config| config.min_wait_timeout_ms))
|
||||
.unwrap_or(default.min_wait_timeout_ms);
|
||||
let max_wait_timeout_ms = profile
|
||||
.and_then(|config| config.max_wait_timeout_ms)
|
||||
.or_else(|| base.and_then(|config| config.max_wait_timeout_ms))
|
||||
.unwrap_or(default.max_wait_timeout_ms);
|
||||
let default_wait_timeout_ms = profile
|
||||
.and_then(|config| config.default_wait_timeout_ms)
|
||||
.or_else(|| base.and_then(|config| config.default_wait_timeout_ms))
|
||||
.unwrap_or(default.default_wait_timeout_ms);
|
||||
let usage_hint_enabled = profile
|
||||
.and_then(|config| config.usage_hint_enabled)
|
||||
.or_else(|| base.and_then(|config| config.usage_hint_enabled))
|
||||
@@ -1996,6 +2012,8 @@ fn resolve_multi_agent_v2_config(
|
||||
MultiAgentV2Config {
|
||||
max_concurrent_threads_per_session,
|
||||
min_wait_timeout_ms,
|
||||
max_wait_timeout_ms,
|
||||
default_wait_timeout_ms,
|
||||
usage_hint_enabled,
|
||||
usage_hint_text,
|
||||
root_agent_usage_hint_text,
|
||||
@@ -2078,6 +2096,22 @@ pub(crate) fn resolve_web_search_mode_for_turn(
|
||||
WebSearchMode::Disabled
|
||||
}
|
||||
|
||||
fn validate_multi_agent_v2_wait_timeout(label: &str, value: i64) -> std::io::Result<()> {
|
||||
if value < HARD_MIN_MULTI_AGENT_V2_TIMEOUT_MS {
|
||||
return Err(std::io::Error::new(
|
||||
std::io::ErrorKind::InvalidInput,
|
||||
format!("{label} must be at least {HARD_MIN_MULTI_AGENT_V2_TIMEOUT_MS}"),
|
||||
));
|
||||
}
|
||||
if value > HARD_MAX_MULTI_AGENT_V2_TIMEOUT_MS {
|
||||
return Err(std::io::Error::new(
|
||||
std::io::ErrorKind::InvalidInput,
|
||||
format!("{label} must be at most {HARD_MAX_MULTI_AGENT_V2_TIMEOUT_MS}"),
|
||||
));
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
impl Config {
|
||||
#[cfg(test)]
|
||||
async fn load_from_base_config_with_overrides(
|
||||
@@ -2652,18 +2686,34 @@ impl Config {
|
||||
"features.multi_agent_v2.max_concurrent_threads_per_session must be at least 1",
|
||||
));
|
||||
}
|
||||
if multi_agent_v2.min_wait_timeout_ms <= 0 {
|
||||
validate_multi_agent_v2_wait_timeout(
|
||||
"features.multi_agent_v2.min_wait_timeout_ms",
|
||||
multi_agent_v2.min_wait_timeout_ms,
|
||||
)?;
|
||||
validate_multi_agent_v2_wait_timeout(
|
||||
"features.multi_agent_v2.max_wait_timeout_ms",
|
||||
multi_agent_v2.max_wait_timeout_ms,
|
||||
)?;
|
||||
validate_multi_agent_v2_wait_timeout(
|
||||
"features.multi_agent_v2.default_wait_timeout_ms",
|
||||
multi_agent_v2.default_wait_timeout_ms,
|
||||
)?;
|
||||
if multi_agent_v2.min_wait_timeout_ms > multi_agent_v2.max_wait_timeout_ms {
|
||||
return Err(std::io::Error::new(
|
||||
std::io::ErrorKind::InvalidInput,
|
||||
"features.multi_agent_v2.min_wait_timeout_ms must be at least 1",
|
||||
"features.multi_agent_v2.min_wait_timeout_ms must be at most features.multi_agent_v2.max_wait_timeout_ms",
|
||||
));
|
||||
}
|
||||
if multi_agent_v2.min_wait_timeout_ms > MAX_MULTI_AGENT_V2_WAIT_TIMEOUT_MS {
|
||||
if multi_agent_v2.default_wait_timeout_ms < multi_agent_v2.min_wait_timeout_ms {
|
||||
return Err(std::io::Error::new(
|
||||
std::io::ErrorKind::InvalidInput,
|
||||
format!(
|
||||
"features.multi_agent_v2.min_wait_timeout_ms must be at most {MAX_MULTI_AGENT_V2_WAIT_TIMEOUT_MS}"
|
||||
),
|
||||
"features.multi_agent_v2.default_wait_timeout_ms must be at least features.multi_agent_v2.min_wait_timeout_ms",
|
||||
));
|
||||
}
|
||||
if multi_agent_v2.default_wait_timeout_ms > multi_agent_v2.max_wait_timeout_ms {
|
||||
return Err(std::io::Error::new(
|
||||
std::io::ErrorKind::InvalidInput,
|
||||
"features.multi_agent_v2.default_wait_timeout_ms must be at most features.multi_agent_v2.max_wait_timeout_ms",
|
||||
));
|
||||
}
|
||||
let agent_max_threads_from_config = cfg.agents.as_ref().and_then(|agents| agents.max_threads);
|
||||
|
||||
@@ -264,6 +264,8 @@ mod tests {
|
||||
enabled: Some(false),
|
||||
max_concurrent_threads_per_session: Some(_),
|
||||
min_wait_timeout_ms: Some(_),
|
||||
max_wait_timeout_ms: Some(_),
|
||||
default_wait_timeout_ms: Some(_),
|
||||
usage_hint_enabled: Some(_),
|
||||
hide_spawn_agent_metadata: Some(_),
|
||||
..
|
||||
|
||||
@@ -64,6 +64,16 @@ pub(super) async fn spawn_review_thread(
|
||||
.enabled(Feature::MultiAgentV2)
|
||||
.then_some(config.multi_agent_v2.min_wait_timeout_ms),
|
||||
)
|
||||
.with_wait_agent_max_timeout_ms(
|
||||
review_features
|
||||
.enabled(Feature::MultiAgentV2)
|
||||
.then_some(config.multi_agent_v2.max_wait_timeout_ms),
|
||||
)
|
||||
.with_wait_agent_default_timeout_ms(
|
||||
review_features
|
||||
.enabled(Feature::MultiAgentV2)
|
||||
.then_some(config.multi_agent_v2.default_wait_timeout_ms),
|
||||
)
|
||||
.with_agent_type_description(crate::agent::role::spawn_tool_spec::build(
|
||||
&config.agent_roles,
|
||||
));
|
||||
|
||||
@@ -234,6 +234,18 @@ impl TurnContext {
|
||||
.enabled(Feature::MultiAgentV2)
|
||||
.then_some(config.multi_agent_v2.min_wait_timeout_ms),
|
||||
)
|
||||
.with_wait_agent_max_timeout_ms(
|
||||
config
|
||||
.features
|
||||
.enabled(Feature::MultiAgentV2)
|
||||
.then_some(config.multi_agent_v2.max_wait_timeout_ms),
|
||||
)
|
||||
.with_wait_agent_default_timeout_ms(
|
||||
config
|
||||
.features
|
||||
.enabled(Feature::MultiAgentV2)
|
||||
.then_some(config.multi_agent_v2.default_wait_timeout_ms),
|
||||
)
|
||||
.with_agent_type_description(crate::agent::role::spawn_tool_spec::build(
|
||||
&config.agent_roles,
|
||||
));
|
||||
@@ -539,6 +551,18 @@ impl Session {
|
||||
.enabled(Feature::MultiAgentV2)
|
||||
.then_some(per_turn_config.multi_agent_v2.min_wait_timeout_ms),
|
||||
)
|
||||
.with_wait_agent_max_timeout_ms(
|
||||
per_turn_config
|
||||
.features
|
||||
.enabled(Feature::MultiAgentV2)
|
||||
.then_some(per_turn_config.multi_agent_v2.max_wait_timeout_ms),
|
||||
)
|
||||
.with_wait_agent_default_timeout_ms(
|
||||
per_turn_config
|
||||
.features
|
||||
.enabled(Feature::MultiAgentV2)
|
||||
.then_some(per_turn_config.multi_agent_v2.default_wait_timeout_ms),
|
||||
)
|
||||
.with_agent_type_description(crate::agent::role::spawn_tool_spec::build(
|
||||
&per_turn_config.agent_roles,
|
||||
));
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
use crate::agent::AgentStatus;
|
||||
use crate::config::Config;
|
||||
use crate::config::DEFAULT_MULTI_AGENT_V2_MIN_WAIT_TIMEOUT_MS;
|
||||
use crate::config::MAX_MULTI_AGENT_V2_WAIT_TIMEOUT_MS;
|
||||
use crate::config::HARD_MAX_MULTI_AGENT_V2_TIMEOUT_MS;
|
||||
use crate::function_tool::FunctionCallError;
|
||||
use crate::session::session::Session;
|
||||
use crate::session::turn_context::TurnContext;
|
||||
@@ -30,7 +30,7 @@ use std::collections::HashMap;
|
||||
/// Minimum wait timeout to prevent tight polling loops from burning CPU.
|
||||
pub(crate) const MIN_WAIT_TIMEOUT_MS: i64 = DEFAULT_MULTI_AGENT_V2_MIN_WAIT_TIMEOUT_MS;
|
||||
pub(crate) const DEFAULT_WAIT_TIMEOUT_MS: i64 = 30_000;
|
||||
pub(crate) const MAX_WAIT_TIMEOUT_MS: i64 = MAX_MULTI_AGENT_V2_WAIT_TIMEOUT_MS;
|
||||
pub(crate) const MAX_WAIT_TIMEOUT_MS: i64 = HARD_MAX_MULTI_AGENT_V2_TIMEOUT_MS;
|
||||
|
||||
pub(crate) fn function_arguments(payload: ToolPayload) -> Result<String, FunctionCallError> {
|
||||
match payload {
|
||||
|
||||
@@ -2775,7 +2775,7 @@ async fn multi_agent_v2_wait_agent_accepts_timeout_only_argument() {
|
||||
session,
|
||||
turn,
|
||||
"wait_agent",
|
||||
function_payload(json!({"timeout_ms": 1000})),
|
||||
function_payload(json!({"timeout_ms": 10_000})),
|
||||
))
|
||||
.await
|
||||
}
|
||||
@@ -2808,7 +2808,7 @@ async fn multi_agent_v2_wait_agent_accepts_timeout_only_argument() {
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn multi_agent_v2_wait_agent_uses_configured_min_timeout() {
|
||||
async fn multi_agent_v2_wait_agent_rejects_timeout_below_configured_min() {
|
||||
let (session, mut turn) = make_session_and_context().await;
|
||||
let mut config = (*turn.config).clone();
|
||||
config
|
||||
@@ -2816,6 +2816,73 @@ async fn multi_agent_v2_wait_agent_uses_configured_min_timeout() {
|
||||
.enable(Feature::MultiAgentV2)
|
||||
.expect("test config should allow feature update");
|
||||
config.multi_agent_v2.min_wait_timeout_ms = 50;
|
||||
config.multi_agent_v2.max_wait_timeout_ms = 1_000;
|
||||
config.multi_agent_v2.default_wait_timeout_ms = 50;
|
||||
turn.config = Arc::new(config);
|
||||
|
||||
let Err(err) = WaitAgentHandlerV2::default()
|
||||
.handle(invocation(
|
||||
Arc::new(session),
|
||||
Arc::new(turn),
|
||||
"wait_agent",
|
||||
function_payload(json!({"timeout_ms": 1})),
|
||||
))
|
||||
.await
|
||||
else {
|
||||
panic!("timeout below configured minimum should be rejected");
|
||||
};
|
||||
assert_eq!(
|
||||
err,
|
||||
FunctionCallError::RespondToModel("timeout_ms must be at least 50".to_string())
|
||||
);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn multi_agent_v2_wait_agent_accepts_explicit_timeout_at_configured_min() {
|
||||
let (session, mut turn) = make_session_and_context().await;
|
||||
let mut config = (*turn.config).clone();
|
||||
config
|
||||
.features
|
||||
.enable(Feature::MultiAgentV2)
|
||||
.expect("test config should allow feature update");
|
||||
config.multi_agent_v2.min_wait_timeout_ms = 1;
|
||||
config.multi_agent_v2.max_wait_timeout_ms = 1_000;
|
||||
config.multi_agent_v2.default_wait_timeout_ms = 50;
|
||||
turn.config = Arc::new(config);
|
||||
|
||||
let output = WaitAgentHandlerV2::default()
|
||||
.handle(invocation(
|
||||
Arc::new(session),
|
||||
Arc::new(turn),
|
||||
"wait_agent",
|
||||
function_payload(json!({"timeout_ms": 1})),
|
||||
))
|
||||
.await
|
||||
.expect("wait_agent should succeed");
|
||||
let (content, success) = expect_text_output(output);
|
||||
let result: crate::tools::handlers::multi_agents_v2::wait::WaitAgentResult =
|
||||
serde_json::from_str(&content).expect("wait_agent result should be json");
|
||||
assert_eq!(
|
||||
result,
|
||||
crate::tools::handlers::multi_agents_v2::wait::WaitAgentResult {
|
||||
message: "Wait timed out.".to_string(),
|
||||
timed_out: true,
|
||||
}
|
||||
);
|
||||
assert_eq!(success, None);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn multi_agent_v2_wait_agent_uses_configured_default_timeout() {
|
||||
let (session, mut turn) = make_session_and_context().await;
|
||||
let mut config = (*turn.config).clone();
|
||||
config
|
||||
.features
|
||||
.enable(Feature::MultiAgentV2)
|
||||
.expect("test config should allow feature update");
|
||||
config.multi_agent_v2.min_wait_timeout_ms = 1;
|
||||
config.multi_agent_v2.max_wait_timeout_ms = 1_000;
|
||||
config.multi_agent_v2.default_wait_timeout_ms = 50;
|
||||
turn.config = Arc::new(config);
|
||||
let session = Arc::new(session);
|
||||
let turn = Arc::new(turn);
|
||||
@@ -2826,13 +2893,13 @@ async fn multi_agent_v2_wait_agent_uses_configured_min_timeout() {
|
||||
session.clone(),
|
||||
turn.clone(),
|
||||
"wait_agent",
|
||||
function_payload(json!({"timeout_ms": 1})),
|
||||
function_payload(json!({})),
|
||||
)),
|
||||
)
|
||||
.await;
|
||||
assert!(
|
||||
early.is_err(),
|
||||
"wait_agent should not return before the configured minimum timeout"
|
||||
"wait_agent should not return before the configured default timeout"
|
||||
);
|
||||
|
||||
let output = timeout(
|
||||
@@ -2841,11 +2908,11 @@ async fn multi_agent_v2_wait_agent_uses_configured_min_timeout() {
|
||||
session,
|
||||
turn,
|
||||
"wait_agent",
|
||||
function_payload(json!({"timeout_ms": 1})),
|
||||
function_payload(json!({})),
|
||||
)),
|
||||
)
|
||||
.await
|
||||
.expect("configured minimum should be shorter than the test timeout")
|
||||
.expect("configured default should be shorter than the test timeout")
|
||||
.expect("wait_agent should succeed");
|
||||
let (content, success) = expect_text_output(output);
|
||||
let result: crate::tools::handlers::multi_agents_v2::wait::WaitAgentResult =
|
||||
@@ -2860,6 +2927,111 @@ async fn multi_agent_v2_wait_agent_uses_configured_min_timeout() {
|
||||
assert_eq!(success, None);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn multi_agent_v2_wait_agent_allows_zero_configured_timeout() {
|
||||
let (session, mut turn) = make_session_and_context().await;
|
||||
let mut config = (*turn.config).clone();
|
||||
config
|
||||
.features
|
||||
.enable(Feature::MultiAgentV2)
|
||||
.expect("test config should allow feature update");
|
||||
config.multi_agent_v2.min_wait_timeout_ms = 0;
|
||||
config.multi_agent_v2.max_wait_timeout_ms = 0;
|
||||
config.multi_agent_v2.default_wait_timeout_ms = 0;
|
||||
turn.config = Arc::new(config);
|
||||
let session = Arc::new(session);
|
||||
let turn = Arc::new(turn);
|
||||
|
||||
let output = timeout(
|
||||
Duration::from_secs(/*secs*/ 1),
|
||||
WaitAgentHandlerV2::default().handle(invocation(
|
||||
session,
|
||||
turn,
|
||||
"wait_agent",
|
||||
function_payload(json!({})),
|
||||
)),
|
||||
)
|
||||
.await
|
||||
.expect("zero timeout should complete immediately")
|
||||
.expect("wait_agent should succeed");
|
||||
let (content, success) = expect_text_output(output);
|
||||
let result: crate::tools::handlers::multi_agents_v2::wait::WaitAgentResult =
|
||||
serde_json::from_str(&content).expect("wait_agent result should be json");
|
||||
assert_eq!(
|
||||
result,
|
||||
crate::tools::handlers::multi_agents_v2::wait::WaitAgentResult {
|
||||
message: "Wait timed out.".to_string(),
|
||||
timed_out: true,
|
||||
}
|
||||
);
|
||||
assert_eq!(success, None);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn multi_agent_v2_wait_agent_rejects_timeout_above_configured_max() {
|
||||
let (session, mut turn) = make_session_and_context().await;
|
||||
let mut config = (*turn.config).clone();
|
||||
config
|
||||
.features
|
||||
.enable(Feature::MultiAgentV2)
|
||||
.expect("test config should allow feature update");
|
||||
config.multi_agent_v2.min_wait_timeout_ms = 1;
|
||||
config.multi_agent_v2.max_wait_timeout_ms = 50;
|
||||
config.multi_agent_v2.default_wait_timeout_ms = 1;
|
||||
turn.config = Arc::new(config);
|
||||
|
||||
let Err(err) = WaitAgentHandlerV2::default()
|
||||
.handle(invocation(
|
||||
Arc::new(session),
|
||||
Arc::new(turn),
|
||||
"wait_agent",
|
||||
function_payload(json!({"timeout_ms": 500})),
|
||||
))
|
||||
.await
|
||||
else {
|
||||
panic!("timeout above configured maximum should be rejected");
|
||||
};
|
||||
assert_eq!(
|
||||
err,
|
||||
FunctionCallError::RespondToModel("timeout_ms must be at most 50".to_string())
|
||||
);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn multi_agent_v2_wait_agent_accepts_explicit_timeout_at_configured_max() {
|
||||
let (session, mut turn) = make_session_and_context().await;
|
||||
let mut config = (*turn.config).clone();
|
||||
config
|
||||
.features
|
||||
.enable(Feature::MultiAgentV2)
|
||||
.expect("test config should allow feature update");
|
||||
config.multi_agent_v2.min_wait_timeout_ms = 1;
|
||||
config.multi_agent_v2.max_wait_timeout_ms = 1;
|
||||
config.multi_agent_v2.default_wait_timeout_ms = 1;
|
||||
turn.config = Arc::new(config);
|
||||
|
||||
let output = WaitAgentHandlerV2::default()
|
||||
.handle(invocation(
|
||||
Arc::new(session),
|
||||
Arc::new(turn),
|
||||
"wait_agent",
|
||||
function_payload(json!({"timeout_ms": 1})),
|
||||
))
|
||||
.await
|
||||
.expect("wait_agent should succeed");
|
||||
let (content, success) = expect_text_output(output);
|
||||
let result: crate::tools::handlers::multi_agents_v2::wait::WaitAgentResult =
|
||||
serde_json::from_str(&content).expect("wait_agent result should be json");
|
||||
assert_eq!(
|
||||
result,
|
||||
crate::tools::handlers::multi_agents_v2::wait::WaitAgentResult {
|
||||
message: "Wait timed out.".to_string(),
|
||||
timed_out: true,
|
||||
}
|
||||
);
|
||||
assert_eq!(success, None);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn wait_agent_returns_not_found_for_missing_agents() {
|
||||
let (mut session, turn) = make_session_and_context().await;
|
||||
@@ -2873,7 +3045,7 @@ async fn wait_agent_returns_not_found_for_missing_agents() {
|
||||
"wait_agent",
|
||||
function_payload(json!({
|
||||
"targets": [id_a.to_string(), id_b.to_string()],
|
||||
"timeout_ms": 1000
|
||||
"timeout_ms": 10_000
|
||||
})),
|
||||
);
|
||||
let output = WaitAgentHandler::default()
|
||||
@@ -3009,7 +3181,7 @@ async fn wait_agent_returns_final_status_without_timeout() {
|
||||
"wait_agent",
|
||||
function_payload(json!({
|
||||
"targets": [agent_id.to_string()],
|
||||
"timeout_ms": 1000
|
||||
"timeout_ms": 10_000
|
||||
})),
|
||||
);
|
||||
let output = WaitAgentHandler::default()
|
||||
@@ -3088,7 +3260,7 @@ async fn multi_agent_v2_wait_agent_returns_summary_for_mailbox_activity() {
|
||||
session,
|
||||
turn,
|
||||
"wait_agent",
|
||||
function_payload(json!({"timeout_ms": 1000})),
|
||||
function_payload(json!({"timeout_ms": 10_000})),
|
||||
))
|
||||
.await
|
||||
}
|
||||
@@ -3179,7 +3351,7 @@ async fn multi_agent_v2_wait_agent_returns_for_already_queued_mail() {
|
||||
session,
|
||||
turn,
|
||||
"wait_agent",
|
||||
function_payload(json!({"timeout_ms": 1000})),
|
||||
function_payload(json!({"timeout_ms": 10_000})),
|
||||
)),
|
||||
)
|
||||
.await
|
||||
@@ -3254,7 +3426,7 @@ async fn multi_agent_v2_wait_agent_wakes_on_any_mailbox_notification() {
|
||||
session,
|
||||
turn,
|
||||
"wait_agent",
|
||||
function_payload(json!({"timeout_ms": 1000})),
|
||||
function_payload(json!({"timeout_ms": 10_000})),
|
||||
))
|
||||
.await
|
||||
}
|
||||
@@ -3339,7 +3511,7 @@ async fn multi_agent_v2_wait_agent_does_not_return_completed_content() {
|
||||
session,
|
||||
turn,
|
||||
"wait_agent",
|
||||
function_payload(json!({"timeout_ms": 1000})),
|
||||
function_payload(json!({"timeout_ms": 10_000})),
|
||||
))
|
||||
.await
|
||||
}
|
||||
|
||||
@@ -40,19 +40,22 @@ impl ToolExecutor<ToolInvocation> for Handler {
|
||||
} = invocation;
|
||||
let arguments = function_arguments(payload)?;
|
||||
let args: WaitArgs = parse_arguments(&arguments)?;
|
||||
let timeout_ms = args.timeout_ms.unwrap_or(DEFAULT_WAIT_TIMEOUT_MS);
|
||||
let min_timeout_ms = turn
|
||||
.config
|
||||
.multi_agent_v2
|
||||
.min_wait_timeout_ms
|
||||
.clamp(1, MAX_WAIT_TIMEOUT_MS);
|
||||
let timeout_ms = match timeout_ms {
|
||||
ms if ms <= 0 => {
|
||||
return Err(FunctionCallError::RespondToModel(
|
||||
"timeout_ms must be greater than zero".to_owned(),
|
||||
));
|
||||
let min_timeout_ms = turn.config.multi_agent_v2.min_wait_timeout_ms;
|
||||
let max_timeout_ms = turn.config.multi_agent_v2.max_wait_timeout_ms;
|
||||
let default_timeout_ms = turn.config.multi_agent_v2.default_wait_timeout_ms;
|
||||
let timeout_ms = match args.timeout_ms {
|
||||
Some(ms) if ms < min_timeout_ms => {
|
||||
return Err(FunctionCallError::RespondToModel(format!(
|
||||
"timeout_ms must be at least {min_timeout_ms}"
|
||||
)));
|
||||
}
|
||||
ms => ms.clamp(min_timeout_ms, MAX_WAIT_TIMEOUT_MS),
|
||||
Some(ms) if ms > max_timeout_ms => {
|
||||
return Err(FunctionCallError::RespondToModel(format!(
|
||||
"timeout_ms must be at most {max_timeout_ms}"
|
||||
)));
|
||||
}
|
||||
Some(ms) => ms,
|
||||
None => default_timeout_ms,
|
||||
};
|
||||
|
||||
let mut mailbox_seq_rx = session.subscribe_mailbox_seq();
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
use crate::config::DEFAULT_MULTI_AGENT_V2_DEFAULT_WAIT_TIMEOUT_MS;
|
||||
use crate::config::DEFAULT_MULTI_AGENT_V2_MAX_WAIT_TIMEOUT_MS;
|
||||
use crate::config::DEFAULT_MULTI_AGENT_V2_MIN_WAIT_TIMEOUT_MS;
|
||||
use crate::shell::Shell;
|
||||
use crate::shell::ShellType;
|
||||
use crate::tools::handlers::multi_agents_common::DEFAULT_WAIT_TIMEOUT_MS;
|
||||
@@ -35,16 +38,29 @@ pub(crate) fn build_specs_with_discoverable_tools(
|
||||
) -> ToolRegistryBuilder {
|
||||
let default_agent_type_description =
|
||||
crate::agent::role::spawn_tool_spec::build(&std::collections::BTreeMap::new());
|
||||
let min_wait_timeout_ms = if config.multi_agent_v2 {
|
||||
config
|
||||
.wait_agent_min_timeout_ms
|
||||
.unwrap_or(MIN_WAIT_TIMEOUT_MS)
|
||||
.clamp(1, MAX_WAIT_TIMEOUT_MS)
|
||||
} else {
|
||||
MIN_WAIT_TIMEOUT_MS
|
||||
};
|
||||
let default_wait_timeout_ms =
|
||||
DEFAULT_WAIT_TIMEOUT_MS.clamp(min_wait_timeout_ms, MAX_WAIT_TIMEOUT_MS);
|
||||
let (min_wait_timeout_ms, max_wait_timeout_ms, default_wait_timeout_ms) =
|
||||
if config.multi_agent_v2 {
|
||||
let min_wait_timeout_ms = config
|
||||
.wait_agent_min_timeout_ms
|
||||
.unwrap_or(DEFAULT_MULTI_AGENT_V2_MIN_WAIT_TIMEOUT_MS);
|
||||
let max_wait_timeout_ms = config
|
||||
.wait_agent_max_timeout_ms
|
||||
.unwrap_or(DEFAULT_MULTI_AGENT_V2_MAX_WAIT_TIMEOUT_MS);
|
||||
let default_wait_timeout_ms = config
|
||||
.wait_agent_default_timeout_ms
|
||||
.unwrap_or(DEFAULT_MULTI_AGENT_V2_DEFAULT_WAIT_TIMEOUT_MS);
|
||||
(
|
||||
min_wait_timeout_ms,
|
||||
max_wait_timeout_ms,
|
||||
default_wait_timeout_ms,
|
||||
)
|
||||
} else {
|
||||
(
|
||||
MIN_WAIT_TIMEOUT_MS,
|
||||
MAX_WAIT_TIMEOUT_MS,
|
||||
DEFAULT_WAIT_TIMEOUT_MS,
|
||||
)
|
||||
};
|
||||
build_tool_registry_builder(
|
||||
config,
|
||||
ToolRegistryBuildParams {
|
||||
@@ -57,7 +73,7 @@ pub(crate) fn build_specs_with_discoverable_tools(
|
||||
wait_agent_timeouts: WaitAgentTimeoutOptions {
|
||||
default_timeout_ms: default_wait_timeout_ms,
|
||||
min_timeout_ms: min_wait_timeout_ms,
|
||||
max_timeout_ms: MAX_WAIT_TIMEOUT_MS,
|
||||
max_timeout_ms: max_wait_timeout_ms,
|
||||
},
|
||||
},
|
||||
)
|
||||
|
||||
@@ -743,11 +743,15 @@ async fn spawn_agent_description_uses_configured_usage_hint_text() {
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn multi_agent_v2_wait_agent_schema_uses_configured_min_timeout() {
|
||||
let wait_agent_min_timeout_ms = Some(60_000);
|
||||
async fn multi_agent_v2_wait_agent_schema_uses_configured_timeouts() {
|
||||
let wait_agent_min_timeout_ms = Some(20_000);
|
||||
let wait_agent_max_timeout_ms = Some(120_000);
|
||||
let wait_agent_default_timeout_ms = Some(60_000);
|
||||
let tools_config = multi_agent_v2_tools_config()
|
||||
.await
|
||||
.with_wait_agent_min_timeout_ms(wait_agent_min_timeout_ms);
|
||||
.with_wait_agent_min_timeout_ms(wait_agent_min_timeout_ms)
|
||||
.with_wait_agent_max_timeout_ms(wait_agent_max_timeout_ms)
|
||||
.with_wait_agent_default_timeout_ms(wait_agent_default_timeout_ms);
|
||||
let (tools, _) = build_specs(
|
||||
&tools_config,
|
||||
/*mcp_tools*/ None,
|
||||
@@ -767,7 +771,7 @@ async fn multi_agent_v2_wait_agent_schema_uses_configured_min_timeout() {
|
||||
|
||||
assert_eq!(
|
||||
timeout_description,
|
||||
Some("Optional timeout in milliseconds. Defaults to 60000, min 60000, max 3600000.")
|
||||
Some("Optional timeout in milliseconds. Defaults to 60000, min 20000, max 120000.")
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -13,9 +13,15 @@ pub struct MultiAgentV2ConfigToml {
|
||||
#[schemars(range(min = 1))]
|
||||
pub max_concurrent_threads_per_session: Option<usize>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
#[schemars(range(min = 1, max = 3600000))]
|
||||
#[schemars(range(min = 0, max = 3600000))]
|
||||
pub min_wait_timeout_ms: Option<i64>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
#[schemars(range(min = 0, max = 3600000))]
|
||||
pub max_wait_timeout_ms: Option<i64>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
#[schemars(range(min = 0, max = 3600000))]
|
||||
pub default_wait_timeout_ms: Option<i64>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub usage_hint_enabled: Option<bool>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub usage_hint_text: Option<String>,
|
||||
|
||||
@@ -482,6 +482,8 @@ fn multi_agent_v2_feature_config_deserializes_table() {
|
||||
enabled = true
|
||||
max_concurrent_threads_per_session = 4
|
||||
min_wait_timeout_ms = 2500
|
||||
max_wait_timeout_ms = 120000
|
||||
default_wait_timeout_ms = 30000
|
||||
usage_hint_enabled = false
|
||||
usage_hint_text = "Custom delegation guidance."
|
||||
root_agent_usage_hint_text = "Root guidance."
|
||||
@@ -502,6 +504,8 @@ non_code_mode_only = true
|
||||
enabled: Some(true),
|
||||
max_concurrent_threads_per_session: Some(4),
|
||||
min_wait_timeout_ms: Some(2500),
|
||||
max_wait_timeout_ms: Some(120000),
|
||||
default_wait_timeout_ms: Some(30000),
|
||||
usage_hint_enabled: Some(false),
|
||||
usage_hint_text: Some("Custom delegation guidance.".to_string()),
|
||||
root_agent_usage_hint_text: Some("Root guidance.".to_string()),
|
||||
@@ -538,6 +542,8 @@ usage_hint_enabled = false
|
||||
enabled: None,
|
||||
max_concurrent_threads_per_session: None,
|
||||
min_wait_timeout_ms: None,
|
||||
max_wait_timeout_ms: None,
|
||||
default_wait_timeout_ms: None,
|
||||
usage_hint_enabled: Some(false),
|
||||
usage_hint_text: None,
|
||||
root_agent_usage_hint_text: None,
|
||||
|
||||
@@ -123,6 +123,8 @@ pub struct ToolsConfig {
|
||||
pub spawn_agent_usage_hint_text: Option<String>,
|
||||
pub max_concurrent_threads_per_session: Option<usize>,
|
||||
pub wait_agent_min_timeout_ms: Option<i64>,
|
||||
pub wait_agent_max_timeout_ms: Option<i64>,
|
||||
pub wait_agent_default_timeout_ms: Option<i64>,
|
||||
pub request_user_input_available_modes: Vec<ModeKind>,
|
||||
pub experimental_supported_tools: Vec<String>,
|
||||
pub agent_jobs_tools: bool,
|
||||
@@ -264,6 +266,8 @@ impl ToolsConfig {
|
||||
spawn_agent_usage_hint_text: None,
|
||||
max_concurrent_threads_per_session: None,
|
||||
wait_agent_min_timeout_ms: None,
|
||||
wait_agent_max_timeout_ms: None,
|
||||
wait_agent_default_timeout_ms: None,
|
||||
request_user_input_available_modes: request_user_input_available_modes(features),
|
||||
experimental_supported_tools: model_info.experimental_supported_tools.clone(),
|
||||
agent_jobs_tools: include_agent_jobs,
|
||||
@@ -346,6 +350,22 @@ impl ToolsConfig {
|
||||
self
|
||||
}
|
||||
|
||||
pub fn with_wait_agent_max_timeout_ms(
|
||||
mut self,
|
||||
wait_agent_max_timeout_ms: Option<i64>,
|
||||
) -> Self {
|
||||
self.wait_agent_max_timeout_ms = wait_agent_max_timeout_ms;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn with_wait_agent_default_timeout_ms(
|
||||
mut self,
|
||||
wait_agent_default_timeout_ms: Option<i64>,
|
||||
) -> Self {
|
||||
self.wait_agent_default_timeout_ms = wait_agent_default_timeout_ms;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn with_allow_login_shell(mut self, allow_login_shell: bool) -> Self {
|
||||
self.allow_login_shell = allow_login_shell;
|
||||
self
|
||||
|
||||
Reference in New Issue
Block a user