summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorsigoden <sigoden@gmail.com>2023-10-30 19:26:06 +0800
committerGitHub <noreply@github.com>2023-10-30 19:26:06 +0800
commite86ad7dc165ba19fbe01d822a54a9975e4b17862 (patch)
treea9c953d4e48cde477af329fc1434fb7a410213db
parent42339fac423136fb647c52429d7dd2228144ec98 (diff)
downloadaichat-e86ad7dc165ba19fbe01d822a54a9975e4b17862.tar.gz
feat: add `.info role` and `.info session` (#183)
-rw-r--r--README.md22
-rw-r--r--src/config/mod.rs36
-rw-r--r--src/config/session.rs37
-rw-r--r--src/main.rs2
-rw-r--r--src/repl/handler.rs28
-rw-r--r--src/repl/mod.rs29
6 files changed, 110 insertions, 44 deletions
diff --git a/README.md b/README.md
index 742fe58..48cf2a7 100644
--- a/README.md
+++ b/README.md
@@ -140,12 +140,14 @@ The Chat REPL supports:
```
〉.help
.help Print this help message
-.info Print system-wide information
+.info Print system info
.edit Multi-line editing (CTRL+S to finish)
.model Switch LLM model
.role Use role
+.info role Show role info
.exit role Leave current role
.session Start a context-aware chat session
+.info session Show session info
.exit session End the current session
.set Modify the configuration parameters
.copy Copy the last reply to the clipboard
@@ -156,7 +158,7 @@ Press Ctrl+C to abort readline, Ctrl+D to exit the REPL
```
-### `.info` - view current configuration information
+### `.info` - view information
```
〉.info
@@ -210,19 +212,16 @@ Select a role:
```
〉.role emoji
-name: emoji
-prompt: I want you to translate the sentences I write into emojis. I will write the sentence, and you will express it with emojis. I just want you to express it with emojis. I don't want you to reply with anything but emoji. When I need to tell you something in English, I will do it by wrapping it in curly brackets like {like this}.
-temperature: null
```
-AI takes the role we specified:
+Send message with role:
```
emoji〉hello
👋
```
-Clear current selected role:
+Leave current role:
```
emoji〉.exit role
@@ -231,6 +230,15 @@ emoji〉.exit role
Hello there! How can I assist you today?
```
+Show role info:
+
+```
+emoji〉.info role
+name: emoji
+prompt: I want you to translate the sentences I write into emojis. I will write the sentence, and you will express it with emojis. I just want you to express it with emojis. I don't want you to reply with anything but emoji. When I need to tell you something in English, I will do it by wrapping it in curly brackets like {like this}.
+temperature: null
+```
+
## `.session` - context-aware conversation
By default, aichat behaves in a one-off request/response manner.
diff --git a/src/config/mod.rs b/src/config/mod.rs
index e6d9238..9f4a11a 100644
--- a/src/config/mod.rs
+++ b/src/config/mod.rs
@@ -148,12 +148,16 @@ impl Config {
Ok(config)
}
- pub fn get_role(&self, name: &str) -> Option<Role> {
- self.roles.iter().find(|v| v.match_name(name)).map(|v| {
- let mut role = v.clone();
- role.complete_prompt_args(name);
- role
- })
+ pub fn retrieve_role(&self, name: &str) -> Result<Role> {
+ self.roles
+ .iter()
+ .find(|v| v.match_name(name))
+ .map(|v| {
+ let mut role = v.clone();
+ role.complete_prompt_args(name);
+ role
+ })
+ .ok_or_else(|| anyhow!("Unknown role `{name}`"))
}
pub fn config_dir() -> Result<PathBuf> {
@@ -235,20 +239,14 @@ impl Config {
Ok(path)
}
- pub fn set_role(&mut self, name: &str) -> Result<String> {
- match self.get_role(name) {
- Some(role) => {
- if let Some(session) = self.session.as_mut() {
- session.update_role(Some(role.clone()))?;
- }
- let output = serde_yaml::to_string(&role)
- .unwrap_or_else(|_| "Unable to echo role details".into());
- self.temperature = role.temperature;
- self.role = Some(role);
- Ok(output)
- }
- None => bail!("Unknown role `{name}`"),
+ pub fn set_role(&mut self, name: &str) -> Result<()> {
+ let role = self.retrieve_role(name)?;
+ if let Some(session) = self.session.as_mut() {
+ session.update_role(Some(role.clone()))?;
}
+ self.temperature = role.temperature;
+ self.role = Some(role);
+ Ok(())
}
pub fn clear_role(&mut self) -> Result<()> {
diff --git a/src/config/session.rs b/src/config/session.rs
index 75b863f..4cc172d 100644
--- a/src/config/session.rs
+++ b/src/config/session.rs
@@ -1,6 +1,8 @@
use super::message::{num_tokens_from_messages, Message, MessageRole};
use super::role::Role;
+use crate::render::MarkdownRender;
+
use anyhow::{bail, Context, Result};
use serde::{Deserialize, Serialize};
use std::fs::{self, read_to_string};
@@ -52,13 +54,46 @@ impl Session {
Ok(session)
}
- pub fn info(&self) -> Result<String> {
+ pub fn export(&self) -> Result<String> {
self.guard_save()?;
let output = serde_yaml::to_string(&self)
.with_context(|| format!("Unable to show info about session {}", &self.name))?;
Ok(output)
}
+ pub fn render(&self, render: &mut MarkdownRender) -> Result<String> {
+ let temperature = self
+ .temperature
+ .map_or_else(|| String::from("-"), |v| v.to_string());
+ let items = vec![
+ ("path", self.path.clone().unwrap_or_else(|| "-".into())),
+ ("model", self.model.clone()),
+ ("tokens", self.tokens.to_string()),
+ ("temperature", temperature),
+ ];
+ let mut lines = vec![];
+ for (name, value) in items {
+ lines.push(format!("{name:<20}{value}"));
+ }
+ lines.push("".into());
+ for message in &self.messages {
+ match message.role {
+ MessageRole::System => {
+ continue;
+ }
+ MessageRole::Assistant => {
+ lines.push(render.render(&message.content));
+ lines.push("".into());
+ }
+ MessageRole::User => {
+ lines.push(format!("{}){}", self.name, message.content));
+ }
+ }
+ }
+ let output = lines.join("\n");
+ Ok(output)
+ }
+
pub fn update_role(&mut self, role: Option<Role>) -> Result<()> {
self.guard_empty()?;
self.temperature = role.as_ref().and_then(|v| v.temperature);
diff --git a/src/main.rs b/src/main.rs
index a23a81f..8e1835a 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -69,7 +69,7 @@ fn main() -> Result<()> {
}
if cli.info {
let info = if let Some(session) = &config.read().session {
- session.info()?
+ session.export()?
} else if let Some(role) = &config.read().role {
role.info()?
} else {
diff --git a/src/repl/handler.rs b/src/repl/handler.rs
index d70abbe..afe016a 100644
--- a/src/repl/handler.rs
+++ b/src/repl/handler.rs
@@ -1,7 +1,7 @@
use crate::client::init_client;
use crate::config::SharedConfig;
use crate::print_now;
-use crate::render::render_stream;
+use crate::render::{render_stream, MarkdownRender};
use std::fs;
use std::io::Read;
@@ -15,7 +15,9 @@ use std::cell::RefCell;
pub enum ReplCmd {
Submit(String),
- ViewInfo,
+ Info,
+ RoleInfo,
+ SessionInfo,
SetModel(String),
SetRole(String),
ExitRole,
@@ -66,7 +68,7 @@ impl ReplCmdHandler {
let _ = self.copy(&buffer);
}
}
- ReplCmd::ViewInfo => {
+ ReplCmd::Info => {
let output = self.config.read().info()?;
print_now!("{}\n\n", output.trim_end());
}
@@ -75,8 +77,15 @@ impl ReplCmdHandler {
print_now!("\n");
}
ReplCmd::SetRole(name) => {
- let output = self.config.write().set_role(&name)?;
- print_now!("{}\n\n", output.trim_end());
+ self.config.write().set_role(&name)?;
+ print_now!("\n");
+ }
+ ReplCmd::RoleInfo => {
+ if let Some(role) = &self.config.read().role {
+ print_now!("{}\n\n", role.info()?);
+ } else {
+ bail!("No role")
+ }
}
ReplCmd::ExitRole => {
self.config.write().clear_role()?;
@@ -86,6 +95,15 @@ impl ReplCmdHandler {
self.config.write().start_session(&name)?;
print_now!("\n");
}
+ ReplCmd::SessionInfo => {
+ if let Some(session) = &self.config.read().session {
+ let render_options = self.config.read().get_render_options();
+ let mut markdown_render = MarkdownRender::init(render_options)?;
+ print_now!("{}\n\n", session.render(&mut markdown_render)?);
+ } else {
+ bail!("No session")
+ }
+ }
ReplCmd::ExitSession => {
self.config.write().end_session()?;
print_now!("\n");
diff --git a/src/repl/mod.rs b/src/repl/mod.rs
index e5691dc..4cf59f7 100644
--- a/src/repl/mod.rs
+++ b/src/repl/mod.rs
@@ -18,14 +18,16 @@ use lazy_static::lazy_static;
use reedline::Signal;
use std::rc::Rc;
-pub const REPL_COMMANDS: [(&str, &str); 12] = [
+pub const REPL_COMMANDS: [(&str, &str); 14] = [
(".help", "Print this help message"),
- (".info", "Print system-wide information"),
+ (".info", "Print system info"),
(".edit", "Multi-line editing (CTRL+S to finish)"),
(".model", "Switch LLM model"),
(".role", "Use role"),
+ (".info role", "Show role info"),
(".exit role", "Leave current role"),
(".session", "Start a context-aware chat session"),
+ (".info session", "Show session info"),
(".exit session", "End the current session"),
(".set", "Modify the configuration parameters"),
(".copy", "Copy the last reply to the clipboard"),
@@ -66,7 +68,7 @@ impl Repl {
}
Err(err) => {
let err = format!("{err:?}");
- print_now!("{}\n\n", err.trim());
+ print_now!("Error: {}\n\n", err.trim());
}
}
}
@@ -95,9 +97,14 @@ impl Repl {
".help" => {
dump_repl_help();
}
- ".info" => {
- handler.handle(ReplCmd::ViewInfo)?;
- }
+ ".info" => match args {
+ Some("role") => handler.handle(ReplCmd::RoleInfo)?,
+ Some("session") => handler.handle(ReplCmd::SessionInfo)?,
+ Some(_) => unknown_command(),
+ None => {
+ handler.handle(ReplCmd::Info)?;
+ }
+ },
".edit" => {
if let Some(text) = args {
handler.handle(ReplCmd::Submit(text.to_string()))?;
@@ -128,7 +135,7 @@ impl Repl {
".exit" => match args {
Some("role") => handler.handle(ReplCmd::ExitRole)?,
Some("session") => handler.handle(ReplCmd::ExitSession)?,
- Some(_) => dump_unknown_command(),
+ Some(_) => unknown_command(),
None => {
return Ok(true);
}
@@ -141,9 +148,9 @@ impl Repl {
Some("session") => {
print_now!("Deprecated. Use '.exit session' instead.\n\n");
}
- _ => dump_unknown_command(),
+ _ => unknown_command(),
},
- _ => dump_unknown_command(),
+ _ => unknown_command(),
},
None => {
handler.handle(ReplCmd::Submit(line.to_string()))?;
@@ -154,8 +161,8 @@ impl Repl {
}
}
-fn dump_unknown_command() {
- print_now!("Unknown command. Type \".help\" for more information.\n\n");
+fn unknown_command() {
+ print_now!("Unknown command. Try `.help`.\n\n");
}
fn dump_repl_help() {