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("target {target:?} is unavailable because its circuit breaker is open")]
55 CircuitOpen {
56 target: ModelId,
58 },
59
60 #[error("both VGR tiers are unavailable: local: {local}; cloud: {cloud}")]
62 VgrTiersUnavailable {
63 local: Box<LibsyError>,
65 cloud: Box<LibsyError>,
67 },
68
69 #[error("{operation} failed: {source}")]
71 External {
72 operation: &'static str,
74 #[source]
76 source: Box<dyn StdError + Send + Sync>,
77 },
78}
79
80impl LibsyError {
81 pub fn client_call(target: impl Into<ModelId>, source: LlmClientError) -> Self {
83 Self::ClientCall {
84 target: target.into(),
85 source,
86 }
87 }
88
89 pub fn external(
91 operation: &'static str,
92 source: impl StdError + Send + Sync + 'static,
93 ) -> Self {
94 Self::External {
95 operation,
96 source: Box::new(source),
97 }
98 }
99}
100
101#[derive(Debug, Error)]
103pub enum DriverError {
104 #[error("driver stream is closed")]
106 StreamClosed,
107
108 #[error("driver response promise was dropped")]
110 ResponseDropped,
111}
112
113#[cfg(test)]
114mod tests {
115 use super::*;
116
117 #[test]
118 fn client_call_preserves_target_and_source() {
119 let error = LibsyError::client_call(
120 "strong",
121 LlmClientError::General("upstream down".to_string()),
122 );
123 match &error {
124 LibsyError::ClientCall { target, source } => {
125 assert_eq!(target, "strong");
126 assert_eq!(source.to_string(), "upstream down");
127 }
128 other => panic!("expected ClientCall, got {other:?}"),
129 }
130 assert_eq!(
131 StdError::source(&error).map(ToString::to_string),
132 Some("upstream down".to_string())
133 );
134 }
135
136 #[test]
137 fn external_preserves_operation_and_source() {
138 let error = LibsyError::external(
139 "loading extension",
140 std::io::Error::other("bad configuration"),
141 );
142 assert!(matches!(
143 &error,
144 LibsyError::External { operation, .. } if *operation == "loading extension"
145 ));
146 assert_eq!(
147 StdError::source(&error).map(ToString::to_string),
148 Some("bad configuration".to_string())
149 );
150 }
151}