crabtalk_runtime/
conversation.rs1use anyhow::{Context, Result};
4use serde::{Deserialize, Serialize};
5use std::{
6 fs,
7 io::{BufRead, BufReader},
8 path::Path,
9};
10use wcore::model::Message;
11
12#[derive(Debug, Clone, Serialize, Deserialize)]
14pub struct ConversationMeta {
15 pub agent: String,
16 pub created_by: String,
17 pub created_at: String,
18}
19
20pub fn list_conversations(conversations_dir: &Path) -> Result<Vec<(String, ConversationMeta)>> {
22 let mut results = Vec::new();
23
24 let entries = match fs::read_dir(conversations_dir) {
25 Ok(e) => e,
26 Err(_) => return Ok(results),
27 };
28
29 for entry in entries.flatten() {
30 let path = entry.path();
31 if path.extension().and_then(|e| e.to_str()) != Some("jsonl") {
32 continue;
33 }
34 let stem = path
35 .file_stem()
36 .and_then(|s| s.to_str())
37 .unwrap_or("")
38 .to_owned();
39
40 if let Ok(meta) = read_meta(&path) {
41 results.push((stem, meta));
42 }
43 }
44
45 results.sort_by(|a, b| b.1.created_at.cmp(&a.1.created_at));
46 Ok(results)
47}
48
49pub fn load_conversation(path: &Path) -> Result<(ConversationMeta, Vec<Message>)> {
51 let file = fs::File::open(path)
52 .with_context(|| format!("open conversation file: {}", path.display()))?;
53 let reader = BufReader::new(file);
54 let mut lines = reader.lines();
55
56 let meta_line = lines
57 .next()
58 .ok_or_else(|| anyhow::anyhow!("empty conversation file"))?
59 .context("read meta line")?;
60 let meta: ConversationMeta =
61 serde_json::from_str(&meta_line).context("parse conversation meta")?;
62
63 let mut messages = Vec::new();
64 for line in lines {
65 let line = line.context("read message line")?;
66 if line.trim().is_empty() {
67 continue;
68 }
69 let msg: Message = serde_json::from_str(&line).context("parse message")?;
70 messages.push(msg);
71 }
72
73 Ok((meta, messages))
74}
75
76fn read_meta(path: &Path) -> Result<ConversationMeta> {
78 let file = fs::File::open(path)?;
79 let mut reader = BufReader::new(file);
80 let mut line = String::new();
81 reader.read_line(&mut line)?;
82 Ok(serde_json::from_str(line.trim())?)
83}