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("target {target:?} was not found")]
19 TargetNotFound {
20 target: ModelId,
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 run ended without a routing outcome")]
41 MissingFinalResponse,
42
43 #[error("client call to target {target:?} failed: {source}")]
45 ClientCall {
46 target: ModelId,
48 #[source]
50 source: LlmClientError,
51 },
52
53 #[error("invalid decision from target {target:?}: {message}")]
55 InvalidDecision {
56 target: ModelId,
58 message: String,
60 },
61
62 #[error("{operation} failed: {source}")]
64 External {
65 operation: &'static str,
67 #[source]
69 source: Box<dyn StdError + Send + Sync>,
70 },
71}
72
73impl LibsyError {
74 pub fn client_call(target: impl Into<ModelId>, source: LlmClientError) -> Self {
76 Self::ClientCall {
77 target: target.into(),
78 source,
79 }
80 }
81
82 pub fn invalid_decision(target: impl Into<ModelId>, message: impl Into<String>) -> Self {
84 Self::InvalidDecision {
85 target: target.into(),
86 message: message.into(),
87 }
88 }
89
90 pub fn external(
92 operation: &'static str,
93 source: impl StdError + Send + Sync + 'static,
94 ) -> Self {
95 Self::External {
96 operation,
97 source: Box::new(source),
98 }
99 }
100}
101
102#[derive(Debug, Error)]
104pub enum DriverError {
105 #[error("driver stream is closed")]
107 StreamClosed,
108
109 #[error("driver response promise was dropped")]
111 ResponseDropped,
112}
113
114#[cfg(test)]
115mod tests {
116 use super::*;
117
118 #[test]
119 fn client_call_preserves_target_and_source() {
120 let error = LibsyError::client_call(
121 "strong",
122 LlmClientError::General("upstream down".to_string()),
123 );
124 match &error {
125 LibsyError::ClientCall { target, source } => {
126 assert_eq!(target, "strong");
127 assert_eq!(source.to_string(), "upstream down");
128 }
129 other => panic!("expected ClientCall, got {other:?}"),
130 }
131 assert_eq!(
132 StdError::source(&error).map(ToString::to_string),
133 Some("upstream down".to_string())
134 );
135 }
136
137 #[test]
138 fn external_preserves_operation_and_source() {
139 let error = LibsyError::external(
140 "loading extension",
141 std::io::Error::other("bad configuration"),
142 );
143 assert!(matches!(
144 &error,
145 LibsyError::External { operation, .. } if *operation == "loading extension"
146 ));
147 assert_eq!(
148 StdError::source(&error).map(ToString::to_string),
149 Some("bad configuration".to_string())
150 );
151 }
152}