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(transparent)]
37 Driver(#[from] DriverError),
38
39 #[error("algorithm task failed: {source}")]
41 AlgorithmTask {
42 #[from]
44 source: tokio::task::JoinError,
45 },
46
47 #[error("algorithm run ended without a final response")]
49 MissingFinalResponse,
50
51 #[error("client call to target {target:?} failed: {source}")]
53 ClientCall {
54 target: String,
56 #[source]
58 source: LlmClientError,
59 },
60
61 #[error("every target exceeded its context window")]
63 AllTargetsExcluded,
64
65 #[error("{operation} failed: {source}")]
67 External {
68 operation: &'static str,
70 #[source]
72 source: Box<dyn StdError + Send + Sync>,
73 },
74}
75
76impl LibsyError {
77 pub fn client_call(target: impl Into<String>, source: LlmClientError) -> Self {
79 Self::ClientCall {
80 target: target.into(),
81 source,
82 }
83 }
84
85 pub fn external(
87 operation: &'static str,
88 source: impl StdError + Send + Sync + 'static,
89 ) -> Self {
90 Self::External {
91 operation,
92 source: Box::new(source),
93 }
94 }
95}
96
97#[derive(Debug, Error, PartialEq, Eq)]
99pub enum DriverError {
100 #[error("driver stream must be taken before calling producer methods")]
102 NotStarted,
103
104 #[error("driver stream is closed")]
106 StreamClosed,
107
108 #[error("driver stream was already taken")]
110 StreamAlreadyTaken,
111
112 #[error("driver response promise was dropped")]
114 ResponseDropped,
115
116 #[error("driver response timed out after {timeout:?}")]
118 ResponseTimedOut {
119 timeout: Duration,
121 },
122
123 #[error("driver payload type mismatch: expected {expected}")]
125 TypeMismatch {
126 expected: &'static str,
128 },
129}
130
131#[cfg(test)]
132mod tests {
133 use super::*;
134
135 #[test]
136 fn client_call_preserves_target_and_source() {
137 let error = LibsyError::client_call(
138 "strong",
139 LlmClientError::General("upstream down".to_string()),
140 );
141 match &error {
142 LibsyError::ClientCall { target, source } => {
143 assert_eq!(target, "strong");
144 assert_eq!(source.to_string(), "upstream down");
145 }
146 other => panic!("expected ClientCall, got {other:?}"),
147 }
148 assert_eq!(
149 StdError::source(&error).map(ToString::to_string),
150 Some("upstream down".to_string())
151 );
152 }
153
154 #[test]
155 fn external_preserves_operation_and_source() {
156 let error = LibsyError::external(
157 "loading extension",
158 std::io::Error::other("bad configuration"),
159 );
160 assert!(matches!(
161 &error,
162 LibsyError::External { operation, .. } if *operation == "loading extension"
163 ));
164 assert_eq!(
165 StdError::source(&error).map(ToString::to_string),
166 Some("bad configuration".to_string())
167 );
168 }
169}