lc_core/output_parsers/base.rs
1use async_trait::async_trait;
2
3/// Unified error type for output parsers
4#[derive(Debug, Clone, thiserror::Error)]
5#[non_exhaustive]
6pub enum OutputParserError {
7 /// Parse failure: the input format does not match expectations
8 #[error("Parse error: {0}")]
9 ParseError(String),
10 /// JSON format error
11 #[error("JSON error: {0}")]
12 JsonError(String),
13 /// Type conversion error
14 #[error("Type error: {0}")]
15 TypeError(String),
16 /// Custom error
17 #[error("{0}")]
18 Custom(String),
19}
20
21impl From<serde_json::Error> for OutputParserError {
22 fn from(e: serde_json::Error) -> Self {
23 OutputParserError::JsonError(e.to_string())
24 }
25}
26
27/// Result type for output parsers
28pub type OutputParserResult<T> = Result<T, OutputParserError>;
29
30/// Core trait for output parsers
31///
32/// Every output parser must implement this trait.
33/// Unlike `Runnable`, `parse` takes no config argument,
34/// so it fits being called inside a Runnable.
35#[async_trait]
36pub trait BaseOutputParser<Output: Send + Sync + 'static>: Send + Sync {
37 /// Parses raw LLM output text into the target type
38 async fn parse(&self, text: &str) -> OutputParserResult<Output>;
39
40 /// Parsing with retry (default: genuinely retries `max_retries` times)
41 ///
42 /// Calls [`parse`](Self::parse) repeatedly on the same text, at most
43 /// `max_retries + 1` times. Retrying the same text only makes sense for
44 /// non-deterministic parsing (e.g. a parser that depends on the network /
45 /// external services); a deterministic parser that fails once will keep
46 /// failing, and the last error is returned. Parsers that need to correct
47 /// the input based on the failure reason should override this method.
48 async fn parse_with_retry(&self, text: &str, max_retries: usize) -> OutputParserResult<Output> {
49 let mut last_err = None;
50 for _ in 0..=max_retries {
51 match self.parse(text).await {
52 Ok(output) => return Ok(output),
53 Err(e) => last_err = Some(e),
54 }
55 }
56 Err(last_err.unwrap_or_else(|| {
57 OutputParserError::ParseError("parse_with_retry made no parse attempts".to_string())
58 }))
59 }
60
61 /// Returns format instructions (to prompt the LLM to output in the expected format)
62 fn get_format_instructions(&self) -> String {
63 String::new()
64 }
65}