switchyard_libsy/
error.rs1use std::error::Error as StdError;
7
8use switchyard_protocol::{LlmClientError, ModelId};
9use thiserror::Error;
10
11pub type Result<T> = std::result::Result<T, LibsyError>;
13
14#[derive(Debug, Error)]
16pub enum LibsyError {
17 #[error("{message}")]
19 InvalidRequest {
20 message: String,
22 },
23
24 #[error("target {target:?} was not found")]
26 TargetNotFound {
27 target: ModelId,
29 },
30
31 #[error("no routing targets are configured")]
33 NoTargets,
34
35 #[error("{message}")]
37 AlgorithmError {
38 message: String,
40 },
41
42 #[error(transparent)]
44 Driver(#[from] DriverError),
45
46 #[error("algorithm run ended without a routing outcome")]
48 MissingFinalResponse,
49
50 #[error("client call to target {target:?} failed: {source}")]
52 ClientCall {
53 target: ModelId,
55 #[source]
57 source: LlmClientError,
58 },
59
60 #[error("{operation} failed: {source}")]
62 External {
63 operation: &'static str,
65 #[source]
67 source: Box<dyn StdError + Send + Sync>,
68 },
69}
70
71impl LibsyError {
72 pub fn client_call(target: impl Into<ModelId>, source: LlmClientError) -> Self {
74 Self::ClientCall {
75 target: target.into(),
76 source,
77 }
78 }
79
80 pub fn external(
82 operation: &'static str,
83 source: impl StdError + Send + Sync + 'static,
84 ) -> Self {
85 Self::External {
86 operation,
87 source: Box::new(source),
88 }
89 }
90}
91
92#[derive(Debug, Error)]
94pub enum DriverError {
95 #[error("driver stream is closed")]
97 StreamClosed,
98
99 #[error("driver response promise was dropped")]
101 ResponseDropped,
102}
103
104#[cfg(test)]
105mod tests {
106 use super::*;
107
108 #[test]
109 fn client_call_preserves_target_and_source() {
110 let error = LibsyError::client_call(
111 "strong",
112 LlmClientError::General("upstream down".to_string()),
113 );
114 match &error {
115 LibsyError::ClientCall { target, source } => {
116 assert_eq!(target, "strong");
117 assert_eq!(source.to_string(), "upstream down");
118 }
119 other => panic!("expected ClientCall, got {other:?}"),
120 }
121 assert_eq!(
122 StdError::source(&error).map(ToString::to_string),
123 Some("upstream down".to_string())
124 );
125 }
126
127 #[test]
128 fn external_preserves_operation_and_source() {
129 let error = LibsyError::external(
130 "loading extension",
131 std::io::Error::other("bad configuration"),
132 );
133 assert!(matches!(
134 &error,
135 LibsyError::External { operation, .. } if *operation == "loading extension"
136 ));
137 assert_eq!(
138 StdError::source(&error).map(ToString::to_string),
139 Some("bad configuration".to_string())
140 );
141 }
142}