use crate::event::{Event, EventData};
use crate::message::Message;
pub fn message_from_event_data(data: &EventData) -> Option<Message> {
match data {
EventData::InputMessage { message } | EventData::OutputMessageCompleted { message, .. } => {
Some(message.clone())
}
EventData::ToolCompleted {
call_id,
output,
parts,
..
} => Some(Message::tool_result_with_parts(
call_id.clone(),
output.clone(),
parts.clone(),
)),
_ => None,
}
}
#[derive(Debug, Clone, Default, PartialEq)]
pub struct History {
messages: Vec<Message>,
}
impl History {
pub fn new() -> Self {
Self::default()
}
pub fn from_events(events: &[Event]) -> Self {
let mut ordered: Vec<&Event> = events.iter().collect();
ordered.sort_by_key(|e| e.sequence);
Self {
messages: ordered
.into_iter()
.filter_map(|event| message_from_event_data(&event.data))
.collect(),
}
}
pub fn apply(&mut self, data: &EventData) {
if let Some(message) = message_from_event_data(data) {
self.messages.push(message);
}
}
pub fn messages(&self) -> &[Message] {
&self.messages
}
pub fn is_empty(&self) -> bool {
self.messages.is_empty()
}
pub fn len(&self) -> usize {
self.messages.len()
}
}
pub fn messages_from_events(events: &[Event]) -> Vec<Message> {
History::from_events(events).messages
}
#[cfg(test)]
mod tests {
use super::*;
use crate::id::{EventId, MessageId, SessionId};
use chrono::Utc;
fn durable(session_id: SessionId, sequence: u64, data: EventData) -> Event {
crate::event::EventRequest::new(session_id, data).into_event(
EventId::new(),
Utc::now(),
sequence,
)
}
#[test]
fn applying_events_one_by_one_matches_a_replay_of_the_same_log() {
let session_id = SessionId::new();
let events = vec![
durable(
session_id,
1,
EventData::InputMessage {
message: Message::user("hi"),
},
),
durable(session_id, 2, EventData::TurnStarted { max_iterations: 16 }),
durable(
session_id,
3,
EventData::OutputMessageCompleted {
message_id: MessageId::new(),
message: Message::assistant("thinking"),
usage: crate::driver::Usage::default(),
},
),
durable(
session_id,
4,
EventData::ToolCompleted {
call_id: "call_0".into(),
name: "echo".into(),
output: "out".into(),
is_error: false,
metadata: serde_json::Value::Null,
parts: Vec::new(),
},
),
];
let mut live = History::new();
for event in &events {
live.apply(&event.data);
}
assert_eq!(live, History::from_events(&events));
assert_eq!(live.len(), 3);
}
#[test]
fn a_streaming_delta_contributes_nothing() {
let mut history = History::new();
history.apply(&EventData::OutputMessageDelta {
message_id: MessageId::new(),
delta: "hi".into(),
accumulated: "hi".into(),
});
assert!(history.is_empty());
}
}