diff --git a/config.example.yaml b/config.example.yaml index 01ee994..003b42a 100644 --- a/config.example.yaml +++ b/config.example.yaml @@ -31,17 +31,12 @@ function_calling: true # Enables or disables function calling (Globall mapping_tools: # Alias for a tool or toolset # fs: 'fs_cat,fs_ls,fs_mkdir,fs_rm,fs_write' use_tools: null # Which tools to use by default -# Regex for seletecting dangerous functions -# User confirmation is required when executing these functions -# e.g. 'execute_command|execute_js_code' 'execute_.*' -dangerously_functions_filter: null # Per-Agent configuration agents: - name: todo-sh model: null temperature: null top_p: null - dangerously_functions_filter: null # ---- RAG ---- rag_embedding_model: null # Specifies the embedding model to use diff --git a/src/config/agent.rs b/src/config/agent.rs index 7a065e7..7d1a43b 100644 --- a/src/config/agent.rs +++ b/src/config/agent.rs @@ -117,10 +117,6 @@ impl Agent { &self.name } - pub fn config(&self) -> &AgentConfig { - &self.config - } - pub fn functions(&self) -> &Functions { &self.functions } @@ -211,8 +207,6 @@ pub struct AgentConfig { pub top_p: Option, #[serde(skip_serializing_if = "Option::is_none")] use_tools: Option, - #[serde(skip_serializing_if = "Option::is_none")] - pub dangerously_functions_filter: Option, } impl AgentConfig { diff --git a/src/config/mod.rs b/src/config/mod.rs index 154dcd9..220b7c8 100644 --- a/src/config/mod.rs +++ b/src/config/mod.rs @@ -18,7 +18,6 @@ use crate::render::{MarkdownRender, RenderOptions}; use crate::utils::*; use anyhow::{anyhow, bail, Context, Result}; -use fancy_regex::Regex; use indexmap::IndexMap; use inquire::{Confirm, Select}; use parking_lot::RwLock; @@ -107,7 +106,6 @@ pub struct Config { pub function_calling: bool, pub mapping_tools: IndexMap, pub use_tools: Option, - pub dangerously_functions_filter: Option, pub agents: Vec, pub rag_embedding_model: Option, @@ -170,7 +168,6 @@ impl Default for Config { function_calling: true, mapping_tools: Default::default(), use_tools: None, - dangerously_functions_filter: None, agents: vec![], rag_embedding_model: None, @@ -1146,26 +1143,6 @@ impl Config { } } - pub fn is_dangerously_function(&self, name: &str) -> bool { - if get_env_bool("no_dangerously_functions") { - return false; - } - let dangerously_functions_filter = match &self.agent { - Some(agent) => agent.config().dangerously_functions_filter.as_ref(), - None => self.dangerously_functions_filter.as_ref(), - }; - match dangerously_functions_filter { - None => false, - Some(regex) => { - let regex = match Regex::new(&format!("^({regex})$")) { - Ok(v) => v, - Err(_) => return false, - }; - regex.is_match(name).unwrap_or(false) - } - } - } - pub fn buffer_editor(&self) -> Option { self.buffer_editor .clone() diff --git a/src/function.rs b/src/function.rs index 0016405..6a7e81e 100644 --- a/src/function.rs +++ b/src/function.rs @@ -5,7 +5,6 @@ use crate::{ use anyhow::{anyhow, bail, Context, Result}; use indexmap::IndexMap; -use inquire::{validator::Validation, Text}; use serde::{Deserialize, Serialize}; use serde_json::{json, Value}; use std::{ @@ -157,7 +156,6 @@ impl ToolCall { pub fn eval(&self, config: &GlobalConfig) -> Result { let function_name = self.name.clone(); - let is_dangerously = config.read().is_dangerously_function(&function_name); let (call_name, cmd_name, mut cmd_args) = match &config.read().agent { Some(agent) => { if let Some(true) = agent.functions().find(&function_name).map(|v| v.agent) { @@ -205,76 +203,28 @@ impl ToolCall { if bin_dir.exists() { envs.insert("PATH".into(), prepend_env_path(&bin_dir)?); } + let temp_file = temp_file("-eval-", ""); + envs.insert("LLM_OUTPUT".into(), temp_file.display().to_string()); #[cfg(windows)] let cmd_name = polyfill_cmd_name(&cmd_name, &bin_dir); - - let output = if is_dangerously { - if *IS_STDOUT_TERMINAL { - println!("{}", dimmed_text(&prompt)); - let answer = Text::new("[1] Run, [2] Run & Retrieve, [3] Skip:") - .with_default("1") - .with_validator(|input: &str| match matches!(input, "1" | "2" | "3") { - true => Ok(Validation::Valid), - false => Ok(Validation::Invalid( - "Invalid input, please select 1, 2 or 3".into(), - )), - }) - .prompt()?; - match answer.as_str() { - "1" => { - let exit_code = run_command(&cmd_name, &cmd_args, Some(envs))?; - if exit_code != 0 { - bail!("Exit {exit_code}"); - } - Value::Null - } - "2" => run_and_retrieve(&cmd_name, &cmd_args, envs)?, - _ => Value::Null, - } - } else { - println!("Skipped {prompt}"); - Value::Null - } - } else { - println!("{}", dimmed_text(&prompt)); - run_and_retrieve(&cmd_name, &cmd_args, envs)? - }; - - Ok(output) - } -} - -fn run_and_retrieve( - cmd_name: &str, - cmd_args: &[String], - envs: HashMap, -) -> Result { - let (success, stdout, stderr) = run_command_with_output(cmd_name, cmd_args, Some(envs))?; - - if success { - if !stderr.is_empty() { - eprintln!("{}", warning_text(&stderr)); + println!("{}", dimmed_text(&prompt)); + let exit_code = run_command(&cmd_name, &cmd_args, Some(envs))?; + if exit_code != 0 { + bail!("Tool call exit with {exit_code}"); } - let value = if !stdout.is_empty() { - serde_json::from_str(&stdout) + let output = if temp_file.exists() { + let contents = + fs::read_to_string(temp_file).context("Failed to retrieve tool call output")?; + + serde_json::from_str(&contents) .ok() - .unwrap_or_else(|| json!({"output": stdout})) + .unwrap_or_else(|| json!({"result": contents})) } else { Value::Null }; - Ok(value) - } else { - let err = if stderr.is_empty() { - if stdout.is_empty() { - "Something wrong" - } else { - &stdout - } - } else { - &stderr - }; - bail!("{err}"); + + Ok(output) } } diff --git a/src/utils/mod.rs b/src/utils/mod.rs index 40967ab..35f41a2 100644 --- a/src/utils/mod.rs +++ b/src/utils/mod.rs @@ -22,7 +22,7 @@ use anyhow::{Context, Result}; use fancy_regex::Regex; use is_terminal::IsTerminal; use lazy_static::lazy_static; -use std::{env, path::PathBuf}; +use std::{env, path::PathBuf, process}; lazy_static! { pub static ref CODE_BLOCK_RE: Regex = Regex::new(r"(?ms)```\w*(.*)```").unwrap(); @@ -42,14 +42,6 @@ pub fn get_env_name(key: &str) -> String { ) } -pub fn get_env_bool(key: &str) -> bool { - if let Ok(value) = env::var(get_env_name(key)) { - value == "1" || value == "true" - } else { - false - } -} - pub fn tokenize(text: &str) -> Vec<&str> { if text.is_ascii() { text.split_inclusive(|c: char| c.is_ascii_whitespace()) @@ -158,8 +150,9 @@ pub fn dimmed_text(input: &str) -> String { pub fn temp_file(prefix: &str, suffix: &str) -> PathBuf { env::temp_dir().join(format!( - "{}{prefix}{}{suffix}", + "{}-{}{prefix}{}{suffix}", env!("CARGO_CRATE_NAME").to_lowercase(), + process::id(), uuid::Uuid::new_v4() )) }