use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};
use super::{normalize_source_ref, CanonicalisedSource};
use crate::memory::chunks::{Metadata, SourceKind};
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct ChatMessage {
pub author: String,
#[serde(
serialize_with = "chrono::serde::ts_milliseconds::serialize",
deserialize_with = "super::deserialize_flexible_timestamp"
)]
pub timestamp: DateTime<Utc>,
pub text: String,
#[serde(default)]
pub source_ref: Option<String>,
}
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct ChatBatch {
pub platform: String,
pub channel_label: String,
pub messages: Vec<ChatMessage>,
}
pub fn canonicalise(
source_id: &str,
owner: &str,
tags: &[String],
batch: ChatBatch,
) -> Result<Option<CanonicalisedSource>, String> {
if batch.messages.is_empty() {
return Ok(None);
}
let mut messages = batch.messages;
messages.sort_by_key(|m| m.timestamp);
let first_ts = messages.first().map(|m| m.timestamp).unwrap();
let last_ts = messages.last().map(|m| m.timestamp).unwrap();
let mut md = String::new();
for msg in &messages {
md.push_str(&format!(
"## {} — {}\n{}\n\n",
msg.timestamp.to_rfc3339(),
msg.author,
msg.text.trim()
));
}
let source_ref = normalize_source_ref(messages.first().and_then(|m| m.source_ref.clone()));
let metadata = Metadata {
source_kind: SourceKind::Chat,
source_id: source_id.to_string(),
owner: owner.to_string(),
timestamp: first_ts,
time_range: (first_ts, last_ts),
tags: tags.to_vec(),
source_ref,
path_scope: None,
};
Ok(Some(CanonicalisedSource {
markdown: md,
metadata,
}))
}
#[cfg(test)]
#[path = "chat_tests.rs"]
mod tests;