Skip to main content

langchain_rust/output_parsers/
boolean_parser.rs

1use super::{
2    typed::{preview, string_output_parser, strip_reasoning},
3    OutputParserError, TypedOutputParser,
4};
5
6/// Parses a yes/no answer into a `bool`.
7///
8/// Matching ignores case, surrounding whitespace, quotes/backticks and a trailing period.
9/// Defaults: `YES`/`TRUE` are true, `NO`/`FALSE` are false. As a chain output parser it
10/// yields `"true"` or `"false"`.
11#[derive(Debug, Clone)]
12pub struct BooleanParser {
13    true_values: Vec<String>,
14    false_values: Vec<String>,
15}
16
17impl Default for BooleanParser {
18    fn default() -> Self {
19        Self::new()
20    }
21}
22
23impl BooleanParser {
24    pub fn new() -> Self {
25        Self {
26            true_values: vec!["YES".into(), "TRUE".into()],
27            false_values: vec!["NO".into(), "FALSE".into()],
28        }
29    }
30
31    /// Replaces the words read as true and false (compared case-insensitively).
32    pub fn with_values<T, F>(mut self, true_values: T, false_values: F) -> Self
33    where
34        T: IntoIterator,
35        T::Item: Into<String>,
36        F: IntoIterator,
37        F::Item: Into<String>,
38    {
39        self.true_values = true_values
40            .into_iter()
41            .map(|v| v.into().to_uppercase())
42            .collect();
43        self.false_values = false_values
44            .into_iter()
45            .map(|v| v.into().to_uppercase())
46            .collect();
47        self
48    }
49}
50
51impl TypedOutputParser for BooleanParser {
52    type Output = bool;
53
54    fn format_instructions(&self) -> String {
55        format!(
56            "Answer with only `{}` or `{}`.",
57            self.true_values
58                .first()
59                .map(String::as_str)
60                .unwrap_or("YES"),
61            self.false_values
62                .first()
63                .map(String::as_str)
64                .unwrap_or("NO"),
65        )
66    }
67
68    fn parse_output(&self, text: &str) -> Result<bool, OutputParserError> {
69        let normalized = strip_reasoning(text)
70            .trim()
71            .trim_end_matches('.')
72            .trim_matches(|c| matches!(c, '\'' | '"' | '`'))
73            .trim()
74            .to_uppercase();
75        if self.true_values.contains(&normalized) {
76            Ok(true)
77        } else if self.false_values.contains(&normalized) {
78            Ok(false)
79        } else {
80            Err(OutputParserError::ParsingError(format!(
81                "expected one of {:?} or {:?}, got {}",
82                self.true_values,
83                self.false_values,
84                preview(text)
85            )))
86        }
87    }
88}
89
90string_output_parser!(BooleanParser, |value| value.to_string());