lc_core/output_parsers/
json_parser.rs1use async_trait::async_trait;
2use futures_util::Stream;
3use std::pin::Pin;
4
5use super::base::{BaseOutputParser, OutputParserError, OutputParserResult};
6use crate::language_models::LLMResult;
7use crate::runnables::{Runnable, RunnableConfig};
8use crate::structured_output::parser::PartialJsonParser;
9
10pub struct JsonOutputParser {
30 partial: bool,
32}
33
34impl JsonOutputParser {
35 pub fn new() -> Self {
37 Self { partial: false }
38 }
39
40 pub fn new_partial() -> Self {
45 Self { partial: true }
46 }
47
48 fn extract_json_str<'a>(&self, text: &'a str) -> OutputParserResult<&'a str> {
54 let json = PartialJsonParser::strip_markdown_fence(text);
55 if json.is_empty() {
56 Ok(text.trim())
58 } else {
59 Ok(json.trim())
60 }
61 }
62}
63
64impl Default for JsonOutputParser {
65 fn default() -> Self {
66 Self::new()
67 }
68}
69
70fn preview_slice(s: &str, max_chars: usize) -> &str {
75 match s.char_indices().nth(max_chars) {
76 Some((i, _)) => &s[..i],
78 None => s,
79 }
80}
81
82#[async_trait]
83impl BaseOutputParser<serde_json::Value> for JsonOutputParser {
84 async fn parse(&self, text: &str) -> OutputParserResult<serde_json::Value> {
85 let json_str = self.extract_json_str(text)?;
86
87 if self.partial {
88 self.parse_partial_json(json_str)
89 } else {
90 serde_json::from_str(json_str).map_err(|e| {
91 OutputParserError::JsonError(format!(
92 "JSON parse failed (position {}:{}): {}, input: {}",
93 e.line(),
94 e.column(),
95 e,
96 preview_slice(json_str, 200)
97 ))
98 })
99 }
100 }
101
102 fn get_format_instructions(&self) -> String {
103 "请使用 JSON 格式输出,例如:{\"key\": \"value\"}。确保 JSON 是合法的。".to_string()
104 }
105}
106
107impl JsonOutputParser {
108 fn parse_partial_json(&self, text: &str) -> OutputParserResult<serde_json::Value> {
113 if let Ok(value) = serde_json::from_str::<serde_json::Value>(text) {
115 return Ok(value);
116 }
117
118 let repaired = self.repair_partial_json(text);
120 if let Ok(value) = serde_json::from_str::<serde_json::Value>(&repaired) {
121 return Ok(value);
122 }
123
124 Err(OutputParserError::JsonError(format!(
125 "partial JSON parse failed: {}",
126 preview_slice(text, 200)
127 )))
128 }
129
130 fn repair_partial_json(&self, text: &str) -> String {
137 let mut repaired = text.trim().to_string();
138
139 if let Some(stripped) = Self::strip_incomplete_token(&repaired) {
141 repaired = stripped;
142 }
143
144 let mut in_string = false;
146 let mut escape_next = false;
147 let mut open_braces = 0usize;
148 let mut close_braces = 0usize;
149 let mut open_brackets = 0usize;
150 let mut close_brackets = 0usize;
151
152 for ch in repaired.chars() {
153 if escape_next {
154 escape_next = false;
155 continue;
156 }
157 if ch == '\\' && in_string {
158 escape_next = true;
159 continue;
160 }
161 if ch == '"' {
162 in_string = !in_string;
163 continue;
164 }
165 if !in_string {
166 match ch {
167 '{' => open_braces += 1,
168 '}' => close_braces += 1,
169 '[' => open_brackets += 1,
170 ']' => close_brackets += 1,
171 _ => {}
172 }
173 }
174 }
175
176 for _ in close_braces..open_braces {
178 repaired.push('}');
179 }
180
181 for _ in close_brackets..open_brackets {
182 repaired.push(']');
183 }
184
185 let mut in_string = false;
188 let mut escape_next = false;
189 let mut last_open_quote_pos: Option<usize> = None;
190
191 for (i, ch) in repaired.char_indices() {
192 if escape_next {
193 escape_next = false;
194 continue;
195 }
196 if ch == '\\' && in_string {
197 escape_next = true;
198 continue;
199 }
200 if ch == '"' {
201 if in_string {
202 in_string = false;
203 last_open_quote_pos = None;
204 } else {
205 in_string = true;
206 last_open_quote_pos = Some(i);
207 }
208 continue;
209 }
210 }
211
212 if in_string {
215 if let Some(open_pos) = last_open_quote_pos {
217 let after_quote = &repaired[open_pos + 1..];
218 if after_quote.contains('\n') {
219 if let Some(pos) = repaired[open_pos + 1..].find('\n') {
221 let newline_pos = pos + open_pos + 1;
222 repaired.truncate(newline_pos);
223 repaired.push('"');
224 }
225 }
226 }
227 }
228
229 repaired
230 }
231
232 fn strip_incomplete_token(s: &str) -> Option<String> {
234 let trimmed = s.trim_end();
235
236 let chars: Vec<char> = trimmed.chars().collect();
240 if chars.is_empty() {
241 return None;
242 }
243
244 let mut i = chars.len();
246 while i > 0 {
247 i -= 1;
248 match chars[i] {
249 ',' | ':' | '{' | '[' | '}' | ']' => {
250 let truncate_at: usize = trimmed
252 .char_indices()
253 .nth(i + 1)
254 .map(|(pos, _)| pos)
255 .unwrap_or(trimmed.len());
256 if truncate_at < s.len() {
257 let result = trimmed[..truncate_at].to_string();
258 if result != s.trim_end() {
259 return Some(result);
260 }
261 }
262 return None;
263 }
264 '"' => {
265 return None;
268 }
269 _ => {}
270 }
271 }
272
273 None
274 }
275}
276
277#[async_trait]
278impl Runnable<LLMResult, serde_json::Value> for JsonOutputParser {
279 type Error = OutputParserError;
280
281 async fn invoke(
282 &self,
283 input: LLMResult,
284 _config: Option<RunnableConfig>,
285 ) -> Result<serde_json::Value, Self::Error> {
286 self.parse(&input.content).await
287 }
288
289 async fn stream(
290 &self,
291 input: LLMResult,
292 _config: Option<RunnableConfig>,
293 ) -> Result<
294 Pin<Box<dyn Stream<Item = Result<serde_json::Value, Self::Error>> + Send>>,
295 Self::Error,
296 > {
297 let result = self.parse(&input.content).await?;
298 let stream = futures_util::stream::once(async move { Ok(result) });
299 Ok(Box::pin(stream))
300 }
301}
302
303#[cfg(test)]
304mod tests {
305 use super::*;
306
307 #[tokio::test]
308 async fn test_json_parser_standard_obj() {
309 let parser = JsonOutputParser::new();
310 let result = parser
311 .parse(r#"{"name": "Rust", "year": 2015}"#)
312 .await
313 .unwrap();
314 assert_eq!(result["name"], "Rust");
315 assert_eq!(result["year"], 2015);
316 }
317
318 #[tokio::test]
319 async fn test_json_parser_from_markdown_block() {
320 let parser = JsonOutputParser::new();
321 let input = "以下是结果:\n```json\n{\"status\": \"ok\"}\n```\n";
322 let result = parser.parse(input).await.unwrap();
323 assert_eq!(result["status"], "ok");
324 }
325
326 #[tokio::test]
327 async fn test_json_parser_array() {
328 let parser = JsonOutputParser::new();
329 let result = parser.parse("[1, 2, 3]").await.unwrap();
330 assert_eq!(result[0], 1);
331 assert_eq!(result[2], 3);
332 }
333
334 #[tokio::test]
335 async fn test_json_parser_from_markdown_block_unclosed_fence() {
336 let parser = JsonOutputParser::new();
338 let input = "以下是结果:\n```json\n{\"status\": \"ok\"}";
339 let result = parser.parse(input).await.unwrap();
340 assert_eq!(result["status"], "ok");
341 }
342
343 #[tokio::test]
344 async fn test_json_parser_from_prose_prefix() {
345 let parser = JsonOutputParser::new();
347 let input = "结果是:\n{\"a\": 1}\n以上";
348 let result = parser.parse(input).await.unwrap();
349 assert_eq!(result["a"], 1);
350 }
351
352 #[tokio::test]
353 async fn test_json_parser_invalid_json() {
354 let parser = JsonOutputParser::new();
355 let result = parser.parse("{invalid}").await;
356 assert!(result.is_err());
357 }
358
359 #[tokio::test]
360 async fn test_json_parser_format_instructions() {
361 let parser = JsonOutputParser::new();
362 let instructions = parser.get_format_instructions();
363 assert!(!instructions.is_empty());
364 }
365
366 #[tokio::test]
367 async fn test_json_parser_invoke_runnable() {
368 let parser = JsonOutputParser::new();
370 let result = parser
371 .invoke(
372 LLMResult {
373 content: r#"{"key": "value"}"#.to_string(),
374 ..Default::default()
375 },
376 None,
377 )
378 .await
379 .unwrap();
380 assert_eq!(result["key"], "value");
381 }
382
383 #[tokio::test]
384 async fn test_json_parser_partial_success() {
385 let parser = JsonOutputParser::new_partial();
386 let result = parser.parse(r#"{"a": 1}"#).await.unwrap();
388 assert_eq!(result["a"], 1);
389 }
390
391 #[tokio::test]
392 async fn test_json_parser_invalid_cjk_over_200_bytes() {
393 let parser = JsonOutputParser::new();
396 let long_cjk = "汉".repeat(200);
397 let bad = format!("{{\"名字\": {}", long_cjk);
398 let result = parser.parse(&bad).await;
399 assert!(result.is_err());
400 }
401
402 #[tokio::test]
403 async fn test_json_parser_partial_invalid_cjk_over_200_bytes() {
404 let parser = JsonOutputParser::new_partial();
406 let long_cjk = "汉".repeat(200);
407 let bad = format!("{{\"名字\": {}", long_cjk);
408 let result = parser.parse(&bad).await;
409 assert!(result.is_err());
410 }
411}