use super::{BlockKind, Transcript};
use saya_agent::AgentEvent;
use super::super::table;
pub(crate) fn push_assistant_text(transcript: &mut Transcript, text: &str) {
if transcript
.blocks()
.last()
.is_some_and(|b| b.kind == BlockKind::System && b.text == RETRY_NOTICE)
&& live_answer_index(transcript).is_some()
{
transcript.reformat_last(BlockKind::Assistant, |current| format!("{current}{text}"));
return;
}
if !matches!(
transcript.blocks().last().map(|b| b.kind),
Some(BlockKind::Assistant)
) {
if transcript
.blocks()
.last()
.is_some_and(|b| !b.text.is_empty())
{
transcript.push(BlockKind::System, String::new());
}
}
transcript.append_delta(BlockKind::Assistant, text);
}
pub(crate) const RETRY_NOTICE: &str = "provider stream interrupted — retrying";
fn live_answer_index(transcript: &Transcript) -> Option<usize> {
let blocks = transcript.blocks();
let chapter = blocks.last()?.chapter;
let index = blocks
.iter()
.rposition(|block| block.kind == BlockKind::Assistant && block.chapter == chapter)?;
match transcript.attempt_start() {
Some(start) if index < start => None,
_ => Some(index),
}
}
pub(crate) fn reset_answer(transcript: &mut Transcript) {
let Some(index) = live_answer_index(transcript) else {
return;
};
let discarded = !transcript.blocks()[index].text.is_empty();
if transcript
.blocks()
.last()
.is_some_and(|block| block.kind == BlockKind::Assistant)
{
transcript.reset_delta(BlockKind::Assistant);
} else {
transcript.reformat_last(BlockKind::Assistant, |_| String::new());
}
if discarded {
transcript.push(BlockKind::System, RETRY_NOTICE);
}
}
pub(crate) fn finish_answer(transcript: &mut Transcript) {
transcript.reformat_last(BlockKind::Assistant, table::format_markdown_tables);
}
#[allow(dead_code)]
pub(crate) fn is_answer_event(event: &AgentEvent) -> bool {
matches!(
event,
AgentEvent::AssistantText { .. } | AgentEvent::TurnReset | AgentEvent::Complete
)
}
pub(crate) fn apply_answer_event(transcript: &mut Transcript, event: AgentEvent) -> bool {
match event {
AgentEvent::AssistantText { text } => {
push_assistant_text(transcript, &text);
true
}
AgentEvent::TurnReset => {
reset_answer(transcript);
true
}
AgentEvent::Complete => {
finish_answer(transcript);
true
}
_ => false,
}
}