Skip to content

Commit

Permalink
feat: .file supports including last reply with %last%
Browse files Browse the repository at this point in the history
  • Loading branch information
sigoden committed Jan 9, 2025
1 parent 723d102 commit 310b2a9
Show file tree
Hide file tree
Showing 3 changed files with 109 additions and 38 deletions.
34 changes: 27 additions & 7 deletions src/config/input.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ pub struct Input {
text: String,
raw: (String, Vec<String>),
patched_text: Option<String>,
last_reply: Option<String>,
continue_output: Option<String>,
regenerate: bool,
medias: Vec<String>,
Expand All @@ -40,6 +41,7 @@ impl Input {
text: text.to_string(),
raw: (text.to_string(), vec![]),
patched_text: None,
last_reply: None,
continue_output: None,
regenerate: false,
medias: Default::default(),
Expand All @@ -62,10 +64,15 @@ impl Input {
let mut external_cmds = vec![];
let mut local_paths = vec![];
let mut remote_urls = vec![];
let mut last_reply = None;
let mut with_last_reply = false;
for path in paths {
match resolve_local_path(&path) {
Some(v) => {
if v.len() > 2 && v.starts_with('`') && v.ends_with('`') {
if v == "%last%" {
with_last_reply = true;
raw_paths.push(v);
} else if v.len() > 2 && v.starts_with('`') && v.ends_with('`') {
external_cmds.push(v[1..v.len() - 1].to_string());
raw_paths.push(v);
} else {
Expand All @@ -89,12 +96,24 @@ impl Input {
if !raw_text.is_empty() {
texts.push(raw_text.to_string());
};
if !files.is_empty() {
texts.push(String::new());
if with_last_reply {
if let Some(LastMessage { input, output, .. }) = config.read().last_message.as_ref() {
if !output.is_empty() {
last_reply = Some(output.clone())
} else if let Some(v) = input.last_reply.as_ref() {
last_reply = Some(v.clone());
}
if let Some(v) = last_reply.clone() {
texts.push(format!("\n{v}\n"));
}
}
if last_reply.is_none() && files.is_empty() && medias.is_empty() {
bail!("No last reply found");
}
}
for (kind, path, contents) in files {
texts.push(format!(
"============ {kind}: {path} ============\n{contents}\n"
"\n============ {kind}: {path} ============\n{contents}"
));
}
let (role, with_session, with_agent) = resolve_role(&config.read(), role);
Expand All @@ -103,6 +122,7 @@ impl Input {
text: texts.join("\n"),
raw: (raw_text.to_string(), raw_paths),
patched_text: None,
last_reply,
continue_output: None,
regenerate: false,
medias,
Expand Down Expand Up @@ -407,10 +427,10 @@ async fn load_documents(
let loaders = config.read().document_loaders.clone();
for file_path in local_files {
if is_image(&file_path) {
let data_url = read_media_to_data_url(&file_path)
let contents = read_media_to_data_url(&file_path)
.with_context(|| format!("Unable to read media file '{file_path}'"))?;
data_urls.insert(sha256(&data_url), file_path);
medias.push(data_url)
data_urls.insert(sha256(&contents), file_path);
medias.push(contents)
} else {
let document = load_file(&loaders, &file_path)
.await
Expand Down
64 changes: 44 additions & 20 deletions src/config/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -156,7 +156,7 @@ pub struct Config {
#[serde(skip)]
pub working_mode: WorkingMode,
#[serde(skip)]
pub last_message: Option<(Input, String)>,
pub last_message: Option<LastMessage>,

#[serde(skip)]
pub cli_info_flag: bool,
Expand Down Expand Up @@ -1051,8 +1051,15 @@ impl Config {
if let Some(session) = session.as_mut() {
if session.is_empty() {
new_session = true;
if let Some((input, output)) = &self.last_message {
if self.agent.is_some() == input.with_agent() {
if let Some(LastMessage {
input,
output,
continuous,
}) = &self.last_message
{
if (*continuous && !output.is_empty())
&& self.agent.is_some() == input.with_agent()
{
let ans = Confirm::new(
"Start a session that incorporates the last question and answer?",
)
Expand Down Expand Up @@ -1093,7 +1100,7 @@ impl Config {
if let Some(mut session) = self.session.take() {
let sessions_dir = self.sessions_dir();
session.exit(&sessions_dir, self.working_mode.is_repl())?;
self.last_message = None;
self.discontinuous_last_message();
}
Ok(())
}
Expand Down Expand Up @@ -1131,7 +1138,7 @@ impl Config {
)
})?;
self.session = Some(Session::load(self, &name, &session_path)?);
self.last_message = None;
self.discontinuous_last_message();
Ok(())
}

Expand All @@ -1144,7 +1151,7 @@ impl Config {
} else {
bail!("No session")
}
self.last_message = None;
self.discontinuous_last_message();
Ok(())
}

Expand Down Expand Up @@ -1225,7 +1232,7 @@ impl Config {
if let Some(session) = config.write().session.as_mut() {
session.compress(format!("{}{}", summary_prompt, summary));
}
config.write().last_message = None;
config.write().discontinuous_last_message();
Ok(())
}

Expand Down Expand Up @@ -1524,7 +1531,7 @@ impl Config {
self.exit_session()?;
if self.agent.take().is_some() {
self.rag.take();
self.last_message = None;
self.discontinuous_last_message();
self.cli_agent_variables = None;
}
Ok(())
Expand Down Expand Up @@ -1793,13 +1800,6 @@ impl Config {
.collect()
}

pub fn last_reply(&self) -> &str {
self.last_message
.as_ref()
.map(|(_, reply)| reply.as_str())
.unwrap_or_default()
}

pub fn render_options(&self) -> Result<RenderOptions> {
let theme = if self.highlight {
let theme_mode = if self.light_theme { "light" } else { "dark" };
Expand Down Expand Up @@ -1939,7 +1939,7 @@ impl Config {
}

pub fn before_chat_completion(&mut self, input: &Input) -> Result<()> {
self.last_message = Some((input.clone(), String::new()));
self.last_message = Some(LastMessage::new(input.clone(), String::new()));
Ok(())
}

Expand All @@ -1949,15 +1949,22 @@ impl Config {
output: &str,
tool_results: &[ToolResult],
) -> Result<()> {
if self.dry_run || output.is_empty() || !tool_results.is_empty() {
self.last_message = None;
if output.is_empty() || !tool_results.is_empty() {
return Ok(());
}
self.last_message = Some((input.clone(), output.to_string()));
self.save_message(input, output)?;
self.last_message = Some(LastMessage::new(input.clone(), output.to_string()));
if !self.dry_run {
self.save_message(input, output)?;
}
Ok(())
}

fn discontinuous_last_message(&mut self) {
if let Some(last_message) = self.last_message.as_mut() {
last_message.continuous = false;
}
}

fn save_message(&mut self, input: &Input, output: &str) -> Result<()> {
let mut input = input.clone();
input.clear_patch();
Expand Down Expand Up @@ -2342,6 +2349,23 @@ impl WorkingMode {
}
}

#[derive(Debug, Clone)]
pub struct LastMessage {
pub input: Input,
pub output: String,
pub continuous: bool,
}

impl LastMessage {
pub fn new(input: Input, output: String) -> Self {
Self {
input,
output,
continuous: true,
}
}
}

bitflags::bitflags! {
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct StateFlags: u32 {
Expand Down
49 changes: 38 additions & 11 deletions src/repl/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ use self::highlighter::ReplHighlighter;
use self::prompt::ReplPrompt;

use crate::client::{call_chat_completions, call_chat_completions_streaming};
use crate::config::{AssertState, Config, GlobalConfig, Input, StateFlags};
use crate::config::{AssertState, Config, GlobalConfig, Input, LastMessage, StateFlags};
use crate::render::render_error;
use crate::utils::{
abortable_run_with_spinner, create_abort_signal, set_text, temp_file, AbortSignal,
Expand Down Expand Up @@ -154,10 +154,10 @@ lazy_static::lazy_static! {
ReplCommand::new(".continue", "Continue the response", AssertState::pass()),
ReplCommand::new(
".regenerate",
"Regenerate the last response",
"Regenerate the response",
AssertState::pass()
),
ReplCommand::new(".copy", "Copy the last response", AssertState::pass()),
ReplCommand::new(".copy", "Copy the last chat response", AssertState::pass()),
ReplCommand::new(".set", "Adjust runtime configuration", AssertState::pass()),
ReplCommand::new(".delete", "Delete roles/sessions/RAGs/agents", AssertState::pass()),
ReplCommand::new(".exit", "Exit the REPL", AssertState::pass()),
Expand Down Expand Up @@ -413,27 +413,44 @@ impl Repl {
ask(&self.config, self.abort_signal.clone(), input, true).await?;
}
None => println!(
r#"Usage: .file <file|dir|url|cmd>... [-- <text>...]
r#"Usage: .file <file|dir|url|%last%|cmd>... [-- <text>...]
.file /tmp/file.txt
.file src/ Cargo.toml -- analyze
.file https://example.com/file.txt -- summarize
.file https://example.com/image.png -- recognize text
.file %last% -- translate to english
.file `git diff` -- Generate git commit message"#
),
},
".continue" => {
let (mut input, output) = match self.config.read().last_message.clone() {
let LastMessage {
mut input, output, ..
} = match self
.config
.read()
.last_message
.as_ref()
.filter(|v| v.continuous && !v.output.is_empty())
.cloned()
{
Some(v) => v,
None => bail!("Unable to continue response"),
None => bail!("Unable to continue the response"),
};
input.set_continue_output(&output);
ask(&self.config, self.abort_signal.clone(), input, true).await?;
}
".regenerate" => {
let (mut input, _) = match self.config.read().last_message.clone() {
let LastMessage { mut input, .. } = match self
.config
.read()
.last_message
.as_ref()
.filter(|v| v.continuous)
.cloned()
{
Some(v) => v,
None => bail!("Unable to regenerate the last response"),
None => bail!("Unable to regenerate the response"),
};
input.set_regenerate();
ask(&self.config, self.abort_signal.clone(), input, true).await?;
Expand All @@ -455,9 +472,19 @@ impl Repl {
}
},
".copy" => {
let config = self.config.read();
self.copy(config.last_reply())
.with_context(|| "Failed to copy the last response")?;
let output = match self
.config
.read()
.last_message
.as_ref()
.filter(|v| v.continuous && !v.output.is_empty())
.map(|v| v.output.clone())
{
Some(v) => v,
None => bail!("No chat response to copy"),
};
self.copy(&output)
.with_context(|| "Failed to copy the last chat response")?;
}
".exit" => match args {
Some("role") => {
Expand Down

0 comments on commit 310b2a9

Please sign in to comment.