Skip to content

Commit a812986

Browse files
committed
feat(http-client): add RestClient timeout config, WebClient support pending Boot 3.5+
Signed-off-by: drow724 <[email protected]>
1 parent d93ab77 commit a812986

File tree

17 files changed

+366
-2
lines changed

17 files changed

+366
-2
lines changed
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
3+
xmlns="http://maven.apache.org/POM/4.0.0"
4+
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
5+
<modelVersion>4.0.0</modelVersion>
6+
<parent>
7+
<groupId>org.springframework.ai</groupId>
8+
<artifactId>spring-ai-parent</artifactId>
9+
<version>1.1.0-SNAPSHOT</version>
10+
<relativePath>../../../../../pom.xml</relativePath>
11+
</parent>
12+
<artifactId>spring-ai-autoconfigure-http-client</artifactId>
13+
<packaging>jar</packaging>
14+
<name>Spring AI Http Client Auto Configuration</name>
15+
<description>Spring AI Http Client Auto Configuration</description>
16+
<url>https://github.com/spring-projects/spring-ai</url>
17+
18+
<scm>
19+
<url>https://github.com/spring-projects/spring-ai</url>
20+
<connection>git://github.com/spring-projects/spring-ai.git</connection>
21+
<developerConnection>[email protected]:spring-projects/spring-ai.git</developerConnection>
22+
</scm>
23+
24+
<dependencies>
25+
26+
<!-- Boot dependencies -->
27+
<dependency>
28+
<groupId>org.springframework.boot</groupId>
29+
<artifactId>spring-boot-starter</artifactId>
30+
</dependency>
31+
32+
<dependency>
33+
<groupId>org.springframework.boot</groupId>
34+
<artifactId>spring-boot-starter-web</artifactId>
35+
</dependency>
36+
37+
<dependency>
38+
<groupId>org.springframework.boot</groupId>
39+
<artifactId>spring-boot-starter-webflux</artifactId>
40+
</dependency>
41+
42+
<dependency>
43+
<groupId>org.springframework.boot</groupId>
44+
<artifactId>spring-boot-configuration-processor</artifactId>
45+
<optional>true</optional>
46+
</dependency>
47+
48+
<dependency>
49+
<groupId>org.springframework.boot</groupId>
50+
<artifactId>spring-boot-autoconfigure-processor</artifactId>
51+
<optional>true</optional>
52+
</dependency>
53+
<!-- Test dependencies -->
54+
<dependency>
55+
<groupId>org.springframework.ai</groupId>
56+
<artifactId>spring-ai-test</artifactId>
57+
<version>${project.parent.version}</version>
58+
<scope>test</scope>
59+
</dependency>
60+
61+
<dependency>
62+
<groupId>org.springframework.boot</groupId>
63+
<artifactId>spring-boot-starter-test</artifactId>
64+
<scope>test</scope>
65+
</dependency>
66+
67+
<dependency>
68+
<groupId>org.mockito</groupId>
69+
<artifactId>mockito-core</artifactId>
70+
<scope>test</scope>
71+
</dependency>
72+
</dependencies>
73+
74+
</project>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
/*
2+
* Copyright 2023-2025 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.ai.http.client.autoconfigure;
18+
19+
import org.springframework.boot.autoconfigure.AutoConfiguration;
20+
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
21+
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
22+
import org.springframework.boot.context.properties.EnableConfigurationProperties;
23+
import org.springframework.boot.http.client.ClientHttpRequestFactoryBuilder;
24+
import org.springframework.boot.http.client.ClientHttpRequestFactorySettings;
25+
import org.springframework.boot.web.client.RestClientCustomizer;
26+
import org.springframework.context.annotation.Bean;
27+
import org.springframework.http.client.*;
28+
import org.springframework.web.client.RestClient;
29+
30+
/**
31+
* {@link AutoConfiguration Auto-configuration} for AI rest-client.
32+
*
33+
* @author Song Jaegeun
34+
*/
35+
@AutoConfiguration
36+
@ConditionalOnClass({ RestClient.class })
37+
@EnableConfigurationProperties({ SpringAiRestClientProperties.class })
38+
public class SpringAiRestClientAutoConfiguration {
39+
40+
@Bean
41+
@ConditionalOnMissingBean(RestClientCustomizer.class)
42+
public RestClientCustomizer restClientCustomizer(SpringAiRestClientProperties props) {
43+
// RestClient.Builder is not registered as a bean in the context,
44+
// so there's no need to use @ConditionalOnMissingBean(RestClient.Builder.class).
45+
// Spring Boot will automatically apply this RestClientCustomizer
46+
// to any RestClient.Builder instance created via RestClient.create().
47+
return restClientBuilder -> {
48+
ClientHttpRequestFactorySettings settings = ClientHttpRequestFactorySettings.defaults()
49+
.withConnectTimeout(props.getConnectionTimeout())
50+
.withReadTimeout(props.getReadTimeout());
51+
52+
ClientHttpRequestFactory factory = ClientHttpRequestFactoryBuilder.detect().build(settings);
53+
restClientBuilder.requestFactory(factory);
54+
};
55+
}
56+
57+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
/*
2+
* Copyright 2023-2024 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.ai.http.client.autoconfigure;
18+
19+
import java.time.Duration;
20+
21+
import org.springframework.boot.context.properties.ConfigurationProperties;
22+
23+
/**
24+
* Properties for AI rest-client.
25+
*
26+
* @author Song Jaegeun
27+
*/
28+
@ConfigurationProperties(SpringAiRestClientProperties.CONFIG_PREFIX)
29+
public class SpringAiRestClientProperties {
30+
31+
public static final String CONFIG_PREFIX = "spring.ai.rest.client";
32+
33+
private Duration connectionTimeout = Duration.ofSeconds(10);
34+
35+
private Duration readTimeout = Duration.ofSeconds(30);
36+
37+
public Duration getConnectionTimeout() {
38+
return connectionTimeout;
39+
}
40+
41+
public void setConnectionTimeout(Duration connectionTimeout) {
42+
this.connectionTimeout = connectionTimeout;
43+
}
44+
45+
public Duration getReadTimeout() {
46+
return readTimeout;
47+
}
48+
49+
public void setReadTimeout(Duration readTimeout) {
50+
this.readTimeout = readTimeout;
51+
}
52+
53+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
#
2+
# Copyright 2025-2025 the original author or authors.
3+
#
4+
# Licensed under the Apache License, Version 2.0 (the "License");
5+
# you may not use this file except in compliance with the License.
6+
# You may obtain a copy of the License at
7+
#
8+
# https://www.apache.org/licenses/LICENSE-2.0
9+
#
10+
# Unless required by applicable law or agreed to in writing, software
11+
# distributed under the License is distributed on an "AS IS" BASIS,
12+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
# See the License for the specific language governing permissions and
14+
# limitations under the License.
15+
#
16+
org.springframework.ai.http.client.autoconfigure.SpringAiRestClientAutoConfiguration
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
/*
2+
* Copyright 2023-2025 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.ai.http.client.autoconfigure;
18+
19+
import org.junit.jupiter.api.Test;
20+
21+
import org.springframework.boot.autoconfigure.AutoConfigurations;
22+
import org.springframework.boot.autoconfigure.web.client.RestClientAutoConfiguration;
23+
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
24+
import org.springframework.boot.web.client.RestClientCustomizer;
25+
import static org.assertj.core.api.Assertions.assertThat;
26+
27+
/**
28+
* @author Song jaegeun
29+
*/
30+
public class SpringAiRestClientAutoConfigurationIT {
31+
32+
private final ApplicationContextRunner contextRunner = new ApplicationContextRunner().withConfiguration(
33+
AutoConfigurations.of(SpringAiRestClientAutoConfigurationIT.class, RestClientAutoConfiguration.class));
34+
35+
@Test
36+
void testRestClientAutoConfiguration() {
37+
this.contextRunner.run(context -> {
38+
assertThat(context).hasSingleBean(RestClientCustomizer.class);
39+
});
40+
}
41+
42+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
/*
2+
* Copyright 2023-2025 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.ai.http.client.autoconfigure;
18+
19+
import org.junit.jupiter.api.Test;
20+
21+
import org.springframework.boot.autoconfigure.AutoConfigurations;
22+
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
23+
24+
import java.time.Duration;
25+
26+
import static org.assertj.core.api.Assertions.assertThat;
27+
28+
/**
29+
* Unit Tests for {@link SpringAiRestClientProperties}.
30+
*
31+
* @author Song Jaegeun
32+
*/
33+
public class SpringAiRestClientPropertiesTests {
34+
35+
@Test
36+
public void restClientDefaultProperties() {
37+
38+
new ApplicationContextRunner()
39+
.withConfiguration(AutoConfigurations.of(SpringAiRestClientAutoConfiguration.class))
40+
.run(context -> {
41+
var httpClientProperties = context.getBean(SpringAiRestClientProperties.class);
42+
43+
assertThat(httpClientProperties.getConnectionTimeout()).isEqualTo(Duration.ofSeconds(10));
44+
assertThat(httpClientProperties.getReadTimeout()).isEqualTo(Duration.ofSeconds(30));
45+
});
46+
}
47+
48+
@Test
49+
public void restClientCustomProperties() {
50+
new ApplicationContextRunner().withPropertyValues(
51+
// @formatter:off
52+
"spring.ai.rest.client.connection-timeout=10s",
53+
"spring.ai.rest.client.read-timeout=30s")
54+
// @formatter:on
55+
.withConfiguration(AutoConfigurations.of(SpringAiRestClientAutoConfiguration.class))
56+
.run(context -> {
57+
var httpClientProperties = context.getBean(SpringAiRestClientProperties.class);
58+
59+
assertThat(httpClientProperties.getConnectionTimeout()).isEqualTo(Duration.ofSeconds(10));
60+
assertThat(httpClientProperties.getReadTimeout()).isEqualTo(Duration.ofSeconds(30));
61+
});
62+
}
63+
64+
}

pom.xml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,8 @@
8282

8383
<module>auto-configurations/common/spring-ai-autoconfigure-retry</module>
8484

85+
<module>auto-configurations/models/http/client/spring-ai-autoconfigure-http-client</module>
86+
8587
<module>auto-configurations/models/tool/spring-ai-autoconfigure-model-tool</module>
8688

8789
<module>auto-configurations/models/chat/client/spring-ai-autoconfigure-model-chat-client</module>
@@ -770,6 +772,8 @@
770772

771773
<exclude>org.springframework.ai.autoconfigure.retry/**/**IT.java</exclude>
772774

775+
<exclude>org.springframework.ai.http.client.autoconfigure/**/**IT.java</exclude>
776+
773777
<exclude>org.springframework.ai.autoconfigure.stabilityai/**/**IT.java</exclude>
774778
<exclude>org.springframework.ai.autoconfigure.transformers/**/**IT.java</exclude>
775779

spring-ai-spring-boot-starters/spring-ai-starter-model-anthropic/pom.xml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,12 @@
6565
<artifactId>spring-ai-autoconfigure-model-chat-memory</artifactId>
6666
<version>${project.parent.version}</version>
6767
</dependency>
68+
69+
<dependency>
70+
<groupId>org.springframework.ai</groupId>
71+
<artifactId>spring-ai-autoconfigure-http-client</artifactId>
72+
<version>${project.parent.version}</version>
73+
</dependency>
6874
</dependencies>
6975

7076
</project>

spring-ai-spring-boot-starters/spring-ai-starter-model-deepseek/pom.xml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,12 @@
6565
<artifactId>spring-ai-autoconfigure-model-chat-memory</artifactId>
6666
<version>${project.parent.version}</version>
6767
</dependency>
68+
69+
<dependency>
70+
<groupId>org.springframework.ai</groupId>
71+
<artifactId>spring-ai-autoconfigure-http-client</artifactId>
72+
<version>${project.parent.version}</version>
73+
</dependency>
6874
</dependencies>
6975

7076
</project>

spring-ai-spring-boot-starters/spring-ai-starter-model-minimax/pom.xml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,12 @@
6565
<artifactId>spring-ai-autoconfigure-model-chat-memory</artifactId>
6666
<version>${project.parent.version}</version>
6767
</dependency>
68+
69+
<dependency>
70+
<groupId>org.springframework.ai</groupId>
71+
<artifactId>spring-ai-autoconfigure-http-client</artifactId>
72+
<version>${project.parent.version}</version>
73+
</dependency>
6874
</dependencies>
6975

7076
</project>

0 commit comments

Comments
 (0)