use crate::reply::agent::{Agent, Fullness, Offer};
use crate::reply::spend::Figure;
use crate::ui::{Model, theme};
pub const HEAD: &str = "the conversation";
pub const NOT_ANSWERED: &str = "waiting to hear what this conversation is";
pub const DISPLAY_ONLY: &str = "a display name only — nothing can be addressed by it";
pub const ABSENT: &str = "the engine's snapshot does not carry it";
pub const NOTHING_OFFERED: &str = "the engine offers nothing on it";
pub fn render(ui: &mut egui::Ui, model: &mut Model) {
theme::paint::section(ui, HEAD);
let Some(row) = model.records.agent.clone() else {
ui.label(NOT_ANSWERED);
return;
};
let row = &row;
ui.label(named(row));
ui.horizontal_wrapped(|ui| {
if row.display_only {
ui.colored_label(theme::accent(theme::State::Annotation), DISPLAY_ONLY);
}
if let Some(said) = row.failure.clone() {
ui.colored_label(theme::accent(theme::State::Error), said);
}
if let Some(said) = parked(row) {
ui.colored_label(theme::accent(theme::State::Annotation), said);
}
});
ui.horizontal_wrapped(|ui| {
if let Some(said) = about(row) {
ui.colored_label(theme::INK_WEAK, said);
}
ui.label(costing(row));
});
ui.horizontal_wrapped(|ui| {
ui.colored_label(theme::INK_WEAK, doing(row));
super::cascade::control(ui, model, row);
});
}
const JOIN: &str = " · ";
pub fn about(row: &Agent) -> Option<String> {
let said: Vec<String> = [descent(row), marked(row), seated(row)]
.into_iter()
.flatten()
.collect();
(!said.is_empty()).then(|| said.join(JOIN))
}
pub fn costing(row: &Agent) -> String {
let said = spent(&row.spend);
match &row.context {
Some(full) => format!("{said}{JOIN}{}", contextual(full)),
None => said,
}
}
pub fn doing(row: &Agent) -> String {
match flighted(row) {
Some(said) => format!("{said}{JOIN}{}", offered(row)),
None => offered(row),
}
}
pub fn named(row: &Agent) -> String {
format!("{} — {} — at {}", row.display, resting(row), tipped(row))
}
fn tipped(row: &Agent) -> String {
if row.tip.is_empty() {
return "no branch tip".to_owned();
}
row.tip.clone()
}
pub fn resting(row: &Agent) -> String {
let said = row.state.label();
if row.refused {
return format!("{said} — the provider refused the latest turn");
}
said
}
pub fn flighted(row: &Agent) -> Option<String> {
match (&row.flight, &row.strip) {
(_, Some(strip)) => Some(format!("{} — {}", strip.class, strip.facts)),
(Some(class), None) => Some(format!("in flight: {class}")),
(None, None) => None,
}
}
pub fn parked(row: &Agent) -> Option<String> {
let held = row.held.as_ref()?;
Some(format!(
"held at {} ({}) — {}",
held.tool, held.tool_use, held.reason
))
}
pub fn descent(row: &Agent) -> Option<String> {
if row.ancestors.is_empty() {
return None;
}
Some(format!("under {}", row.ancestors.join(" / ")))
}
pub fn marked(row: &Agent) -> Option<String> {
(!row.marks.is_empty()).then(|| format!("marked {}", row.marks.join(", ")))
}
pub fn seated(row: &Agent) -> Option<String> {
if row.seats.is_empty() {
return None;
}
let said: Vec<String> = row
.seats
.iter()
.map(|seat| format!("{} {}", seat.name, seat.doing))
.collect();
Some(said.join(", "))
}
pub fn spent(figure: &Figure) -> String {
let mut said = format!("{} tokens", figure.tokens.total);
if let Some(usd) = &figure.usd {
said = format!("{said} — {usd}");
}
match &figure.attribution.label {
Some(label) => format!("{said}, {label}"),
None => said,
}
}
pub fn contextual(full: &Fullness) -> String {
format!(
"context {}% — {} of {} for {}",
full.percent, full.prompt_tokens, full.window, full.model
)
}
pub fn offered(row: &Agent) -> String {
if !row.present {
return ABSENT.to_owned();
}
if row.offers.is_empty() {
return NOTHING_OFFERED.to_owned();
}
let words: Vec<&str> = row.offers.iter().copied().map(word).collect();
format!("the engine offers {}", words.join(", "))
}
pub(crate) fn word(offer: Offer) -> &'static str {
match offer {
Offer::Nudge => "nudge",
Offer::Stop => "stop",
Offer::Children => "stop with its children",
}
}
#[cfg(test)]
mod tests;