feat: add .info role and .info session (#183)

This commit is contained in:
sigoden 2023-10-30 19:26:06 +08:00 committed by GitHub
parent 42339fac42
commit e86ad7dc16
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 110 additions and 44 deletions

View File

@ -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.

View File

@ -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<()> {

View File

@ -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);

View File

@ -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 {

View File

@ -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");

View File

@ -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() {