Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 22 additions & 1 deletion sidecar/src/agentic/tool/code_edit/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,27 @@ impl CodeEditingPartialRequest {
self.fs_file_path, self.instruction
)
}

pub fn to_json() -> serde_json::Value {
serde_json::json!({
"name": "code_edit_input",
"description": r#"Edit a file. The tool is able to edit the file precisely based on instruction. If the file doesn't exist, it will be CREATED. The tool will automatically CREATE any directories needed to write the file. BE CONCISE AND DIRECT, DO NOT BE VERBOSE IN YOUR CODEBLOCKS and only give an overview of the changes."#,
"input_schema": {
"type": "object",
"properties": {
"fs_file_path": {
"type": "string",
"description": "(required) The ABSOLUTE path of the file to write to, will be created if not already present."
},
"instruction": {
"type": "string",
"description": "(required) The edit instruction, if you are going to output code blocks make sure they are properly placed in ```{{language}} blocks and extensively use `rest of the code` and `...` placeholders, the goal is to be concise.\nOnly given instructions here which are concise and contain the relevant changes, DO NOT BE VERBOSE, BE CONCISE AND DIRECT.",
}
},
"required": ["fs_file_path", "instruction"],
},
})
}
}

#[derive(Clone, Debug)]
Expand Down Expand Up @@ -494,7 +515,7 @@ impl Tool for CodeEditingTool {

fn tool_description(&self) -> String {
"### code_edit_input
Edit a file. The tool is able to edit the file precisely based on instruction. If the file doesn't exist, it will be CREATED. The tool will automatically CREATE any directories needed to write the file. BE CONCISE AND DIRECT, DO NOT BE VERBOSE IN YOUR CODEBLOCKS and only give an overview of the chagnes.".to_owned()
Edit a file. The tool is able to edit the file precisely based on instruction. If the file doesn't exist, it will be CREATED. The tool will automatically CREATE any directories needed to write the file. BE CONCISE AND DIRECT, DO NOT BE VERBOSE IN YOUR CODEBLOCKS and only give an overview of the changes.".to_owned()
}

fn tool_input_format(&self) -> String {
Expand Down
2 changes: 1 addition & 1 deletion sidecar/src/agentic/tool/input.rs
Original file line number Diff line number Diff line change
Expand Up @@ -207,7 +207,7 @@ impl ToolInputPartial {

pub fn to_json(tool_type: ToolType) -> Option<serde_json::Value> {
match tool_type {
ToolType::CodeEditing => None,
ToolType::CodeEditing => Some(CodeEditingPartialRequest::to_json()),
ToolType::ListFiles => Some(ListFilesInputPartial::to_json()),
ToolType::SearchFileContentWithRegex => Some(SearchFileContentInputPartial::to_json()),
ToolType::OpenFile => Some(OpenFileRequestPartial::to_json()),
Expand Down
25 changes: 15 additions & 10 deletions sidecar/src/agentic/tool/session/service.rs
Original file line number Diff line number Diff line change
Expand Up @@ -434,16 +434,21 @@ impl SessionService {
)
.set_context_crunching_llm(context_crunching_llm.clone());

session = session
.human_message_tool_use(
exchange_id.to_owned(),
user_message.to_owned(),
all_files,
open_files,
shell.to_owned(),
user_context.clone(),
)
.await;
// only when it is json mode that we switch the human message
if tool_agent.is_json_mode() {
session = session.pr_description(exchange_id.to_owned(), user_message.to_owned());
} else {
session = session
.human_message_tool_use(
exchange_id.to_owned(),
user_message.to_owned(),
all_files,
open_files,
shell.to_owned(),
user_context.clone(),
)
.await;
}
let _ = self
.save_to_storage(&session, mcts_log_directory.clone())
.await;
Expand Down
30 changes: 29 additions & 1 deletion sidecar/src/agentic/tool/session/session.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1006,6 +1006,33 @@ impl Session {
self
}

// creates a message which tells the agent that this is a PR description
pub fn pr_description(mut self, exchange_id: String, human_message: String) -> Session {
let user_message = format!(
r#"<pr_description>
{human_message}
</pr_description>"#
);

// add the action node
let mut action_node = ActionNode::default_with_index(self.exchanges());
action_node = action_node
.set_message(human_message)
.update_user_context(UserContext::default());
self.action_nodes.push(action_node);

// push the exchange
let exchange = Exchange::human_chat(
exchange_id,
user_message,
UserContext::default(),
self.project_labels.to_vec(),
self.repo_ref.clone(),
);
self.exchanges.push(exchange);
self
}

pub async fn human_message_tool_use(
mut self,
exchange_id: String,
Expand Down Expand Up @@ -1332,8 +1359,9 @@ impl Session {
message_properties: SymbolEventMessageProperties,
) -> Result<AgentToolUseOutput, SymbolError> {
let mut converted_messages = vec![];
let is_json_mode = tool_use_agent.is_json_mode();
for previous_message in self.exchanges.iter() {
let converted_message = previous_message.to_conversation_message(false).await;
let converted_message = previous_message.to_conversation_message(is_json_mode).await;
if let Some(converted_message) = converted_message {
converted_messages.push(converted_message);
}
Expand Down
27 changes: 25 additions & 2 deletions sidecar/src/agentic/tool/session/tool_use_agent.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ use crate::{
session::chat::SessionChatRole,
terminal::terminal::TerminalInputPartial,
test_runner::runner::TestRunnerRequestPartial,
thinking::thinking::ThinkingPartialInput,
},
},
mcts::action_node::ActionNode,
Expand Down Expand Up @@ -348,7 +349,7 @@ pub struct ToolUseAgentProperties {
shell: String,
// keeping this disabled for now while we write out the prompts and run a few
// evals on this to measure how the performance is
_thinking: AgentThinkingMode,
thinking: AgentThinkingMode,
// if the current agent is running under a eval harness, this helps tune the system
// prompt for the agent appropriately
is_eval_run: bool,
Expand All @@ -369,7 +370,7 @@ impl ToolUseAgentProperties {
in_editor,
shell,
is_eval_run,
_thinking: thinking,
thinking,
repo_name,
aide_rules,
}
Expand Down Expand Up @@ -404,6 +405,14 @@ impl ToolUseAgent {
}
}

// should use json mode for tool calling
pub fn is_json_mode(&self) -> bool {
// right now gate it behind an eval run and only when we are doing
// tool based thinking: we provide think as a tool to the agent
self.properties.is_eval_run
&& matches!(&self.properties.thinking, AgentThinkingMode::ToolBased)
}

/// Update the temperature for the tool use agent
pub fn set_temperature(mut self, temperature: f32) -> Self {
self.temperature = temperature;
Expand Down Expand Up @@ -1398,6 +1407,20 @@ You accomplish a given task iteratively, breaking it down into clear steps and w
SymbolError::ToolError(ToolError::SerdeConversionFailed)
})?,
),
"code_edit_input" => ToolInputPartial::CodeEditing(
serde_json::from_str::<CodeEditingPartialRequest>(&tool_input).map_err(
|e| {
println!("code_edit_input::error::{:?}", e);
SymbolError::ToolError(ToolError::SerdeConversionFailed)
},
)?,
),
"Think" => ToolInputPartial::Thinking(
serde_json::from_str::<ThinkingPartialInput>(&tool_input).map_err(|e| {
println!("think::error::{:?}", e);
SymbolError::ToolError(ToolError::SerdeConversionFailed)
})?,
),
_ => {
println!("unknow tool found: {}", tool_type);
return Err(SymbolError::WrongToolOutput);
Expand Down
Loading