roder_api/
provider_error.rs1use std::fmt;
3use tokio::time::Instant;
4
5#[derive(Debug, Clone, Copy, PartialEq, Eq)]
6pub enum ProviderFailureKind {
7 Transport,
8 StreamInterrupted,
9 RateLimited,
10 ServerOverloaded,
11 ContextWindowExceeded,
12 UsageLimit,
13 QuotaExceeded,
14 Authentication,
15 InvalidRequest,
16 Protocol,
17 ToolSearchExhausted,
18 RequestTooLarge,
19}
20
21impl ProviderFailureKind {
22 pub fn is_retryable(self) -> bool {
23 matches!(
24 self,
25 Self::Transport | Self::StreamInterrupted | Self::RateLimited | Self::ServerOverloaded
26 )
27 }
28
29 pub fn retry_cause(self) -> &'static str {
30 match self {
31 Self::Transport => "transport_error",
32 Self::StreamInterrupted => "stream_interrupted",
33 Self::RateLimited => "rate_limit",
34 Self::ServerOverloaded => "server_overloaded",
35 Self::ContextWindowExceeded => "context_window_exceeded",
36 Self::UsageLimit => "usage_limit",
37 Self::QuotaExceeded => "quota_exceeded",
38 Self::Authentication => "authentication",
39 Self::InvalidRequest => "invalid_request",
40 Self::Protocol => "protocol_error",
41 Self::ToolSearchExhausted => "tool_search_exhausted",
42 Self::RequestTooLarge => "request_too_large",
43 }
44 }
45}
46
47#[derive(Debug, Clone)]
48pub struct ProviderFailure {
49 pub kind: ProviderFailureKind,
50 pub message: String,
51 pub provider_code: Option<String>,
52 pub status: Option<u16>,
53 pub request_id: Option<String>,
54 pub response_id: Option<String>,
55 pub retry_not_before: Option<Instant>,
57 pub retry_budget_exhausted: bool,
59}
60
61impl ProviderFailure {
62 pub fn metadata(&self) -> serde_json::Value {
63 serde_json::json!({"kind":"provider_failure","cause":self.kind.retry_cause(),
64 "providerCode":self.provider_code,"status":self.status,"requestId":self.request_id,
65 "responseId":self.response_id,"retryable":self.kind.is_retryable(),
66 "retryBudgetExhausted":self.retry_budget_exhausted,
67 "retryAfterMs":self.retry_not_before.map(|deadline|deadline.saturating_duration_since(Instant::now()).as_millis().min(u64::MAX as u128) as u64)})
68 }
69
70 pub fn new(kind: ProviderFailureKind, message: impl Into<String>) -> Self {
71 Self {
72 kind,
73 message: message.into(),
74 provider_code: None,
75 status: None,
76 request_id: None,
77 response_id: None,
78 retry_not_before: None,
79 retry_budget_exhausted: false,
80 }
81 }
82}
83
84impl fmt::Display for ProviderFailure {
85 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
86 self.message.fmt(f)
87 }
88}
89impl std::error::Error for ProviderFailure {}