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 final response")]
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("{operation} failed: {source}")]
55 External {
56 operation: &'static str,
58 #[source]
60 source: Box<dyn StdError + Send + Sync>,
61 },
62}
63
64impl LibsyError {
65 pub fn client_call(target: impl Into<ModelId>, source: LlmClientError) -> Self {
67 Self::ClientCall {
68 target: target.into(),
69 source,
70 }
71 }
72
73 pub fn external(
75 operation: &'static str,
76 source: impl StdError + Send + Sync + 'static,
77 ) -> Self {
78 Self::External {
79 operation,
80 source: Box::new(source),
81 }
82 }
83}
84
85#[derive(Debug, Error)]
87pub enum DriverError {
88 #[error("driver stream is closed")]
90 StreamClosed,
91
92 #[error("driver response promise was dropped")]
94 ResponseDropped,
95
96 #[error("model call was abandoned by the consumer")]
100 Abandoned,
101}
102
103#[cfg(test)]
104mod tests {
105 use super::*;
106
107 #[test]
108 fn client_call_preserves_target_and_source() {
109 let error = LibsyError::client_call(
110 "strong",
111 LlmClientError::General("upstream down".to_string()),
112 );
113 match &error {
114 LibsyError::ClientCall { target, source } => {
115 assert_eq!(target, "strong");
116 assert_eq!(source.to_string(), "upstream down");
117 }
118 other => panic!("expected ClientCall, got {other:?}"),
119 }
120 assert_eq!(
121 StdError::source(&error).map(ToString::to_string),
122 Some("upstream down".to_string())
123 );
124 }
125
126 #[test]
127 fn external_preserves_operation_and_source() {
128 let error = LibsyError::external(
129 "loading extension",
130 std::io::Error::other("bad configuration"),
131 );
132 assert!(matches!(
133 &error,
134 LibsyError::External { operation, .. } if *operation == "loading extension"
135 ));
136 assert_eq!(
137 StdError::source(&error).map(ToString::to_string),
138 Some("bad configuration".to_string())
139 );
140 }
141}