Skip to content

Commit 5c3ed11

Browse files
impactCntzolov
authored andcommitted
Add support of Bedrock API timeout
Configure the amount of time to allow the client to complete the execution of an API call. This timeout covers the entire client execution except for marshalling. This includes request handler execution, all HTTP requests including retries, unmarshalling, etc. This value should always be positive, if present. - Add timeout filed to the AbstractBedrockApi, used to initialize the BedrockRuntimeClient and the BedrockStreamingRuntimeClient. Update all classes that extend the AbstractBedrockApi. - Keep the previous constructors for backward compatibility using timeout value of 5 min. - Add a common AWS connection timeout auto-config property and update the documentation. Defaults to 5 min. Additional changes: - Fix Anthropic 3 straming response - add bedrock metrics field. - Increate the default timeout to 5 min. Update the docs. - Increase the ITs.
1 parent dd5757c commit 5c3ed11

File tree

47 files changed

+390
-78
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

47 files changed

+390
-78
lines changed

models/spring-ai-bedrock/src/main/java/org/springframework/ai/bedrock/anthropic/api/AnthropicChatBedrockApi.java

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
*/
1616
package org.springframework.ai.bedrock.anthropic.api;
1717

18+
import java.time.Duration;
1819
import java.util.List;
1920

2021
import com.fasterxml.jackson.annotation.JsonInclude;
@@ -54,6 +55,16 @@ public AnthropicChatBedrockApi(String modelId, String region) {
5455
super(modelId, region);
5556
}
5657

58+
/**
59+
* Create a new AnthropicChatBedrockApi instance using the default credentials provider chain, the default object.
60+
* @param modelId The model id to use. See the {@link AnthropicChatModel} for the supported models.
61+
* @param region The AWS region to use.
62+
* @param timeout The timeout to use.
63+
*/
64+
public AnthropicChatBedrockApi(String modelId, String region, Duration timeout) {
65+
super(modelId, region, timeout);
66+
}
67+
5768
/**
5869
* Create a new AnthropicChatBedrockApi instance using the provided credentials provider, region and object mapper.
5970
*
@@ -67,6 +78,20 @@ public AnthropicChatBedrockApi(String modelId, AwsCredentialsProvider credential
6778
super(modelId, credentialsProvider, region, objectMapper);
6879
}
6980

81+
/**
82+
* Create a new AnthropicChatBedrockApi instance using the provided credentials provider, region and object mapper.
83+
*
84+
* @param modelId The model id to use. See the {@link AnthropicChatModel} for the supported models.
85+
* @param credentialsProvider The credentials provider to connect to AWS.
86+
* @param region The AWS region to use.
87+
* @param objectMapper The object mapper to use for JSON serialization and deserialization.
88+
* @param timeout The timeout to use.
89+
*/
90+
public AnthropicChatBedrockApi(String modelId, AwsCredentialsProvider credentialsProvider, String region,
91+
ObjectMapper objectMapper, Duration timeout) {
92+
super(modelId, credentialsProvider, region, objectMapper, timeout);
93+
}
94+
7095
// https://github.com/build-on-aws/amazon-bedrock-java-examples/blob/main/example_code/bedrock-runtime/src/main/java/aws/community/examples/InvokeBedrockStreamingAsync.java
7196

7297
// https://docs.anthropic.com/claude/reference/complete_post

models/spring-ai-bedrock/src/main/java/org/springframework/ai/bedrock/anthropic3/api/Anthropic3ChatBedrockApi.java

Lines changed: 36 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
import reactor.core.publisher.Flux;
2828
import software.amazon.awssdk.auth.credentials.AwsCredentialsProvider;
2929

30+
import java.time.Duration;
3031
import java.util.List;
3132

