switchyard_libsy/
error.rs1use std::{error::Error as StdError, time::Duration};
7
8use switchyard_protocol::LlmClientError;
9use thiserror::Error;
10
11pub type Result<T> = std::result::Result<T, LibsyError>;
13
14#[derive(Debug, Error)]
16pub enum LibsyError {
17 #[error("target {target:?} was not found")]
19 TargetNotFound {
20 target: String,
22 },
23
24 #[error("no routing targets are configured")]
26 NoTargets,
27
28 #[error("{message}")]
30 AlgorithmError {
31 message: String,
33 },
34
35 #[error("target {target:?} has no client to serve the call")]
37 MissingClient {
38 target: String,
40 },
41
42 #[error(transparent)]
44 Driver(#[from] DriverError),
45
46 #[error("algorithm task failed: {source}")]
48 AlgorithmTask {
49 #[from]
51 source: tokio::task::JoinError,
52 },
53
54 #[error("algorithm run ended without a final response")]
56 MissingFinalResponse,
57
58 #[error("client call to target {target:?} failed: {source}")]
60 ClientCall {
61 target: String,
63 #[source]
65 source: LlmClientError,
66 },
67
68 #[error("every target exceeded its context window")]
70 AllTargetsExcluded,
71
72 #[error("{operation} failed: {source}")]
74 External {
75 operation: &'static str,
77 #[source]
79 source: Box<dyn StdError + Send + Sync>,
80 },
81}
82
83impl LibsyError {
84 pub fn client_call(target: impl Into<String>, source: LlmClientError) -> Self {
86 Self::ClientCall {
87 target: target.into(),
88 source,
89 }
90 }
91
92 pub fn external(
94 operation: &'static str,
95 source: impl StdError + Send + Sync + 'static,
96 ) -> Self {
97 Self::External {
98 operation,
99 source: Box::new(source),
100 }
101 }
102}
103
104#[derive(Debug, Error, PartialEq, Eq)]
106pub enum DriverError {
107 #[error("driver stream must be taken before calling producer methods")]
109 NotStarted,
110
111 #[error("driver stream is closed")]
113 StreamClosed,
114
115 #[error("driver stream was already taken")]
117 StreamAlreadyTaken,
118
119 #[error("driver response promise was dropped")]
121 ResponseDropped,
122
123 #[error("driver response timed out after {timeout:?}")]
125 ResponseTimedOut {
126 timeout: Duration,
128 },
129
130 #[error("driver payload type mismatch: expected {expected}")]
132 TypeMismatch {
133 expected: &'static str,
135 },
136}
137
138#[cfg(test)]
139mod tests {
140 use super::*;
141
142 #[test]
143 fn client_call_preserves_target_and_source() {
144 let error = LibsyError::client_call(
145 "strong",
146 LlmClientError::General("upstream down".to_string()),
147 );
148 match &error {
149 LibsyError::ClientCall { target, source } => {
150 assert_eq!(target, "strong");
151 assert_eq!(source.to_string(), "upstream down");
152 }
153 other => panic!("expected ClientCall, got {other:?}"),
154 }
155 assert_eq!(
156 StdError::source(&error).map(ToString::to_string),
157 Some("upstream down".to_string())
158 );
159 }
160
161 #[test]
162 fn external_preserves_operation_and_source() {
163 let error = LibsyError::external(
164 "loading extension",
165 std::io::Error::other("bad configuration"),
166 );
167 assert!(matches!(
168 &error,
169 LibsyError::External { operation, .. } if *operation == "loading extension"
170 ));
171 assert_eq!(
172 StdError::source(&error).map(ToString::to_string),
173 Some("bad configuration".to_string())
174 );
175 }
176}