1use lago_core::EventEnvelope;
2
3use super::format::{SseFormat, SseFrame};
4
5pub struct LagoFormat;
8
9impl SseFormat for LagoFormat {
10 fn format(&self, event: &EventEnvelope) -> Vec<SseFrame> {
11 let data = match serde_json::to_string(event) {
12 Ok(json) => json,
13 Err(e) => {
14 tracing::warn!(error = %e, "failed to serialize event envelope");
15 return Vec::new();
16 }
17 };
18
19 vec![SseFrame {
20 event: Some("event".to_string()),
21 data,
22 id: Some(event.seq.to_string()),
23 }]
24 }
25
26 fn done_frame(&self) -> Option<SseFrame> {
27 Some(SseFrame {
28 event: Some("done".to_string()),
29 data: r#"{"type":"done"}"#.to_string(),
30 id: None,
31 })
32 }
33
34 fn extra_headers(&self) -> Vec<(String, String)> {
35 Vec::new()
36 }
37
38 fn name(&self) -> &str {
39 "lago"
40 }
41}
42
43#[cfg(test)]
44mod tests {
45 use super::*;
46 use lago_core::event::EventPayload;
47 use lago_core::id::*;
48 use std::collections::HashMap;
49
50 fn make_envelope(payload: EventPayload, seq: u64) -> EventEnvelope {
51 EventEnvelope {
52 event_id: EventId::from_string("EVT001"),
53 session_id: SessionId::from_string("SESS001"),
54 branch_id: BranchId::from_string("main"),
55 run_id: None,
56 seq,
57 timestamp: 1_700_000_000_000_000,
58 parent_id: None,
59 payload,
60 metadata: HashMap::new(),
61 }
62 }
63
64 #[test]
65 fn lago_format_passes_through_all_events() {
66 let fmt = LagoFormat;
67
68 let event = make_envelope(
70 EventPayload::Message {
71 role: "user".into(),
72 content: "hi".into(),
73 model: None,
74 token_usage: None,
75 },
76 1,
77 );
78 let frames = fmt.format(&event);
79 assert_eq!(frames.len(), 1);
80 assert_eq!(frames[0].event.as_deref(), Some("event"));
81 assert_eq!(frames[0].id.as_deref(), Some("1"));
82
83 let back: EventEnvelope = serde_json::from_str(&frames[0].data).unwrap();
85 assert_eq!(back.event_id.as_str(), "EVT001");
86 }
87
88 #[test]
89 fn lago_format_includes_file_events() {
90 let fmt = LagoFormat;
91 let event = make_envelope(
92 EventPayload::FileDelete {
93 path: "/tmp/x".into(),
94 },
95 5,
96 );
97 let frames = fmt.format(&event);
98 assert_eq!(frames.len(), 1);
99 let back: EventEnvelope = serde_json::from_str(&frames[0].data).unwrap();
101 if let EventPayload::FileDelete { path } = &back.payload {
102 assert_eq!(path, "/tmp/x");
103 } else {
104 panic!("wrong payload");
105 }
106 }
107
108 #[test]
109 fn lago_done_frame() {
110 let fmt = LagoFormat;
111 let done = fmt.done_frame().unwrap();
112 assert_eq!(done.event.as_deref(), Some("done"));
113 assert_eq!(done.data, r#"{"type":"done"}"#);
114 }
115
116 #[test]
117 fn lago_no_extra_headers() {
118 assert!(LagoFormat.extra_headers().is_empty());
119 }
120
121 #[test]
122 fn name_is_lago() {
123 assert_eq!(LagoFormat.name(), "lago");
124 }
125}