///|
/// Everything a provider call can fail with. The four cases are the only distinctions
/// a caller can act on: retry, parse differently, or stop asking.
#derive(Eq, Debug)
pub(all) suberror LlmError {
/// The endpoint answered with a non-success status. `retry_after` is the seconds the
/// endpoint asked to be left alone for, when it said.
Status(code~ : Int32, body~ : String, retry_after~ : Int64?)
/// The stream broke: a frame that could not be read, or one that arrived out of order.
Stream(String)
/// The body was not the shape this dialect promised.
Decode(String)
/// The endpoint does not offer this capability at all, such as a chat-only server
/// asked for embeddings.
Unsupported(String)
}
///|
/// A one-line description, for logs and for retry classification.
pub fn LlmError::describe(self : LlmError) -> String {
match self {
Status(code~, body~, ..) => "http " + code.to_string() + ": " + body
Stream(message) => "stream: " + message
Decode(message) => "decode: " + message
Unsupported(what) => "unsupported: " + what
}
}
///|
/// Whether asking again could plausibly succeed. A rate limit, a server fault and a
/// dropped stream qualify; a malformed body or a missing capability does not.
pub fn LlmError::is_retryable(self : LlmError) -> Bool {
match self {
Status(code~, ..) =>
code == 408 || code == 409 || code == 429 || code >= 500
Stream(_) => true
Decode(_) => false
Unsupported(_) => false
}
}
///|
/// The wait the endpoint asked for, when it named one.
pub fn LlmError::retry_after(self : LlmError) -> Int64? {
match self {
Status(retry_after~, ..) => retry_after
_ => None
}
}
///|
pub extend LlmError with Eq::{not_equal, equal}
///|
pub extend LlmError with Debug::{to_repr}