Add agent context compaction
This commit is contained in:
@@ -341,6 +341,13 @@ pub(crate) struct GenerationOutput {
|
||||
pub(crate) checkpoint_bytes: u64,
|
||||
}
|
||||
|
||||
#[cfg(target_os = "macos")]
|
||||
pub(crate) struct CompactionOutput {
|
||||
pub(crate) summary: String,
|
||||
pub(crate) tail: Vec<ChatTurn>,
|
||||
pub(crate) context_tokens: u32,
|
||||
}
|
||||
|
||||
#[cfg(target_os = "macos")]
|
||||
impl Generator {
|
||||
pub(crate) fn open(settings: &EngineSettings, metrics: Arc<Metrics>) -> Result<Self, String> {
|
||||
@@ -496,6 +503,95 @@ impl Generator {
|
||||
Ok(output)
|
||||
}
|
||||
|
||||
pub(crate) fn compact(
|
||||
&mut self,
|
||||
messages: &[ChatTurn],
|
||||
settings: &TurnSettings,
|
||||
reason: &str,
|
||||
cancelled: &AtomicBool,
|
||||
mut progress: impl FnMut(u32, u32, Option<f32>),
|
||||
) -> Result<CompactionOutput, String> {
|
||||
self.executor.reset()?;
|
||||
self.checkpoint = None;
|
||||
let result = (|| {
|
||||
let mut private_messages = messages.to_vec();
|
||||
private_messages.push(ChatTurn {
|
||||
user: true,
|
||||
tool: false,
|
||||
skip_previous_eos: false,
|
||||
reasoning: None,
|
||||
reasoning_complete: true,
|
||||
content: crate::compaction::summary_prompt(reason),
|
||||
});
|
||||
let mut private_settings = settings.clone();
|
||||
private_settings.reasoning_mode = ReasoningMode::Direct;
|
||||
private_settings.max_generated_tokens = crate::compaction::SUMMARY_MAX_TOKENS;
|
||||
private_settings.temperature = 0.0;
|
||||
private_settings.stops.extend([
|
||||
"<|DSML|".into(),
|
||||
"<DSML|".into(),
|
||||
"<tool_call>".into(),
|
||||
"<think>".into(),
|
||||
"</think>".into(),
|
||||
]);
|
||||
let (output, prompt_complete) = self.generate_inner(
|
||||
&private_messages,
|
||||
&private_settings,
|
||||
cancelled,
|
||||
&mut |_, _| {},
|
||||
&mut progress,
|
||||
)?;
|
||||
if cancelled.load(Ordering::Relaxed) || !prompt_complete {
|
||||
return Err("context compaction interrupted".into());
|
||||
}
|
||||
let summary = crate::compaction::sanitize_summary(&output.message.content);
|
||||
if summary.is_empty() {
|
||||
return Err("context compaction produced an empty summary".into());
|
||||
}
|
||||
|
||||
let full = self.executor.model().render_conversation(
|
||||
&settings.system_prompt,
|
||||
messages,
|
||||
settings.reasoning_mode,
|
||||
);
|
||||
let mut starts = Vec::with_capacity(messages.len());
|
||||
for index in 0..messages.len() {
|
||||
starts.push(
|
||||
self.executor
|
||||
.model()
|
||||
.render_conversation(
|
||||
&settings.system_prompt,
|
||||
&messages[..index],
|
||||
settings.reasoning_mode,
|
||||
)
|
||||
.len() as u32,
|
||||
);
|
||||
}
|
||||
let start = crate::compaction::tail_start(
|
||||
messages,
|
||||
&starts,
|
||||
full.len() as u32,
|
||||
crate::compaction::tail_budget(self.executor.context()),
|
||||
);
|
||||
let tail = messages[start..].to_vec();
|
||||
let rebuilt_system =
|
||||
crate::compaction::summary_system_prompt(&settings.system_prompt, Some(&summary));
|
||||
let context_tokens = self
|
||||
.executor
|
||||
.model()
|
||||
.render_conversation(&rebuilt_system, &tail, settings.reasoning_mode)
|
||||
.len() as u32;
|
||||
Ok(CompactionOutput {
|
||||
summary,
|
||||
tail,
|
||||
context_tokens,
|
||||
})
|
||||
})();
|
||||
self.executor.reset()?;
|
||||
self.checkpoint = None;
|
||||
result
|
||||
}
|
||||
|
||||
fn select_checkpoint(
|
||||
&mut self,
|
||||
checkpoint: &Path,
|
||||
|
||||
Reference in New Issue
Block a user