eval_magic/adapters/
transcript.rs1use serde::{Deserialize, Serialize};
8use std::fs;
9use std::io;
10use std::path::Path;
11
12use crate::core::ToolInvocation;
13
14pub(crate) fn read_jsonl<T: serde::de::DeserializeOwned>(path: &Path) -> io::Result<Vec<T>> {
18 let raw = fs::read_to_string(path)?;
19 Ok(raw
20 .split('\n')
21 .filter(|line| !line.trim().is_empty())
22 .filter_map(|line| serde_json::from_str::<T>(line).ok())
23 .collect())
24}
25
26#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
28pub struct TranscriptSummary {
29 pub tool_invocations: Vec<ToolInvocation>,
30 pub total_tokens: Option<i64>,
33 pub duration_ms: Option<i64>,
35 pub final_text: Option<String>,
37}
38
39#[cfg(test)]
40mod tests {
41 use super::read_jsonl;
42 use serde_json::Value;
43 use std::fs;
44 use tempfile::TempDir;
45
46 #[test]
47 fn read_jsonl_skips_malformed_lines() {
48 let dir = TempDir::new().unwrap();
49 let path = dir.path().join("events.jsonl");
50 fs::write(&path, "{\"a\":1}\nnot json\n{\"a\":2}\n").unwrap();
51 let values: Vec<Value> = read_jsonl(&path).unwrap();
52 assert_eq!(values.len(), 2);
53 assert_eq!(values[0]["a"], 1);
54 assert_eq!(values[1]["a"], 2);
55 }
56
57 #[test]
58 fn read_jsonl_skips_blank_and_whitespace_only_lines() {
59 let dir = TempDir::new().unwrap();
60 let path = dir.path().join("events.jsonl");
61 fs::write(&path, "{\"a\":1}\n\n \n\t\n{\"a\":2}\n").unwrap();
62 let values: Vec<Value> = read_jsonl(&path).unwrap();
63 assert_eq!(values.len(), 2);
64 }
65
66 #[test]
67 fn read_jsonl_errors_on_a_missing_file() {
68 let dir = TempDir::new().unwrap();
69 let missing = dir.path().join("absent.jsonl");
70 assert!(read_jsonl::<Value>(&missing).is_err());
71 }
72}