lc_core/output_parsers/
structured_parser.rs1use async_trait::async_trait;
2use futures_util::Stream;
3use serde::de::DeserializeOwned;
4use std::collections::HashMap;
5use std::marker::PhantomData;
6use std::pin::Pin;
7
8use super::base::{BaseOutputParser, OutputParserError, OutputParserResult};
9use crate::language_models::LLMResult;
10use crate::runnables::{Runnable, RunnableConfig};
11
12pub struct StructuredOutputParser {
34 separator: char,
36}
37
38impl StructuredOutputParser {
39 pub fn new() -> Self {
40 Self { separator: ':' }
41 }
42
43 pub fn with_separator(separator: char) -> Self {
45 Self { separator }
46 }
47}
48
49impl Default for StructuredOutputParser {
50 fn default() -> Self {
51 Self::new()
52 }
53}
54
55#[async_trait]
56impl BaseOutputParser<HashMap<String, String>> for StructuredOutputParser {
57 async fn parse(&self, text: &str) -> OutputParserResult<HashMap<String, String>> {
58 let mut map = HashMap::new();
59
60 for line in text.lines() {
61 let line = line.trim();
62 if line.is_empty() {
63 continue;
64 }
65
66 if let Some(pos) = line.find(self.separator) {
67 let sep_len = self.separator.len_utf8();
70 let key = line[..pos].trim().to_string();
71 let value = line[pos + sep_len..].trim().to_string();
72
73 if !key.is_empty() {
74 map.insert(key, value);
75 }
76 }
77 }
78
79 Ok(map)
80 }
81
82 fn get_format_instructions(&self) -> String {
83 format!(
84 "请按以下格式输出(每行一个键值对,使用 '{}' 分隔):\n键{}值",
85 self.separator, self.separator
86 )
87 }
88}
89
90#[async_trait]
91impl Runnable<LLMResult, HashMap<String, String>> for StructuredOutputParser {
92 type Error = OutputParserError;
93
94 async fn invoke(
95 &self,
96 input: LLMResult,
97 _config: Option<RunnableConfig>,
98 ) -> Result<HashMap<String, String>, Self::Error> {
99 self.parse(&input.content).await
100 }
101
102 async fn stream(
103 &self,
104 input: LLMResult,
105 _config: Option<RunnableConfig>,
106 ) -> Result<
107 Pin<Box<dyn Stream<Item = Result<HashMap<String, String>, Self::Error>> + Send>>,
108 Self::Error,
109 > {
110 let result = self.parse(&input.content).await?;
111 let stream = futures_util::stream::once(async move { Ok(result) });
112 Ok(Box::pin(stream))
113 }
114}
115
116pub struct TypedOutputParser<T> {
139 _phantom: PhantomData<T>,
140}
141
142impl<T> TypedOutputParser<T> {
143 pub fn new() -> Self {
144 Self {
145 _phantom: PhantomData,
146 }
147 }
148}
149
150impl<T: DeserializeOwned> Default for TypedOutputParser<T> {
151 fn default() -> Self {
152 Self::new()
153 }
154}
155
156#[async_trait]
157impl<T: DeserializeOwned + Send + Sync + 'static> BaseOutputParser<T> for TypedOutputParser<T> {
158 async fn parse(&self, text: &str) -> OutputParserResult<T> {
159 let text = text.trim();
160
161 let json_str = Self::extract_from_markdown(text).unwrap_or(text);
163
164 serde_json::from_str::<serde_json::Value>(json_str)
166 .map_err(|e| OutputParserError::JsonError(format!("输入不是合法 JSON:{}", e)))?;
167
168 serde_json::from_str::<T>(json_str).map_err(|e| {
170 OutputParserError::TypeError(format!(
171 "类型反序列化失败(请检查 JSON 字段是否匹配):{}",
172 e
173 ))
174 })
175 }
176}
177
178impl<T: DeserializeOwned> TypedOutputParser<T> {
179 fn extract_from_markdown(text: &str) -> Option<&str> {
181 if let Some(start) = text.find("```json") {
183 let after = &text[start + 7..];
184 if let Some(end) = after.find("```") {
185 return Some(after[..end].trim());
186 }
187 }
188 if let Some(start) = text.find("```") {
190 let after = &text[start + 3..];
191 let after = after.trim();
192 let skip = after.find('\n').unwrap_or(0);
193 let after = &after[skip..].trim();
194 if let Some(end) = after.find("```") {
195 return Some(after[..end].trim());
196 }
197 }
198 None
199 }
200}
201
202#[async_trait]
203impl<T: DeserializeOwned + Send + Sync + 'static> Runnable<LLMResult, T> for TypedOutputParser<T> {
204 type Error = OutputParserError;
205
206 async fn invoke(
207 &self,
208 input: LLMResult,
209 _config: Option<RunnableConfig>,
210 ) -> Result<T, Self::Error> {
211 self.parse(&input.content).await
212 }
213
214 async fn stream(
215 &self,
216 input: LLMResult,
217 _config: Option<RunnableConfig>,
218 ) -> Result<Pin<Box<dyn Stream<Item = Result<T, Self::Error>> + Send>>, Self::Error> {
219 let result = self.parse(&input.content).await?;
220 let stream = futures_util::stream::once(async move { Ok(result) });
221 Ok(Box::pin(stream))
222 }
223}
224
225#[cfg(test)]
226mod tests {
227 use super::*;
228
229 #[tokio::test]
230 async fn test_structured_parser_default_colon() {
231 let parser = StructuredOutputParser::new();
232 let map = parser.parse("姓名: 张三\n年龄: 28").await.unwrap();
233 assert_eq!(map.get("姓名").unwrap(), "张三");
234 assert_eq!(map.get("年龄").unwrap(), "28");
235 }
236
237 #[tokio::test]
238 async fn test_structured_parser_fullwidth_separator() {
239 let parser = StructuredOutputParser::with_separator(':');
241 let map = parser.parse("姓名:张三\n年龄:28").await.unwrap();
242 assert_eq!(map.get("姓名").unwrap(), "张三");
243 assert_eq!(map.get("年龄").unwrap(), "28");
244 }
245
246 #[tokio::test]
247 async fn test_structured_parser_runnable_invoke() {
248 let parser = StructuredOutputParser::new();
249 let map = parser
250 .invoke(
251 LLMResult {
252 content: "状态: 成功".to_string(),
253 ..Default::default()
254 },
255 None,
256 )
257 .await
258 .unwrap();
259 assert_eq!(map.get("状态").unwrap(), "成功");
260 }
261}