From 4ed7598ae85fe1e15cc45051b5a86da3d4983d21 Mon Sep 17 00:00:00 2001 From: Vidur Khanal Date: Tue, 20 Jan 2026 23:23:10 -0800 Subject: [PATCH 1/3] feat(umem_ai): add embedding API and provider support - Introduce `EmbeddingModel`, `EmbeddingRequest`, and `EmbeddingResponse` types. - Add `Embeds` async trait for embedding functionality. - Implement `Embeds` for `AmazonBedrockProvider` with parallel, retryable embedding requests. - Add `do_embed` async method to `AIProvider`. - Add `embed` module with retry logic and request struct. - Add `futures` as a workspace dependency. - Provide `Default` impls for `OpenAIProvider` and `CohereProvider`. - Extend `ResponseGeneratorError` with new error variants for embedding. - Update module exports and imports accordingly. --- Cargo.lock | 1 + crates/umem_ai/Cargo.toml | 1 + crates/umem_ai/src/lib.rs | 25 ++++ .../umem_ai/src/providers/amazon_bedrock.rs | 114 +++++++++++++++++- crates/umem_ai/src/providers/cohere.rs | 12 ++ crates/umem_ai/src/providers/openai.rs | 12 ++ .../umem_ai/src/response_generators/embed.rs | 59 +++++++++ crates/umem_ai/src/response_generators/mod.rs | 7 +- 8 files changed, 225 insertions(+), 6 deletions(-) create mode 100644 crates/umem_ai/src/response_generators/embed.rs diff --git a/Cargo.lock b/Cargo.lock index ab4b2d3..6bf145c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5168,6 +5168,7 @@ dependencies = [ "aws-smithy-types", "backon", "base64", + "futures", "lazy_static", "mime", "reqwest", diff --git a/crates/umem_ai/Cargo.toml b/crates/umem_ai/Cargo.toml index 1392093..b877ef1 100644 --- a/crates/umem_ai/Cargo.toml +++ b/crates/umem_ai/Cargo.toml @@ -25,3 +25,4 @@ aws-sdk-bedrockruntime = "1.120.0" aws-smithy-types = {version = "1.3.5", features = ["serde-deserialize", "serde-serialize", "rt-tokio"]} serde-saphyr = "0.0.14" aws-sdk-bedrockagentruntime = "1.119.0" +futures.workspace = true diff --git a/crates/umem_ai/src/lib.rs b/crates/umem_ai/src/lib.rs index 1393ead..a7bd7f8 100644 --- a/crates/umem_ai/src/lib.rs +++ b/crates/umem_ai/src/lib.rs @@ -16,6 +16,8 @@ use thiserror::Error; use tokio::sync::OnceCell; use umem_config::CONFIG; +use crate::response_generators::embed::{EmbeddingRequest, EmbeddingResponse}; + pub type HashMap = rustc_hash::FxHashMap; lazy_static! { @@ -101,6 +103,11 @@ impl RerankingModel { } } +pub struct EmbeddingModel { + pub provider: Arc, + pub model_name: String, +} + #[derive(Debug)] pub enum AIProvider { OpenAI(OpenAIProvider), @@ -164,6 +171,16 @@ impl AIProvider { _ => unimplemented!(), } } + + pub(crate) async fn do_embed( + &self, + request: EmbeddingRequest, + ) -> Result { + match self { + _ => unimplemented!(), + } + .await + } } #[async_trait] @@ -200,6 +217,14 @@ pub trait ReranksStructuredData { T: Serialize + Clone + Send + Sync; } +#[async_trait] +pub trait Embeds { + async fn embed( + &self, + request: EmbeddingRequest, + ) -> Result; +} + impl From for AIProvider { fn from(config: OpenAIProvider) -> Self { AIProvider::OpenAI(config) diff --git a/crates/umem_ai/src/providers/amazon_bedrock.rs b/crates/umem_ai/src/providers/amazon_bedrock.rs index 6918c8a..2d7b441 100644 --- a/crates/umem_ai/src/providers/amazon_bedrock.rs +++ b/crates/umem_ai/src/providers/amazon_bedrock.rs @@ -1,7 +1,8 @@ use crate::{ - GenerateObjectRequest, GenerateObjectResponse, GeneratesObject, GeneratesText, OpenAIProvider, - Ranking, RerankRequest, RerankResponse, Reranks, ReranksStructuredData, SerializationMode, - StructuredRanking, StructuredRerankRequest, StructuredRerankResponse, + Embeds, GenerateObjectRequest, GenerateObjectResponse, GeneratesObject, GeneratesText, + OpenAIProvider, Ranking, RerankRequest, RerankResponse, Reranks, ReranksStructuredData, + SerializationMode, StructuredRanking, StructuredRerankRequest, StructuredRerankResponse, + embed::{EmbeddingRequest, EmbeddingResponse}, messages::{FilePart, UserModelMessage}, response_generators::{ self, GenerateTextRequest, GenerateTextResponse, ResponseGeneratorError, @@ -18,18 +19,20 @@ use aws_sdk_bedrockagentruntime::types::{ }; use aws_sdk_bedrockruntime::{ error::BuildError, - operation::converse::builders::ConverseFluentBuilder, + operation::{converse::builders::ConverseFluentBuilder, invoke_model::InvokeModelOutput}, types::{ AnyToolChoice, ContentBlock, ConverseOutput, ImageBlock, InferenceConfiguration, Message, Tool, ToolChoice, ToolConfiguration, ToolInputSchema, ToolSpecification, }, }; use base64::Engine; +use futures::future::join_all; use schemars::JsonSchema; -use serde::{Serialize, de::DeserializeOwned}; +use serde::{Deserialize, Serialize, de::DeserializeOwned}; use serde_json::Map; use std::sync::Arc; use thiserror::Error; +use tokio::sync::Semaphore; #[derive(Clone, Debug)] pub struct AmazonBedrockProvider { @@ -38,6 +41,20 @@ pub struct AmazonBedrockProvider { bedrockagentruntime_client: Arc, } +impl AmazonBedrockProvider { + async fn default() -> Self { + Self::builder() + .region(std::env::var("AWS_REGION").expect("AWS_REGION not set")) + .access_key_id(std::env::var("AWS_ACCESS_KEY_ID").expect("AWS_ACCESS_KEY_ID not set")) + .secret_access_key( + std::env::var("AWS_SECRET_ACCESS_KEY").expect("AWS_SECRET_ACCESS_KEY not set"), + ) + .build() + .await + .expect("Failed to build AmazonBedrockProvider based on default environment variables") + } +} + #[async_trait] impl GeneratesText for AmazonBedrockProvider { async fn generate_text( @@ -439,6 +456,93 @@ impl ReranksStructuredData for AmazonBedrockProvider { } } +#[async_trait] +impl Embeds for AmazonBedrockProvider { + async fn embed( + &self, + request: EmbeddingRequest, + ) -> Result { + if request.input.is_empty() { + return Err(ResponseGeneratorError::InvalidArgumentsProvided( + "Embedding input cannot be empty".to_string(), + )); + } + + let semaphore = Arc::new(Semaphore::new(request.max_parallels)); + let mut embedding_invoke_handles = Vec::with_capacity(request.input.len()); + + for data in request.input.into_iter() { + let permit = semaphore.clone().acquire_owned().await.map_err(|e| { + ResponseGeneratorError::InternalServerError(format!( + "Failed to acquire thread lock while makng multiple requests. Details: {e}" + )) + })?; + + let bedrockruntime_client = Arc::clone(&self.bedrockruntime_client); + let model_name = request.model.model_name.clone(); + + let handle = tokio::spawn(async move { + let invoke_res = bedrockruntime_client + .invoke_model() + .model_id(&model_name) + .body(aws_smithy_types::Blob::new(data)) + .send() + .await + .map_err(|e| { + ResponseGeneratorError::BedrockRerankInvokeError(format!( + "Failed to invoke Bedrock reranking model: {}", + e + )) + }); + drop(permit); + invoke_res + }); + + embedding_invoke_handles.push(handle) + } + + let results: Result, ResponseGeneratorError> = + join_all(embedding_invoke_handles) + .await + .into_iter() + .map(|r| { + r.map_err(|e| { + ResponseGeneratorError::InternalServerError(format!( + "Failed to acquire result from the relevant thread. Details: {e}" + )) + }) + }) + .map(|r| r.and_then(|inner| inner)) + .collect(); + + let embeddings = results? + .into_iter() + .map(|r| { + serde_json::from_slice::( + &r.body.into_inner(), + ) + }) + .collect::, _>>() + .map_err(|e| { + ResponseGeneratorError::Deserialization( + e, + "Failed to deserialize Bedrock embedding response".to_string(), + ) + })?; + + Ok(EmbeddingResponse { + embeddings: embeddings.into_iter().map(|e| e.embedding).collect(), + }) + } +} + +#[derive(Debug, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +struct AmazonBedrockEmbeddingInvokeModelResponse { + embedding: Vec, + input_text_token_count: usize, +} + impl AmazonBedrockProvider { fn normalize_generate_object_request< T: Clone + JsonSchema + Serialize + Send + Sync + DeserializeOwned, diff --git a/crates/umem_ai/src/providers/cohere.rs b/crates/umem_ai/src/providers/cohere.rs index bc96b80..5539362 100644 --- a/crates/umem_ai/src/providers/cohere.rs +++ b/crates/umem_ai/src/providers/cohere.rs @@ -36,6 +36,18 @@ pub struct CohereProvider { headers: HeaderMap, } +impl Default for CohereProvider { + fn default() -> Self { + Self { + base_url: "https://api.cohere.com/v2".into(), + api_key: std::env::var("COHERE_API_KEY").expect( + "COHERE_API_KEY must be set to get a default implementation of cohere provider", + ), + headers: HeaderMap::new(), + } + } +} + #[async_trait] impl Reranks for CohereProvider { async fn rerank( diff --git a/crates/umem_ai/src/providers/openai.rs b/crates/umem_ai/src/providers/openai.rs index b23497d..dcd7d9a 100644 --- a/crates/umem_ai/src/providers/openai.rs +++ b/crates/umem_ai/src/providers/openai.rs @@ -35,6 +35,18 @@ pub struct OpenAIProvider { pub project: Option, } +impl Default for OpenAIProvider { + fn default() -> Self { + Self { + api_key: std::env::var("OPENAI_API_KEY").expect("OPENAI_API_KEY must be set"), + base_url: "https://api.openai.com/v1".into(), + default_headers: HeaderMap::new(), + organization: None, + project: None, + } + } +} + impl OpenAIProvider { pub fn normalize_generate_object_request< T: Clone + JsonSchema + Serialize + Send + Sync + DeserializeOwned, diff --git a/crates/umem_ai/src/response_generators/embed.rs b/crates/umem_ai/src/response_generators/embed.rs new file mode 100644 index 0000000..db24edd --- /dev/null +++ b/crates/umem_ai/src/response_generators/embed.rs @@ -0,0 +1,59 @@ +use crate::{EmbeddingModel, ResponseGeneratorError, utils::is_retryable_error}; +use backon::{ExponentialBuilder, Retryable}; +use std::{sync::Arc, time::Duration}; +use thiserror::Error; + +pub async fn embed( + mut request: EmbeddingRequest, +) -> Result { + let per_request_timeout = request.timeout; + let max_retries = request.max_retries; + let total_delay = per_request_timeout.mul_f32(max_retries as f32 / 2.0); + + let generation = || { + let model = Arc::clone(&request.model); + let provider = Arc::clone(&model.provider); + let request = EmbeddingRequest { + model: Arc::clone(&model), + input: std::mem::take(&mut request.input), + custom_headers: std::mem::take(&mut request.custom_headers), + timeout: request.timeout.clone(), + max_retries, + max_parallels: request.max_parallels, + }; + + async move { + tokio::time::timeout(per_request_timeout, provider.do_embed(request)) + .await + .map_err(ResponseGeneratorError::TimeoutError) + .flatten() + } + }; + + generation + .retry( + ExponentialBuilder::default() + .with_max_times(max_retries) + .with_total_delay(Some(total_delay)), + ) + .sleep(tokio::time::sleep) + .when(is_retryable_error) + .notify(|err, dur| { + tracing::debug!("retrying {:?} after {:?}", err, dur); + }) + .await +} + +#[derive(Clone)] +pub struct EmbeddingRequest { + pub model: Arc, + pub input: Vec, + pub max_retries: usize, + pub max_parallels: usize, + pub custom_headers: Vec<(String, String)>, + pub timeout: Duration, +} + +pub struct EmbeddingResponse { + pub embeddings: Vec>, +} diff --git a/crates/umem_ai/src/response_generators/mod.rs b/crates/umem_ai/src/response_generators/mod.rs index b967ef0..3ef8b23 100644 --- a/crates/umem_ai/src/response_generators/mod.rs +++ b/crates/umem_ai/src/response_generators/mod.rs @@ -1,3 +1,4 @@ +pub mod embed; pub mod generate_object; pub mod generate_text; pub mod messages; @@ -9,8 +10,8 @@ pub use generate_text::*; pub use messages::*; pub use rerank::*; pub use structured_rerank::*; - use thiserror::Error; + #[derive(Error, Debug)] pub enum ResponseGeneratorError { #[error(transparent)] @@ -23,12 +24,16 @@ pub enum ResponseGeneratorError { BedrockConverseError(String), #[error("BedrockAgentRuntime Rerank Command error, Details: {0}")] BedrockAgentRerankCommandSendError(String), + #[error("BedrockRuntime Rerank Invoke API error, Details: {0}")] + BedrockRerankInvokeError(String), #[error("empty response from AI provider")] EmptyProviderResponse, #[error("invalid response from AI provider, Details: {0}")] InvalidProviderResponse(String), #[error("invalid arguments provided: {0}")] InvalidArgumentsProvided(String), + #[error("Internal Server Error: {0}")] + InternalServerError(String), #[error(transparent)] Transient(#[from] anyhow::Error), #[error("yaml serialization error: {0}")] From 19781b23dc99f84d2be795a22324d229530e08bf Mon Sep 17 00:00:00 2001 From: Vidur Khanal Date: Wed, 21 Jan 2026 19:59:22 -0800 Subject: [PATCH 2/3] feat(embed): add Amazon Bedrock embedding support and enhance EmbeddingRequest API - Implement embedding support for Amazon Bedrock provider, including request routing and response handling. - Refactor `EmbeddingRequest` with `TypedBuilder`, add fields for dimensions and normalization, and improve header handling. - Update error handling to use `meta()` for Bedrock errors and rename `BedrockRerankInvokeError` to `BedrockInvokeError`. - Add embedding test for Amazon Bedrock provider. --- crates/umem_ai/src/lib.rs | 2 + .../umem_ai/src/providers/amazon_bedrock.rs | 60 ++++++++++++++----- .../umem_ai/src/response_generators/embed.rs | 41 +++++++++---- crates/umem_ai/src/response_generators/mod.rs | 4 +- crates/umem_ai/src/utils.rs | 8 +++ 5 files changed, 87 insertions(+), 28 deletions(-) diff --git a/crates/umem_ai/src/lib.rs b/crates/umem_ai/src/lib.rs index a7bd7f8..2e9c6b3 100644 --- a/crates/umem_ai/src/lib.rs +++ b/crates/umem_ai/src/lib.rs @@ -103,6 +103,7 @@ impl RerankingModel { } } +#[derive(Debug, Clone)] pub struct EmbeddingModel { pub provider: Arc, pub model_name: String, @@ -177,6 +178,7 @@ impl AIProvider { request: EmbeddingRequest, ) -> Result { match self { + AIProvider::AmazonBedrock(provider) => provider.embed(request), _ => unimplemented!(), } .await diff --git a/crates/umem_ai/src/providers/amazon_bedrock.rs b/crates/umem_ai/src/providers/amazon_bedrock.rs index 2d7b441..b3a366b 100644 --- a/crates/umem_ai/src/providers/amazon_bedrock.rs +++ b/crates/umem_ai/src/providers/amazon_bedrock.rs @@ -2,7 +2,7 @@ use crate::{ Embeds, GenerateObjectRequest, GenerateObjectResponse, GeneratesObject, GeneratesText, OpenAIProvider, Ranking, RerankRequest, RerankResponse, Reranks, ReranksStructuredData, SerializationMode, StructuredRanking, StructuredRerankRequest, StructuredRerankResponse, - embed::{EmbeddingRequest, EmbeddingResponse}, + embed::{EmbeddingRequest, EmbeddingResponse, embed as embedFn}, messages::{FilePart, UserModelMessage}, response_generators::{ self, GenerateTextRequest, GenerateTextResponse, ResponseGeneratorError, @@ -18,7 +18,7 @@ use aws_sdk_bedrockagentruntime::types::{ RerankTextDocument, RerankingConfiguration, RerankingConfigurationType, }; use aws_sdk_bedrockruntime::{ - error::BuildError, + error::{BuildError, ProvideErrorMetadata}, operation::{converse::builders::ConverseFluentBuilder, invoke_model::InvokeModelOutput}, types::{ AnyToolChoice, ContentBlock, ConverseOutput, ImageBlock, InferenceConfiguration, Message, @@ -30,7 +30,7 @@ use futures::future::join_all; use schemars::JsonSchema; use serde::{Deserialize, Serialize, de::DeserializeOwned}; use serde_json::Map; -use std::sync::Arc; +use std::{error::Error, sync::Arc}; use thiserror::Error; use tokio::sync::Semaphore; @@ -89,7 +89,7 @@ impl GeneratesText for AmazonBedrockProvider { .await .map_err(|e| { tracing::error!("{}", e); - ResponseGeneratorError::BedrockConverseError(format!("{:?}", e)) + ResponseGeneratorError::BedrockConverseError(format!("{:?}", e.meta())) })?; let converse_output = match converse_response.output { @@ -154,7 +154,7 @@ impl GeneratesObject for AmazonBedrockProvider { )) .send() .await - .map_err(|e| ResponseGeneratorError::BedrockConverseError(e.to_string()))?; + .map_err(|e| ResponseGeneratorError::BedrockConverseError(e.meta().to_string()))?; let converse_output = match converse_response.output { Some(output) => output, @@ -264,7 +264,7 @@ impl Reranks for AmazonBedrockProvider { ) .send() .await - .map_err(|e| ResponseGeneratorError::BedrockAgentRerankCommandSendError(e.to_string()))?; + .map_err(|e| ResponseGeneratorError::BedrockAgentRerankCommandSendError(e.meta().to_string()))?; let results = response.results(); @@ -423,7 +423,7 @@ impl ReranksStructuredData for AmazonBedrockProvider { ) .send() .await - .map_err(|e| ResponseGeneratorError::BedrockAgentRerankCommandSendError(e.to_string()))?; + .map_err(|e| ResponseGeneratorError::BedrockAgentRerankCommandSendError(e.meta().to_string()))?; let results = response.results(); @@ -485,13 +485,15 @@ impl Embeds for AmazonBedrockProvider { let invoke_res = bedrockruntime_client .invoke_model() .model_id(&model_name) - .body(aws_smithy_types::Blob::new(data)) + .body(aws_smithy_types::Blob::new( + serde_json::json!({"inputText":data, "dimensions": request.dimensions, "normalize": request.normalize}).to_string(), + )) .send() .await .map_err(|e| { - ResponseGeneratorError::BedrockRerankInvokeError(format!( - "Failed to invoke Bedrock reranking model: {}", - e + ResponseGeneratorError::BedrockInvokeError(format!( + "Failed to invoke Bedrock embedding model: {}", + e.meta() )) }); drop(permit); @@ -767,9 +769,9 @@ impl AmazonBedrockProviderBuilder { mod tests { use super::*; use crate::{ - AIProvider, GenerateObjectRequestBuilder, GenerateTextRequestBuilder, LanguageModel, - RerankingModel, SerializationFormat, generate_object, generate_text, rerank, - structured_rerank, + AIProvider, EmbeddingModel, GenerateObjectRequestBuilder, GenerateTextRequestBuilder, + LanguageModel, RerankingModel, SerializationFormat, embed, generate_object, generate_text, + rerank, structured_rerank, }; use serde::Deserialize; use std::sync::Arc; @@ -993,4 +995,34 @@ mod tests { let rerank_response = structured_rerank(request).await.unwrap(); dbg!(&rerank_response); } + + #[tokio::test] + async fn embedding_test() { + let provider = Arc::new(AIProvider::from( + AmazonBedrockProviderBuilder::default() + .region("REGION") + .access_key_id("ACESS_KEY_ID") + .secret_access_key("SECRET_ACCESS_KEY") + .build() + .await + .unwrap(), + )); + + let model = Arc::new(EmbeddingModel { + provider, + model_name: "amazon.titan-embed-text-v2:0".to_string(), + }); + + let request = EmbeddingRequest::builder() + .model(model) + .input(vec![ + "The quick brown fox jumps over the lazy dog.".to_string(), + "To be or not to be, that is the question.".to_string(), + "All that glitters is not gold.".to_string(), + ]) + .build(); + + let embedding_response = embedFn(request).await.unwrap(); + dbg!(&embedding_response); + } } diff --git a/crates/umem_ai/src/response_generators/embed.rs b/crates/umem_ai/src/response_generators/embed.rs index db24edd..872e7e8 100644 --- a/crates/umem_ai/src/response_generators/embed.rs +++ b/crates/umem_ai/src/response_generators/embed.rs @@ -1,7 +1,10 @@ -use crate::{EmbeddingModel, ResponseGeneratorError, utils::is_retryable_error}; +use crate::{ + EmbeddingModel, ResponseGeneratorError, + utils::{self, is_retryable_error}, +}; use backon::{ExponentialBuilder, Retryable}; +use reqwest::header::HeaderMap; use std::{sync::Arc, time::Duration}; -use thiserror::Error; pub async fn embed( mut request: EmbeddingRequest, @@ -13,14 +16,7 @@ pub async fn embed( let generation = || { let model = Arc::clone(&request.model); let provider = Arc::clone(&model.provider); - let request = EmbeddingRequest { - model: Arc::clone(&model), - input: std::mem::take(&mut request.input), - custom_headers: std::mem::take(&mut request.custom_headers), - timeout: request.timeout.clone(), - max_retries, - max_parallels: request.max_parallels, - }; + let request = request.clone(); async move { tokio::time::timeout(per_request_timeout, provider.do_embed(request)) @@ -44,16 +40,37 @@ pub async fn embed( .await } -#[derive(Clone)] +#[derive(Clone, Debug, typed_builder::TypedBuilder)] pub struct EmbeddingRequest { pub model: Arc, + + #[builder(setter(transform = |value: impl IntoIterator| + value.into_iter().collect()) + )] pub input: Vec, + + #[builder(default = 3_usize)] pub max_retries: usize, + + #[builder(default = 1000_usize)] pub max_parallels: usize, - pub custom_headers: Vec<(String, String)>, + + #[builder(default, setter(transform = |value: Vec<(String, String)>| + utils::build_header_map(value.as_slice()).unwrap_or_default() + ))] + pub custom_headers: HeaderMap, + + #[builder(default = Duration::from_secs(60))] pub timeout: Duration, + + #[builder(default = 1024)] + pub dimensions: usize, + + #[builder(default = true)] + pub normalize: bool, } +#[derive(Debug, Clone)] pub struct EmbeddingResponse { pub embeddings: Vec>, } diff --git a/crates/umem_ai/src/response_generators/mod.rs b/crates/umem_ai/src/response_generators/mod.rs index 3ef8b23..a7c4b9a 100644 --- a/crates/umem_ai/src/response_generators/mod.rs +++ b/crates/umem_ai/src/response_generators/mod.rs @@ -24,8 +24,8 @@ pub enum ResponseGeneratorError { BedrockConverseError(String), #[error("BedrockAgentRuntime Rerank Command error, Details: {0}")] BedrockAgentRerankCommandSendError(String), - #[error("BedrockRuntime Rerank Invoke API error, Details: {0}")] - BedrockRerankInvokeError(String), + #[error("BedrockRuntime Invoke API error, Details: {0}")] + BedrockInvokeError(String), #[error("empty response from AI provider")] EmptyProviderResponse, #[error("invalid response from AI provider, Details: {0}")] diff --git a/crates/umem_ai/src/utils.rs b/crates/umem_ai/src/utils.rs index 8edd6d1..a71ad91 100644 --- a/crates/umem_ai/src/utils.rs +++ b/crates/umem_ai/src/utils.rs @@ -64,6 +64,14 @@ pub fn is_retryable_error(e: &ResponseGeneratorError) -> bool { } ResponseGeneratorError::BedrockAgentRerankCommandSendError(e) => { tracing::error!("Bedrock agent rerank command send error: {}", e); + false + } + ResponseGeneratorError::BedrockInvokeError(e) => { + tracing::error!("Bedrock agent embed invoke command error: {}", e); + false + } + ResponseGeneratorError::InternalServerError(e) => { + tracing::error!("Internal Server Error: {}", e); true } } From a0b1a8f8f085d40f21f2d374d23fa75927415b1c Mon Sep 17 00:00:00 2001 From: Vidur Khanal <65837961+vidurkhanal@users.noreply.github.com> Date: Wed, 21 Jan 2026 23:03:11 -0500 Subject: [PATCH 3/3] Update crates/umem_ai/src/providers/amazon_bedrock.rs Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com> --- crates/umem_ai/src/providers/amazon_bedrock.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/umem_ai/src/providers/amazon_bedrock.rs b/crates/umem_ai/src/providers/amazon_bedrock.rs index b3a366b..ea8d46a 100644 --- a/crates/umem_ai/src/providers/amazon_bedrock.rs +++ b/crates/umem_ai/src/providers/amazon_bedrock.rs @@ -474,7 +474,7 @@ impl Embeds for AmazonBedrockProvider { for data in request.input.into_iter() { let permit = semaphore.clone().acquire_owned().await.map_err(|e| { ResponseGeneratorError::InternalServerError(format!( - "Failed to acquire thread lock while makng multiple requests. Details: {e}" + "Failed to acquire thread lock while making multiple requests. Details: {e}" )) })?;