Skip to content

Commit ee51d44

Browse files
academeyspring-builds
authored andcommitted
Make OpenAiApi available as a bean for injection
- Extract OpenAiApi creation to a separate @bean method - Update OpenAiChatModel to inject the OpenAiApi bean - Add test to verify OpenAiApi bean registration This allows users to inject OpenAiApi directly as suggested in the documentation for configuring multiple OpenAI-compatible ChatClients. Fixes #3878 Signed-off-by: Hyunjoon Park <academey@gmail.com> (cherry picked from commit f41c27d)
1 parent 65b4ead commit ee51d44

File tree

2 files changed

+35
-27
lines changed

2 files changed

+35
-27
lines changed

auto-configurations/models/spring-ai-autoconfigure-model-openai/src/main/java/org/springframework/ai/model/openai/autoconfigure/OpenAiChatAutoConfiguration.java

Lines changed: 23 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -66,18 +66,33 @@ public class OpenAiChatAutoConfiguration {
6666

6767
@Bean
6868
@ConditionalOnMissingBean
69-
public OpenAiChatModel openAiChatModel(OpenAiConnectionProperties commonProperties,
70-
OpenAiChatProperties chatProperties, ObjectProvider<RestClient.Builder> restClientBuilderProvider,
71-
ObjectProvider<WebClient.Builder> webClientBuilderProvider, ToolCallingManager toolCallingManager,
72-
RetryTemplate retryTemplate, ResponseErrorHandler responseErrorHandler,
69+
public OpenAiApi openAiApi(OpenAiConnectionProperties commonProperties, OpenAiChatProperties chatProperties,
70+
ObjectProvider<RestClient.Builder> restClientBuilderProvider,
71+
ObjectProvider<WebClient.Builder> webClientBuilderProvider, ResponseErrorHandler responseErrorHandler) {
72+
73+
OpenAIAutoConfigurationUtil.ResolvedConnectionProperties resolved = resolveConnectionProperties(
74+
commonProperties, chatProperties, "chat");
75+
76+
return OpenAiApi.builder()
77+
.baseUrl(resolved.baseUrl())
78+
.apiKey(new SimpleApiKey(resolved.apiKey()))
79+
.headers(resolved.headers())
80+
.completionsPath(chatProperties.getCompletionsPath())
81+
.embeddingsPath(OpenAiEmbeddingProperties.DEFAULT_EMBEDDINGS_PATH)
82+
.restClientBuilder(restClientBuilderProvider.getIfAvailable(RestClient::builder))
83+
.webClientBuilder(webClientBuilderProvider.getIfAvailable(WebClient::builder))
84+
.responseErrorHandler(responseErrorHandler)
85+
.build();
86+
}
87+
88+
@Bean
89+
@ConditionalOnMissingBean
90+
public OpenAiChatModel openAiChatModel(OpenAiApi openAiApi, OpenAiChatProperties chatProperties,
91+
ToolCallingManager toolCallingManager, RetryTemplate retryTemplate,
7392
ObjectProvider<ObservationRegistry> observationRegistry,
7493
ObjectProvider<ChatModelObservationConvention> observationConvention,
7594
ObjectProvider<ToolExecutionEligibilityPredicate> openAiToolExecutionEligibilityPredicate) {
7695

77-
var openAiApi = openAiApi(chatProperties, commonProperties,
78-
restClientBuilderProvider.getIfAvailable(RestClient::builder),
79-
webClientBuilderProvider.getIfAvailable(WebClient::builder), responseErrorHandler, "chat");
80-
8196
var chatModel = OpenAiChatModel.builder()
8297
.openAiApi(openAiApi)
8398
.defaultOptions(chatProperties.getOptions())
@@ -93,23 +108,4 @@ public OpenAiChatModel openAiChatModel(OpenAiConnectionProperties commonProperti
93108
return chatModel;
94109
}
95110

96-
private OpenAiApi openAiApi(OpenAiChatProperties chatProperties, OpenAiConnectionProperties commonProperties,
97-
RestClient.Builder restClientBuilder, WebClient.Builder webClientBuilder,
98-
ResponseErrorHandler responseErrorHandler, String modelType) {
99-
100-
OpenAIAutoConfigurationUtil.ResolvedConnectionProperties resolved = resolveConnectionProperties(
101-
commonProperties, chatProperties, modelType);
102-
103-
return OpenAiApi.builder()
104-
.baseUrl(resolved.baseUrl())
105-
.apiKey(new SimpleApiKey(resolved.apiKey()))
106-
.headers(resolved.headers())
107-
.completionsPath(chatProperties.getCompletionsPath())
108-
.embeddingsPath(OpenAiEmbeddingProperties.DEFAULT_EMBEDDINGS_PATH)
109-
.restClientBuilder(restClientBuilder)
110-
.webClientBuilder(webClientBuilder)
111-
.responseErrorHandler(responseErrorHandler)
112-
.build();
113-
}
114-
115111
}

auto-configurations/models/spring-ai-autoconfigure-model-openai/src/test/java/org/springframework/ai/model/openai/autoconfigure/OpenAiModelConfigurationTests.java

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
import org.springframework.ai.openai.OpenAiEmbeddingModel;
2525
import org.springframework.ai.openai.OpenAiImageModel;
2626
import org.springframework.ai.openai.OpenAiModerationModel;
27+
import org.springframework.ai.openai.api.OpenAiApi;
2728
import org.springframework.boot.autoconfigure.AutoConfigurations;
2829
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
2930

@@ -42,6 +43,7 @@ public class OpenAiModelConfigurationTests {
4243
@Test
4344
void chatModelActivation() {
4445
this.contextRunner.withConfiguration(AutoConfigurations.of(OpenAiChatAutoConfiguration.class)).run(context -> {
46+
assertThat(context.getBeansOfType(OpenAiApi.class)).isNotEmpty();
4547
assertThat(context.getBeansOfType(OpenAiChatModel.class)).isNotEmpty();
4648
assertThat(context.getBeansOfType(OpenAiEmbeddingModel.class)).isEmpty();
4749
assertThat(context.getBeansOfType(OpenAiImageModel.class)).isEmpty();
@@ -303,4 +305,14 @@ void moderationModelActivation() {
303305
});
304306
}
305307

308+
@Test
309+
void openAiApiBean() {
310+
// Test that OpenAiApi bean is registered and can be injected
311+
this.contextRunner.withConfiguration(AutoConfigurations.of(OpenAiChatAutoConfiguration.class)).run(context -> {
312+
assertThat(context.getBeansOfType(OpenAiApi.class)).hasSize(1);
313+
OpenAiApi openAiApi = context.getBean(OpenAiApi.class);
314+
assertThat(openAiApi).isNotNull();
315+
});
316+
}
317+
306318
}

0 commit comments

Comments
 (0)