use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SummaryRecord {
pub id: String,
pub session_id: String,
pub created_at: DateTime<Utc>,
pub title: String,
pub body: String,
pub files: Vec<String>,
pub decisions: Vec<String>,
pub next_steps: Vec<String>,
pub tool_calls: u64,
}
impl SummaryRecord {
pub(crate) fn searchable_text(&self) -> String {
let mut t = String::with_capacity(self.title.len() + self.body.len() + 16);
t.push_str(&self.title);
t.push('\n');
t.push_str(&self.body);
t
}
fn with_text(&self, admit: impl Fn(&str) -> Option<String>) -> Option<Self> {
let admit = |text: &str| {
if text.is_empty() {
Some(String::new())
} else {
admit(text)
}
};
let each = |items: &[String]| -> Option<Vec<String>> {
items.iter().map(|item| admit(item)).collect()
};
Some(Self {
title: admit(&self.title)?,
body: admit(&self.body)?,
decisions: each(&self.decisions)?,
next_steps: each(&self.next_steps)?,
..self.clone()
})
}
pub(crate) fn admitted_for_store(&self) -> Option<Self> {
self.with_text(|text| crate::core::context_admission::carrier::admit_for_store(text).ok())
}
pub(crate) fn served(&self) -> Option<Self> {
self.with_text(|text| {
crate::core::context_admission::stored::serve_stored(text, None, None).ok()
})
}
}
#[derive(Debug, Clone)]
pub struct SummaryCandidate {
pub session_id: String,
pub created_at: DateTime<Utc>,
pub title: String,
pub body: String,
pub files: Vec<String>,
pub decisions: Vec<String>,
pub next_steps: Vec<String>,
pub tool_calls: u64,
pub has_content: bool,
}
impl SummaryCandidate {
pub(crate) fn into_record(self, seq: u32) -> SummaryRecord {
let short = self
.session_id
.split('-')
.next()
.unwrap_or(&self.session_id);
SummaryRecord {
id: format!("{short}-{seq:04}"),
session_id: self.session_id,
created_at: self.created_at,
title: self.title,
body: self.body,
files: self.files,
decisions: self.decisions,
next_steps: self.next_steps,
tool_calls: self.tool_calls,
}
}
}