aither_core/llm/failure/
mod.rs1use alloc::boxed::Box;
9use core::{convert::Infallible, time::Duration};
10
11#[cfg(feature = "zenwave")]
12pub mod zenwave;
13
14#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
16pub enum FailureKind {
17 Network,
21 Transient,
24 Quota,
27 Permanent,
30}
31
32#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
34pub struct Failure {
35 pub kind: FailureKind,
37 pub retry_after: Option<Duration>,
40}
41
42impl Failure {
43 #[must_use]
45 pub const fn new(kind: FailureKind) -> Self {
46 Self {
47 kind,
48 retry_after: None,
49 }
50 }
51
52 #[must_use]
54 pub const fn network() -> Self {
55 Self::new(FailureKind::Network)
56 }
57
58 #[must_use]
60 pub const fn transient() -> Self {
61 Self::new(FailureKind::Transient)
62 }
63
64 #[must_use]
66 pub const fn quota() -> Self {
67 Self::new(FailureKind::Quota)
68 }
69
70 #[must_use]
72 pub const fn permanent() -> Self {
73 Self::new(FailureKind::Permanent)
74 }
75
76 #[must_use]
78 pub const fn with_retry_after(mut self, retry_after: Option<Duration>) -> Self {
79 self.retry_after = retry_after;
80 self
81 }
82
83 #[must_use]
86 pub const fn is_retryable(&self) -> bool {
87 matches!(self.kind, FailureKind::Network | FailureKind::Transient)
88 }
89}
90
91pub trait Classify {
97 fn failure(&self) -> Failure;
99}
100
101impl<T: Classify + ?Sized> Classify for &T {
102 fn failure(&self) -> Failure {
103 T::failure(self)
104 }
105}
106
107impl<T: Classify + ?Sized> Classify for Box<T> {
108 fn failure(&self) -> Failure {
109 T::failure(self)
110 }
111}
112
113impl Classify for Infallible {
114 fn failure(&self) -> Failure {
115 Failure::permanent()
117 }
118}
119
120impl<E: Classify> Classify for super::GenerateError<E> {
121 fn failure(&self) -> Failure {
122 match self {
123 Self::Provider(error) => error.failure(),
124 Self::Parse { .. } => Failure::permanent(),
125 }
126 }
127}
128
129#[cfg(test)]
130mod tests {
131 use super::*;
132
133 #[test]
134 fn retryable_kinds() {
135 assert!(Failure::network().is_retryable());
136 assert!(Failure::transient().is_retryable());
137 assert!(!Failure::quota().is_retryable());
138 assert!(!Failure::permanent().is_retryable());
139 }
140
141 #[test]
142 fn retry_after_is_carried() {
143 let failure = Failure::transient().with_retry_after(Some(Duration::from_secs(3)));
144 assert_eq!(failure.retry_after, Some(Duration::from_secs(3)));
145 }
146}