From af9e3824f58f7735f36df0b5e7650dcba3debeeb Mon Sep 17 00:00:00 2001 From: "mathieu.stennier" Date: Fri, 15 May 2026 09:57:37 +0200 Subject: [PATCH] feat: improve AWS HTTP client timeout handling --- .github/workflows/DEPLOY_SNAPSHOTS.yaml | 22 ++++++++++++++++--- .../ChatModelHttpProxySupport.java | 19 +++++++++++++--- .../provider/BedrockChatModelProvider.java | 15 +++++++------ .../ChatModelHttpProxySupportTest.java | 8 ++++++- .../BedrockChatModelProviderTest.java | 12 +++++++++- 5 files changed, 61 insertions(+), 15 deletions(-) diff --git a/.github/workflows/DEPLOY_SNAPSHOTS.yaml b/.github/workflows/DEPLOY_SNAPSHOTS.yaml index 2b83e7d5594..7061e909788 100644 --- a/.github/workflows/DEPLOY_SNAPSHOTS.yaml +++ b/.github/workflows/DEPLOY_SNAPSHOTS.yaml @@ -6,7 +6,13 @@ on: - main - 'stable/**' - 'alpha/**' - workflow_dispatch: { } + workflow_dispatch: + inputs: + docker_tag: + description: 'Custom Docker tag (e.g. "agentcore-memory-preview"). Leave empty for default SNAPSHOT tag.' + required: false + default: '' + defaults: run: @@ -106,14 +112,24 @@ jobs: # Set the version locally (no need to commit) mvn -B versions:set -DnewVersion="${FINAL_VERSION}" -DgenerateBackupPoms=false -f parent + + # Use custom docker tag if provided, otherwise default + CUSTOM_TAG="${{ github.event.inputs.docker_tag }}" + if [ -n "${CUSTOM_TAG}" ]; then + DOCKER_VERSION="${CUSTOM_TAG}-SNAPSHOT" + echo "::notice::Using custom Docker tag: ${DOCKER_VERSION}" + else + DOCKER_VERSION="${MINOR_VERSION}-SNAPSHOT" + echo "::notice::Using default Docker tag: ${DOCKER_VERSION}" + fi # Output versions for use in later steps echo "maven_version=${FINAL_VERSION}" >> "$GITHUB_OUTPUT" echo "minor_version=${MINOR_VERSION}" >> "$GITHUB_OUTPUT" - echo "docker_version=${MINOR_VERSION}-SNAPSHOT" >> "$GITHUB_OUTPUT" + echo "docker_version=${DOCKER_VERSION}" >> "$GITHUB_OUTPUT" echo "::notice::Maven version set to: ${FINAL_VERSION}" - echo "::notice::Docker version will be: ${MINOR_VERSION}-SNAPSHOT" + echo "::notice::Docker version will be: ${DOCKER_VERSION}" - name: Build Artifacts run: mvn -B -U compile generate-sources source:jar javadoc:jar deploy -DskipTests diff --git a/connectors/agentic-ai/src/main/java/io/camunda/connector/agenticai/aiagent/framework/langchain4j/ChatModelHttpProxySupport.java b/connectors/agentic-ai/src/main/java/io/camunda/connector/agenticai/aiagent/framework/langchain4j/ChatModelHttpProxySupport.java index 63497cc3d66..ce83fc51866 100644 --- a/connectors/agentic-ai/src/main/java/io/camunda/connector/agenticai/aiagent/framework/langchain4j/ChatModelHttpProxySupport.java +++ b/connectors/agentic-ai/src/main/java/io/camunda/connector/agenticai/aiagent/framework/langchain4j/ChatModelHttpProxySupport.java @@ -14,6 +14,7 @@ import java.net.InetSocketAddress; import java.net.URI; import java.net.http.HttpClient; +import java.time.Duration; import java.util.Optional; import java.util.stream.Collectors; import org.slf4j.Logger; @@ -41,11 +42,23 @@ public JdkHttpClientBuilder createJdkHttpClientBuilder() { } public SdkHttpClient createAwsHttpClient(URI endpointOverride) { + return createAwsHttpClient(endpointOverride, null); + } + + public SdkHttpClient createAwsHttpClient(URI endpointOverride, Duration timeout) { String schemeName = endpointOverride != null ? endpointOverride.getScheme() : ProxyConfiguration.SCHEME_HTTPS; - return ApacheHttpClient.builder() - .proxyConfiguration(createAwsProxyConfiguration(schemeName)) - .build(); + var httpClientBuilder = + ApacheHttpClient.builder().proxyConfiguration(createAwsProxyConfiguration(schemeName)); + + if (timeout != null && timeout.isPositive()) { + httpClientBuilder + .connectionTimeout(timeout) + .connectionAcquisitionTimeout(timeout) + .socketTimeout(timeout); + } + + return httpClientBuilder.build(); } software.amazon.awssdk.http.apache.ProxyConfiguration createAwsProxyConfiguration( diff --git a/connectors/agentic-ai/src/main/java/io/camunda/connector/agenticai/aiagent/framework/langchain4j/provider/BedrockChatModelProvider.java b/connectors/agentic-ai/src/main/java/io/camunda/connector/agenticai/aiagent/framework/langchain4j/provider/BedrockChatModelProvider.java index 02a841e7af9..1cf2ffe5504 100644 --- a/connectors/agentic-ai/src/main/java/io/camunda/connector/agenticai/aiagent/framework/langchain4j/provider/BedrockChatModelProvider.java +++ b/connectors/agentic-ai/src/main/java/io/camunda/connector/agenticai/aiagent/framework/langchain4j/provider/BedrockChatModelProvider.java @@ -15,6 +15,7 @@ import io.camunda.connector.agenticai.aiagent.model.request.provider.BedrockProviderConfiguration; import io.camunda.connector.agenticai.autoconfigure.AgenticAiConnectorsConfigurationProperties.ChatModelProperties; import java.net.URI; +import java.time.Duration; import java.util.Optional; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -44,13 +45,14 @@ public String type() { @Override public ChatModel createChatModel(BedrockProviderConfiguration bedrock) { final var connection = bedrock.bedrock(); + final var timeout = + deriveTimeoutSetting("Bedrock model call", config, connection.timeouts(), LOGGER); final var builder = BedrockChatModel.builder() - .client(createBedrockClient(connection)) + .client(createBedrockClient(connection, timeout)) .modelId(connection.model().model()) - .timeout( - deriveTimeoutSetting("Bedrock model call", config, connection.timeouts(), LOGGER)); + .timeout(timeout); applyBedrockModelParametersIfPresent(connection, builder); @@ -58,7 +60,7 @@ public ChatModel createChatModel(BedrockProviderConfiguration bedrock) { } private BedrockRuntimeClient createBedrockClient( - BedrockProviderConfiguration.BedrockConnection connection) { + BedrockProviderConfiguration.BedrockConnection connection, Duration timeout) { var bedrockClientBuilder = BedrockRuntimeClient.builder().region(Region.of(connection.region())); var overrideClientConfigurationBuilder = ClientOverrideConfiguration.builder(); @@ -73,10 +75,9 @@ private BedrockRuntimeClient createBedrockClient( bedrockClientBuilder.endpointOverride(endpointOverride); } - overrideClientConfigurationBuilder.apiCallTimeout( - deriveTimeoutSetting("Bedrock API call", config, connection.timeouts(), LOGGER)); + overrideClientConfigurationBuilder.apiCallTimeout(timeout); - SdkHttpClient httpClient = proxySupport.createAwsHttpClient(endpointOverride); + SdkHttpClient httpClient = proxySupport.createAwsHttpClient(endpointOverride, timeout); bedrockClientBuilder.httpClient(httpClient); bedrockClientBuilder.overrideConfiguration(overrideClientConfigurationBuilder.build()); diff --git a/connectors/agentic-ai/src/test/java/io/camunda/connector/agenticai/aiagent/framework/langchain4j/ChatModelHttpProxySupportTest.java b/connectors/agentic-ai/src/test/java/io/camunda/connector/agenticai/aiagent/framework/langchain4j/ChatModelHttpProxySupportTest.java index 6ceff7909f5..5373bd6060e 100644 --- a/connectors/agentic-ai/src/test/java/io/camunda/connector/agenticai/aiagent/framework/langchain4j/ChatModelHttpProxySupportTest.java +++ b/connectors/agentic-ai/src/test/java/io/camunda/connector/agenticai/aiagent/framework/langchain4j/ChatModelHttpProxySupportTest.java @@ -24,6 +24,7 @@ import io.camunda.connector.http.client.proxy.ProxyConfiguration.ProxyDetails; import java.net.URI; import java.net.http.HttpClient; +import java.time.Duration; import java.util.Optional; import java.util.stream.Stream; import org.junit.jupiter.api.BeforeEach; @@ -48,6 +49,7 @@ class ChatModelHttpProxySupportTest { private static final String HTTPS_ENDPOINT = "https://example.com"; private static final String HTTP_ENDPOINT = "http://example.com"; + private static final Duration AWS_HTTP_CLIENT_TIMEOUT = Duration.ofSeconds(145); private static final String NON_PROXY_HOST_LOCALHOST = "localhost"; private static final String NON_PROXY_HOST_LOCALHOST_REGEX = "localhost.*"; @@ -95,7 +97,8 @@ void shouldCreateAwsHttpClientWithHttpsEndpoint() { // when SdkHttpClient result = - proxySupport.createAwsHttpClient(URI.create("https://bedrock.amazonaws.com")); + proxySupport.createAwsHttpClient( + URI.create("https://bedrock.amazonaws.com"), AWS_HTTP_CLIENT_TIMEOUT); // then assertThat(result).isNotNull(); @@ -103,6 +106,9 @@ void shouldCreateAwsHttpClientWithHttpsEndpoint() { verify(httpClientBuilder) .proxyConfiguration( notNull(software.amazon.awssdk.http.apache.ProxyConfiguration.class)); + verify(httpClientBuilder).connectionTimeout(AWS_HTTP_CLIENT_TIMEOUT); + verify(httpClientBuilder).connectionAcquisitionTimeout(AWS_HTTP_CLIENT_TIMEOUT); + verify(httpClientBuilder).socketTimeout(AWS_HTTP_CLIENT_TIMEOUT); } } diff --git a/connectors/agentic-ai/src/test/java/io/camunda/connector/agenticai/aiagent/framework/langchain4j/provider/BedrockChatModelProviderTest.java b/connectors/agentic-ai/src/test/java/io/camunda/connector/agenticai/aiagent/framework/langchain4j/provider/BedrockChatModelProviderTest.java index 2d40babf960..577db635f76 100644 --- a/connectors/agentic-ai/src/test/java/io/camunda/connector/agenticai/aiagent/framework/langchain4j/provider/BedrockChatModelProviderTest.java +++ b/connectors/agentic-ai/src/test/java/io/camunda/connector/agenticai/aiagent/framework/langchain4j/provider/BedrockChatModelProviderTest.java @@ -300,11 +300,21 @@ private void testBedrockChatModelBuilder( assertThat(chatModel).isNotNull().isInstanceOf(BedrockChatModel.class); assertThat(chatModel).isSameAs(chatModelResultCaptor.getResult()); - verify(proxySupport).createAwsHttpClient(expectedEndpointOverride); + verify(proxySupport) + .createAwsHttpClient(expectedEndpointOverride, expectedTimeout(providerConfig)); builderAssertions.accept(builders); } } + private Duration expectedTimeout(BedrockProviderConfiguration providerConfig) { + var timeouts = providerConfig.bedrock().timeouts(); + if (timeouts != null && timeouts.timeout() != null && timeouts.timeout().isPositive()) { + return timeouts.timeout(); + } + + return Duration.ofMinutes(3); + } + static Stream nullModelParameters() { return Stream.of(new BedrockModelParameters(null, null, null)); }