Skip to main content

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}