use super::super::{BlockKind, Transcript};
use super::PendingToolCall;
#[derive(Debug, Clone)]
pub(crate) struct AttemptMark {
blocks: usize,
tail_assistant_len: Option<usize>,
pending_tools: Vec<PendingToolCall>,
}
impl Transcript {
pub(crate) fn attempt_start(&self) -> Option<usize> {
self.attempt.as_ref().map(|mark| mark.blocks)
}
pub(crate) fn mark_attempt(&mut self) {
let tail_assistant_len = self
.blocks
.last()
.filter(|block| block.kind == BlockKind::Assistant)
.map(|block| block.text.len());
self.attempt = Some(AttemptMark {
blocks: self.blocks.len(),
tail_assistant_len,
pending_tools: self.pending_tools.clone(),
});
}
pub(crate) fn rollback_attempt(&mut self) -> bool {
let Some(mark) = self.attempt.clone() else {
return false;
};
let discarded = self.blocks[mark.blocks.min(self.blocks.len())..]
.iter()
.any(|block| block.kind == BlockKind::Assistant && !block.text.is_empty())
|| mark
.tail_assistant_len
.zip(self.blocks.get(mark.blocks.saturating_sub(1)))
.is_some_and(|(len, block)| {
block.kind == BlockKind::Assistant && block.text.len() > len
});
self.mutate(|t| {
t.blocks.truncate(mark.blocks);
if let Some(len) = mark.tail_assistant_len
&& let Some(block) = t.blocks.last_mut()
&& block.kind == BlockKind::Assistant
{
block.text.truncate(len);
}
t.pending_tools = mark.pending_tools;
});
if discarded {
self.push(
BlockKind::System,
crate::interactive::tui::stream_events::answer::RETRY_NOTICE,
);
}
true
}
}