switchyard_libsy/
error.rs1use std::error::Error as StdError;
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 is closed")]
102 StreamClosed,
103
104 #[error("driver response promise was dropped")]
106 ResponseDropped,
107
108 #[error("model call was abandoned by the consumer")]
112 Abandoned,
113}
114
115#[cfg(test)]
116mod tests {
117 use super::*;
118
119 #[test]
120 fn client_call_preserves_target_and_source() {
121 let error = LibsyError::client_call(
122 "strong",
123 LlmClientError::General("upstream down".to_string()),
124 );
125 match &error {
126 LibsyError::ClientCall { target, source } => {
127 assert_eq!(target, "strong");
128 assert_eq!(source.to_string(), "upstream down");
129 }
130 other => panic!("expected ClientCall, got {other:?}"),
131 }
132 assert_eq!(
133 StdError::source(&error).map(ToString::to_string),
134 Some("upstream down".to_string())
135 );
136 }
137
138 #[test]
139 fn external_preserves_operation_and_source() {
140 let error = LibsyError::external(
141 "loading extension",
142 std::io::Error::other("bad configuration"),
143 );
144 assert!(matches!(
145 &error,
146 LibsyError::External { operation, .. } if *operation == "loading extension"
147 ));
148 assert_eq!(
149 StdError::source(&error).map(ToString::to_string),
150 Some("bad configuration".to_string())
151 );
152 }
153}