Skip to content

Commit 4ed2ff3

Browse files
committed
[sidecar] add think tool
1 parent b10a58e commit 4ed2ff3

File tree

8 files changed

+89
-45
lines changed

8 files changed

+89
-45
lines changed

sidecar/src/agentic/tool/input.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,7 @@ use super::{
8686
swe_bench::test_tool::SWEBenchTestRequest,
8787
terminal::terminal::{TerminalInput, TerminalInputPartial},
8888
test_runner::runner::{TestRunnerRequest, TestRunnerRequestPartial},
89+
thinking::thinking::ThinkingPartialInput,
8990
};
9091

9192
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
@@ -107,6 +108,7 @@ pub enum ToolInputPartial {
107108
RequestScreenshot(RequestScreenshotInputPartial),
108109
ContextCrunching(ContextCrunchingInputPartial),
109110
McpTool(McpToolPartial),
111+
Thinking(ThinkingPartialInput),
110112
}
111113

112114
impl ToolInputPartial {
@@ -129,6 +131,7 @@ impl ToolInputPartial {
129131
Self::RequestScreenshot(_) => ToolType::RequestScreenshot,
130132
Self::ContextCrunching(_) => ToolType::ContextCrunching,
131133
Self::McpTool(partial) => ToolType::McpTool(partial.full_name.clone()),
134+
Self::Thinking(_) => ToolType::Think,
132135
}
133136
}
134137

@@ -157,6 +160,7 @@ impl ToolInputPartial {
157160
Self::RequestScreenshot(request_screenshot) => request_screenshot.to_string(),
158161
Self::ContextCrunching(context_crunching) => context_crunching.to_string(),
159162
Self::McpTool(mcp_partial) => mcp_partial.to_string(),
163+
Self::Thinking(thinking_partial) => thinking_partial.to_string(),
160164
}
161165
}
162166

@@ -197,6 +201,7 @@ impl ToolInputPartial {
197201
serde_json::to_value(context_crunching).ok()
198202
}
199203
Self::McpTool(mcp_partial) => serde_json::to_value(mcp_partial).ok(),
204+
Self::Thinking(thinking_partial) => serde_json::to_value(thinking_partial).ok(),
200205
}
201206
}
202207

@@ -215,6 +220,7 @@ impl ToolInputPartial {
215220
ToolType::CodeEditorTool => Some(CodeEditorParameters::to_json()),
216221
ToolType::RequestScreenshot => Some(RequestScreenshotInputPartial::to_json()),
217222
ToolType::McpTool(_name) => None,
223+
ToolType::Think => Some(ThinkingPartialInput::to_json()),
218224
_ => None,
219225
}
220226
}

sidecar/src/agentic/tool/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,4 +43,5 @@ pub mod session;
4343
pub mod swe_bench;
4444
pub mod terminal;
4545
pub mod test_runner;
46+
pub mod thinking;
4647
pub mod r#type;

sidecar/src/agentic/tool/session/service.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1287,6 +1287,9 @@ impl SessionService {
12871287
tool_type.to_string().bright_white().to_string()
12881288
}
12891289
ToolInputPartial::McpTool(_) => tool_type.to_string().cyan().to_string(),
1290+
ToolInputPartial::Thinking(_) => {
1291+
tool_type.to_string().bright_blue().to_string()
1292+
}
12901293
};
12911294
state_params.push(tool_str);
12921295
}

sidecar/src/agentic/tool/session/session.rs

Lines changed: 31 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1100,12 +1100,12 @@ impl Session {
11001100
Ok(self)
11011101
}
11021102

