Skip to content

Commit 7b88929

Browse files
committed
perf(deepseek): Adjust the configuration timeout method
Signed-off-by: yinh <[email protected]>
1 parent 265ab8a commit 7b88929

File tree

5 files changed

+84
-307
lines changed

5 files changed

+84
-307
lines changed

auto-configurations/models/spring-ai-autoconfigure-model-deepseek/src/main/java/org/springframework/ai/model/deepseek/autoconfigure/DeepSeekChatAutoConfiguration.java

Lines changed: 35 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -35,12 +35,17 @@
3535
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
3636
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
3737
import org.springframework.boot.context.properties.EnableConfigurationProperties;
38-
import org.springframework.boot.restclient.RestClientCustomizer;
38+
import org.springframework.boot.http.client.ClientHttpRequestFactoryBuilder;
39+
import org.springframework.boot.http.client.HttpClientSettings;
40+
import org.springframework.boot.http.client.autoconfigure.HttpClientSettingsPropertyMapper;
41+
import org.springframework.boot.http.client.reactive.ClientHttpConnectorBuilder;
3942
import org.springframework.boot.restclient.autoconfigure.RestClientAutoConfiguration;
4043
import org.springframework.boot.ssl.SslBundles;
4144
import org.springframework.boot.webclient.autoconfigure.WebClientAutoConfiguration;
4245
import org.springframework.context.annotation.Bean;
4346
import org.springframework.core.retry.RetryTemplate;
47+
import org.springframework.http.client.ClientHttpRequestFactory;
48+
import org.springframework.http.client.reactive.ClientHttpConnector;
4449
import org.springframework.util.Assert;
4550
import org.springframework.util.StringUtils;
4651
import org.springframework.web.client.ResponseErrorHandler;
@@ -61,15 +66,6 @@
6166
matchIfMissing = true)
6267
public class DeepSeekChatAutoConfiguration {
6368

64-
@Bean
65-
@ConditionalOnMissingBean(name = "deepSeekRestClientCustomizer")
66-
@ConditionalOnProperty(prefix = "spring.ai.deepseek.http-client", name = "enabled", havingValue = "true",
67-
matchIfMissing = true)
68-
public RestClientCustomizer deepSeekRestClientCustomizer(DeepSeekConnectionProperties connectionProperties,
69-
ObjectProvider<SslBundles> sslBundles) {
70-
return new DeepSeekRestClientCustomizer(connectionProperties.getHttpClient(), sslBundles.getIfAvailable());
71-
}
72-
7369
@Bean
7470
@ConditionalOnMissingBean
7571
public DeepSeekChatModel deepSeekChatModel(DeepSeekConnectionProperties commonProperties,
@@ -78,10 +74,24 @@ public DeepSeekChatModel deepSeekChatModel(DeepSeekConnectionProperties commonPr
7874
RetryTemplate retryTemplate, ResponseErrorHandler responseErrorHandler,
7975
ObjectProvider<ObservationRegistry> observationRegistry,
8076
ObjectProvider<ChatModelObservationConvention> observationConvention,
81-
ObjectProvider<ToolExecutionEligibilityPredicate> deepseekToolExecutionEligibilityPredicate) {
77+
ObjectProvider<ToolExecutionEligibilityPredicate> deepseekToolExecutionEligibilityPredicate,
78+
ObjectProvider<SslBundles> sslBundles, ObjectProvider<HttpClientSettings> globalHttpClientSettings,
79+
ObjectProvider<ClientHttpRequestFactoryBuilder<?>> factoryBuilder,
80+
ObjectProvider<ClientHttpConnectorBuilder<?>> webConnectorBuilderProvider) {
81+
82+
HttpClientSettingsPropertyMapper mapper = new HttpClientSettingsPropertyMapper(sslBundles.getIfAvailable(),
83+
globalHttpClientSettings.getIfAvailable());
84+
HttpClientSettings httpClientSettings = mapper.map(commonProperties);
85+
86+
RestClient.Builder restClientBuilder = restClientBuilderProvider.getIfAvailable(RestClient::builder);
87+
applyRestClientSettings(restClientBuilder, httpClientSettings,
88+
factoryBuilder.getIfAvailable(ClientHttpRequestFactoryBuilder::detect));
8289

83-
var deepSeekApi = deepSeekApi(chatProperties, commonProperties,
84-
restClientBuilderProvider.getIfAvailable(RestClient::builder),
90+
WebClient.Builder webClientBuilder = webClientBuilderProvider.getIfAvailable(WebClient::builder);
91+
applyWebClientSettings(webClientBuilder, httpClientSettings,
92+
webConnectorBuilderProvider.getIfAvailable(ClientHttpConnectorBuilder::detect));
93+
94+
var deepSeekApi = deepSeekApi(chatProperties, commonProperties, restClientBuilder,
8595
webClientBuilderProvider.getIfAvailable(WebClient::builder), responseErrorHandler);
8696

8797
var chatModel = DeepSeekChatModel.builder()
@@ -122,4 +132,16 @@ private DeepSeekApi deepSeekApi(DeepSeekChatProperties chatProperties,
122132
.build();
123133
}
124134

135+
private void applyRestClientSettings(RestClient.Builder builder, HttpClientSettings httpClientSettings,
136+
ClientHttpRequestFactoryBuilder<?> factoryBuilder) {
137+
ClientHttpRequestFactory requestFactory = factoryBuilder.build(httpClientSettings);
138+
builder.requestFactory(requestFactory);
139+
}
140+
141+
private void applyWebClientSettings(WebClient.Builder builder, HttpClientSettings httpClientSettings,
142+
ClientHttpConnectorBuilder<?> connectorBuilder) {
143+
ClientHttpConnector connector = connectorBuilder.build(httpClientSettings);
144+
builder.clientConnector(connector);
145+
}
146+
125147
}

auto-configurations/models/spring-ai-autoconfigure-model-deepseek/src/main/java/org/springframework/ai/model/deepseek/autoconfigure/DeepSeekConnectionProperties.java

Lines changed: 12 additions & 107 deletions
Original file line numberDiff line numberDiff line change
@@ -16,132 +16,37 @@
1616

1717
package org.springframework.ai.model.deepseek.autoconfigure;
1818

19-
import java.time.Duration;
20-
2119
import org.springframework.boot.context.properties.ConfigurationProperties;
22-
import org.springframework.boot.context.properties.NestedConfigurationProperty;
23-
import org.springframework.boot.http.client.HttpClientSettings;
24-
import org.springframework.boot.http.client.HttpRedirects;
25-
import org.springframework.boot.ssl.SslBundle;
26-
import org.springframework.boot.ssl.SslBundles;
20+
import org.springframework.boot.http.client.autoconfigure.HttpClientSettingsProperties;
2721

2822
/**
2923
* Parent properties for DeepSeek.
3024
*
3125
* @author Geng Rong
3226
*/
3327
@ConfigurationProperties(DeepSeekConnectionProperties.CONFIG_PREFIX)
34-
public class DeepSeekConnectionProperties extends DeepSeekParentProperties {
28+
public class DeepSeekConnectionProperties extends HttpClientSettingsProperties {
3529

3630
public static final String CONFIG_PREFIX = "spring.ai.deepseek";
3731

38-
public static final String DEFAULT_BASE_URL = "https://api.deepseek.com";
32+
private String apiKey;
3933

40-
/**
41-
* HTTP client settings for DeepSeek API calls.
42-
*/
43-
@NestedConfigurationProperty
44-
private HttpClientConfig httpClient = new HttpClientConfig();
34+
private String baseUrl = "https://api.deepseek.com";
4535

46-
public DeepSeekConnectionProperties() {
47-
super.setBaseUrl(DEFAULT_BASE_URL);
36+
public String getApiKey() {
37+
return this.apiKey;
4838
}
4939

50-
public HttpClientConfig getHttpClient() {
51-
return this.httpClient;
40+
public void setApiKey(String apiKey) {
41+
this.apiKey = apiKey;
5242
}
5343

54-
public void setHttpClient(HttpClientConfig httpClient) {
55-
this.httpClient = httpClient;
44+
public String getBaseUrl() {
45+
return this.baseUrl;
5646
}
5747

58-
/**
59-
* HTTP client configuration settings. This inner class mirrors the structure of
60-
* Spring Boot's HttpClientSettings to provide full control over HTTP client behavior.
61-
*/
62-
public static class HttpClientConfig {
63-
64-
/**
65-
* Whether to enable custom HTTP client configuration.
66-
*/
67-
private boolean enabled = true;
68-
69-
/**
70-
* Connection timeout.
71-
*/
72-
private Duration connectTimeout = Duration.ofSeconds(10);
73-
74-
/**
75-
* Read timeout.
76-
*/
77-
private Duration readTimeout = Duration.ofSeconds(60);
78-
79-
/**
80-
* HTTP redirect strategy.
81-
*/
82-
private HttpRedirects redirects;
83-
84-
/**
85-
* SSL bundle name for secure connections.
86-
*/
87-
private String sslBundle;
88-
89-
public boolean isEnabled() {
90-
return this.enabled;
91-
}
92-
93-
public void setEnabled(boolean enabled) {
94-
this.enabled = enabled;
95-
}
96-
97-
public Duration getConnectTimeout() {
98-
return this.connectTimeout;
99-
}
100-
101-
public void setConnectTimeout(Duration connectTimeout) {
102-
this.connectTimeout = connectTimeout;
103-
}
104-
105-
public Duration getReadTimeout() {
106-
return this.readTimeout;
107-
}
108-
109-
public void setReadTimeout(Duration readTimeout) {
110-
this.readTimeout = readTimeout;
111-
}
112-
113-
public HttpRedirects getRedirects() {
114-
return this.redirects;
115-
}
116-
117-
public void setRedirects(HttpRedirects redirects) {
118-
this.redirects = redirects;
119-
}
120-
121-
public String getSslBundle() {
122-
return this.sslBundle;
123-
}
124-
125-
public void setSslBundle(String sslBundle) {
126-
this.sslBundle = sslBundle;
127-
}
128-
129-
/**
130-
* Convert to Spring Boot's HttpClientSettings.
131-
* @param sslBundles the SSL bundles registry
132-
* @return HttpClientSettings instance
133-
*/
134-
public HttpClientSettings toHttpClientSettings(SslBundles sslBundles) {
135-
SslBundle bundle = (this.sslBundle != null && sslBundles != null) ? sslBundles.getBundle(this.sslBundle)
136-
: null;
137-
138-
return HttpClientSettings.defaults()
139-
.withConnectTimeout(this.connectTimeout)
140-
.withReadTimeout(this.readTimeout)
141-
.withRedirects(this.redirects)
142-
.withSslBundle(bundle);
143-
}
144-
48+
public void setBaseUrl(String baseUrl) {
49+
this.baseUrl = baseUrl;
14550
}
14651

14752
}

auto-configurations/models/spring-ai-autoconfigure-model-deepseek/src/main/java/org/springframework/ai/model/deepseek/autoconfigure/DeepSeekRestClientCustomizer.java

Lines changed: 0 additions & 57 deletions
This file was deleted.

auto-configurations/models/spring-ai-autoconfigure-model-deepseek/src/test/java/org/springframework/ai/model/deepseek/autoconfigure/DeepSeekAutoConfigurationIT.java

Lines changed: 31 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616

1717
package org.springframework.ai.model.deepseek.autoconfigure;
1818

19+
import java.time.Duration;
1920
import java.util.Objects;
2021
import java.util.stream.Collectors;
2122

@@ -77,16 +78,16 @@ void generateStreaming() {
7778
void generateWithCustomTimeout() {
7879
new ApplicationContextRunner()
7980
.withPropertyValues("spring.ai.deepseek.apiKey=" + System.getenv("DEEPSEEK_API_KEY"),
80-
"spring.ai.deepseek.http-client.connect-timeout=5s",
81-
"spring.ai.deepseek.http-client.read-timeout=30s")
81+
"spring.ai.deepseek.connect-timeout=5s",
82+
"spring.ai.deepseek.read-timeout=30s")
8283
.withConfiguration(SpringAiTestAutoConfigurations.of(DeepSeekChatAutoConfiguration.class))
8384
.run(context -> {
8485
DeepSeekChatModel client = context.getBean(DeepSeekChatModel.class);
8586

8687
// Verify that the HTTP client configuration is applied
8788
var connectionProperties = context.getBean(DeepSeekConnectionProperties.class);
88-
assertThat(connectionProperties.getHttpClient().getConnectTimeout().getSeconds()).isEqualTo(5);
89-
assertThat(connectionProperties.getHttpClient().getReadTimeout().getSeconds()).isEqualTo(30);
89+
assertThat(connectionProperties.getConnectTimeout()).isEqualTo(Duration.ofSeconds(5));
90+
assertThat(connectionProperties.getReadTimeout()).isEqualTo(Duration.ofSeconds(30));
9091

9192
// Verify that the client can actually make requests with the configured
9293
// timeout
@@ -96,4 +97,30 @@ void generateWithCustomTimeout() {
9697
});
9798
}
9899

100+
@Test
101+
void generateStreamingWithCustomTimeout() {
102+
new ApplicationContextRunner()
103+
.withPropertyValues("spring.ai.deepseek.apiKey=" + "sk-2567813d742c40e79fa6f1f2ee2f830c",
104+
"spring.ai.deepseek.connect-timeout=1s",
105+
"spring.ai.deepseek.read-timeout=1s")
106+
.withConfiguration(SpringAiTestAutoConfigurations.of(DeepSeekChatAutoConfiguration.class))
107+
.run(context -> {
108+
DeepSeekChatModel client = context.getBean(DeepSeekChatModel.class);
109+
110+
// Verify that the HTTP client configuration is applied
111+
var connectionProperties = context.getBean(DeepSeekConnectionProperties.class);
112+
assertThat(connectionProperties.getConnectTimeout()).isEqualTo(Duration.ofSeconds(1));
113+
assertThat(connectionProperties.getReadTimeout()).isEqualTo(Duration.ofSeconds(1));
114+
115+
Flux<ChatResponse> responseFlux = client.stream(new Prompt(new UserMessage("Hello")));
116+
String response = Objects.requireNonNull(responseFlux.collectList().block())
117+
.stream()
118+
.map(chatResponse -> chatResponse.getResults().get(0).getOutput().getText())
119+
.collect(Collectors.joining());
120+
121+
assertThat(response).isNotEmpty();
122+
logger.info("Response with custom timeout: " + response);
123+
});
124+
}
125+
99126
}

0 commit comments

Comments
 (0)