3233
/**
@@ -58,6 +59,16 @@ public Anthropic3ChatBedrockApi(String modelId, String region) {
5859
super(modelId, region);
5960
}
6061

62+
/**
63+
* Create a new AnthropicChatBedrockApi instance using the default credentials provider chain, the default object.
64+
* @param modelId The model id to use. See the {@link AnthropicChatModel} for the supported models.
65+
* @param region The AWS region to use.
66+
* @param timeout The timeout to use.
67+
*/
68+
public Anthropic3ChatBedrockApi(String modelId, String region, Duration timeout) {
69+
super(modelId, region, timeout);
70+
}
71+
6172
/**
6273
* Create a new AnthropicChatBedrockApi instance using the provided credentials provider, region and object mapper.
6374
*
@@ -71,6 +82,20 @@ public Anthropic3ChatBedrockApi(String modelId, AwsCredentialsProvider credentia
7182
super(modelId, credentialsProvider, region, objectMapper);
7283
}
7384

85+
/**
86+
* Create a new AnthropicChatBedrockApi instance using the provided credentials provider, region and object mapper.
87+
*
88+
* @param modelId The model id to use. See the {@link AnthropicChatModel} for the supported models.
89+
* @param credentialsProvider The credentials provider to connect to AWS.
90+
* @param region The AWS region to use.
91+
* @param objectMapper The object mapper to use for JSON serialization and deserialization.
92+
* @param timeout The timeout to use.
93+
*/
94+
public Anthropic3ChatBedrockApi(String modelId, AwsCredentialsProvider credentialsProvider, String region,
95+
ObjectMapper objectMapper, Duration timeout) {
96+
super(modelId, credentialsProvider, region, objectMapper, timeout);
97+
}
98+
7499
// https://github.com/build-on-aws/amazon-bedrock-java-examples/blob/main/example_code/bedrock-runtime/src/main/java/aws/community/examples/InvokeBedrockStreamingAsync.java
75100

