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