From bd9a6a87254bb2748beb9cd3b58ea15be6c9686c Mon Sep 17 00:00:00 2001 From: sigoden Date: Wed, 27 Mar 2024 10:02:09 +0800 Subject: feat: `save_session` config item can be null (#380) --- README.md | 2 +- config.example.yaml | 2 +- src/cli.rs | 2 +- src/config/mod.rs | 100 +++++++++++++++++++++++++++++++------------------- src/config/session.rs | 25 ++++++------- src/main.rs | 2 +- 6 files changed, 79 insertions(+), 54 deletions(-) diff --git a/README.md b/README.md index f5b2fab..a7139fb 100644 --- a/README.md +++ b/README.md @@ -82,7 +82,7 @@ Feel free to adjust the configuration according to your needs. model: openai:gpt-3.5-turbo # LLM model temperature: 1.0 # LLM temperature save: true # Whether to save the message -save_session: false # Whether to save the session automatically +save_session: null # Whether to save the session, if null, asking highlight: true # Set false to turn highlight light_theme: false # Whether to use a light theme wrap: no # Specify the text-wrapping mode (no, auto, ) diff --git a/config.example.yaml b/config.example.yaml index 37af28c..3e1eb59 100644 --- a/config.example.yaml +++ b/config.example.yaml @@ -1,7 +1,7 @@ model: openai:gpt-3.5-turbo # LLM model temperature: 1.0 # LLM temperature save: true # Whether to save the message -save_session: false # Whether to save the session automatically +save_session: null # Whether to save the session, if null, asking highlight: true # Set false to turn highlight light_theme: false # Whether to use a light theme wrap: no # Specify the text-wrapping mode (no, auto, ) diff --git a/src/cli.rs b/src/cli.rs index 344e2a6..0bab3e0 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -12,7 +12,7 @@ pub struct Cli { /// Create or reuse a session #[clap(short = 's', long)] pub session: Option>, - /// Whether to save the session automatically + /// Whether to save the session #[clap(long)] pub save_session: bool, /// Execute commands using natural language diff --git a/src/config/mod.rs b/src/config/mod.rs index 0254365..cd857b8 100644 --- a/src/config/mod.rs +++ b/src/config/mod.rs @@ -52,8 +52,8 @@ pub struct Config { pub dry_run: bool, /// Whether to save the message pub save: bool, - /// Whether to save the session automatically - pub save_session: bool, + /// Whether to save the session + pub save_session: Option, /// Whether to disable highlight pub highlight: bool, /// Whether to use a light theme @@ -101,6 +101,7 @@ impl Default for Config { model_id: None, temperature: None, save: true, + save_session: None, highlight: true, dry_run: false, light_theme: false, @@ -109,7 +110,6 @@ impl Default for Config { auto_copy: false, keybindings: Default::default(), prelude: String::new(), - save_session: false, compress_threshold: 2000, summarize_prompt: "Summarize the discussion briefly in 200 words or less to use as a prompt for future context.".to_string(), summary_prompt: "This is a summary of the chat history as a recap: ".into(), @@ -353,7 +353,7 @@ impl Config { } } - pub fn set_save_session(&mut self, value: bool) { + pub fn set_save_session(&mut self, value: Option) { if let Some(session) = self.session.as_mut() { session.set_save_session(value); } else { @@ -422,9 +422,6 @@ impl Config { pub fn sys_info(&self) -> Result { let display_path = |path: &Path| path.display().to_string(); - let temperature = self - .temperature - .map_or_else(|| String::from("-"), |v| v.to_string()); let wrap = self .wrap .clone() @@ -436,10 +433,10 @@ impl Config { }; let items = vec![ ("model", self.model.id()), - ("temperature", temperature), + ("temperature", format_option(&self.temperature)), ("dry_run", self.dry_run.to_string()), ("save", self.save.to_string()), - ("save_session", self.save_session.to_string()), + ("save_session", format_option(&self.save_session)), ("highlight", self.highlight.to_string()), ("light_theme", self.light_theme.to_string()), ("wrap", wrap), @@ -518,19 +515,19 @@ impl Config { }; (values, args[0]) } else if args.len() == 2 { - let to_vec = |v: bool| vec![v.to_string()]; let values = match args[0] { - "save" => to_vec(!self.save), + "save" => complete_bool(self.save), "save_session" => { - if let Some(session) = &self.session { - to_vec(!session.save_session()) + let save_session = if let Some(session) = &self.session { + session.save_session() } else { - to_vec(!self.save_session) - } + self.save_session + }; + complete_option_bool(save_session) } - "highlight" => to_vec(!self.highlight), - "dry_run" => to_vec(!self.dry_run), - "auto_copy" => to_vec(!self.auto_copy), + "highlight" => complete_bool(self.highlight), + "dry_run" => complete_bool(self.dry_run), + "auto_copy" => complete_bool(self.auto_copy), _ => vec![], }; (values, args[1]) @@ -550,24 +547,13 @@ impl Config { } let key = parts[0]; let value = parts[1]; - let unset = value == "null"; match key { "temperature" => { - let value = if unset { - None - } else { - let value = value.parse().with_context(|| "Invalid value")?; - Some(value) - }; + let value = parse_value(value)?; self.set_temperature(value); } "compress_threshold" => { - let value = if unset { - None - } else { - let value = value.parse().with_context(|| "Invalid value")?; - Some(value) - }; + let value = parse_value(value)?; self.set_compress_threshold(value); } "save" => { @@ -575,7 +561,7 @@ impl Config { self.save = value; } "save_session" => { - let value = value.parse().with_context(|| "Invalid value")?; + let value = parse_value(value)?; self.set_save_session(value); } "highlight" => { @@ -609,7 +595,9 @@ impl Config { format!("Failed to cleanup previous '{TEMP_SESSION_NAME}' session") })?; } - self.session = Some(Session::new(self, TEMP_SESSION_NAME)); + let mut session = Session::new(self, TEMP_SESSION_NAME); + session.set_save_session(None); + self.session = Some(session); } Some(name) => { let session_path = Self::session_file(name)?; @@ -645,10 +633,9 @@ impl Config { pub fn end_session(&mut self, interactive: bool) -> Result<()> { if let Some(mut session) = self.session.take() { self.last_message = None; - if session.dirty { - // If it's a temporary session, we'll always prompt to save on exit - // If it's named, we'll save automatically if they've set the save flag and prompt if they haven't - if !session.save_session() || session.is_temp() { + let save_session = session.save_session(); + if session.dirty && save_session != Some(false) { + if save_session.is_none() { if !interactive { // If we're not interactive, we will not prompt and will not save return Ok(()); @@ -842,6 +829,7 @@ impl Config { } if let Some(session) = &self.session { output.insert("session", session.name().to_string()); + output.insert("dirty", session.dirty.to_string()); let (tokens, percent) = session.tokens_and_percent(); output.insert("consume_tokens", tokens.to_string()); output.insert("consume_percent", percent.to_string()); @@ -1124,6 +1112,44 @@ fn set_bool(target: &mut bool, value: &str) { } } +fn parse_value(value: &str) -> Result> +where + T: std::str::FromStr, +{ + let value = if value == "null" { + None + } else { + let value = match value.parse() { + Ok(value) => value, + Err(_) => bail!("Invalid value '{}'", value), + }; + Some(value) + }; + Ok(value) +} + +fn format_option(value: &Option) -> String +where + T: std::fmt::Display, +{ + match value { + Some(value) => value.to_string(), + None => "-".to_string(), + } +} + +fn complete_bool(value: bool) -> Vec { + vec![(!value).to_string()] +} + +fn complete_option_bool(value: Option) -> Vec { + match value { + Some(true) => vec!["false".to_string(), "null".to_string()], + Some(false) => vec!["true".to_string(), "null".to_string()], + None => vec!["true".to_string(), "false".to_string()], + } +} + #[cfg(debug_assertions)] fn setup_logger() -> Result<()> { use simplelog::{LevelFilter, WriteLogger}; diff --git a/src/config/session.rs b/src/config/session.rs index feade33..9af11aa 100644 --- a/src/config/session.rs +++ b/src/config/session.rs @@ -19,7 +19,7 @@ pub struct Session { model_id: String, temperature: Option, #[serde(default)] - save_session: bool, + save_session: Option, messages: Vec, #[serde(default)] data_urls: HashMap, @@ -80,7 +80,7 @@ impl Session { self.temperature } - pub fn save_session(&self) -> bool { + pub fn save_session(&self) -> Option { self.save_session } @@ -100,7 +100,9 @@ impl Session { } pub fn export(&self) -> Result { - self.guard_save()?; + if self.path.is_none() { + bail!("Not found session '{}'", self.name) + } let (tokens, percent) = self.tokens_and_percent(); let mut data = json!({ "path": self.path, @@ -109,7 +111,9 @@ impl Session { if let Some(temperature) = self.temperature() { data["temperature"] = temperature.into(); } - data["save_session"] = self.save_session.into(); + if let Some(save_session) = self.save_session() { + data["save_session"] = save_session.into(); + } data["total_tokens"] = tokens.into(); if let Some(conext_window) = self.model.max_input_tokens { data["max_input_tokens"] = conext_window.into(); @@ -137,7 +141,9 @@ impl Session { items.push(("temperature", temperature.to_string())); } - items.push(("save_session", self.save_session.to_string())); + if let Some(save_session) = self.save_session() { + items.push(("save_session", save_session.to_string())); + } if let Some(compress_threshold) = self.compress_threshold { items.push(("compress_threshold", compress_threshold.to_string())); @@ -201,7 +207,7 @@ impl Session { } } - pub fn set_save_session(&mut self, value: bool) { + pub fn set_save_session(&mut self, value: Option) { if self.save_session != value { self.save_session = value; self.dirty = true; @@ -255,13 +261,6 @@ impl Session { Ok(()) } - pub fn guard_save(&self) -> Result<()> { - if self.path.is_none() { - bail!("Not found session '{}'", self.name) - } - Ok(()) - } - pub fn guard_empty(&self) -> Result<()> { if !self.is_empty() { bail!("Cannot perform this action in a session with messages") diff --git a/src/main.rs b/src/main.rs index 8abca98..dc64a34 100644 --- a/src/main.rs +++ b/src/main.rs @@ -76,7 +76,7 @@ fn main() -> Result<()> { config.write().set_model(model)?; } if cli.save_session { - config.write().set_save_session(true); + config.write().set_save_session(Some(true)); } if cli.no_highlight { config.write().highlight = false; -- cgit v1.2.3