From 631f3c16cafdff18d7c34c5a404b4014e57b74bb Mon Sep 17 00:00:00 2001 From: Dhanji Prasanna Date: Tue, 4 Nov 2025 14:35:11 +1100 Subject: [PATCH] compact on tool call if > 90% --- crates/g3-cli/src/lib.rs | 19 +++++++++++++++++++ crates/g3-config/src/lib.rs | 3 +++ crates/g3-core/src/lib.rs | 26 ++++++++++++++++++++++++++ 3 files changed, 48 insertions(+) diff --git a/crates/g3-cli/src/lib.rs b/crates/g3-cli/src/lib.rs index 6bb1fe1..53638f8 100644 --- a/crates/g3-cli/src/lib.rs +++ b/crates/g3-cli/src/lib.rs @@ -184,6 +184,10 @@ pub struct Cli { #[arg(short, long)] pub verbose: bool, + /// Enable manual control of context compaction (disables auto-compact at 90%) + #[arg(long = "manual-compact")] + pub manual_compact: bool, + /// Show the system prompt being sent to the LLM #[arg(long)] pub show_prompt: bool, @@ -338,6 +342,11 @@ pub async fn run() -> Result<()> { config.webdriver.enabled = true; } + // Apply no-auto-compact flag override + if cli.manual_compact { + config.agent.auto_compact = false; + } + // Validate provider if specified if let Some(ref provider) = cli.provider { let valid_providers = ["anthropic", "databricks", "embedded", "openai"]; @@ -558,6 +567,11 @@ async fn run_accumulative_mode( config.webdriver.enabled = true; } + // Apply no-auto-compact flag override + if cli.manual_compact { + config.agent.auto_compact = false; + } + // Create agent for interactive mode with requirements context let ui_writer = ConsoleUiWriter::new(); let agent = Agent::new_with_readme_and_quiet( @@ -635,6 +649,11 @@ async fn run_accumulative_mode( config.webdriver.enabled = true; } + // Apply no-auto-compact flag override + if cli.manual_compact { + config.agent.auto_compact = false; + } + // Create agent for this autonomous run let ui_writer = ConsoleUiWriter::new(); let agent = Agent::new_autonomous_with_readme_and_quiet( diff --git a/crates/g3-config/src/lib.rs b/crates/g3-config/src/lib.rs index 272367d..d9f0602 100644 --- a/crates/g3-config/src/lib.rs +++ b/crates/g3-config/src/lib.rs @@ -65,6 +65,7 @@ pub struct AgentConfig { pub max_context_length: usize, pub enable_streaming: bool, pub timeout_seconds: u64, + pub auto_compact: bool, } #[derive(Debug, Clone, Serialize, Deserialize)] @@ -135,6 +136,7 @@ impl Default for Config { max_context_length: 8192, enable_streaming: true, timeout_seconds: 60, + auto_compact: true, }, computer_control: ComputerControlConfig::default(), webdriver: WebDriverConfig::default(), @@ -250,6 +252,7 @@ impl Config { max_context_length: 8192, enable_streaming: true, timeout_seconds: 60, + auto_compact: true, }, computer_control: ComputerControlConfig::default(), webdriver: WebDriverConfig::default(), diff --git a/crates/g3-core/src/lib.rs b/crates/g3-core/src/lib.rs index 79c3bd4..e0d5a57 100644 --- a/crates/g3-core/src/lib.rs +++ b/crates/g3-core/src/lib.rs @@ -681,6 +681,8 @@ pub struct Agent { providers: ProviderRegistry, context_window: ContextWindow, thinning_events: Vec, // chars saved per thinning event + pending_90_summarization: bool, // flag to trigger summarization at 90% + auto_compact: bool, // whether to auto-compact at 90% before tool calls summarization_events: Vec, // chars saved per summarization event first_token_times: Vec, // time to first token for each completion config: Config, @@ -894,6 +896,8 @@ impl Agent { Ok(Self { providers, context_window, + auto_compact: config.agent.auto_compact, + pending_90_summarization: false, thinning_events: Vec::new(), summarization_events: Vec::new(), first_token_times: Vec::new(), @@ -1339,6 +1343,19 @@ Template: // Save context window at the end of successful interaction self.save_context_window("completed"); + // Check if we need to do 90% auto-compaction + if self.pending_90_summarization { + self.ui_writer.print_context_status( + "\n⚡ Context window reached 90% - auto-compacting...\n" + ); + if let Err(e) = self.force_summarize().await { + warn!("Failed to auto-compact at 90%: {}", e); + } else { + self.ui_writer.println(""); + } + self.pending_90_summarization = false; + } + // Return the task result which already includes timing if needed Ok(task_result) } @@ -2635,6 +2652,14 @@ Template: if let Some(tool_call) = completed_tools.into_iter().next() { debug!("Processing completed tool call: {:?}", tool_call); + // Check if we should auto-compact at 90% BEFORE executing the tool + // We need to do this before any borrows of self + if self.auto_compact && self.context_window.percentage_used() >= 90.0 { + // Set flag to trigger summarization after this turn completes + // We can't do it now due to borrow checker constraints + self.pending_90_summarization = true; + } + // Check if we should thin the context BEFORE executing the tool if self.context_window.should_thin() { let (thin_summary, chars_saved) = self.context_window.thin_context(); @@ -2643,6 +2668,7 @@ Template: self.ui_writer.print_context_thinning(&thin_summary); } + // Track what we've already displayed before getting new text // This prevents re-displaying old content after tool execution let already_displayed_chars = current_response.chars().count();