Skip to content

Commit 0ff770f

Browse files
committed
Add observability to Bedrock Titan Embedding model
1 parent b6ce7f3 commit 0ff770f

File tree

1 file changed

+51
-9
lines changed

1 file changed

+51
-9
lines changed

models/spring-ai-bedrock/src/main/java/org/springframework/ai/bedrock/titan/BedrockTitanEmbeddingModel.java

+51-9
Original file line numberDiff line numberDiff line change
@@ -34,9 +34,14 @@
3434
import org.springframework.ai.embedding.EmbeddingResponse;
3535
import org.springframework.util.Assert;
3636

37+
import io.micrometer.observation.ObservationRegistry;
38+
import io.micrometer.observation.Observation;
39+
3740
/**
38-
* {@link org.springframework.ai.embedding.EmbeddingModel} implementation that uses the
39-
* Bedrock Titan Embedding API. Titan Embedding supports text and image (encoded in
41+
* {@link org.springframework.ai.embedding.EmbeddingModel} implementation that
42+
* uses the
43+
* Bedrock Titan Embedding API. Titan Embedding supports text and image (encoded
44+
* in
4045
* base64) inputs.
4146
*
4247
* Note: Titan Embedding does not support batch embedding.
@@ -51,17 +56,24 @@ public class BedrockTitanEmbeddingModel extends AbstractEmbeddingModel {
5156

5257
private final TitanEmbeddingBedrockApi embeddingApi;
5358

59+
private final ObservationRegistry observationRegistry;
60+
5461
/**
55-
* Titan Embedding API input types. Could be either text or image (encoded in base64).
62+
* Titan Embedding API input types. Could be either text or image (encoded in
63+
* base64).
5664
*/
5765
private InputType inputType = InputType.TEXT;
5866

59-
public BedrockTitanEmbeddingModel(TitanEmbeddingBedrockApi titanEmbeddingBedrockApi) {
67+
public BedrockTitanEmbeddingModel(TitanEmbeddingBedrockApi titanEmbeddingBedrockApi,
68+
ObservationRegistry observationRegistry) {
6069
this.embeddingApi = titanEmbeddingBedrockApi;
70+
this.observationRegistry = observationRegistry;
6171
}
6272

6373
/**
64-
* Titan Embedding API input types. Could be either text or image (encoded in base64).
74+
* Titan Embedding API input types. Could be either text or image (encoded in
75+
* base64).
76+
*
6577
* @param inputType the input type to use.
6678
*/
6779
public BedrockTitanEmbeddingModel withInputType(InputType inputType) {
@@ -78,17 +90,40 @@ public float[] embed(Document document) {
7890
public EmbeddingResponse call(EmbeddingRequest request) {
7991
Assert.notEmpty(request.getInstructions(), "At least one text is required!");
8092
if (request.getInstructions().size() != 1) {
81-
logger.warn(
82-
"Titan Embedding does not support batch embedding. Will make multiple API calls to embed(Document)");
93+
logger.warn("Titan Embedding does not support batch embedding. Multiple API calls will be made.");
8394
}
8495

8596
List<Embedding> embeddings = new ArrayList<>();
8697
var indexCounter = new AtomicInteger(0);
98+
8799
for (String inputContent : request.getInstructions()) {
88100
var apiRequest = createTitanEmbeddingRequest(inputContent, request.getOptions());
89-
TitanEmbeddingResponse response = this.embeddingApi.embedding(apiRequest);
90-
embeddings.add(new Embedding(response.embedding(), indexCounter.getAndIncrement()));
101+
102+
try {
103+
TitanEmbeddingResponse response = Observation
104+
.createNotStarted("bedrock.embedding", this.observationRegistry)
105+
.lowCardinalityKeyValue("model", "titan")
106+
.lowCardinalityKeyValue("input_type", this.inputType.name().toLowerCase())
107+
.highCardinalityKeyValue("input_length", String.valueOf(inputContent.length()))
108+
.observe(() -> {
109+
TitanEmbeddingResponse r = this.embeddingApi.embedding(apiRequest);
110+
Assert.notNull(r, "Embedding API returned null response");
111+
return r;
112+
});
113+
114+
if (response.embedding() == null || response.embedding().length == 0) {
115+
logger.warn("Empty embedding vector returned for input at index {}. Skipping.", indexCounter.get());
116+
continue;
117+
}
118+
119+
embeddings.add(new Embedding(response.embedding(), indexCounter.getAndIncrement()));
120+
} catch (Exception ex) {
121+
logger.error("Titan API embedding failed for input at index {}: {}", indexCounter.get(),
122+
summarizeInput(inputContent), ex);
123+
throw ex; // Optional: Continue instead of throwing if you want partial success
124+
}
91125
}
126+
92127
return new EmbeddingResponse(embeddings);
93128
}
94129

@@ -117,6 +152,13 @@ public int dimensions() {
117152

118153
}
119154

155+
private String summarizeInput(String input) {
156+
if (this.inputType == InputType.IMAGE) {
157+
return "[image content omitted, length=" + input.length() + "]";
158+
}
159+
return input.length() > 100 ? input.substring(0, 100) + "..." : input;
160+
}
161+
120162
public enum InputType {
121163

122164
TEXT, IMAGE

0 commit comments

Comments
 (0)