llm_pipeline/
streaming.rs1use serde_json::Value;
7
8use crate::output_parser::streaming::auto_complete_json;
9
10pub struct StreamingDecoder {
33 buffer: String,
34}
35
36impl StreamingDecoder {
37 pub fn new() -> Self {
39 Self {
40 buffer: String::new(),
41 }
42 }
43
44 pub fn decode(&mut self, chunk: &[u8]) -> Vec<Value> {
49 let text = String::from_utf8_lossy(chunk);
50 self.buffer.push_str(&text);
51
52 let mut values = Vec::new();
53
54 while let Some(pos) = self.buffer.find('\n') {
55 let line: String = self.buffer.drain(..=pos).collect();
56 let line = line.trim();
57 if line.is_empty() {
58 continue;
59 }
60 if let Ok(val) = serde_json::from_str::<Value>(line) {
61 values.push(val);
62 }
63 }
64
65 values
66 }
67
68 pub fn flush(&mut self) -> Option<Value> {
75 let remaining = self.buffer.trim().to_string();
76 self.buffer.clear();
77 if remaining.is_empty() {
78 return None;
79 }
80 if let Ok(val) = serde_json::from_str::<Value>(&remaining) {
82 return Some(val);
83 }
84 if let Some(completed) = auto_complete_json(&remaining) {
86 return serde_json::from_str::<Value>(&completed).ok();
87 }
88 None
89 }
90}
91
92impl Default for StreamingDecoder {
93 fn default() -> Self {
94 Self::new()
95 }
96}
97
98#[cfg(test)]
99mod tests {
100 use super::*;
101 use serde_json::json;
102
103 #[test]
104 fn test_complete_lines() {
105 let mut decoder = StreamingDecoder::new();
106 let chunk = b"{\"response\":\"hello\"}\n{\"response\":\"world\"}\n";
107 let values = decoder.decode(chunk);
108 assert_eq!(values.len(), 2);
109 assert_eq!(values[0]["response"], "hello");
110 assert_eq!(values[1]["response"], "world");
111 }
112
113 #[test]
114 fn test_split_across_chunks() {
115 let mut decoder = StreamingDecoder::new();
116
117 let values1 = decoder.decode(b"{\"response\":");
119 assert!(values1.is_empty());
120
121 let values2 = decoder.decode(b"\"hello\"}\n");
123 assert_eq!(values2.len(), 1);
124 assert_eq!(values2[0]["response"], "hello");
125 }
126
127 #[test]
128 fn test_split_mid_value() {
129 let mut decoder = StreamingDecoder::new();
130
131 let v1 = decoder.decode(b"{\"response\":\"hel");
132 assert!(v1.is_empty());
133
134 let v2 = decoder.decode(b"lo wor");
135 assert!(v2.is_empty());
136
137 let v3 = decoder.decode(b"ld\"}\n");
138 assert_eq!(v3.len(), 1);
139 assert_eq!(v3[0]["response"], "hello world");
140 }
141
142 #[test]
143 fn test_multiple_chunks_multiple_lines() {
144 let mut decoder = StreamingDecoder::new();
145
146 let v1 = decoder.decode(b"{\"a\":1}\n{\"b\":");
148 assert_eq!(v1.len(), 1);
149 assert_eq!(v1[0]["a"], 1);
150
151 let v2 = decoder.decode(b"2}\n");
152 assert_eq!(v2.len(), 1);
153 assert_eq!(v2[0]["b"], 2);
154 }
155
156 #[test]
157 fn test_empty_chunks() {
158 let mut decoder = StreamingDecoder::new();
159 let v = decoder.decode(b"");
160 assert!(v.is_empty());
161 let v = decoder.decode(b"\n\n");
162 assert!(v.is_empty());
163 }
164
165 #[test]
166 fn test_flush_remaining() {
167 let mut decoder = StreamingDecoder::new();
168 decoder.decode(b"{\"done\":true}");
169 let flushed = decoder.flush();
171 assert!(flushed.is_some());
172 assert_eq!(flushed.unwrap()["done"], json!(true));
173 }
174
175 #[test]
176 fn test_flush_empty() {
177 let mut decoder = StreamingDecoder::new();
178 assert!(decoder.flush().is_none());
179 }
180
181 #[test]
182 fn test_ollama_streaming_simulation() {
183 let mut decoder = StreamingDecoder::new();
184
185 let full_stream = concat!(
186 "{\"model\":\"llama3\",\"response\":\"Hello\"}\n",
187 "{\"model\":\"llama3\",\"response\":\" world\"}\n",
188 "{\"model\":\"llama3\",\"response\":\"!\",\"done\":true}\n",
189 );
190 let bytes = full_stream.as_bytes();
191
192 let mut all_values = Vec::new();
194 let splits = [15, 37, 60, bytes.len()];
195 let mut start = 0;
196 for &end in &splits {
197 let end = end.min(bytes.len());
198 let chunk = &bytes[start..end];
199 all_values.extend(decoder.decode(chunk));
200 start = end;
201 }
202
203 assert_eq!(all_values.len(), 3);
204 assert_eq!(all_values[0]["response"], "Hello");
205 assert_eq!(all_values[1]["response"], " world");
206 assert_eq!(all_values[2]["response"], "!");
207 assert_eq!(all_values[2]["done"], json!(true));
208 }
209
210 #[test]
211 fn test_streaming_decoder_flush_recovers_truncated() {
212 let mut decoder = StreamingDecoder::new();
213 decoder.decode(b"{\"name\": \"Alice\", \"age\": 30");
215 let flushed = decoder.flush();
216 assert!(flushed.is_some());
217 let val = flushed.unwrap();
218 assert_eq!(val["name"], "Alice");
219 assert_eq!(val["age"], 30);
220 }
221
222 #[test]
223 fn test_non_json_lines_skipped() {
224 let mut decoder = StreamingDecoder::new();
225 let chunk = b"not json\n{\"ok\":true}\ngarbage\n";
226 let values = decoder.decode(chunk);
227 assert_eq!(values.len(), 1);
228 assert_eq!(values[0]["ok"], json!(true));
229 }
230}