diff --git a/src/config/mod.rs b/src/config/mod.rs index f862a15..ef0432c 100644 --- a/src/config/mod.rs +++ b/src/config/mod.rs @@ -35,7 +35,7 @@ pub use self::request_context::{RenderMode, RequestContext, should_inject_skill_ pub use self::role::{ CODE_ROLE, CREATE_TITLE_ROLE, EXPLAIN_SHELL_ROLE, Role, RoleLike, SHELL_ROLE, }; -use self::session::Session; +pub use self::session::Session; #[allow(unused_imports)] pub use self::skill::Skill; #[allow(unused_imports)] diff --git a/src/repl/mod.rs b/src/repl/mod.rs index cced1ff..1dea401 100644 --- a/src/repl/mod.rs +++ b/src/repl/mod.rs @@ -1,15 +1,13 @@ mod completer; mod highlighter; mod prompt; +mod replay; use self::completer::ReplCompleter; use self::highlighter::ReplHighlighter; use self::prompt::ReplPrompt; -use crate::client::{ - Message, MessageRole, call_chat_completions, call_chat_completions_streaming, init_client, - oauth, -}; +use crate::client::{call_chat_completions, call_chat_completions_streaming, init_client, oauth}; use crate::config::{ AgentVariables, AppConfig, AssertState, Input, LastMessage, RequestContext, StateFlags, macro_execute, @@ -362,54 +360,16 @@ Type ".help" for additional help. } { - let (messages_snapshot, compressed_count) = { + let (compressed, active) = { let ctx = self.ctx.read(); - if let Some(session) = &ctx.session { - let msgs: Vec = session - .messages() - .iter() - .filter(|m| !m.role.is_system()) - .cloned() - .collect(); - let compressed = session.compressed_messages().len(); - (msgs, compressed) - } else { - (vec![], 0) + match &ctx.session { + Some(session) => replay::snapshot(session), + None => (Vec::new(), Vec::new()), } }; - - if !messages_snapshot.is_empty() || compressed_count > 0 { + if !compressed.is_empty() || !active.is_empty() { let app = Arc::clone(&self.ctx.read().app.config); - if compressed_count > 0 { - println!( - "{}", - dimmed_text(&format!( - "({compressed_count} earlier messages not shown; compressed for context)" - )) - ); - println!(); - } - - for message in &messages_snapshot { - match message.role { - MessageRole::User => { - if let Some(text) = message.content.as_text() { - println!("{}", dimmed_text("You:")); - println!("{text}"); - println!(); - } - } - MessageRole::Assistant => { - if let Some(text) = message.content.as_text() { - app.print_markdown(text)?; - println!(); - } - } - _ => {} - } - } - println!("{}", dimmed_text("─── ↑ previous conversation ↑ ───")); - println!(); + replay::render(app.as_ref(), &compressed, &active)?; } } @@ -859,44 +819,8 @@ pub async fn run_repl_command( } } if let Some(session) = &ctx.session { - let messages_snapshot: Vec = session - .messages() - .iter() - .filter(|m| !m.role.is_system()) - .cloned() - .collect(); - let compressed_count = session.compressed_messages().len(); - if !messages_snapshot.is_empty() || compressed_count > 0 { - if compressed_count > 0 { - println!( - "{}", - dimmed_text(&format!( - "({compressed_count} earlier messages not shown — compressed for context)" - )) - ); - println!(); - } - for message in &messages_snapshot { - match message.role { - MessageRole::User => { - if let Some(text) = message.content.as_text() { - println!("{}", dimmed_text("You:")); - println!("{text}"); - println!(); - } - } - MessageRole::Assistant => { - if let Some(text) = message.content.as_text() { - app.print_markdown(text)?; - println!(); - } - } - _ => {} - } - } - println!("{}", dimmed_text("─── ↑ previous conversation ↑ ───")); - println!(); - } + let (compressed, active) = replay::snapshot(session); + replay::render(app.as_ref(), &compressed, &active)?; } } ".install" => { @@ -953,44 +877,8 @@ pub async fn run_repl_command( ctx.use_agent(app.as_ref(), agent_name, session_name, abort_signal.clone()) .await?; if let Some(session) = &ctx.session { - let messages_snapshot: Vec = session - .messages() - .iter() - .filter(|m| !m.role.is_system()) - .cloned() - .collect(); - let compressed_count = session.compressed_messages().len(); - if !messages_snapshot.is_empty() || compressed_count > 0 { - if compressed_count > 0 { - println!( - "{}", - dimmed_text(&format!( - "({compressed_count} earlier messages not shown — compressed for context)" - )) - ); - println!(); - } - for message in &messages_snapshot { - match message.role { - MessageRole::User => { - if let Some(text) = message.content.as_text() { - println!("{}", dimmed_text("You:")); - println!("{text}"); - println!(); - } - } - MessageRole::Assistant => { - if let Some(text) = message.content.as_text() { - app.print_markdown(text)?; - println!(); - } - } - _ => {} - } - } - println!("{}", dimmed_text("─── ↑ previous conversation ↑ ───")); - println!(); - } + let (compressed, active) = replay::snapshot(session); + replay::render(app.as_ref(), &compressed, &active)?; } } None => { diff --git a/src/repl/replay.rs b/src/repl/replay.rs new file mode 100644 index 0000000..a73c25a --- /dev/null +++ b/src/repl/replay.rs @@ -0,0 +1,58 @@ +use anyhow::Result; + +use crate::client::{Message, MessageRole}; +use crate::config::{AppConfig, Session}; +use crate::utils::dimmed_text; + +pub fn snapshot(session: &Session) -> (Vec, Vec) { + ( + filter_for_display(session.compressed_messages()), + filter_for_display(session.messages()), + ) +} + +pub fn render(app: &AppConfig, compressed: &[Message], active: &[Message]) -> Result<()> { + if compressed.is_empty() && active.is_empty() { + return Ok(()); + } + + render_messages(app, compressed)?; + if !compressed.is_empty() && !active.is_empty() { + println!("{}", dimmed_text("─── ↑ pre-compression history ↑ ───")); + println!(); + } + render_messages(app, active)?; + println!("{}", dimmed_text("─── ↑ previous conversation ↑ ───")); + println!(); + Ok(()) +} + +fn filter_for_display(messages: &[Message]) -> Vec { + messages + .iter() + .filter(|m| !m.role.is_system()) + .cloned() + .collect() +} + +fn render_messages(app: &AppConfig, messages: &[Message]) -> Result<()> { + for message in messages { + match message.role { + MessageRole::User => { + if let Some(text) = message.content.as_text() { + println!("{}", dimmed_text("You:")); + println!("{text}"); + println!(); + } + } + MessageRole::Assistant => { + if let Some(text) = message.content.as_text() { + app.print_markdown(text)?; + println!(); + } + } + _ => {} + } + } + Ok(()) +}