agent_commander/streaming/
input_stream.rs1use super::ndjson::stringify_ndjson_line;
5use serde_json::{json, Value};
6
7#[derive(Debug, Clone)]
10pub struct JsonInputStream {
11 compact: bool,
12 messages: Vec<Value>,
13}
14
15impl Default for JsonInputStream {
16 fn default() -> Self {
17 Self::new(true)
18 }
19}
20
21impl JsonInputStream {
22 pub fn new(compact: bool) -> Self {
27 Self {
28 compact,
29 messages: Vec::new(),
30 }
31 }
32
33 pub fn add(&mut self, message: Value) -> &mut Self {
41 if !message.is_null() {
42 self.messages.push(message);
43 }
44 self
45 }
46
47 pub fn add_prompt(&mut self, content: &str) -> &mut Self {
55 self.add(json!({
56 "type": "user_prompt",
57 "content": content
58 }))
59 }
60
61 pub fn add_system_message(&mut self, content: &str) -> &mut Self {
69 self.add(json!({
70 "type": "system",
71 "content": content
72 }))
73 }
74
75 pub fn add_config(&mut self, config: Value) -> &mut Self {
83 let mut msg = json!({"type": "config"});
84 if let (Some(obj), Some(cfg)) = (msg.as_object_mut(), config.as_object()) {
85 for (k, v) in cfg {
86 obj.insert(k.clone(), v.clone());
87 }
88 }
89 self.add(msg)
90 }
91
92 pub fn to_string(&self) -> String {
94 self.messages
95 .iter()
96 .map(|msg| stringify_ndjson_line(msg, self.compact))
97 .collect()
98 }
99
100 pub fn to_bytes(&self) -> Vec<u8> {
102 self.to_string().into_bytes()
103 }
104
105 pub fn size(&self) -> usize {
107 self.messages.len()
108 }
109
110 pub fn clear(&mut self) -> &mut Self {
112 self.messages.clear();
113 self
114 }
115
116 pub fn get_messages(&self) -> &[Value] {
118 &self.messages
119 }
120
121 pub fn from_messages(messages: Vec<Value>, compact: bool) -> Self {
127 Self { compact, messages }
128 }
129}
130
131impl std::fmt::Display for JsonInputStream {
132 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
133 write!(f, "{}", self.to_string())
134 }
135}
136
137#[cfg(test)]
138mod tests {
139 use super::*;
140 use serde_json::json;
141
142 #[test]
143 fn test_add_message() {
144 let mut stream = JsonInputStream::new(true);
145 stream.add(json!({"type": "hello"}));
146 assert_eq!(stream.size(), 1);
147 }
148
149 #[test]
150 fn test_to_string_produces_ndjson() {
151 let mut stream = JsonInputStream::new(true);
152 stream.add(json!({"a": 1}));
153 stream.add(json!({"b": 2}));
154
155 let output = stream.to_string();
156 assert_eq!(output, "{\"a\":1}\n{\"b\":2}\n");
157 }
158
159 #[test]
160 fn test_add_prompt() {
161 let mut stream = JsonInputStream::new(true);
162 stream.add_prompt("Hello");
163
164 let messages = stream.get_messages();
165 assert_eq!(messages.len(), 1);
166 assert_eq!(messages[0]["type"], "user_prompt");
167 assert_eq!(messages[0]["content"], "Hello");
168 }
169
170 #[test]
171 fn test_add_system_message() {
172 let mut stream = JsonInputStream::new(true);
173 stream.add_system_message("You are helpful");
174
175 let messages = stream.get_messages();
176 assert_eq!(messages[0]["type"], "system");
177 }
178
179 #[test]
180 fn test_chaining() {
181 let mut stream = JsonInputStream::new(true);
182 stream
183 .add_system_message("System")
184 .add_prompt("User")
185 .add(json!({"custom": true}));
186
187 assert_eq!(stream.size(), 3);
188 }
189
190 #[test]
191 fn test_clear() {
192 let mut stream = JsonInputStream::new(true);
193 stream.add(json!({"a": 1}));
194 stream.clear();
195 assert_eq!(stream.size(), 0);
196 }
197
198 #[test]
199 fn test_from_messages() {
200 let messages = vec![json!({"a": 1}), json!({"b": 2})];
201 let stream = JsonInputStream::from_messages(messages, true);
202 assert_eq!(stream.size(), 2);
203 }
204
205 #[test]
206 fn test_to_bytes() {
207 let mut stream = JsonInputStream::new(true);
208 stream.add(json!({"test": true}));
209
210 let bytes = stream.to_bytes();
211 let str_result = String::from_utf8(bytes).unwrap();
212 assert_eq!(str_result, "{\"test\":true}\n");
213 }
214}