From 105aac48440c7a6ba6fb5bba9e8a4f2e1cf079b9 Mon Sep 17 00:00:00 2001
From: PSriVarshan <psvvarshan@gmail.com>
Date: Tue, 6 May 2025 18:24:02 +0530
Subject: [PATCH 1/6] Add observability to Bedrock Titan Embedding model

Signed-off-by: PSriVarshan <psvvarshan@gmail.com>
---
 .../titan/BedrockTitanEmbeddingModel.java     | 60 ++++++++++++++++---
 1 file changed, 51 insertions(+), 9 deletions(-)

diff --git a/models/spring-ai-bedrock/src/main/java/org/springframework/ai/bedrock/titan/BedrockTitanEmbeddingModel.java b/models/spring-ai-bedrock/src/main/java/org/springframework/ai/bedrock/titan/BedrockTitanEmbeddingModel.java
index 2eabc32d4f1..d1bc99083b3 100644
--- a/models/spring-ai-bedrock/src/main/java/org/springframework/ai/bedrock/titan/BedrockTitanEmbeddingModel.java
+++ b/models/spring-ai-bedrock/src/main/java/org/springframework/ai/bedrock/titan/BedrockTitanEmbeddingModel.java
@@ -34,9 +34,14 @@
 import org.springframework.ai.embedding.EmbeddingResponse;
 import org.springframework.util.Assert;
 
+import io.micrometer.observation.ObservationRegistry;
+import io.micrometer.observation.Observation;
+
 /**
- * {@link org.springframework.ai.embedding.EmbeddingModel} implementation that uses the
- * Bedrock Titan Embedding API. Titan Embedding supports text and image (encoded in
+ * {@link org.springframework.ai.embedding.EmbeddingModel} implementation that
+ * uses the
+ * Bedrock Titan Embedding API. Titan Embedding supports text and image (encoded
+ * in
  * base64) inputs.
  *
  * Note: Titan Embedding does not support batch embedding.
@@ -51,17 +56,24 @@ public class BedrockTitanEmbeddingModel extends AbstractEmbeddingModel {
 
 	private final TitanEmbeddingBedrockApi embeddingApi;
 
+	private final ObservationRegistry observationRegistry;
+
 	/**
-	 * Titan Embedding API input types. Could be either text or image (encoded in base64).
+	 * Titan Embedding API input types. Could be either text or image (encoded in
+	 * base64).
 	 */
 	private InputType inputType = InputType.TEXT;
 
