use super::unified_provider::ProviderError;
#[derive(Debug, Clone)]
pub struct ContextualError {
pub inner: ProviderError,
pub request_id: String,
pub model: Option<String>,
pub timestamp: chrono::DateTime<chrono::Utc>,
}
impl std::fmt::Display for ContextualError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "[request_id={}] {}", self.request_id, self.inner)?;
if let Some(model) = &self.model {
write!(f, " (model: {})", model)?;
}
Ok(())
}
}
impl std::error::Error for ContextualError {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
Some(&self.inner)
}
}
impl ContextualError {
pub fn new(inner: ProviderError, request_id: impl Into<String>, model: Option<&str>) -> Self {
Self {
inner,
request_id: request_id.into(),
model: model.map(|s| s.to_string()),
timestamp: chrono::Utc::now(),
}
}
pub fn inner(&self) -> &ProviderError {
&self.inner
}
pub fn request_id(&self) -> &str {
&self.request_id
}
pub fn model(&self) -> Option<&str> {
self.model.as_deref()
}
pub fn is_retryable(&self) -> bool {
self.inner.is_retryable()
}
pub fn retry_delay(&self) -> Option<u64> {
self.inner.retry_delay()
}
pub fn http_status(&self) -> u16 {
self.inner.http_status()
}
pub fn provider(&self) -> &'static str {
self.inner.provider()
}
pub fn to_error_response(&self) -> serde_json::Value {
serde_json::json!({
"error": {
"message": self.inner.to_string(),
"type": format!("{:?}", std::mem::discriminant(&self.inner)),
"code": self.http_status(),
"request_id": self.request_id,
"model": self.model,
"provider": self.provider(),
"retryable": self.is_retryable(),
"retry_after": self.retry_delay(),
}
})
}
}
impl From<ContextualError> for ProviderError {
fn from(err: ContextualError) -> Self {
err.inner
}
}