1103-
pub async fn move_to_checkpoint(
1104-
mut self,
1105-
exchange_id: &str,
1106-
) -> Result<Self, SymbolError> {
1103+
pub async fn move_to_checkpoint(mut self, exchange_id: &str) -> Result<Self, SymbolError> {
11071104
// Find the index of the target exchange
1108-
let target_index = self.exchanges.iter().position(|exchange| &exchange.exchange_id == exchange_id);
1105+
let target_index = self
1106+
.exchanges
1107+
.iter()
1108+
.position(|exchange| &exchange.exchange_id == exchange_id);
11091109

11101110
if let Some(target_index) = target_index {
11111111
// Mark exchanges based on their position relative to the checkpoint
@@ -2365,9 +2365,15 @@ impl Session {
23652365
}
23662366

23672367
pub fn truncate_hidden_exchanges(&mut self) {
2368-
println!("session::truncate_hidden_exchanges::before({})", self.exchanges.len());
2368+
println!(
2369+
"session::truncate_hidden_exchanges::before({})",
2370+
self.exchanges.len()
2371+
);
23692372
self.exchanges.retain(|exchange| !exchange.is_hidden);
2370-
println!("session::truncate_hidden_exchanges::after({})", self.exchanges.len());
2373+
println!(
2374+
"session::truncate_hidden_exchanges::after({})",
2375+
self.exchanges.len()
2376+
);
23712377
}
23722378

23732379
pub fn has_running_code_edits(&self, exchange_id: &str) -> bool {
@@ -3055,6 +3061,23 @@ reason: {}"#,
30553061
ToolInputPartial::Reasoning(_) => {
30563062
// we do not call this as a tool explicitly
30573063
}
3064+
ToolInputPartial::Thinking(_) => {
3065+
// we don't do any tool calling here but take the input of the thought
3066+
// and store it as part of our observation, not even sending the UI event
3067+
// here since we are running stateless from now on
3068+
3069+
if let Some(action_node) = self.action_nodes.last_mut() {
3070+
action_node.add_observation_mut("Your thought has been logged.".to_owned());
3071+
action_node.set_time_taken_seconds(tool_use_time_taken.elapsed().as_secs_f32());
3072+
}
3073+
self = self.tool_output(
3074+
&exchange_id,
3075+
tool_type.clone(),
3076+
"Your thought has been logged".to_owned(),
3077+
UserContext::default(),
3078+
exchange_id.to_owned(),
3079+
);
3080+
}
30583081
ToolInputPartial::RequestScreenshot(_) => {
30593082
println!("request_screenshot");
30603083
let request_screenshot_input =
@@ -3171,4 +3194,4 @@ reason: {}"#,
31713194
let serialized = serde_json::to_string(self).unwrap();
31723195
Self::atomic_file_operation(self.storage_path(), serialized).await
31733196
}
3174-
}
3197+
}
Lines changed: 41 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1,42 +1,49 @@
1-
//! The thinking tool for the agent, where it gets to explore a bit about the problem
2-
//! space and come up with plans
1+
//! The thinking tool allows the LLM to log a thought for itself
2+
//! This can be extremely useful when forcing the agent to think explicitly
33
4-
use std::sync::Arc;
5-
6-
use async_trait::async_trait;
7-
use llm_client::broker::LLMBroker;
4+
/// Helps with logging the thought from the LLM and nothing more than that
5+
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
6+
pub struct ThinkingPartialInput {
7+
thought: String,
8+
}
89

9-
use crate::agentic::{
10-
symbol::identifier::LLMProperties,
11-
tool::{errors::ToolError, input::ToolInput, output::ToolOutput, r#type::Tool},
12-
};
10+
impl ThinkingPartialInput {
11+
pub fn thought(&self) -> &str {
12+
&self.thought
13+
}
1314

14-
pub struct BeforeCodeEditThinkingRequest {
15-
llm_properties: LLMProperties,
16-
original_user_query: String,
17-
plan: String,
18-
symbol_content: String,
19-
content_prefix: String,
20-
context_suffix: String,
21-
}
15+
pub fn to_string(&self) -> String {
16+
format!(
17+
r#"<thought>
18+
{}
19+
</thought>"#,
20+
self.thought
21+
)
22+
}
2223

23-
// This probably needs to run in a loop kind of, cause we want to either exhaust
24-
// the search space or stop at some point, if we keep varying this to an extent
25-
// we should be able to get all the information
26-
// we really need to start keeping history somewhere
27-
pub struct BeforeCodeEditThinkingResponse {
28-
// we will probably get symbols which we have to ask questions to
29-
questions_to_ask: Vec<()>,
30-
steps_to_take_after: Vec<()>,
31-
}
24+
pub fn to_json() -> serde_json::Value {
25+
serde_json::json!({
26+
"name": "Think",
27+
"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.
3228
33-
pub struct Thinking {
34-
llm_broker: Arc<LLMBroker>,
35-
}
29+
Common use cases:
30+
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
31+
2. After receiving test results, use this tool to brainstorm ways to fix failing tests
32+
3. When planning a complex refactoring, use this tool to outline different approaches and their tradeoffs
33+
4. When designing a new feature, use this tool to think through architecture decisions and implementation details
34+
5. When debugging a complex issue, use this tool to organize your thoughts and hypotheses
3635
37-
#[async_trait]
38-
impl Tool for Thinking {
39-
async fn invoke(&self, input: ToolInput) -> Result<ToolOutput, ToolError> {
40-
todo!("")
36+
The tool simply logs your thought process for better transparency and does not execute any code or make changes."#,
37+
"input_schema": {
38+
"type": "object",
39+
"properties": {
40+
"thought": {
41+
"type": "string",
42+
"description": "(required) Your thoughts."
43+
}
44+
},
45+
"required": ["thought"],
46+
},
47+
})
4148
}
4249
}

sidecar/src/agentic/tool/type.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -158,8 +158,8 @@ pub enum ToolType {
158158
RequestScreenshot,
159159
// Context crunching
160160
ContextCrunching,
161-
// Think tool
162-
ThinkTool,
161+
// Think tool, helps log a thought
162+
Think,
163163
// dynamically configured MCP servers
164164
McpTool(String),
165165
}
@@ -266,7 +266,7 @@ impl std::fmt::Display for ToolType {
266266
ToolType::FindFiles => write!(f, "find_file"),
267267
ToolType::RequestScreenshot => write!(f, "request_screenshot"),
268268
ToolType::ContextCrunching => write!(f, "context_crunching"),
269-
ToolType::ThinkTool => write!(f, "think_tool"),
269+
ToolType::Think => write!(f, "Think"),
270270
ToolType::McpTool(name) => write!(f, "{}", name),
271271
}
272272
}

sidecar/src/mcts/action_node.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2009,6 +2009,9 @@ impl SearchTree {
20092009
tool_type.to_string().bright_white().to_string()
20102010
}
20112011
ToolInputPartial::McpTool(_) => tool_type.to_string().cyan().to_string(),
2012+
ToolInputPartial::Thinking(_) => {
2013+
tool_type.to_string().bright_blue().to_string()
2014+
}
20122015
};
20132016
state_params.push(tool_str);
20142017
}

sidecar/src/mcts/execution/inference.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -576,6 +576,7 @@ Always include the <thinking></thinking> section before using the tool."#
576576
// we never hit this branch for ask followup
577577
Err(InferenceError::WrongToolOutput)
578578
}
579+
ToolInputPartial::Thinking(_) => Err(InferenceError::WrongToolOutput),
579580
ToolInputPartial::AttemptCompletion(attemp_completion) => {
580581
let message = attemp_completion.to_string();
581582
Ok(ActionObservation::new(

0 commit comments

Comments
 (0)