use super::config::RouterConfig;
use super::deployment::DeploymentId;
use super::error::{CooldownReason, RouterError};
use super::fallback::{ExecutionResult, FallbackType};
use crate::core::providers::unified_provider::ProviderError;
use std::time::Duration;
pub fn is_retryable_error(error: &ProviderError) -> bool {
matches!(
error,
ProviderError::RateLimit { .. }
| ProviderError::Timeout { .. }
| ProviderError::ProviderUnavailable { .. }
| ProviderError::Network { .. }
)
}
pub fn calculate_retry_delay(config: &RouterConfig, attempt: u32) -> Duration {
let base = config.retry_after_secs.max(1);
let delay = base * (2_u64.pow(attempt.saturating_sub(1)));
Duration::from_secs(delay.min(30)) }
pub fn infer_fallback_type(error: &ProviderError) -> FallbackType {
match error {
ProviderError::ContextLengthExceeded { .. } => FallbackType::ContextWindow,
ProviderError::ContentFiltered { .. } => FallbackType::ContentPolicy,
ProviderError::RateLimit { .. } => FallbackType::RateLimit,
_ => FallbackType::General,
}
}
pub fn infer_cooldown_reason(error: &ProviderError) -> CooldownReason {
match error {
ProviderError::RateLimit { .. } => CooldownReason::RateLimit,
ProviderError::Authentication { .. } => CooldownReason::AuthError,
ProviderError::ModelNotFound { .. } | ProviderError::DeploymentError { .. } => {
CooldownReason::NotFound
}
ProviderError::Timeout { .. } => CooldownReason::Timeout,
ProviderError::ApiError { status, .. } => match *status {
401 => CooldownReason::AuthError,
404 => CooldownReason::NotFound,
408 => CooldownReason::Timeout,
429 => CooldownReason::RateLimit,
_ => CooldownReason::ConsecutiveFailures,
},
_ => CooldownReason::ConsecutiveFailures,
}
}
pub fn router_error_to_provider_error(err: RouterError) -> ProviderError {
match err {
RouterError::ModelNotFound(msg) => ProviderError::model_not_found("router", msg),
RouterError::NoAvailableDeployment(msg) => ProviderError::ProviderUnavailable {
provider: "router",
message: format!("No available deployment: {}", msg),
},
RouterError::AllDeploymentsInCooldown(msg) => ProviderError::ProviderUnavailable {
provider: "router",
message: format!("All deployments in cooldown: {}", msg),
},
RouterError::DeploymentNotFound(msg) => ProviderError::DeploymentError {
provider: "router",
deployment: msg.clone(),
message: "Deployment not found".to_string(),
},
RouterError::RateLimitExceeded(_msg) => ProviderError::rate_limit("router", Some(60)),
RouterError::AliasCycle(msg) => ProviderError::Other {
provider: "router",
message: format!("Circular alias detected: {}", msg),
},
RouterError::FallbackCycle(msg) => ProviderError::Other {
provider: "router",
message: format!("Circular fallback chain detected: {}", msg),
},
}
}
pub fn provider_error_to_router_error(err: ProviderError, model_name: &str) -> RouterError {
match err {
ProviderError::ModelNotFound { model, .. } => RouterError::ModelNotFound(model),
ProviderError::RateLimit { .. } => RouterError::RateLimitExceeded(model_name.to_string()),
_ => RouterError::NoAvailableDeployment(format!("{}: {}", model_name, err)),
}
}
pub fn build_execution_result<T>(
result: T,
deployment_id: DeploymentId,
attempts: u32,
model_used: String,
used_fallback: bool,
latency_us: u64,
) -> ExecutionResult<T> {
ExecutionResult {
result,
deployment_id,
attempts,
model_used,
used_fallback,
latency_us,
}
}