diff --git a/Cargo.lock b/Cargo.lock index 0761018274..5965d11c65 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5530,6 +5530,7 @@ dependencies = [ "test-log", "thiserror", "tokio", + "tokio-retry", "tokio-test", "tonic 0.11.0", "tonic-types", @@ -5948,6 +5949,17 @@ dependencies = [ "syn 2.0.74", ] +[[package]] +name = "tokio-retry" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f57eb36ecbe0fc510036adff84824dd3c24bb781e21bfa67b69d556aa85214f" +dependencies = [ + "pin-project", + "rand", + "tokio", +] + [[package]] name = "tokio-rustls" version = "0.24.1" diff --git a/Cargo.toml b/Cargo.toml index 8c9ad84e2e..cc761a26b8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -169,6 +169,7 @@ indenter = "0.3.3" derive_more = { workspace = true } enum_dispatch = "0.3.13" strum = "0.26.2" +tokio-retry = "0.3.0" [dev-dependencies] tailcall-prettier = { path = "tailcall-prettier" } diff --git a/src/cli/llm/infer_type_name.rs b/src/cli/llm/infer_type_name.rs index ed0869dfd5..38548d48aa 100644 --- a/src/cli/llm/infer_type_name.rs +++ b/src/cli/llm/infer_type_name.rs @@ -102,47 +102,37 @@ impl InferTypeName { .collect(), }; - let mut delay = 3; - loop { - let answer = wizard.ask(question.clone()).await; - match answer { - Ok(answer) => { - let name = &answer.suggestions.join(", "); - for name in answer.suggestions { - if config.types.contains_key(&name) - || new_name_mappings.contains_key(&name) - { - continue; - } - new_name_mappings.insert(name, type_name.to_owned()); - break; + let answer = wizard.ask(question).await; + match answer { + Ok(answer) => { + let name = &answer.suggestions.join(", "); + for name in answer.suggestions { + if config.types.contains_key(&name) || new_name_mappings.contains_key(&name) + { + continue; } - tracing::info!( - "Suggestions for {}: [{}] - {}/{}", - type_name, - name, - i + 1, - total - ); - - // TODO: case where suggested names are already used, then extend the base - // question with `suggest different names, we have already used following - // names: [names list]` + new_name_mappings.insert(name, type_name.to_owned()); break; } - Err(e) => { - // TODO: log errors after certain number of retries. - if let Error::GenAI(_) = e { - // TODO: retry only when it's required. - tracing::warn!( - "Unable to retrieve a name for the type '{}'. Retrying in {}s", - type_name, - delay - ); - tokio::time::sleep(tokio::time::Duration::from_secs(delay)).await; - delay *= std::cmp::min(delay * 2, 60); - } - } + tracing::info!( + "Suggestions for {}: [{}] - {}/{}", + type_name, + name, + i + 1, + total + ); + + // TODO: case where suggested names are already used, then extend the base + // question with `suggest different names, we have already used following + // names: [names list]` + break; + } + Err(e) => { + tracing::warn!( + "Unable to retrieve a name for the type '{}', skipping with error {}", + type_name, + e + ); } } } diff --git a/src/cli/llm/wizard.rs b/src/cli/llm/wizard.rs index 1604d7f15f..25889db17c 100644 --- a/src/cli/llm/wizard.rs +++ b/src/cli/llm/wizard.rs @@ -1,8 +1,11 @@ +use std::time::Duration; + use derive_setters::Setters; use genai::adapter::AdapterKind; use genai::chat::{ChatOptions, ChatRequest, ChatResponse}; use genai::resolver::AuthResolver; use genai::Client; +use tokio_retry::strategy::ExponentialBackoff; use super::Result; use crate::cli::llm::model::Model; @@ -41,13 +44,22 @@ impl Wizard { pub async fn ask(&self, q: Q) -> Result where - Q: TryInto, + Q: TryInto + Clone, A: TryFrom, { - let response = self - .client - .exec_chat(self.model.as_str(), q.try_into()?, None) - .await?; - A::try_from(response) + let retry = ExponentialBackoff::from_millis(1000) + .max_delay(Duration::from_secs(60)) + .map(tokio_retry::strategy::jitter) + .take(10); + + tokio_retry::Retry::spawn(retry, || async { + let response = self + .client + .exec_chat(self.model.as_str(), q.clone().try_into()?, None) + .await?; + + A::try_from(response) + }) + .await } }