diff --git a/src/cli/llm/infer_type_name.rs b/src/cli/llm/infer_type_name.rs index ed0869dfd5..d12570ec47 100644 --- a/src/cli/llm/infer_type_name.rs +++ b/src/cli/llm/infer_type_name.rs @@ -1,8 +1,6 @@ use std::collections::HashMap; - use genai::chat::{ChatMessage, ChatRequest, ChatResponse}; use serde::{Deserialize, Serialize}; - use super::model::groq; use super::{Error, Result, Wizard}; use crate::core::config::Config; @@ -77,6 +75,7 @@ impl InferTypeName { pub fn new(secret: Option) -> InferTypeName { Self { secret } } + pub async fn generate(&mut self, config: &Config) -> Result> { let secret = self.secret.as_ref().map(|s| s.to_owned()); @@ -84,16 +83,13 @@ impl InferTypeName { let mut new_name_mappings: HashMap = HashMap::new(); - // removed root type from types. let types_to_be_processed = config .types .iter() .filter(|(type_name, _)| !config.is_root_operation_type(type_name)) .collect::>(); - let total = types_to_be_processed.len(); - for (i, (type_name, type_)) in types_to_be_processed.into_iter().enumerate() { - // convert type to sdl format. + for (type_name, type_) in types_to_be_processed.into_iter() { let question = Question { fields: type_ .fields @@ -102,48 +98,19 @@ 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; - } + match wizard.ask(question).await { + Ok(answer) => { + for name in answer.suggestions { + if !config.types.contains_key(&name) + && !new_name_mappings.contains_key(&name) { new_name_mappings.insert(name, type_name.to_owned()); break; } - 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) => { - // 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); - } } } + Err(e) => { + tracing::error!("Failed to retrieve a name for the type '{}': {:?}", type_name, e); + } } } @@ -181,4 +148,4 @@ mod test { let answer = Answer::try_from(resp).unwrap(); insta::assert_debug_snapshot!(answer); } -} +} \ No newline at end of file diff --git a/src/cli/llm/wizard.rs b/src/cli/llm/wizard.rs index 1604d7f15f..dddef183d2 100644 --- a/src/cli/llm/wizard.rs +++ b/src/cli/llm/wizard.rs @@ -1,11 +1,13 @@ -use derive_setters::Setters; +use tokio_retry::strategy::{ExponentialBackoff, jitter}; +use tokio_retry::Retry; use genai::adapter::AdapterKind; use genai::chat::{ChatOptions, ChatRequest, ChatResponse}; use genai::resolver::AuthResolver; use genai::Client; - +use super::Error; use super::Result; use crate::cli::llm::model::Model; +use derive_setters::Setters; #[derive(Setters, Clone)] pub struct Wizard { @@ -39,15 +41,31 @@ impl Wizard { } } - pub async fn ask(&self, q: Q) -> Result + pub async fn ask_with_retry(&self, q: Q) -> Result where - Q: TryInto, - A: TryFrom, + Q: TryInto, + A: TryFrom, { - let response = self - .client - .exec_chat(self.model.as_str(), q.try_into()?, None) - .await?; - A::try_from(response) + let strategy = ExponentialBackoff::from_millis(100).map(jitter).take(5); + + let retry_future = Retry::spawn(strategy, || async { + let response = self + .client + .exec_chat(self.model.as_str(), q.clone().try_into()?, None) + .await; + + match response { + Ok(res) => { + if res.status_code() == 429 { + Err(Error::GenAI("API rate limit exceeded".into())) + } else { + A::try_from(res).map_err(|e| Error::GenAI(e.to_string())) + } + }, + Err(e) => Err(Error::GenAI(e.to_string())), + } + }); + + retry_future.await } } diff --git a/tailcall-fixtures/fixtures/configs/yaml-recursive-input.yaml b/tailcall-fixtures/fixtures/configs/yaml-recursive-input.yaml index 41448b3fa7..2e847e53f0 100644 --- a/tailcall-fixtures/fixtures/configs/yaml-recursive-input.yaml +++ b/tailcall-fixtures/fixtures/configs/yaml-recursive-input.yaml @@ -21,11 +21,11 @@ types: type: Bar graphql: args: - - key: baz - value: '{{.args.baz}}' + - key: baz + value: "{{.args.baz}}" baseURL: http://localhost name: bars Foo: fields: name: - type: String \ No newline at end of file + type: String diff --git a/tailcall-wasm/example/browser/index.html b/tailcall-wasm/example/browser/index.html index a9cacc5cf1..ed07277f43 100644 --- a/tailcall-wasm/example/browser/index.html +++ b/tailcall-wasm/example/browser/index.html @@ -1,48 +1,47 @@ - - - hello-wasm example - - + + + hello-wasm example + + +
+ + + +

+
-
- - - -

-
+ - + let builder = new TailcallBuilder() + builder = await builder.with_config(schemaUrl) + executor = await builder.build() + let btn = document.getElementById("btn") + btn.addEventListener("click", runQuery) + } catch (error) { + alert("error: " + error) + } + } + async function runQuery() { + let query = document.getElementById("queryInput").value + try { + document.getElementById("result").textContent = await executor.execute(query) + } catch (error) { + console.error("Error executing query: " + error) + document.getElementById("result").textContent = "Error: " + error + } + } + setup() + +