diff --git a/auto-configurations/models/http/client/spring-ai-autoconfigure-http-client/pom.xml b/auto-configurations/models/http/client/spring-ai-autoconfigure-http-client/pom.xml new file mode 100644 index 00000000000..5613d88a141 --- /dev/null +++ b/auto-configurations/models/http/client/spring-ai-autoconfigure-http-client/pom.xml @@ -0,0 +1,69 @@ + + + 4.0.0 + + org.springframework.ai + spring-ai-parent + 1.0.0-SNAPSHOT + ../../../../../pom.xml + + spring-ai-autoconfigure-http-client + jar + Spring AI Http Client Auto Configuration + Spring AI Http Client Auto Configuration + https://github.com/spring-projects/spring-ai + + + https://github.com/spring-projects/spring-ai + git://github.com/spring-projects/spring-ai.git + git@github.com:spring-projects/spring-ai.git + + + + + + + org.springframework.boot + spring-boot-starter + + + + org.springframework.boot + spring-boot-starter-web + + + + org.springframework.boot + spring-boot-starter-webflux + + + + org.springframework.boot + spring-boot-configuration-processor + true + + + + + org.springframework.ai + spring-ai-test + ${project.parent.version} + test + + + + org.springframework.boot + spring-boot-starter-test + test + + + + org.mockito + mockito-core + test + + + + diff --git a/auto-configurations/models/http/client/spring-ai-autoconfigure-http-client/src/main/java/org/springframework/ai/http/client/autoconfigure/SpringAiHttpClientAutoConfiguration.java b/auto-configurations/models/http/client/spring-ai-autoconfigure-http-client/src/main/java/org/springframework/ai/http/client/autoconfigure/SpringAiHttpClientAutoConfiguration.java new file mode 100644 index 00000000000..4218b4d2fe2 --- /dev/null +++ b/auto-configurations/models/http/client/spring-ai-autoconfigure-http-client/src/main/java/org/springframework/ai/http/client/autoconfigure/SpringAiHttpClientAutoConfiguration.java @@ -0,0 +1,58 @@ +/* + * Copyright 2023-2025 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.ai.http.client.autoconfigure; + +import org.springframework.boot.autoconfigure.AutoConfiguration; +import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.boot.http.client.ClientHttpRequestFactoryBuilder; +import org.springframework.boot.http.client.ClientHttpRequestFactorySettings; +import org.springframework.boot.web.client.RestClientCustomizer; +import org.springframework.context.annotation.Bean; +import org.springframework.http.client.*; +import org.springframework.web.client.RestClient; +import org.springframework.web.reactive.function.client.WebClient; + +/** + * {@link AutoConfiguration Auto-configuration} for AI http-client. + * + * @author Song Jaegeun + */ +@AutoConfiguration +@ConditionalOnClass({ RestClient.class }) +@EnableConfigurationProperties({ SpringAiHttpClientProperties.class }) +public class SpringAiHttpClientAutoConfiguration { + + @Bean + @ConditionalOnMissingBean(RestClientCustomizer.class) + public RestClientCustomizer restClientCustomizer(SpringAiHttpClientProperties props) { + // RestClient.Builder is not registered as a bean in the context, + // so there's no need to use @ConditionalOnMissingBean(RestClient.Builder.class). + // Spring Boot will automatically apply this RestClientCustomizer + // to any RestClient.Builder instance created via RestClient.create(). + return restClientBuilder -> { + ClientHttpRequestFactorySettings settings = ClientHttpRequestFactorySettings.defaults() + .withConnectTimeout(props.getConnectionTimeout()) + .withReadTimeout(props.getReadTimeout()); + + ClientHttpRequestFactory factory = ClientHttpRequestFactoryBuilder.detect().build(settings); + restClientBuilder.requestFactory(factory); + }; + } + +} diff --git a/auto-configurations/models/http/client/spring-ai-autoconfigure-http-client/src/main/java/org/springframework/ai/http/client/autoconfigure/SpringAiHttpClientProperties.java b/auto-configurations/models/http/client/spring-ai-autoconfigure-http-client/src/main/java/org/springframework/ai/http/client/autoconfigure/SpringAiHttpClientProperties.java new file mode 100644 index 00000000000..51f4331e218 --- /dev/null +++ b/auto-configurations/models/http/client/spring-ai-autoconfigure-http-client/src/main/java/org/springframework/ai/http/client/autoconfigure/SpringAiHttpClientProperties.java @@ -0,0 +1,53 @@ +/* + * Copyright 2023-2024 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.ai.http.client.autoconfigure; + +import java.time.Duration; + +import org.springframework.boot.context.properties.ConfigurationProperties; + +/** + * Properties for AI http-client. + * + * @author Song Jaegeun + */ +@ConfigurationProperties(SpringAiHttpClientProperties.CONFIG_PREFIX) +public class SpringAiHttpClientProperties { + + public static final String CONFIG_PREFIX = "spring.ai.http.client"; + + private Duration connectionTimeout = Duration.ofSeconds(10); + + private Duration readTimeout = Duration.ofSeconds(30); + + public Duration getConnectionTimeout() { + return connectionTimeout; + } + + public void setConnectionTimeout(Duration connectionTimeout) { + this.connectionTimeout = connectionTimeout; + } + + public Duration getReadTimeout() { + return readTimeout; + } + + public void setReadTimeout(Duration readTimeout) { + this.readTimeout = readTimeout; + } + +} diff --git a/auto-configurations/models/http/client/spring-ai-autoconfigure-http-client/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports b/auto-configurations/models/http/client/spring-ai-autoconfigure-http-client/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports new file mode 100644 index 00000000000..2d7408af7d1 --- /dev/null +++ b/auto-configurations/models/http/client/spring-ai-autoconfigure-http-client/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports @@ -0,0 +1,16 @@ +# +# Copyright 2025-2025 the original author or authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +org.springframework.ai.http.client.autoconfigure.SpringAiHttpClientAutoConfiguration diff --git a/auto-configurations/models/http/client/spring-ai-autoconfigure-http-client/src/test/java/org/springframework/ai/http/client/autoconfigure/SpringAiHttpClientAutoConfigurationIT.java b/auto-configurations/models/http/client/spring-ai-autoconfigure-http-client/src/test/java/org/springframework/ai/http/client/autoconfigure/SpringAiHttpClientAutoConfigurationIT.java new file mode 100644 index 00000000000..dcda33f1c25 --- /dev/null +++ b/auto-configurations/models/http/client/spring-ai-autoconfigure-http-client/src/test/java/org/springframework/ai/http/client/autoconfigure/SpringAiHttpClientAutoConfigurationIT.java @@ -0,0 +1,42 @@ +/* + * Copyright 2023-2025 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.ai.http.client.autoconfigure; + +import org.junit.jupiter.api.Test; + +import org.springframework.boot.autoconfigure.AutoConfigurations; +import org.springframework.boot.autoconfigure.web.client.RestClientAutoConfiguration; +import org.springframework.boot.test.context.runner.ApplicationContextRunner; +import org.springframework.boot.web.client.RestClientCustomizer; +import static org.assertj.core.api.Assertions.assertThat; + +/** + * @author Song jaegeun + */ +public class SpringAiHttpClientAutoConfigurationIT { + + private final ApplicationContextRunner contextRunner = new ApplicationContextRunner().withConfiguration( + AutoConfigurations.of(SpringAiHttpClientAutoConfigurationIT.class, RestClientAutoConfiguration.class)); + + @Test + void testHttpClientAutoConfiguration() { + this.contextRunner.run(context -> { + assertThat(context).hasSingleBean(RestClientCustomizer.class); + }); + } + +} diff --git a/auto-configurations/models/http/client/spring-ai-autoconfigure-http-client/src/test/java/org/springframework/ai/http/client/autoconfigure/SpringAiHttpClientPropertiesTests.java b/auto-configurations/models/http/client/spring-ai-autoconfigure-http-client/src/test/java/org/springframework/ai/http/client/autoconfigure/SpringAiHttpClientPropertiesTests.java new file mode 100644 index 00000000000..7393d87f819 --- /dev/null +++ b/auto-configurations/models/http/client/spring-ai-autoconfigure-http-client/src/test/java/org/springframework/ai/http/client/autoconfigure/SpringAiHttpClientPropertiesTests.java @@ -0,0 +1,64 @@ +/* + * Copyright 2023-2025 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.ai.http.client.autoconfigure; + +import org.junit.jupiter.api.Test; + +import org.springframework.boot.autoconfigure.AutoConfigurations; +import org.springframework.boot.test.context.runner.ApplicationContextRunner; + +import java.time.Duration; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * Unit Tests for {@link SpringAiHttpClientPropertiesTests}. + * + * @author Song Jaegeun + */ +public class SpringAiHttpClientPropertiesTests { + + @Test + public void httpClientDefaultProperties() { + + new ApplicationContextRunner() + .withConfiguration(AutoConfigurations.of(SpringAiHttpClientAutoConfiguration.class)) + .run(context -> { + var httpClientProperties = context.getBean(SpringAiHttpClientProperties.class); + + assertThat(httpClientProperties.getConnectionTimeout()).isEqualTo(Duration.ofSeconds(10)); + assertThat(httpClientProperties.getReadTimeout()).isEqualTo(Duration.ofSeconds(30)); + }); + } + + @Test + public void httpClientCustomProperties() { + new ApplicationContextRunner().withPropertyValues( + // @formatter:off + "spring.ai.http.client.connection-timeout=10s", + "spring.ai.http.client.read-timeout=30s") + // @formatter:on + .withConfiguration(AutoConfigurations.of(SpringAiHttpClientAutoConfiguration.class)) + .run(context -> { + var httpClientProperties = context.getBean(SpringAiHttpClientProperties.class); + + assertThat(httpClientProperties.getConnectionTimeout()).isEqualTo(Duration.ofSeconds(10)); + assertThat(httpClientProperties.getReadTimeout()).isEqualTo(Duration.ofSeconds(30)); + }); + } + +} diff --git a/pom.xml b/pom.xml index aa0e007182d..5aa87ac7674 100644 --- a/pom.xml +++ b/pom.xml @@ -47,6 +47,8 @@ auto-configurations/common/spring-ai-autoconfigure-retry + auto-configurations/models/http/client/spring-ai-autoconfigure-http-client + auto-configurations/models/tool/spring-ai-autoconfigure-model-tool auto-configurations/models/chat/client/spring-ai-autoconfigure-model-chat-client @@ -763,6 +765,8 @@ org.springframework.ai.autoconfigure.retry/**/**IT.java + org.springframework.ai.http.client.autoconfigure/**/**IT.java + org.springframework.ai.autoconfigure.stabilityai/**/**IT.java org.springframework.ai.autoconfigure.transformers/**/**IT.java diff --git a/spring-ai-spring-boot-starters/spring-ai-starter-model-anthropic/pom.xml b/spring-ai-spring-boot-starters/spring-ai-starter-model-anthropic/pom.xml index 0129169caf6..be71b4ebc34 100644 --- a/spring-ai-spring-boot-starters/spring-ai-starter-model-anthropic/pom.xml +++ b/spring-ai-spring-boot-starters/spring-ai-starter-model-anthropic/pom.xml @@ -65,6 +65,12 @@ spring-ai-autoconfigure-model-chat-memory ${project.parent.version} + + + org.springframework.ai + spring-ai-autoconfigure-http-client + ${project.parent.version} + diff --git a/spring-ai-spring-boot-starters/spring-ai-starter-model-minimax/pom.xml b/spring-ai-spring-boot-starters/spring-ai-starter-model-minimax/pom.xml index d8e554a8b2b..d492187dc32 100644 --- a/spring-ai-spring-boot-starters/spring-ai-starter-model-minimax/pom.xml +++ b/spring-ai-spring-boot-starters/spring-ai-starter-model-minimax/pom.xml @@ -65,6 +65,12 @@ spring-ai-autoconfigure-model-chat-memory ${project.parent.version} + + + org.springframework.ai + spring-ai-autoconfigure-http-client + ${project.parent.version} + diff --git a/spring-ai-spring-boot-starters/spring-ai-starter-model-mistral-ai/pom.xml b/spring-ai-spring-boot-starters/spring-ai-starter-model-mistral-ai/pom.xml index 3d30900fec0..fd6028c3b82 100644 --- a/spring-ai-spring-boot-starters/spring-ai-starter-model-mistral-ai/pom.xml +++ b/spring-ai-spring-boot-starters/spring-ai-starter-model-mistral-ai/pom.xml @@ -65,6 +65,12 @@ spring-ai-autoconfigure-model-chat-memory ${project.parent.version} + + + org.springframework.ai + spring-ai-autoconfigure-http-client + ${project.parent.version} + diff --git a/spring-ai-spring-boot-starters/spring-ai-starter-model-ollama/pom.xml b/spring-ai-spring-boot-starters/spring-ai-starter-model-ollama/pom.xml index 8e981140f29..b65c10fb313 100644 --- a/spring-ai-spring-boot-starters/spring-ai-starter-model-ollama/pom.xml +++ b/spring-ai-spring-boot-starters/spring-ai-starter-model-ollama/pom.xml @@ -65,6 +65,12 @@ spring-ai-autoconfigure-model-chat-memory ${project.parent.version} + + + org.springframework.ai + spring-ai-autoconfigure-http-client + ${project.parent.version} + diff --git a/spring-ai-spring-boot-starters/spring-ai-starter-model-openai/pom.xml b/spring-ai-spring-boot-starters/spring-ai-starter-model-openai/pom.xml index f2d003c250e..63c2b2d3afe 100644 --- a/spring-ai-spring-boot-starters/spring-ai-starter-model-openai/pom.xml +++ b/spring-ai-spring-boot-starters/spring-ai-starter-model-openai/pom.xml @@ -65,6 +65,12 @@ spring-ai-autoconfigure-model-chat-memory ${project.parent.version} + + + org.springframework.ai + spring-ai-autoconfigure-http-client + ${project.parent.version} + diff --git a/spring-ai-spring-boot-starters/spring-ai-starter-model-stability-ai/pom.xml b/spring-ai-spring-boot-starters/spring-ai-starter-model-stability-ai/pom.xml index dfbabbbc29d..4edb97f49a8 100644 --- a/spring-ai-spring-boot-starters/spring-ai-starter-model-stability-ai/pom.xml +++ b/spring-ai-spring-boot-starters/spring-ai-starter-model-stability-ai/pom.xml @@ -53,6 +53,12 @@ spring-ai-stability-ai ${project.parent.version} + + + org.springframework.ai + spring-ai-autoconfigure-http-client + ${project.parent.version} + diff --git a/spring-ai-spring-boot-starters/spring-ai-starter-model-zhipuai/pom.xml b/spring-ai-spring-boot-starters/spring-ai-starter-model-zhipuai/pom.xml index d5abba2b358..3f59b82b222 100644 --- a/spring-ai-spring-boot-starters/spring-ai-starter-model-zhipuai/pom.xml +++ b/spring-ai-spring-boot-starters/spring-ai-starter-model-zhipuai/pom.xml @@ -65,6 +65,12 @@ spring-ai-autoconfigure-model-chat-memory ${project.parent.version} + + + org.springframework.ai + spring-ai-autoconfigure-http-client + ${project.parent.version} + diff --git a/spring-ai-spring-boot-starters/spring-ai-starter-vector-store-chroma/pom.xml b/spring-ai-spring-boot-starters/spring-ai-starter-vector-store-chroma/pom.xml index 72161327721..3ed5f2d5414 100644 --- a/spring-ai-spring-boot-starters/spring-ai-starter-vector-store-chroma/pom.xml +++ b/spring-ai-spring-boot-starters/spring-ai-starter-vector-store-chroma/pom.xml @@ -55,6 +55,12 @@ spring-ai-chroma-store ${project.parent.version} + + + org.springframework.ai + spring-ai-autoconfigure-http-client + ${project.parent.version} + diff --git a/spring-ai-template-st/pom.xml b/spring-ai-template-st/pom.xml index 12f2fa5d1ec..070a55f32fd 100644 --- a/spring-ai-template-st/pom.xml +++ b/spring-ai-template-st/pom.xml @@ -16,7 +16,7 @@ --> 4.0.0 @@ -73,5 +73,17 @@ spring-boot-starter-test test + + + org.springframework.ai + spring-ai-autoconfigure-model-chat-client + ${project.parent.version} + + + + org.springframework.ai + spring-ai-autoconfigure-http-client + ${project.parent.version} + - \ No newline at end of file +