76101
// https://docs.anthropic.com/claude/reference/complete_post
@@ -307,10 +332,12 @@ public record AnthropicUsage(@JsonProperty("input_tokens") Integer inputTokens,
307332
* @param usage Metrics about the model invocation.
308333
*/
309334
@JsonInclude(Include.NON_NULL)
310-
public record AnthropicChatResponse(@JsonProperty("id") String id, @JsonProperty("model") String model,
311-
@JsonProperty("type") String type, @JsonProperty("role") String role,
312-
@JsonProperty("content") List<MediaContent> content, @JsonProperty("stop_reason") String stopReason,
313-
@JsonProperty("stop_sequence") String stopSequence, @JsonProperty("usage") AnthropicUsage usage) {
335+
public record AnthropicChatResponse(// formatter:off
336+
@JsonProperty("id") String id, @JsonProperty("model") String model, @JsonProperty("type") String type,
337+
@JsonProperty("role") String role, @JsonProperty("content") List<MediaContent> content,
338+
@JsonProperty("stop_reason") String stopReason, @JsonProperty("stop_sequence") String stopSequence,
339+
@JsonProperty("usage") AnthropicUsage usage,
340+
@JsonProperty("amazon-bedrock-invocationMetrics") AmazonBedrockInvocationMetrics amazonBedrockInvocationMetrics) { // formatter:on
314341
}
315342

316343
/**
@@ -326,10 +353,11 @@ public record AnthropicChatResponse(@JsonProperty("id") String id, @JsonProperty
326353
* @param usage The usage data.
327354
*/
328355
@JsonInclude(Include.NON_NULL)
329-
public record AnthropicChatStreamingResponse(@JsonProperty("type") StreamingType type,
330-
@JsonProperty("message") AnthropicChatResponse message, @JsonProperty("index") Integer index,
331-
@JsonProperty("content_block") MediaContent contentBlock, @JsonProperty("delta") Delta delta,
332-
@JsonProperty("usage") AnthropicUsage usage) {
356+
public record AnthropicChatStreamingResponse(// formatter:off
357+
@JsonProperty("type") StreamingType type, @JsonProperty("message") AnthropicChatResponse message,
358+
@JsonProperty("index") Integer index, @JsonProperty("content_block") MediaContent contentBlock,
359+
@JsonProperty("delta") Delta delta, @JsonProperty("usage") AnthropicUsage usage,
360+
@JsonProperty("amazon-bedrock-invocationMetrics") AmazonBedrockInvocationMetrics amazonBedrockInvocationMetrics) { // formatter:on
333361

334362
/**
335363
* The streaming type of this message.

models/spring-ai-bedrock/src/main/java/org/springframework/ai/bedrock/api/AbstractBedrockApi.java

Lines changed: 44 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,6 @@
2424
import com.fasterxml.jackson.annotation.JsonInclude.Include;
2525
import com.fasterxml.jackson.annotation.JsonProperty;
2626
import com.fasterxml.jackson.core.JsonProcessingException;
27-
import com.fasterxml.jackson.databind.DeserializationFeature;
2827
import com.fasterxml.jackson.databind.ObjectMapper;
2928
import org.slf4j.Logger;
3029
import org.slf4j.LoggerFactory;
@@ -44,6 +43,9 @@
4443
import software.amazon.awssdk.services.bedrockruntime.model.InvokeModelWithResponseStreamResponseHandler;
4544
import software.amazon.awssdk.services.bedrockruntime.model.ResponseStream;
4645

46+
import org.springframework.ai.model.ModelOptionsUtils;
47+
import org.springframework.util.Assert;
48+
4749
/**
4850
* Abstract class for the Bedrock API. It provides the basic functionality to invoke the chat completion model and
4951
* receive the response for streaming and non-streaming requests.
@@ -67,7 +69,6 @@ public abstract class AbstractBedrockApi<I, O, SO> {
6769

6870
private final String modelId;
6971
private final ObjectMapper objectMapper;
70-
private final AwsCredentialsProvider credentialsProvider;
7172
private final String region;
7273
private final BedrockRuntimeClient client;
7374
private final BedrockRuntimeAsyncClient clientStreaming;
@@ -79,48 +80,81 @@ public abstract class AbstractBedrockApi<I, O, SO> {
7980
* @param region The AWS region to use.
8081
*/
8182
public AbstractBedrockApi(String modelId, String region) {
82-
this(modelId, ProfileCredentialsProvider.builder().build(), region, new ObjectMapper());
83+
this(modelId, ProfileCredentialsProvider.builder().build(), region, ModelOptionsUtils.OBJECT_MAPPER, Duration.ofMinutes(5));
84+
}
85+
/**
86+
* Create a new AbstractBedrockApi instance using default credentials provider and object mapper.
87+
*
88+
* @param modelId The model id to use.
89+
* @param region The AWS region to use.
90+
* @param timeout The timeout to use.
91+
*/
92+
public AbstractBedrockApi(String modelId, String region, Duration timeout) {
93+
this(modelId, ProfileCredentialsProvider.builder().build(), region, ModelOptionsUtils.OBJECT_MAPPER, timeout);
8394
}
8495

96+
/**
97+
* Create a new AbstractBedrockApi instance using the provided credentials provider, region and object mapper.
98+
*
99+
* @param modelId The model id to use.
100+
* @param credentialsProvider The credentials provider to connect to AWS.
101+
* @param region The AWS region to use.
102+
* @param objectMapper The object mapper to use for JSON serialization and deserialization.
103+
*/
104+
public AbstractBedrockApi(String modelId, AwsCredentialsProvider credentialsProvider, String region,
105+
ObjectMapper objectMapper) {
106+
this(modelId, credentialsProvider, region, objectMapper, Duration.ofMinutes(5));
107+
}
85108
/**
86109
* Create a new AbstractBedrockApi instance using the provided credentials provider, region and object mapper.
87110
*
88111
* @param modelId The model id to use.
89112
* @param credentialsProvider The credentials provider to connect to AWS.
90113
* @param region The AWS region to use.
91114
* @param objectMapper The object mapper to use for JSON serialization and deserialization.
115+
* @param timeout Configure the amount of time to allow the client to complete the execution of an API call.
116+
* This timeout covers the entire client execution except for marshalling. This includes request handler execution,
117+
* all HTTP requests including retries, unmarshalling, etc. This value should always be positive, if present.
92118
*/
93119
public AbstractBedrockApi(String modelId, AwsCredentialsProvider credentialsProvider, String region,
94-
ObjectMapper objectMapper) {
120+
ObjectMapper objectMapper, Duration timeout) {
121+
122+
Assert.hasText(modelId, "Model id must not be empty");
123+
Assert.notNull(credentialsProvider, "Credentials provider must not be null");
124+
Assert.hasText(region, "Region must not be empty");
125+
Assert.notNull(objectMapper, "Object mapper must not be null");
126+
Assert.notNull(timeout, "Timeout must not be null");
95127

96128
this.modelId = modelId;
97-
this.objectMapper = objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
98-
this.credentialsProvider = credentialsProvider;
129+
this.objectMapper = objectMapper;
99130
this.region = region;
100131

132+
101133
this.client = BedrockRuntimeClient.builder()
102134
.region(Region.of(this.region))
103-
.credentialsProvider(this.credentialsProvider)
135+
.credentialsProvider(credentialsProvider)
136+
.overrideConfiguration(c -> c.apiCallTimeout(timeout))
104137
.build();
105138

106139
this.clientStreaming = BedrockRuntimeAsyncClient.builder()
107140
.region(Region.of(this.region))
108-
.credentialsProvider(this.credentialsProvider)
141+
.credentialsProvider(credentialsProvider)
142+
.overrideConfiguration(c -> c.apiCallTimeout(timeout))
109143
.build();
110144
}
111145

112146
/**
113147
* @return The model id.
114148
*/
115149
public String getModelId() {
116-
return modelId;
150+
return this.modelId;
117151
}
118152

119153
/**
120154
* @return The AWS region.
121155
*/
122156
public String getRegion() {
123-
return region;
157+
return this.region;
124158
}
125159

126160
/**

models/spring-ai-bedrock/src/main/java/org/springframework/ai/bedrock/cohere/api/CohereChatBedrockApi.java

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
// @formatter:off
1717
package org.springframework.ai.bedrock.cohere.api;
1818

19+
import java.time.Duration;
1920
import java.util.List;
2021

2122
import com.fasterxml.jackson.annotation.JsonInclude;
@@ -64,6 +65,32 @@ public CohereChatBedrockApi(String modelId, AwsCredentialsProvider credentialsPr
6465
super(modelId, credentialsProvider, region, objectMapper);
6566
}
6667

68+
/**
69+
* Create a new CohereChatBedrockApi instance using the default credentials provider chain, the default object
70+
* mapper, default temperature and topP values.
71+
*
72+
* @param modelId The model id to use. See the {@link CohereChatModel} for the supported models.
73+
* @param region The AWS region to use.
74+
* @param timeout The timeout to use.
75+
*/
76+
public CohereChatBedrockApi(String modelId, String region, Duration timeout) {
77+
super(modelId, region, timeout);
78+
}
79+
80+
/**
81+
* Create a new CohereChatBedrockApi instance using the provided credentials provider, region and object mapper.
82+
*
83+
* @param modelId The model id to use. See the {@link CohereChatModel} for the supported models.
84+
* @param credentialsProvider The credentials provider to connect to AWS.
85+
* @param region The AWS region to use.
86+
* @param objectMapper The object mapper to use for JSON serialization and deserialization.
87+
* @param timeout The timeout to use.
88+
*/
89+
public CohereChatBedrockApi(String modelId, AwsCredentialsProvider credentialsProvider, String region,
90+
ObjectMapper objectMapper, Duration timeout) {
91+
super(modelId, credentialsProvider, region, objectMapper, timeout);
92+
}
93+
6794
/**
6895
* CohereChatRequest encapsulates the request parameters for the Cohere command model.
6996
*

models/spring-ai-bedrock/src/main/java/org/springframework/ai/bedrock/cohere/api/CohereEmbeddingBedrockApi.java

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
// @formatter:off
1717
package org.springframework.ai.bedrock.cohere.api;
1818

19+
import java.time.Duration;
1920
import java.util.List;
2021

2122
import com.fasterxml.jackson.annotation.JsonInclude;
@@ -63,6 +64,33 @@ public CohereEmbeddingBedrockApi(String modelId, AwsCredentialsProvider credenti
6364
super(modelId, credentialsProvider, region, objectMapper);
6465
}
6566

67+
/**
68+
* Create a new CohereEmbeddingBedrockApi instance using the default credentials provider chain, the default object
69+
* mapper, default temperature and topP values.
70+
*
71+
* @param modelId The model id to use. See the {@link CohereEmbeddingModel} for the supported models.
72+
* @param region The AWS region to use.
73+
* @param timeout The timeout to use.
74+
*/
75+
public CohereEmbeddingBedrockApi(String modelId, String region, Duration timeout) {
76+
super(modelId, region, timeout);
77+
}
78+
79+
/**
80+
* Create a new CohereEmbeddingBedrockApi instance using the provided credentials provider, region and object
81+
* mapper.
82+
*
83+
* @param modelId The model id to use. See the {@link CohereEmbeddingModel} for the supported models.
84+
* @param credentialsProvider The credentials provider to connect to AWS.
85+
* @param region The AWS region to use.
86+
* @param objectMapper The object mapper to use for JSON serialization and deserialization.
87+
* @param timeout The timeout to use.
88+
*/
89+
public CohereEmbeddingBedrockApi(String modelId, AwsCredentialsProvider credentialsProvider, String region,
90+
ObjectMapper objectMapper, Duration timeout) {
91+
super(modelId, credentialsProvider, region, objectMapper, timeout);
92+
}
93+
6694
/**
6795
* The Cohere Embed model request.
6896
*
@@ -140,6 +168,7 @@ public record CohereEmbeddingResponse(
140168
@JsonProperty("id") String id,
141169
@JsonProperty("embeddings") List<List<Double>> embeddings,
142170
@JsonProperty("texts") List<String> texts,
171+
@JsonProperty("response_type") String responseType,
143172
// For future use: Currently bedrock doesn't return invocationMetrics for the cohere embedding model.
144173
@JsonProperty("amazon-bedrock-invocationMetrics") AmazonBedrockInvocationMetrics amazonBedrockInvocationMetrics) {
145174
}

models/spring-ai-bedrock/src/main/java/org/springframework/ai/bedrock/jurassic2/api/Ai21Jurassic2ChatBedrockApi.java

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
// @formatter:off
1717
package org.springframework.ai.bedrock.jurassic2.api;
1818

19+
import java.time.Duration;
1920
import java.util.List;
2021

2122
import com.fasterxml.jackson.annotation.JsonInclude;
@@ -64,6 +65,32 @@ public Ai21Jurassic2ChatBedrockApi(String modelId, AwsCredentialsProvider creden
6465
super(modelId, credentialsProvider, region, objectMapper);
6566
}
6667

68+
/**
69+
* Create a new Ai21Jurassic2ChatBedrockApi instance using the default credentials provider chain, the default
70+
* object mapper, default temperature and topP values.
71+
*
72+
* @param modelId The model id to use. See the {@link Ai21Jurassic2ChatModel} for the supported models.
73+
* @param region The AWS region to use.
74+
* @param timeout The timeout to use.
75+
*/
76+
public Ai21Jurassic2ChatBedrockApi(String modelId, String region, Duration timeout) {
77+
super(modelId, region, timeout);
78+
}
79+
80+
81+
/**
82+
* Create a new Ai21Jurassic2ChatBedrockApi instance.
83+
*
84+
* @param modelId The model id to use. See the {@link Ai21Jurassic2ChatBedrockApi.Ai21Jurassic2ChatModel} for the supported models.
85+
* @param credentialsProvider The credentials provider to connect to AWS.
86+
* @param region The AWS region to use.
87+
* @param objectMapper The object mapper to use for JSON serialization and deserialization.
88+
* @param timeout The timeout to use.
89+
*/
90+
public Ai21Jurassic2ChatBedrockApi(String modelId, AwsCredentialsProvider credentialsProvider, String region,
91+
ObjectMapper objectMapper, Duration timeout) {
92+
super(modelId, credentialsProvider, region, objectMapper, timeout);
93+
}
6794

6895
/**
6996
* AI21 Jurassic2 chat request parameters.

0 commit comments

Comments
 (0)