From 4ed2ff3f3aae38fc34fc23fdb137d6f34c62e7ef Mon Sep 17 00:00:00 2001 From: skcd Date: Sun, 2 Mar 2025 17:40:12 +0530 Subject: [PATCH] [sidecar] add think tool --- sidecar/src/agentic/tool/input.rs | 6 ++ sidecar/src/agentic/tool/mod.rs | 1 + sidecar/src/agentic/tool/session/service.rs | 3 + sidecar/src/agentic/tool/session/session.rs | 39 ++++++++-- sidecar/src/agentic/tool/thinking/thinking.rs | 75 ++++++++++--------- sidecar/src/agentic/tool/type.rs | 6 +- sidecar/src/mcts/action_node.rs | 3 + sidecar/src/mcts/execution/inference.rs | 1 + 8 files changed, 89 insertions(+), 45 deletions(-) diff --git a/sidecar/src/agentic/tool/input.rs b/sidecar/src/agentic/tool/input.rs index aa0dd0cb4..9d0ee9254 100644 --- a/sidecar/src/agentic/tool/input.rs +++ b/sidecar/src/agentic/tool/input.rs @@ -86,6 +86,7 @@ use super::{ swe_bench::test_tool::SWEBenchTestRequest, terminal::terminal::{TerminalInput, TerminalInputPartial}, test_runner::runner::{TestRunnerRequest, TestRunnerRequestPartial}, + thinking::thinking::ThinkingPartialInput, }; #[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] @@ -107,6 +108,7 @@ pub enum ToolInputPartial { RequestScreenshot(RequestScreenshotInputPartial), ContextCrunching(ContextCrunchingInputPartial), McpTool(McpToolPartial), + Thinking(ThinkingPartialInput), } impl ToolInputPartial { @@ -129,6 +131,7 @@ impl ToolInputPartial { Self::RequestScreenshot(_) => ToolType::RequestScreenshot, Self::ContextCrunching(_) => ToolType::ContextCrunching, Self::McpTool(partial) => ToolType::McpTool(partial.full_name.clone()), + Self::Thinking(_) => ToolType::Think, } } @@ -157,6 +160,7 @@ impl ToolInputPartial { Self::RequestScreenshot(request_screenshot) => request_screenshot.to_string(), Self::ContextCrunching(context_crunching) => context_crunching.to_string(), Self::McpTool(mcp_partial) => mcp_partial.to_string(), + Self::Thinking(thinking_partial) => thinking_partial.to_string(), } } @@ -197,6 +201,7 @@ impl ToolInputPartial { serde_json::to_value(context_crunching).ok() } Self::McpTool(mcp_partial) => serde_json::to_value(mcp_partial).ok(), + Self::Thinking(thinking_partial) => serde_json::to_value(thinking_partial).ok(), } } @@ -215,6 +220,7 @@ impl ToolInputPartial { ToolType::CodeEditorTool => Some(CodeEditorParameters::to_json()), ToolType::RequestScreenshot => Some(RequestScreenshotInputPartial::to_json()), ToolType::McpTool(_name) => None, + ToolType::Think => Some(ThinkingPartialInput::to_json()), _ => None, } } diff --git a/sidecar/src/agentic/tool/mod.rs b/sidecar/src/agentic/tool/mod.rs index 02bc45a0a..f06712951 100644 --- a/sidecar/src/agentic/tool/mod.rs +++ b/sidecar/src/agentic/tool/mod.rs @@ -43,4 +43,5 @@ pub mod session; pub mod swe_bench; pub mod terminal; pub mod test_runner; +pub mod thinking; pub mod r#type; diff --git a/sidecar/src/agentic/tool/session/service.rs b/sidecar/src/agentic/tool/session/service.rs index 2838c1ac8..d978b8f72 100644 --- a/sidecar/src/agentic/tool/session/service.rs +++ b/sidecar/src/agentic/tool/session/service.rs @@ -1287,6 +1287,9 @@ impl SessionService { tool_type.to_string().bright_white().to_string() } ToolInputPartial::McpTool(_) => tool_type.to_string().cyan().to_string(), + ToolInputPartial::Thinking(_) => { + tool_type.to_string().bright_blue().to_string() + } }; state_params.push(tool_str); } diff --git a/sidecar/src/agentic/tool/session/session.rs b/sidecar/src/agentic/tool/session/session.rs index 3c91bca44..c32839348 100644 --- a/sidecar/src/agentic/tool/session/session.rs +++ b/sidecar/src/agentic/tool/session/session.rs @@ -1100,12 +1100,12 @@ impl Session { Ok(self) } - pub async fn move_to_checkpoint( - mut self, - exchange_id: &str, - ) -> Result { + pub async fn move_to_checkpoint(mut self, exchange_id: &str) -> Result { // Find the index of the target exchange - let target_index = self.exchanges.iter().position(|exchange| &exchange.exchange_id == exchange_id); + let target_index = self + .exchanges + .iter() + .position(|exchange| &exchange.exchange_id == exchange_id); if let Some(target_index) = target_index { // Mark exchanges based on their position relative to the checkpoint @@ -2365,9 +2365,15 @@ impl Session { } pub fn truncate_hidden_exchanges(&mut self) { - println!("session::truncate_hidden_exchanges::before({})", self.exchanges.len()); + println!( + "session::truncate_hidden_exchanges::before({})", + self.exchanges.len() + ); self.exchanges.retain(|exchange| !exchange.is_hidden); - println!("session::truncate_hidden_exchanges::after({})", self.exchanges.len()); + println!( + "session::truncate_hidden_exchanges::after({})", + self.exchanges.len() + ); } pub fn has_running_code_edits(&self, exchange_id: &str) -> bool { @@ -3055,6 +3061,23 @@ reason: {}"#, ToolInputPartial::Reasoning(_) => { // we do not call this as a tool explicitly } + ToolInputPartial::Thinking(_) => { + // we don't do any tool calling here but take the input of the thought + // and store it as part of our observation, not even sending the UI event + // here since we are running stateless from now on + + if let Some(action_node) = self.action_nodes.last_mut() { + action_node.add_observation_mut("Your thought has been logged.".to_owned()); + action_node.set_time_taken_seconds(tool_use_time_taken.elapsed().as_secs_f32()); + } + self = self.tool_output( + &exchange_id, + tool_type.clone(), + "Your thought has been logged".to_owned(), + UserContext::default(), + exchange_id.to_owned(), + ); + } ToolInputPartial::RequestScreenshot(_) => { println!("request_screenshot"); let request_screenshot_input = @@ -3171,4 +3194,4 @@ reason: {}"#, let serialized = serde_json::to_string(self).unwrap(); Self::atomic_file_operation(self.storage_path(), serialized).await } -} \ No newline at end of file +} diff --git a/sidecar/src/agentic/tool/thinking/thinking.rs b/sidecar/src/agentic/tool/thinking/thinking.rs index c679c2309..4deefc674 100644 --- a/sidecar/src/agentic/tool/thinking/thinking.rs +++ b/sidecar/src/agentic/tool/thinking/thinking.rs @@ -1,42 +1,49 @@ -//! The thinking tool for the agent, where it gets to explore a bit about the problem -//! space and come up with plans +//! The thinking tool allows the LLM to log a thought for itself +//! This can be extremely useful when forcing the agent to think explicitly -use std::sync::Arc; - -use async_trait::async_trait; -use llm_client::broker::LLMBroker; +/// Helps with logging the thought from the LLM and nothing more than that +#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] +pub struct ThinkingPartialInput { + thought: String, +} -use crate::agentic::{ - symbol::identifier::LLMProperties, - tool::{errors::ToolError, input::ToolInput, output::ToolOutput, r#type::Tool}, -}; +impl ThinkingPartialInput { + pub fn thought(&self) -> &str { + &self.thought + } -pub struct BeforeCodeEditThinkingRequest { - llm_properties: LLMProperties, - original_user_query: String, - plan: String, - symbol_content: String, - content_prefix: String, - context_suffix: String, -} + pub fn to_string(&self) -> String { + format!( + r#" +{} +"#, + self.thought + ) + } -// This probably needs to run in a loop kind of, cause we want to either exhaust -// the search space or stop at some point, if we keep varying this to an extent -// we should be able to get all the information -// we really need to start keeping history somewhere -pub struct BeforeCodeEditThinkingResponse { - // we will probably get symbols which we have to ask questions to - questions_to_ask: Vec<()>, - steps_to_take_after: Vec<()>, -} + pub fn to_json() -> serde_json::Value { + serde_json::json!({ + "name": "Think", + "description": r#"Use the tool to think about something. It will not obtain new information or make any changes to the repository, but just log the thought. Use it when complex reasoning or brainstorming is needed. -pub struct Thinking { - llm_broker: Arc, -} +Common use cases: +1. When exploring a repository and discovering the source of a bug, call this tool to brainstorm several unique ways of fixing the bug, and assess which change(s) are likely to be simplest and most effective +2. After receiving test results, use this tool to brainstorm ways to fix failing tests +3. When planning a complex refactoring, use this tool to outline different approaches and their tradeoffs +4. When designing a new feature, use this tool to think through architecture decisions and implementation details +5. When debugging a complex issue, use this tool to organize your thoughts and hypotheses -#[async_trait] -impl Tool for Thinking { - async fn invoke(&self, input: ToolInput) -> Result { - todo!("") +The tool simply logs your thought process for better transparency and does not execute any code or make changes."#, + "input_schema": { + "type": "object", + "properties": { + "thought": { + "type": "string", + "description": "(required) Your thoughts." + } + }, + "required": ["thought"], + }, + }) } } diff --git a/sidecar/src/agentic/tool/type.rs b/sidecar/src/agentic/tool/type.rs index 8943d1cd8..33b7ec3fa 100644 --- a/sidecar/src/agentic/tool/type.rs +++ b/sidecar/src/agentic/tool/type.rs @@ -158,8 +158,8 @@ pub enum ToolType { RequestScreenshot, // Context crunching ContextCrunching, - // Think tool - ThinkTool, + // Think tool, helps log a thought + Think, // dynamically configured MCP servers McpTool(String), } @@ -266,7 +266,7 @@ impl std::fmt::Display for ToolType { ToolType::FindFiles => write!(f, "find_file"), ToolType::RequestScreenshot => write!(f, "request_screenshot"), ToolType::ContextCrunching => write!(f, "context_crunching"), - ToolType::ThinkTool => write!(f, "think_tool"), + ToolType::Think => write!(f, "Think"), ToolType::McpTool(name) => write!(f, "{}", name), } } diff --git a/sidecar/src/mcts/action_node.rs b/sidecar/src/mcts/action_node.rs index e2f662d1b..f45665917 100644 --- a/sidecar/src/mcts/action_node.rs +++ b/sidecar/src/mcts/action_node.rs @@ -2009,6 +2009,9 @@ impl SearchTree { tool_type.to_string().bright_white().to_string() } ToolInputPartial::McpTool(_) => tool_type.to_string().cyan().to_string(), + ToolInputPartial::Thinking(_) => { + tool_type.to_string().bright_blue().to_string() + } }; state_params.push(tool_str); } diff --git a/sidecar/src/mcts/execution/inference.rs b/sidecar/src/mcts/execution/inference.rs index b30d24f12..450b7094a 100644 --- a/sidecar/src/mcts/execution/inference.rs +++ b/sidecar/src/mcts/execution/inference.rs @@ -576,6 +576,7 @@ Always include the section before using the tool."# // we never hit this branch for ask followup Err(InferenceError::WrongToolOutput) } + ToolInputPartial::Thinking(_) => Err(InferenceError::WrongToolOutput), ToolInputPartial::AttemptCompletion(attemp_completion) => { let message = attemp_completion.to_string(); Ok(ActionObservation::new(