Make compaction transitions recoverable
This commit is contained in:
@@ -20,13 +20,14 @@ pub(super) struct TitleRequest {
|
||||
|
||||
#[cfg(target_os = "macos")]
|
||||
pub(super) enum PendingContinuation {
|
||||
None,
|
||||
User(String),
|
||||
Tool(String),
|
||||
}
|
||||
|
||||
#[cfg(target_os = "macos")]
|
||||
pub(super) struct CompactionRequest {
|
||||
active: ActiveGeneration,
|
||||
pub(super) active: ActiveGeneration,
|
||||
pending: PendingContinuation,
|
||||
}
|
||||
|
||||
@@ -92,6 +93,16 @@ impl App {
|
||||
return;
|
||||
}
|
||||
#[cfg(target_os = "macos")]
|
||||
if prompt == "/compact" {
|
||||
self.composer.clear();
|
||||
if let Err(error) =
|
||||
self.start_compaction(PendingContinuation::None, "manual /compact request")
|
||||
{
|
||||
self.error = Some(error);
|
||||
}
|
||||
return;
|
||||
}
|
||||
#[cfg(target_os = "macos")]
|
||||
if !std::mem::take(&mut self.skip_compaction_once)
|
||||
&& crate::compaction::should_compact(self.context_used, self.context_limit)
|
||||
{
|
||||
@@ -466,7 +477,7 @@ impl App {
|
||||
}
|
||||
|
||||
#[cfg(target_os = "macos")]
|
||||
fn start_compaction(
|
||||
pub(super) fn start_compaction(
|
||||
&mut self,
|
||||
pending: PendingContinuation,
|
||||
reason: &str,
|
||||
@@ -506,6 +517,10 @@ impl App {
|
||||
effective.turn,
|
||||
messages,
|
||||
reason,
|
||||
session_compaction_checkpoint_path(
|
||||
self.selected_session
|
||||
.ok_or_else(|| "The active session is unavailable.".to_owned())?,
|
||||
),
|
||||
idle_timeout,
|
||||
)?;
|
||||
self.active_compaction = Some(CompactionRequest { active, pending });
|
||||
@@ -537,6 +552,7 @@ impl App {
|
||||
match result {
|
||||
Ok(compacted) => {
|
||||
if let Err(error) = self.apply_compaction(&compacted) {
|
||||
let _ = fs::remove_file(&compacted.checkpoint);
|
||||
self.generating = false;
|
||||
self.activity = None;
|
||||
self.error = Some(error);
|
||||
@@ -546,6 +562,7 @@ impl App {
|
||||
self.generating = false;
|
||||
self.activity = None;
|
||||
match request.pending {
|
||||
PendingContinuation::None => {}
|
||||
PendingContinuation::User(prompt) => {
|
||||
self.composer = prompt;
|
||||
self.skip_compaction_once = true;
|
||||
@@ -595,7 +612,7 @@ impl App {
|
||||
let session_id = self
|
||||
.selected_session
|
||||
.ok_or_else(|| "The active session is unavailable.".to_owned())?;
|
||||
let tail = compacted
|
||||
let mut tail = compacted
|
||||
.tail
|
||||
.iter()
|
||||
.map(|message| crate::database::MessageDraft {
|
||||
@@ -606,11 +623,32 @@ impl App {
|
||||
content: message.content.clone(),
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
if let Some((tools_session, tools)) = &self.agent_tools
|
||||
&& *tools_session == session_id
|
||||
&& let Some(observation) = tools
|
||||
.lock()
|
||||
.unwrap_or_else(|poisoned| poisoned.into_inner())
|
||||
.compaction_observation()
|
||||
{
|
||||
tail.push(crate::database::MessageDraft {
|
||||
user: false,
|
||||
tool: true,
|
||||
reasoning: None,
|
||||
reasoning_complete: true,
|
||||
content: observation,
|
||||
});
|
||||
}
|
||||
let messages = self
|
||||
.database
|
||||
.as_mut()
|
||||
.ok_or_else(|| "The project database is unavailable.".to_owned())?
|
||||
.replace_with_compacted_transcript(session_id, &compacted.summary, &tail)
|
||||
.replace_with_compacted_transcript(
|
||||
session_id,
|
||||
&compacted.summary,
|
||||
&tail,
|
||||
compacted.context_tokens,
|
||||
self.context_limit,
|
||||
)
|
||||
.map_err(|error| format!("Could not save compacted conversation: {error}"))?;
|
||||
self.conversation = messages.into_iter().map(ChatMessage::from).collect();
|
||||
if let Some(session) = self
|
||||
@@ -620,8 +658,16 @@ impl App {
|
||||
.find(|session| session.id == session_id)
|
||||
{
|
||||
session.compacted_summary = Some(compacted.summary.clone());
|
||||
session.context_used = compacted.context_tokens as i32;
|
||||
session.context_limit = self.context_limit as i32;
|
||||
session.last_tokens_per_second = None;
|
||||
}
|
||||
let _ = fs::remove_file(session_checkpoint_path(session_id));
|
||||
let final_checkpoint = session_checkpoint_path(session_id);
|
||||
fs::rename(&compacted.checkpoint, &final_checkpoint).map_err(|error| {
|
||||
format!(
|
||||
"The compacted conversation was saved, but its checkpoint could not be promoted: {error}. It will rebuild on next use."
|
||||
)
|
||||
})?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user