From 1e07fc0b0993f5b95147547b970f03e90129d049 Mon Sep 17 00:00:00 2001 From: Bryan Helmkamp Date: Sun, 1 Mar 2026 12:54:23 -0500 Subject: [PATCH] Bump Anthropic max_tokens default and add per-node max_tokens override Raise the Anthropic adapter fallback from 4096 to 16384 to prevent truncation of large tool call JSON when the model isn't in the catalog. Add max_tokens as a configurable DOT node attribute that flows through SessionConfig to LLM requests, following the same pattern as reasoning_effort. Priority: node attribute > catalog > provider default. Ported from kilroy (danshapiro/kilroy) commits 99a5cd7 and 78fadad. Co-Authored-By: Claude Opus 4.6 --- crates/arc-agent/src/config.rs | 5 +++++ crates/arc-agent/src/session.rs | 5 +++-- crates/arc-llm/src/providers/anthropic.rs | 2 +- crates/arc-workflows/src/cli/backend.rs | 4 +++- crates/arc-workflows/src/graph/types.rs | 7 ++++++- 5 files changed, 18 insertions(+), 5 deletions(-) diff --git a/crates/arc-agent/src/config.rs b/crates/arc-agent/src/config.rs index ab9255774..64358c1f1 100644 --- a/crates/arc-agent/src/config.rs +++ b/crates/arc-agent/src/config.rs @@ -17,6 +17,9 @@ pub struct SessionConfig { pub reasoning_effort: Option, pub tool_output_limits: HashMap, pub tool_line_limits: HashMap, + /// Override the provider's default max_tokens when set. + /// Node-level attribute takes priority over the model catalog default. + pub max_tokens: Option, pub enable_loop_detection: bool, pub loop_detection_window: usize, pub max_subagent_depth: usize, @@ -45,6 +48,7 @@ impl std::fmt::Debug for SessionConfig { &self.default_command_timeout_ms, ) .field("max_command_timeout_ms", &self.max_command_timeout_ms) + .field("max_tokens", &self.max_tokens) .field("reasoning_effort", &self.reasoning_effort) .field("tool_output_limits", &self.tool_output_limits) .field("tool_line_limits", &self.tool_line_limits) @@ -73,6 +77,7 @@ impl Default for SessionConfig { max_tool_rounds_per_input: 200, default_command_timeout_ms: 10_000, max_command_timeout_ms: 600_000, + max_tokens: None, reasoning_effort: None, tool_output_limits: HashMap::new(), tool_line_limits: HashMap::new(), diff --git a/crates/arc-agent/src/session.rs b/crates/arc-agent/src/session.rs index 1de97ac8c..31a706395 100644 --- a/crates/arc-agent/src/session.rs +++ b/crates/arc-agent/src/session.rs @@ -622,8 +622,9 @@ impl Session { response_format: None, temperature: None, top_p: None, - max_tokens: arc_llm::catalog::get_model_info(self.provider_profile.model()) - .and_then(|m| m.max_output), + max_tokens: self.config.max_tokens.or_else(|| + arc_llm::catalog::get_model_info(self.provider_profile.model()) + .and_then(|m| m.max_output)), stop_sequences: None, reasoning_effort: self.config.reasoning_effort.clone(), metadata: None, diff --git a/crates/arc-llm/src/providers/anthropic.rs b/crates/arc-llm/src/providers/anthropic.rs index 77124388c..138794118 100644 --- a/crates/arc-llm/src/providers/anthropic.rs +++ b/crates/arc-llm/src/providers/anthropic.rs @@ -1066,7 +1066,7 @@ fn build_api_request( let api_request = ApiRequest { model: request.model.clone(), messages: api_messages, - max_tokens: request.max_tokens.unwrap_or(4096), + max_tokens: request.max_tokens.unwrap_or(16384), system: system_value, temperature: request.temperature, top_p: request.top_p, diff --git a/crates/arc-workflows/src/cli/backend.rs b/crates/arc-workflows/src/cli/backend.rs index 04975bdaf..b25646861 100644 --- a/crates/arc-workflows/src/cli/backend.rs +++ b/crates/arc-workflows/src/cli/backend.rs @@ -59,6 +59,7 @@ impl AgentBackend { let mut profile = self.build_profile(); let config = SessionConfig { + max_tokens: node.max_tokens(), reasoning_effort: Some(node.reasoning_effort().to_string()), ..SessionConfig::default() }; @@ -131,7 +132,8 @@ impl CodergenBackend for AgentBackend { .map(String::from) .or_else(|| Some(self.provider.as_str().to_string())); - let max_tokens = arc_llm::catalog::get_model_info(model).and_then(|m| m.max_output); + let max_tokens = node.max_tokens().or_else(|| + arc_llm::catalog::get_model_info(model).and_then(|m| m.max_output)); let request = arc_llm::types::Request { model: model.to_string(), diff --git a/crates/arc-workflows/src/graph/types.rs b/crates/arc-workflows/src/graph/types.rs index 3d440a830..d9fb8ff09 100644 --- a/crates/arc-workflows/src/graph/types.rs +++ b/crates/arc-workflows/src/graph/types.rs @@ -205,7 +205,12 @@ impl Node { self.str_attr("llm_provider") } - #[must_use] + #[must_use] + pub fn max_tokens(&self) -> Option { + self.int_attr("max_tokens").filter(|&v| v > 0) + } + + #[must_use] pub fn reasoning_effort(&self) -> &str { self.str_attr("reasoning_effort").unwrap_or("high") }