Skip to main content

rig_memory/
listing.rs

1//! One agent's conversation of a saved session, read without the rest
2//! ([`read_conversation`]), as a session picker previews it.
3
4use std::io;
5
6use rig_core::conversation::Conversation;
7
8use crate::format::SESSION;
9use crate::session::{Fold, Folded, fold, log_lines, read_header};
10use crate::{SessionError, Store, main_order};
11
12/// The conversation of `agent` (the main agent when `None`, found from the
13/// logs' headers alone) of the session in `store`, folded from that
14/// agent's log only, writing nothing. Returns the agent's id and its
15/// conversation, with ids and made-times kept.
16///
17/// # Errors
18///
19/// As [`load`](crate::load), or when there is no such agent.
20pub fn read_conversation(
21    store: &dyn Store,
22    agent: Option<&str>,
23) -> Result<(String, Conversation), SessionError> {
24    let how = Fold::WHOLE;
25    let folded = match agent {
26        Some(agent) => fold(store, agent, how)?,
27        None => main_log(store, how)?,
28    };
29    let Some(folded) = folded.filter(|folded| folded.name != SESSION) else {
30        let why = format!("the session has no agent {}", agent.unwrap_or("to talk to"));
31        return Err(io::Error::new(io::ErrorKind::NotFound, why).into());
32    };
33    Ok((folded.name, folded.conversation))
34}
35
36/// The main agent's log, folded: of the logs whose header names no parent,
37/// the first by [`main_order`] that is not retired, as
38/// [`Session::main_agent`](crate::Session::main_agent) finds it; only its
39/// candidates are folded.
40fn main_log(store: &dyn Store, how: Fold) -> Result<Option<Folded>, SessionError> {
41    let mut candidates = Vec::new();
42    for name in store.logs()? {
43        if name == SESSION {
44            continue;
45        }
46        let Some(lines) = log_lines(store, &name)? else {
47            continue;
48        };
49        if let Some((_, _, first)) = lines.iter().next() {
50            let (_, header) = read_header(&name, first)?;
51            if header.parent.is_none() {
52                candidates.push(name);
53            }
54        }
55    }
56    candidates.sort_by(|a, b| main_order(false, a).cmp(&main_order(false, b)));
57    for name in candidates {
58        if let Some(folded) = fold(store, &name, how)?.filter(|folded| !folded.retired) {
59            return Ok(Some(folded));
60        }
61    }
62    Ok(None)
63}
64
65#[cfg(test)]
66mod tests;