Execute a single request with retry logic and circuit breaker
(
provider: Arc<RwLock<Box<dyn LlmProviderTrait>>>,
circuit_breaker: Arc<CircuitBreaker>,
stats: Arc<RwLock<ClientStats>>,
config: ClientConfig,
request: LlmReq
| 1017 | |
| 1018 | /// Execute a single request with retry logic and circuit breaker |
| 1019 | async fn execute_request_with_resilience( |
| 1020 | provider: Arc<RwLock<Box<dyn LlmProviderTrait>>>, |
| 1021 | circuit_breaker: Arc<CircuitBreaker>, |
| 1022 | stats: Arc<RwLock<ClientStats>>, |
| 1023 | config: ClientConfig, |
| 1024 | request: LlmRequest, |
| 1025 | ) -> Result<graphbit_core::llm::LlmResponse, PyErr> { |
| 1026 | let start_time = Instant::now(); |
| 1027 | |
| 1028 | // Update stats |
| 1029 | { |
| 1030 | let mut stats_guard = stats.write().await; |
| 1031 | stats_guard.total_requests += 1; |
| 1032 | } |
| 1033 | |
| 1034 | // Check circuit breaker |
| 1035 | if !circuit_breaker.can_execute().await { |
| 1036 | return Err(PyErr::new::<pyo3::exceptions::PyRuntimeError, _>( |
| 1037 | "Circuit breaker is open, request rejected", |
| 1038 | )); |
| 1039 | } |
| 1040 | |
| 1041 | let mut last_error = None; |
| 1042 | let mut delay = config.base_retry_delay; |
| 1043 | |
| 1044 | for attempt in 0..=config.max_retries { |
| 1045 | if config.debug { |
| 1046 | debug!("Executing LLM request (attempt {})", attempt + 1); |
| 1047 | } |
| 1048 | |
| 1049 | let result = timeout(config.request_timeout, async { |
| 1050 | let guard = provider.read().await; |
| 1051 | guard.complete(request.clone()).await |
| 1052 | }) |
| 1053 | .await; |
| 1054 | |
| 1055 | match result { |
| 1056 | Ok(Ok(response)) => { |
| 1057 | // Success - update stats and circuit breaker |
| 1058 | let duration = start_time.elapsed(); |
| 1059 | |
| 1060 | { |
| 1061 | let mut stats_guard = stats.write().await; |
| 1062 | stats_guard.successful_requests += 1; |
| 1063 | |
| 1064 | // Update average response time (simple moving average) |
| 1065 | let total_requests = stats_guard.total_requests as f64; |
| 1066 | stats_guard.average_response_time_ms = |
| 1067 | (stats_guard.average_response_time_ms * (total_requests - 1.0) |
| 1068 | + duration.as_millis() as f64) |
| 1069 | / total_requests; |
| 1070 | } |
| 1071 | |
| 1072 | circuit_breaker.record_success().await; |
| 1073 | if config.debug { |
| 1074 | info!("LLM request completed successfully in {:?}", duration); |
| 1075 | } |
| 1076 |
nothing calls this directly
no test coverage detected