-	public BedrockTitanEmbeddingModel(TitanEmbeddingBedrockApi titanEmbeddingBedrockApi) {
+	public BedrockTitanEmbeddingModel(TitanEmbeddingBedrockApi titanEmbeddingBedrockApi,
+			ObservationRegistry observationRegistry) {
 		this.embeddingApi = titanEmbeddingBedrockApi;
+		this.observationRegistry = observationRegistry;
 	}
 
 	/**
-	 * Titan Embedding API input types. Could be either text or image (encoded in base64).
+	 * Titan Embedding API input types. Could be either text or image (encoded in
+	 * base64).
+	 * 
 	 * @param inputType the input type to use.
 	 */
 	public BedrockTitanEmbeddingModel withInputType(InputType inputType) {
@@ -78,17 +90,40 @@ public float[] embed(Document document) {
 	public EmbeddingResponse call(EmbeddingRequest request) {
 		Assert.notEmpty(request.getInstructions(), "At least one text is required!");
 		if (request.getInstructions().size() != 1) {
-			logger.warn(
-					"Titan Embedding does not support batch embedding. Will make multiple API calls to embed(Document)");
+			logger.warn("Titan Embedding does not support batch embedding. Multiple API calls will be made.");
 		}
 
 		List<Embedding> embeddings = new ArrayList<>();
 		var indexCounter = new AtomicInteger(0);
+
 		for (String inputContent : request.getInstructions()) {
 			var apiRequest = createTitanEmbeddingRequest(inputContent, request.getOptions());
-			TitanEmbeddingResponse response = this.embeddingApi.embedding(apiRequest);
-			embeddings.add(new Embedding(response.embedding(), indexCounter.getAndIncrement()));
+
+			try {
+				TitanEmbeddingResponse response = Observation
+						.createNotStarted("bedrock.embedding", this.observationRegistry)
+						.lowCardinalityKeyValue("model", "titan")
+						.lowCardinalityKeyValue("input_type", this.inputType.name().toLowerCase())
+						.highCardinalityKeyValue("input_length", String.valueOf(inputContent.length()))
+						.observe(() -> {
+							TitanEmbeddingResponse r = this.embeddingApi.embedding(apiRequest);
+							Assert.notNull(r, "Embedding API returned null response");
+							return r;
+						});
+
+				if (response.embedding() == null || response.embedding().length == 0) {
+					logger.warn("Empty embedding vector returned for input at index {}. Skipping.", indexCounter.get());
+					continue;
+				}
+
+				embeddings.add(new Embedding(response.embedding(), indexCounter.getAndIncrement()));
+			} catch (Exception ex) {
+				logger.error("Titan API embedding failed for input at index {}: {}", indexCounter.get(),
+						summarizeInput(inputContent), ex);
+				throw ex; // Optional: Continue instead of throwing if you want partial success
+			}
 		}
+
 		return new EmbeddingResponse(embeddings);
 	}
 
@@ -117,6 +152,13 @@ public int dimensions() {
 
 	}
 
+	private String summarizeInput(String input) {
+		if (this.inputType == InputType.IMAGE) {
+			return "[image content omitted, length=" + input.length() + "]";
+		}
+		return input.length() > 100 ? input.substring(0, 100) + "..." : input;
+	}
+
 	public enum InputType {
 
 		TEXT, IMAGE

From ed4ae45ce1ca0c2a1b35ea88b5cbee0813446178 Mon Sep 17 00:00:00 2001
From: PSriVarshan <psvvarshan@gmail.com>
Date: Tue, 6 May 2025 19:41:18 +0530
Subject: [PATCH 2/6] Fix formatting issues in BedrockTitanEmbeddingModel.java

Signed-off-by: PSriVarshan <psvvarshan@gmail.com>
---
 .../titan/BedrockTitanEmbeddingModel.java     | 37 +++++++++----------
 1 file changed, 17 insertions(+), 20 deletions(-)

diff --git a/models/spring-ai-bedrock/src/main/java/org/springframework/ai/bedrock/titan/BedrockTitanEmbeddingModel.java b/models/spring-ai-bedrock/src/main/java/org/springframework/ai/bedrock/titan/BedrockTitanEmbeddingModel.java
index d1bc99083b3..01f6d58e65b 100644
--- a/models/spring-ai-bedrock/src/main/java/org/springframework/ai/bedrock/titan/BedrockTitanEmbeddingModel.java
+++ b/models/spring-ai-bedrock/src/main/java/org/springframework/ai/bedrock/titan/BedrockTitanEmbeddingModel.java
@@ -38,10 +38,8 @@
 import io.micrometer.observation.Observation;
 
 /**
- * {@link org.springframework.ai.embedding.EmbeddingModel} implementation that
- * uses the
- * Bedrock Titan Embedding API. Titan Embedding supports text and image (encoded
- * in
+ * {@link org.springframework.ai.embedding.EmbeddingModel} implementation that uses the
+ * Bedrock Titan Embedding API. Titan Embedding supports text and image (encoded in
  * base64) inputs.
  *
  * Note: Titan Embedding does not support batch embedding.
@@ -59,8 +57,7 @@ public class BedrockTitanEmbeddingModel extends AbstractEmbeddingModel {
 	private final ObservationRegistry observationRegistry;
 
 	/**
-	 * Titan Embedding API input types. Could be either text or image (encoded in
-	 * base64).
+	 * Titan Embedding API input types. Could be either text or image (encoded in base64).
 	 */
 	private InputType inputType = InputType.TEXT;
 
@@ -71,9 +68,7 @@ public BedrockTitanEmbeddingModel(TitanEmbeddingBedrockApi titanEmbeddingBedrock
 	}
 
 	/**
-	 * Titan Embedding API input types. Could be either text or image (encoded in
-	 * base64).
-	 * 
+	 * Titan Embedding API input types. Could be either text or image (encoded in base64).
 	 * @param inputType the input type to use.
 	 */
 	public BedrockTitanEmbeddingModel withInputType(InputType inputType) {
@@ -101,15 +96,15 @@ public EmbeddingResponse call(EmbeddingRequest request) {
 
 			try {
 				TitanEmbeddingResponse response = Observation
-						.createNotStarted("bedrock.embedding", this.observationRegistry)
-						.lowCardinalityKeyValue("model", "titan")
-						.lowCardinalityKeyValue("input_type", this.inputType.name().toLowerCase())
-						.highCardinalityKeyValue("input_length", String.valueOf(inputContent.length()))
-						.observe(() -> {
-							TitanEmbeddingResponse r = this.embeddingApi.embedding(apiRequest);
-							Assert.notNull(r, "Embedding API returned null response");
-							return r;
-						});
+					.createNotStarted("bedrock.embedding", this.observationRegistry)
+					.lowCardinalityKeyValue("model", "titan")
+					.lowCardinalityKeyValue("input_type", this.inputType.name().toLowerCase())
+					.highCardinalityKeyValue("input_length", String.valueOf(inputContent.length()))
+					.observe(() -> {
+						TitanEmbeddingResponse r = this.embeddingApi.embedding(apiRequest);
+						Assert.notNull(r, "Embedding API returned null response");
+						return r;
+					});
 
 				if (response.embedding() == null || response.embedding().length == 0) {
 					logger.warn("Empty embedding vector returned for input at index {}. Skipping.", indexCounter.get());
@@ -117,10 +112,12 @@ public EmbeddingResponse call(EmbeddingRequest request) {
 				}
 
 				embeddings.add(new Embedding(response.embedding(), indexCounter.getAndIncrement()));
-			} catch (Exception ex) {
+			}
+			catch (Exception ex) {
 				logger.error("Titan API embedding failed for input at index {}: {}", indexCounter.get(),
 						summarizeInput(inputContent), ex);
-				throw ex; // Optional: Continue instead of throwing if you want partial success
+				throw ex; // Optional: Continue instead of throwing if you want partial
+							// success
 			}
 		}
 

From 4dccbd113e873f46fd11259b37adc8c2a79d2ca8 Mon Sep 17 00:00:00 2001
From: PSriVarshan <psvvarshan@gmail.com>
Date: Tue, 6 May 2025 21:53:53 +0530
Subject: [PATCH 3/6] Fix formatting issues in BedrockTitanEmbeddingModel.java

Signed-off-by: PSriVarshan <psvvarshan@gmail.com>
---
 .../ai/bedrock/titan/BedrockTitanEmbeddingModelIT.java | 10 ++++++++--
 1 file changed, 8 insertions(+), 2 deletions(-)

diff --git a/models/spring-ai-bedrock/src/test/java/org/springframework/ai/bedrock/titan/BedrockTitanEmbeddingModelIT.java b/models/spring-ai-bedrock/src/test/java/org/springframework/ai/bedrock/titan/BedrockTitanEmbeddingModelIT.java
index 17ad44f00b8..4ade446f1d4 100644
--- a/models/spring-ai-bedrock/src/test/java/org/springframework/ai/bedrock/titan/BedrockTitanEmbeddingModelIT.java
+++ b/models/spring-ai-bedrock/src/test/java/org/springframework/ai/bedrock/titan/BedrockTitanEmbeddingModelIT.java
@@ -40,6 +40,8 @@
 
 import static org.assertj.core.api.Assertions.assertThat;
 
+import io.micrometer.observation.tck.TestObservationRegistry;
+
 @SpringBootTest
 @RequiresAwsCredentials
 class BedrockTitanEmbeddingModelIT {
@@ -47,6 +49,9 @@ class BedrockTitanEmbeddingModelIT {
 	@Autowired
 	private BedrockTitanEmbeddingModel embeddingModel;
 
+	@Autowired
+	TestObservationRegistry observationRegistry;
+
 	@Test
 	void singleEmbedding() {
 		assertThat(this.embeddingModel).isNotNull();
@@ -82,8 +87,9 @@ public TitanEmbeddingBedrockApi titanEmbeddingApi() {
 		}
 
 		@Bean
-		public BedrockTitanEmbeddingModel titanEmbedding(TitanEmbeddingBedrockApi titanEmbeddingApi) {
-			return new BedrockTitanEmbeddingModel(titanEmbeddingApi);
+		public BedrockTitanEmbeddingModel titanEmbedding(TitanEmbeddingBedrockApi titanEmbeddingApi,
+				TestObservationRegistry observationRegistry) {
+			return new BedrockTitanEmbeddingModel(titanEmbeddingApi, observationRegistry);
 		}
 
 	}

From 4696eb54f20474a394c9965a7c0f76c76d8734c1 Mon Sep 17 00:00:00 2001
From: PSriVarshan <psvvarshan@gmail.com>
Date: Tue, 6 May 2025 22:13:37 +0530
Subject: [PATCH 4/6] Fixing issues in BedrockTitanEmbeddingModel.java

Signed-off-by: PSriVarshan <psvvarshan@gmail.com>
---
 .../SpringAiRetryAutoConfiguration.java       | 61 +++++++++++--------
 .../pom.xml                                   |  8 ++-
 ...edrockTitanEmbeddingAutoConfiguration.java |  9 ++-
 3 files changed, 50 insertions(+), 28 deletions(-)

diff --git a/auto-configurations/common/spring-ai-autoconfigure-retry/src/main/java/org/springframework/ai/retry/autoconfigure/SpringAiRetryAutoConfiguration.java b/auto-configurations/common/spring-ai-autoconfigure-retry/src/main/java/org/springframework/ai/retry/autoconfigure/SpringAiRetryAutoConfiguration.java
index f44ec961369..9de2c004d45 100644
--- a/auto-configurations/common/spring-ai-autoconfigure-retry/src/main/java/org/springframework/ai/retry/autoconfigure/SpringAiRetryAutoConfiguration.java
+++ b/auto-configurations/common/spring-ai-autoconfigure-retry/src/main/java/org/springframework/ai/retry/autoconfigure/SpringAiRetryAutoConfiguration.java
@@ -41,9 +41,11 @@
 import org.springframework.web.client.ResponseErrorHandler;
 
 /**
- * {@link AutoConfiguration Auto-configuration} for AI Retry.
+ * {@link AutoConfiguration Auto-configuration} for AI Retry. Provides beans for retry
+ * template and response error handling. Handles transient and non-transient exceptions
+ * based on HTTP status codes.
  *
- * @author Christian Tzolov
+ * Author: Christian Tzolov
  */
 @AutoConfiguration
 @ConditionalOnClass(RetryUtils.class)
@@ -63,9 +65,10 @@ public RetryTemplate retryTemplate(SpringAiRetryProperties properties) {
 			.withListener(new RetryListener() {
 
 				@Override
-				public <T extends Object, E extends Throwable> void onError(RetryContext context,
-						RetryCallback<T, E> callback, Throwable throwable) {
-					logger.warn("Retry error. Retry count:" + context.getRetryCount(), throwable);
+				public <T, E extends Throwable> void onError(RetryContext context, RetryCallback<T, E> callback,
+						Throwable throwable) {
+					logger.warn("Retry error. Retry count: {}, Exception: {}", context.getRetryCount(),
+							throwable.getMessage(), throwable);
 				}
 			})
 			.build();
@@ -84,29 +87,35 @@ public boolean hasError(@NonNull ClientHttpResponse response) throws IOException
 
 			@Override
 			public void handleError(@NonNull ClientHttpResponse response) throws IOException {
-				if (response.getStatusCode().isError()) {
-					String error = StreamUtils.copyToString(response.getBody(), StandardCharsets.UTF_8);
-					String message = String.format("%s - %s", response.getStatusCode().value(), error);
-
-					// Explicitly configured transient codes
-					if (properties.getOnHttpCodes().contains(response.getStatusCode().value())) {
-						throw new TransientAiException(message);
-					}
-
-					// onClientErrors - If true, do not throw a NonTransientAiException,
-					// and do not attempt retry for 4xx client error codes, false by
-					// default.
-					if (!properties.isOnClientErrors() && response.getStatusCode().is4xxClientError()) {
-						throw new NonTransientAiException(message);
-					}
-
-					// Explicitly configured non-transient codes
-					if (!CollectionUtils.isEmpty(properties.getExcludeOnHttpCodes())
-							&& properties.getExcludeOnHttpCodes().contains(response.getStatusCode().value())) {
-						throw new NonTransientAiException(message);
-					}
+				if (!response.getStatusCode().isError()) {
+					return;
+				}
+
+				String error = StreamUtils.copyToString(response.getBody(), StandardCharsets.UTF_8);
+				if (error == null || error.isEmpty()) {
+					error = "No response body available";
+				}
+
+				String message = String.format("HTTP %s - %s", response.getStatusCode().value(), error);
+
+				// Explicitly configured transient codes
+				if (properties.getOnHttpCodes().contains(response.getStatusCode().value())) {
 					throw new TransientAiException(message);
 				}
+
+				// Handle client errors (4xx)
+				if (!properties.isOnClientErrors() && response.getStatusCode().is4xxClientError()) {
+					throw new NonTransientAiException(message);
+				}
+
+				// Explicitly configured non-transient codes
+				if (!CollectionUtils.isEmpty(properties.getExcludeOnHttpCodes())
+						&& properties.getExcludeOnHttpCodes().contains(response.getStatusCode().value())) {
+					throw new NonTransientAiException(message);
+				}
+
+				// Default to transient exception
+				throw new TransientAiException(message);
 			}
 		};
 	}
diff --git a/auto-configurations/models/spring-ai-autoconfigure-model-bedrock-ai/pom.xml b/auto-configurations/models/spring-ai-autoconfigure-model-bedrock-ai/pom.xml
index 673d769174a..f9317976a19 100644
--- a/auto-configurations/models/spring-ai-autoconfigure-model-bedrock-ai/pom.xml
+++ b/auto-configurations/models/spring-ai-autoconfigure-model-bedrock-ai/pom.xml
@@ -110,6 +110,12 @@
 			<artifactId>mockito-core</artifactId>
 			<scope>test</scope>
 		</dependency>
-	</dependencies>
+	
+   <dependency>
+     <groupId>io.micrometer</groupId>
+     <artifactId>micrometer-observation</artifactId>
+     <version>1.15.0-RC1</version>
+   </dependency>
+ </dependencies>
 
 </project>
diff --git a/auto-configurations/models/spring-ai-autoconfigure-model-bedrock-ai/src/main/java/org/springframework/ai/model/bedrock/titan/autoconfigure/BedrockTitanEmbeddingAutoConfiguration.java b/auto-configurations/models/spring-ai-autoconfigure-model-bedrock-ai/src/main/java/org/springframework/ai/model/bedrock/titan/autoconfigure/BedrockTitanEmbeddingAutoConfiguration.java
index 73795fc494f..fe33b2824e6 100644
--- a/auto-configurations/models/spring-ai-autoconfigure-model-bedrock-ai/src/main/java/org/springframework/ai/model/bedrock/titan/autoconfigure/BedrockTitanEmbeddingAutoConfiguration.java
+++ b/auto-configurations/models/spring-ai-autoconfigure-model-bedrock-ai/src/main/java/org/springframework/ai/model/bedrock/titan/autoconfigure/BedrockTitanEmbeddingAutoConfiguration.java
@@ -17,6 +17,8 @@
 package org.springframework.ai.model.bedrock.titan.autoconfigure;
 
 import com.fasterxml.jackson.databind.ObjectMapper;
+
+import io.micrometer.observation.ObservationRegistry;
 import software.amazon.awssdk.auth.credentials.AwsCredentialsProvider;
 import software.amazon.awssdk.regions.providers.AwsRegionProvider;
 
@@ -26,6 +28,7 @@
 import org.springframework.ai.model.SpringAIModels;
 import org.springframework.ai.model.bedrock.autoconfigure.BedrockAwsConnectionConfiguration;
 import org.springframework.ai.model.bedrock.autoconfigure.BedrockAwsConnectionProperties;
+import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.boot.autoconfigure.AutoConfiguration;
 import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
 import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
@@ -50,6 +53,9 @@
 @Import(BedrockAwsConnectionConfiguration.class)
 public class BedrockTitanEmbeddingAutoConfiguration {
 
+	@Autowired
+	private ObservationRegistry observationRegistry;
+
 	@Bean
 	@ConditionalOnMissingBean
 	@ConditionalOnBean({ AwsCredentialsProvider.class, AwsRegionProvider.class })
@@ -65,7 +71,8 @@ public TitanEmbeddingBedrockApi titanEmbeddingBedrockApi(AwsCredentialsProvider
 	@ConditionalOnBean(TitanEmbeddingBedrockApi.class)
 	public BedrockTitanEmbeddingModel titanEmbeddingModel(TitanEmbeddingBedrockApi titanEmbeddingApi,
 			BedrockTitanEmbeddingProperties properties) {
-		return new BedrockTitanEmbeddingModel(titanEmbeddingApi).withInputType(properties.getInputType());
+		return new BedrockTitanEmbeddingModel(titanEmbeddingApi, observationRegistry)
+			.withInputType(properties.getInputType());
 	}
 
 }

From 1f4f61ab7470a189d7899a34622bf147baba584a Mon Sep 17 00:00:00 2001
From: PSriVarshan <psvvarshan@gmail.com>
Date: Tue, 6 May 2025 22:32:23 +0530
Subject: [PATCH 5/6] Fix formatting issues in BedrockTitanEmbeddingModel.java

Signed-off-by: PSriVarshan <psvvarshan@gmail.com>
---
 .../pom.xml                                   |  6 ++++
 ...edrockTitanEmbeddingAutoConfiguration.java | 32 +++++++++++++------
 2 files changed, 29 insertions(+), 9 deletions(-)

diff --git a/auto-configurations/models/spring-ai-autoconfigure-model-bedrock-ai/pom.xml b/auto-configurations/models/spring-ai-autoconfigure-model-bedrock-ai/pom.xml
index f9317976a19..35cf2c7ac71 100644
--- a/auto-configurations/models/spring-ai-autoconfigure-model-bedrock-ai/pom.xml
+++ b/auto-configurations/models/spring-ai-autoconfigure-model-bedrock-ai/pom.xml
@@ -79,6 +79,12 @@
 			<optional>true</optional>
 		</dependency>
 
+		<dependency>
+			<groupId>org.springframework.boot</groupId>
+			<artifactId>spring-boot-test</artifactId>
+			<scope>test</scope>
+    	</dependency>
+
 		<dependency>
 			<groupId>org.springframework.boot</groupId>
 			<artifactId>spring-boot-configuration-processor</artifactId>
diff --git a/auto-configurations/models/spring-ai-autoconfigure-model-bedrock-ai/src/main/java/org/springframework/ai/model/bedrock/titan/autoconfigure/BedrockTitanEmbeddingAutoConfiguration.java b/auto-configurations/models/spring-ai-autoconfigure-model-bedrock-ai/src/main/java/org/springframework/ai/model/bedrock/titan/autoconfigure/BedrockTitanEmbeddingAutoConfiguration.java
index fe33b2824e6..200e0adcbee 100644
--- a/auto-configurations/models/spring-ai-autoconfigure-model-bedrock-ai/src/main/java/org/springframework/ai/model/bedrock/titan/autoconfigure/BedrockTitanEmbeddingAutoConfiguration.java
+++ b/auto-configurations/models/spring-ai-autoconfigure-model-bedrock-ai/src/main/java/org/springframework/ai/model/bedrock/titan/autoconfigure/BedrockTitanEmbeddingAutoConfiguration.java
@@ -28,7 +28,6 @@
 import org.springframework.ai.model.SpringAIModels;
 import org.springframework.ai.model.bedrock.autoconfigure.BedrockAwsConnectionConfiguration;
 import org.springframework.ai.model.bedrock.autoconfigure.BedrockAwsConnectionProperties;
-import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.boot.autoconfigure.AutoConfiguration;
 import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
 import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
@@ -39,7 +38,8 @@
 import org.springframework.context.annotation.Import;
 
 /**
- * {@link AutoConfiguration Auto-configuration} for Bedrock Titan Embedding Model.
+ * {@link AutoConfiguration Auto-configuration} for Bedrock Titan Embedding
+ * Model.
  *
  * @author Christian Tzolov
  * @author Wei Jiang
@@ -48,31 +48,45 @@
 @AutoConfiguration
 @ConditionalOnClass(TitanEmbeddingBedrockApi.class)
 @EnableConfigurationProperties({ BedrockTitanEmbeddingProperties.class, BedrockAwsConnectionProperties.class })
-@ConditionalOnProperty(name = SpringAIModelProperties.EMBEDDING_MODEL, havingValue = SpringAIModels.BEDROCK_TITAN,
-		matchIfMissing = true)
+@ConditionalOnProperty(name = SpringAIModelProperties.EMBEDDING_MODEL, havingValue = SpringAIModels.BEDROCK_TITAN, matchIfMissing = true)
 @Import(BedrockAwsConnectionConfiguration.class)
 public class BedrockTitanEmbeddingAutoConfiguration {
 
-	@Autowired
-	private ObservationRegistry observationRegistry;
-
 	@Bean
 	@ConditionalOnMissingBean
 	@ConditionalOnBean({ AwsCredentialsProvider.class, AwsRegionProvider.class })
 	public TitanEmbeddingBedrockApi titanEmbeddingBedrockApi(AwsCredentialsProvider credentialsProvider,
 			AwsRegionProvider regionProvider, BedrockTitanEmbeddingProperties properties,
 			BedrockAwsConnectionProperties awsProperties, ObjectMapper objectMapper) {
+
+		// Validate required properties
+		if (properties.getModel() == null || awsProperties.getTimeout() == null) {
+			throw new IllegalArgumentException("Required properties for TitanEmbeddingBedrockApi are missing.");
+		}
+
 		return new TitanEmbeddingBedrockApi(properties.getModel(), credentialsProvider, regionProvider.getRegion(),
 				objectMapper, awsProperties.getTimeout());
 	}
 
+	@Bean
+	@ConditionalOnMissingBean
+	public ObservationRegistry observationRegistry() {
+		return ObservationRegistry.create();
+	}
+
 	@Bean
 	@ConditionalOnMissingBean
 	@ConditionalOnBean(TitanEmbeddingBedrockApi.class)
 	public BedrockTitanEmbeddingModel titanEmbeddingModel(TitanEmbeddingBedrockApi titanEmbeddingApi,
-			BedrockTitanEmbeddingProperties properties) {
+			BedrockTitanEmbeddingProperties properties, ObservationRegistry observationRegistry) {
+
+		// Validate required properties
+		if (properties.getInputType() == null) {
+			throw new IllegalArgumentException("InputType property for BedrockTitanEmbeddingModel is missing.");
+		}
+
 		return new BedrockTitanEmbeddingModel(titanEmbeddingApi, observationRegistry)
-			.withInputType(properties.getInputType());
+				.withInputType(properties.getInputType());
 	}
 
 }

From b1276615634019b589b73ca61f2fcc46fe381a1e Mon Sep 17 00:00:00 2001
From: PSriVarshan <psvvarshan@gmail.com>
Date: Tue, 6 May 2025 23:04:50 +0530
Subject: [PATCH 6/6] Add observability to Bedrock Titan Embedding model

Signed-off-by: PSriVarshan <psvvarshan@gmail.com>
---
 .../BedrockTitanEmbeddingAutoConfiguration.java           | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/auto-configurations/models/spring-ai-autoconfigure-model-bedrock-ai/src/main/java/org/springframework/ai/model/bedrock/titan/autoconfigure/BedrockTitanEmbeddingAutoConfiguration.java b/auto-configurations/models/spring-ai-autoconfigure-model-bedrock-ai/src/main/java/org/springframework/ai/model/bedrock/titan/autoconfigure/BedrockTitanEmbeddingAutoConfiguration.java
index 200e0adcbee..b4d904cc518 100644
--- a/auto-configurations/models/spring-ai-autoconfigure-model-bedrock-ai/src/main/java/org/springframework/ai/model/bedrock/titan/autoconfigure/BedrockTitanEmbeddingAutoConfiguration.java
+++ b/auto-configurations/models/spring-ai-autoconfigure-model-bedrock-ai/src/main/java/org/springframework/ai/model/bedrock/titan/autoconfigure/BedrockTitanEmbeddingAutoConfiguration.java
@@ -38,8 +38,7 @@
 import org.springframework.context.annotation.Import;
 
 /**
- * {@link AutoConfiguration Auto-configuration} for Bedrock Titan Embedding
- * Model.
+ * {@link AutoConfiguration Auto-configuration} for Bedrock Titan Embedding Model.
  *
  * @author Christian Tzolov
  * @author Wei Jiang
@@ -48,7 +47,8 @@
 @AutoConfiguration
 @ConditionalOnClass(TitanEmbeddingBedrockApi.class)
 @EnableConfigurationProperties({ BedrockTitanEmbeddingProperties.class, BedrockAwsConnectionProperties.class })
-@ConditionalOnProperty(name = SpringAIModelProperties.EMBEDDING_MODEL, havingValue = SpringAIModels.BEDROCK_TITAN, matchIfMissing = true)
+@ConditionalOnProperty(name = SpringAIModelProperties.EMBEDDING_MODEL, havingValue = SpringAIModels.BEDROCK_TITAN,
+		matchIfMissing = true)
 @Import(BedrockAwsConnectionConfiguration.class)
 public class BedrockTitanEmbeddingAutoConfiguration {
 
@@ -86,7 +86,7 @@ public BedrockTitanEmbeddingModel titanEmbeddingModel(TitanEmbeddingBedrockApi t
 		}
 
 		return new BedrockTitanEmbeddingModel(titanEmbeddingApi, observationRegistry)
-				.withInputType(properties.getInputType());
+			.withInputType(properties.getInputType());
 	}
 
 }