Skip to main content

use_reasoning/
lib.rs

1#![forbid(unsafe_code)]
2#![doc = include_str!("../README.md")]
3
4use core::{fmt, str::FromStr};
5use std::error::Error;
6
7pub mod prelude {
8    pub use crate::{
9        ReasoningArtifactKind, ReasoningEffort, ReasoningError, ReasoningErrorKind, ReasoningMode,
10        ReasoningStepKind, ReasoningStrategy, ReasoningTraceStatus, ReasoningVisibility,
11    };
12}
13
14macro_rules! reasoning_enum {
15    ($name:ident { $($variant:ident => $label:literal),+ $(,)? }) => {
16        #[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
17        pub enum $name {
18            $($variant),+
19        }
20
21        impl $name {
22            pub const ALL: &'static [Self] = &[$(Self::$variant),+];
23
24            pub const fn as_str(self) -> &'static str {
25                match self {
26                    $(Self::$variant => $label),+
27                }
28            }
29        }
30
31        impl fmt::Display for $name {
32            fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
33                formatter.write_str(self.as_str())
34            }
35        }
36
37        impl FromStr for $name {
38            type Err = ReasoningError;
39
40            fn from_str(value: &str) -> Result<Self, Self::Err> {
41                match normalized_label(value)?.as_str() {
42                    $($label => Ok(Self::$variant),)+
43                    _ => Err(ReasoningError::UnknownLabel),
44                }
45            }
46        }
47    };
48}
49
50reasoning_enum!(ReasoningMode {
51    None => "none",
52    Direct => "direct",
53    ChainOfThoughtLike => "chain-of-thought-like",
54    TreeOfThoughtLike => "tree-of-thought-like",
55    GraphOfThoughtLike => "graph-of-thought-like",
56    ProgramAided => "program-aided",
57    ToolAugmented => "tool-augmented",
58    Reflective => "reflective",
59    Custom => "custom",
60});
61
62reasoning_enum!(ReasoningVisibility {
63    Hidden => "hidden",
64    SummaryOnly => "summary-only",
65    UserVisible => "user-visible",
66    Redacted => "redacted",
67});
68
69reasoning_enum!(ReasoningStepKind {
70    Observe => "observe",
71    Decompose => "decompose",
72    Infer => "infer",
73    Retrieve => "retrieve",
74    Calculate => "calculate",
75    Compare => "compare",
76    Verify => "verify",
77    Reflect => "reflect",
78    Decide => "decide",
79    Explain => "explain",
80    Custom => "custom",
81});
82
83reasoning_enum!(ReasoningTraceStatus {
84    Unavailable => "unavailable",
85    Available => "available",
86    Redacted => "redacted",
87    Summarized => "summarized",
88    Disallowed => "disallowed",
89});
90
91reasoning_enum!(ReasoningEffort {
92    Minimal => "minimal",
93    Low => "low",
94    Medium => "medium",
95    High => "high",
96    Max => "max",
97    Unknown => "unknown",
98});
99
100reasoning_enum!(ReasoningStrategy {
101    Deductive => "deductive",
102    Inductive => "inductive",
103    Abductive => "abductive",
104    Analogical => "analogical",
105    Causal => "causal",
106    Probabilistic => "probabilistic",
107    Geometric => "geometric",
108    Symbolic => "symbolic",
109    Custom => "custom",
110});
111
112reasoning_enum!(ReasoningArtifactKind {
113    Summary => "summary",
114    Scratchpad => "scratchpad",
115    ProofSketch => "proof-sketch",
116    Calculation => "calculation",
117    Plan => "plan",
118    Diagram => "diagram",
119    Code => "code",
120    CitationMap => "citation-map",
121    Custom => "custom",
122});
123
124reasoning_enum!(ReasoningErrorKind {
125    UnsupportedPremise => "unsupported-premise",
126    MissingEvidence => "missing-evidence",
127    Contradiction => "contradiction",
128    Hallucination => "hallucination",
129    ToolError => "tool-error",
130    CalculationError => "calculation-error",
131    Unknown => "unknown",
132});
133
134#[derive(Clone, Copy, Debug, Eq, PartialEq)]
135pub enum ReasoningError {
136    Empty,
137    UnknownLabel,
138}
139
140impl fmt::Display for ReasoningError {
141    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
142        match self {
143            Self::Empty => formatter.write_str("reasoning metadata label cannot be empty"),
144            Self::UnknownLabel => formatter.write_str("unknown reasoning metadata label"),
145        }
146    }
147}
148
149impl Error for ReasoningError {}
150
151fn normalized_label(value: &str) -> Result<String, ReasoningError> {
152    let trimmed = value.trim();
153    if trimmed.is_empty() {
154        Err(ReasoningError::Empty)
155    } else {
156        Ok(trimmed.to_ascii_lowercase().replace(['_', ' '], "-"))
157    }
158}
159
160#[cfg(test)]
161mod tests {
162    use super::{
163        ReasoningArtifactKind, ReasoningEffort, ReasoningError, ReasoningErrorKind, ReasoningMode,
164        ReasoningStepKind, ReasoningStrategy, ReasoningTraceStatus, ReasoningVisibility,
165    };
166    use core::{fmt, str::FromStr};
167
168    fn assert_enum_family<T>(variants: &[T]) -> Result<(), ReasoningError>
169    where
170        T: Copy + Eq + fmt::Debug + fmt::Display + FromStr<Err = ReasoningError>,
171    {
172        for variant in variants {
173            let label = variant.to_string();
174            assert_eq!(label.parse::<T>()?, *variant);
175            assert_eq!(label.replace('-', "_").parse::<T>()?, *variant);
176            assert_eq!(label.replace('-', " ").parse::<T>()?, *variant);
177        }
178        Ok(())
179    }
180
181    #[test]
182    fn displays_and_parses_reasoning_enums() -> Result<(), ReasoningError> {
183        assert_enum_family(ReasoningMode::ALL)?;
184        assert_enum_family(ReasoningVisibility::ALL)?;
185        assert_enum_family(ReasoningStepKind::ALL)?;
186        assert_enum_family(ReasoningTraceStatus::ALL)?;
187        assert_enum_family(ReasoningEffort::ALL)?;
188        assert_enum_family(ReasoningStrategy::ALL)?;
189        assert_enum_family(ReasoningArtifactKind::ALL)?;
190        assert_enum_family(ReasoningErrorKind::ALL)?;
191        assert_eq!(
192            "tool augmented".parse::<ReasoningMode>()?,
193            ReasoningMode::ToolAugmented
194        );
195        assert_eq!("".parse::<ReasoningMode>(), Err(ReasoningError::Empty));
196        Ok(())
197    }
198}