Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
49 commits
Select commit Hold shift + click to select a range
6e2e988
First implementation of the OpenAI Java SDK, supporting only Embeddings
jdubois Oct 21, 2025
f0278fa
Implementation of the OpenAI Java SDK
jdubois Oct 22, 2025
e9d7382
Implementation of the OpenAI Java SDK
jdubois Oct 23, 2025
2bc511d
Implementation of the OpenAI Java SDK
jdubois Oct 23, 2025
54fe12b
Implementation of the OpenAI Java SDK
jdubois Oct 31, 2025
eeba408
Implementation of the OpenAI Java SDK
jdubois Nov 3, 2025
68df074
Implementation of the OpenAI Java SDK
jdubois Nov 3, 2025
4653f05
Implementation of the OpenAI Java SDK
jdubois Nov 4, 2025
e80d37b
Implementation of the OpenAI Java SDK
jdubois Nov 12, 2025
dfa333c
Implementation of the OpenAI Java SDK
jdubois Nov 12, 2025
d4c675c
Implementation of the OpenAI Java SDK
jdubois Nov 12, 2025
73f6b18
Implementation of the OpenAI Java SDK
jdubois Nov 13, 2025
acc4e28
Implementation of the OpenAI Java SDK
jdubois Nov 14, 2025
1e88cb5
Implementation of the OpenAI Java SDK
jdubois Nov 14, 2025
cfe0190
Implementation of the OpenAI Java SDK
jdubois Nov 14, 2025
72ebb33
Implementation of the OpenAI Java SDK
jdubois Nov 14, 2025
54f5ceb
Implementation of the OpenAI Java SDK
jdubois Nov 17, 2025
67f18c9
Implementation of the OpenAI Java SDK
jdubois Nov 17, 2025
df4f135
Implementation of the OpenAI Java SDK
jdubois Nov 17, 2025
91333a0
Implementation of the OpenAI Java SDK
jdubois Nov 18, 2025
e337e1c
Implementation of the OpenAI Java SDK
jdubois Nov 18, 2025
dfa8e31
Implementation of the OpenAI Java SDK
jdubois Nov 18, 2025
d781d4d
Implementation of the OpenAI Java SDK
jdubois Nov 18, 2025
8c35a84
Implementation of the OpenAI Java SDK
jdubois Nov 18, 2025
7eb40b9
Implementation of the OpenAI Java SDK
jdubois Nov 19, 2025
532a776
Implementation of the OpenAI Java SDK
jdubois Nov 19, 2025
fd7de33
Implementation of the OpenAI Java SDK
jdubois Nov 20, 2025
94d13f0
refactor(openai-sdk): fix checkstyle issues add docs
tzolov Nov 21, 2025
1a095fe
feat: add openai-sdk auto-configuration and decouples the Spring AI A…
tzolov Nov 22, 2025
5c2194b
feat: Add audio output support and improve OpenAI SDK chat configuration
tzolov Nov 23, 2025
3bdcf90
Implementation of the OpenAI Java SDK
jdubois Nov 24, 2025
a59c811
Implementation of the OpenAI Java SDK
jdubois Nov 24, 2025
4c995fd
Implementation of the OpenAI Java SDK
jdubois Nov 24, 2025
3578bd5
Implementation of the OpenAI Java SDK
jdubois Nov 24, 2025
5c4c439
Implementation of the OpenAI Java SDK
jdubois Nov 24, 2025
533e426
Implementation of the OpenAI Java SDK
jdubois Nov 24, 2025
fb8bad1
Fix format
tzolov Nov 24, 2025
4a65611
Fix checkstyle
tzolov Nov 24, 2025
1c42563
Implementation of the OpenAI Java SDK
jdubois Nov 24, 2025
27e5f78
Implementation of the OpenAI Java SDK
jdubois Nov 25, 2025
21b0f31
checkstyle fix
tzolov Nov 25, 2025
d0dc52c
Implementation of the OpenAI Java SDK
jdubois Nov 25, 2025
fa316b1
Improve test robustness
tzolov Nov 25, 2025
6f84f5a
Update openai-sdk chat docs
tzolov Nov 25, 2025
f20b80b
Add embeding and image docs
tzolov Nov 25, 2025
8c32436
Update to use Microsoft Foundry everywhere in the documentation
jdubois Nov 25, 2025
e88c09f
Update to use Microsoft Foundry everywhere in the documentation
jdubois Nov 25, 2025
8bfdb47
Add native structured output support
tzolov Nov 25, 2025
bd52277
improve test resilience
tzolov Nov 25, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ target
.classpath
.project
.settings
.env
bin
build.log
integration-repo
Expand Down Expand Up @@ -55,4 +56,3 @@ tmp


plans

Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,7 @@ public Function<Request, Response> weatherFunction() {
@Bean
public Function<MockWeatherService.Request, MockWeatherService.Response> weatherFunction3() {
MockWeatherService weatherService = new MockWeatherService();
return (weatherService::apply);
return weatherService::apply;
}

}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://maven.apache.org/POM/4.0.0"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-parent</artifactId>
<version>2.0.0-SNAPSHOT</version>
<relativePath>../../../pom.xml</relativePath>
</parent>
<artifactId>spring-ai-autoconfigure-model-openai-sdk</artifactId>
<packaging>jar</packaging>
<name>Spring AI OpenAI SDK Auto Configuration</name>
<description>Spring AI OpenAI SDK Auto Configuration</description>
<url>https://github.com/spring-projects/spring-ai</url>

<scm>
<url>https://github.com/spring-projects/spring-ai</url>
<connection>git://github.com/spring-projects/spring-ai.git</connection>
<developerConnection>git@github.com:spring-projects/spring-ai.git</developerConnection>
</scm>


<dependencies>

<!-- Spring AI dependencies -->

<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-openai-sdk</artifactId>
<version>${project.parent.version}</version>
</dependency>

<!-- Spring AI auto configurations -->

<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-autoconfigure-model-tool</artifactId>
<version>${project.parent.version}</version>
</dependency>

<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-autoconfigure-model-chat-observation</artifactId>
<version>${project.parent.version}</version>
</dependency>

<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-autoconfigure-model-embedding-observation</artifactId>
<version>${project.parent.version}</version>
</dependency>

<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-autoconfigure-model-image-observation</artifactId>
<version>${project.parent.version}</version>
</dependency>

<!-- Boot dependencies -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
<optional>true</optional>
</dependency>

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-autoconfigure-processor</artifactId>
<optional>true</optional>
</dependency>

<!-- Non Spring Boot dependencies -->

<dependency>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-reflect</artifactId>
<optional>true</optional>
</dependency>

<!-- Test dependencies -->
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-test</artifactId>
<version>${project.parent.version}</version>
<scope>test</scope>
</dependency>

<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-autoconfigure-model-chat-client</artifactId>
<version>${project.parent.version}</version>
<scope>test</scope>
</dependency>

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>

<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<scope>test</scope>
</dependency>
</dependencies>

</project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
/*
* 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.
*/

package org.springframework.ai.model.openaisdk.autoconfigure;

import org.springframework.ai.openaisdk.AbstractOpenAiSdkOptions;
import org.springframework.util.StringUtils;

public final class OpenAiSdkAutoConfigurationUtil {

private OpenAiSdkAutoConfigurationUtil() {
// Avoids instantiation
}

public static ResolvedConnectionProperties resolveConnectionProperties(AbstractOpenAiSdkOptions commonProperties,
AbstractOpenAiSdkOptions modelProperties) {

var resolved = new ResolvedConnectionProperties();

resolved.setBaseUrl(StringUtils.hasText(modelProperties.getBaseUrl()) ? modelProperties.getBaseUrl()
: commonProperties.getBaseUrl());

resolved.setApiKey(StringUtils.hasText(modelProperties.getApiKey()) ? modelProperties.getApiKey()
: commonProperties.getApiKey());

String organizationId = StringUtils.hasText(modelProperties.getOrganizationId())
? modelProperties.getOrganizationId() : commonProperties.getOrganizationId();
resolved.setOrganizationId(organizationId);

resolved.setCredential(modelProperties.getCredential() != null ? modelProperties.getCredential()
: commonProperties.getCredential());

resolved.setTimeout(
modelProperties.getTimeout() != null ? modelProperties.getTimeout() : commonProperties.getTimeout());

resolved.setModel(StringUtils.hasText(modelProperties.getModel()) ? modelProperties.getModel()
: commonProperties.getModel());

resolved.setMicrosoftDeploymentName(StringUtils.hasText(modelProperties.getMicrosoftDeploymentName())
? modelProperties.getMicrosoftDeploymentName() : commonProperties.getMicrosoftDeploymentName());

resolved.setMicrosoftFoundryServiceVersion(modelProperties.getMicrosoftFoundryServiceVersion() != null
? modelProperties.getMicrosoftFoundryServiceVersion()
: commonProperties.getMicrosoftFoundryServiceVersion());

// For boolean properties, use modelProperties value, defaulting to
// commonProperties if needed
resolved.setMicrosoftFoundry(modelProperties.isMicrosoftFoundry() || commonProperties.isMicrosoftFoundry());

resolved.setGitHubModels(modelProperties.isGitHubModels() || commonProperties.isGitHubModels());

resolved.setMaxRetries(modelProperties.getMaxRetries() != null ? modelProperties.getMaxRetries()
: commonProperties.getMaxRetries());

resolved
.setProxy(modelProperties.getProxy() != null ? modelProperties.getProxy() : commonProperties.getProxy());

resolved.setCustomHeaders(modelProperties.getCustomHeaders() != null ? modelProperties.getCustomHeaders()
: commonProperties.getCustomHeaders());

return resolved;
}

public static class ResolvedConnectionProperties extends AbstractOpenAiSdkOptions {

}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
/*
* 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.
*/

package org.springframework.ai.model.openaisdk.autoconfigure;

import com.openai.client.OpenAIClient;
import com.openai.client.OpenAIClientAsync;
import io.micrometer.observation.ObservationRegistry;

import org.springframework.ai.chat.observation.ChatModelObservationConvention;
import org.springframework.ai.model.SpringAIModelProperties;
import org.springframework.ai.model.SpringAIModels;
import org.springframework.ai.model.tool.DefaultToolExecutionEligibilityPredicate;
import org.springframework.ai.model.tool.ToolCallingManager;
import org.springframework.ai.model.tool.ToolExecutionEligibilityPredicate;
import org.springframework.ai.model.tool.autoconfigure.ToolCallingAutoConfiguration;
import org.springframework.ai.openaisdk.AbstractOpenAiSdkOptions;
import org.springframework.ai.openaisdk.OpenAiSdkChatModel;
import org.springframework.ai.openaisdk.setup.OpenAiSdkSetup;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;

/**
* Chat {@link AutoConfiguration Auto-configuration} for OpenAI SDK.
*
* @author Christian Tzolov
*/
@AutoConfiguration(after = { ToolCallingAutoConfiguration.class })
@EnableConfigurationProperties({ OpenAiSdkConnectionProperties.class, OpenAiSdkChatProperties.class })
@ConditionalOnProperty(name = SpringAIModelProperties.CHAT_MODEL, havingValue = SpringAIModels.OPENAI_SDK,
matchIfMissing = true)
public class OpenAiSdkChatAutoConfiguration {

@Bean
@ConditionalOnMissingBean
public OpenAiSdkChatModel openAiChatModel(OpenAiSdkConnectionProperties commonProperties,
OpenAiSdkChatProperties chatProperties, ToolCallingManager toolCallingManager,
ObjectProvider<ObservationRegistry> observationRegistry,
ObjectProvider<ChatModelObservationConvention> observationConvention,
ObjectProvider<ToolExecutionEligibilityPredicate> openAiToolExecutionEligibilityPredicate) {

OpenAiSdkAutoConfigurationUtil.ResolvedConnectionProperties resolvedConnectionProperties = OpenAiSdkAutoConfigurationUtil
.resolveConnectionProperties(commonProperties, chatProperties);

OpenAIClient openAIClient = this.openAiClient(resolvedConnectionProperties);

OpenAIClientAsync openAIClientAsync = this.openAiClientAsync(resolvedConnectionProperties);

var chatModel = new OpenAiSdkChatModel(openAIClient, openAIClientAsync, chatProperties.getOptions(),
toolCallingManager, observationRegistry.getIfUnique(() -> ObservationRegistry.NOOP),
openAiToolExecutionEligibilityPredicate.getIfUnique(DefaultToolExecutionEligibilityPredicate::new));

observationConvention.ifAvailable(chatModel::setObservationConvention);

return chatModel;
}

private OpenAIClient openAiClient(AbstractOpenAiSdkOptions resolved) {

return OpenAiSdkSetup.setupSyncClient(resolved.getBaseUrl(), resolved.getApiKey(), resolved.getCredential(),
resolved.getMicrosoftDeploymentName(), resolved.getMicrosoftFoundryServiceVersion(),
resolved.getOrganizationId(), resolved.isMicrosoftFoundry(), resolved.isGitHubModels(),
resolved.getModel(), resolved.getTimeout(), resolved.getMaxRetries(), resolved.getProxy(),
resolved.getCustomHeaders());
}

private OpenAIClientAsync openAiClientAsync(AbstractOpenAiSdkOptions resolved) {

return OpenAiSdkSetup.setupAsyncClient(resolved.getBaseUrl(), resolved.getApiKey(), resolved.getCredential(),
resolved.getMicrosoftDeploymentName(), resolved.getMicrosoftFoundryServiceVersion(),
resolved.getOrganizationId(), resolved.isMicrosoftFoundry(), resolved.isGitHubModels(),
resolved.getModel(), resolved.getTimeout(), resolved.getMaxRetries(), resolved.getProxy(),
resolved.getCustomHeaders());
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
/*
* 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.
*/

package org.springframework.ai.model.openaisdk.autoconfigure;

import org.springframework.ai.openaisdk.AbstractOpenAiSdkOptions;
import org.springframework.ai.openaisdk.OpenAiSdkChatOptions;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.context.properties.NestedConfigurationProperty;

/**
* OpenAI SDK Chat autoconfiguration properties.
*
* @author Christian Tzolov
*/
@ConfigurationProperties(OpenAiSdkChatProperties.CONFIG_PREFIX)
public class OpenAiSdkChatProperties extends AbstractOpenAiSdkOptions {

public static final String CONFIG_PREFIX = "spring.ai.openai-sdk.chat";

public static final String DEFAULT_CHAT_MODEL = OpenAiSdkChatOptions.DEFAULT_CHAT_MODEL;

private static final Double DEFAULT_TEMPERATURE = 1.0;

@NestedConfigurationProperty
private final OpenAiSdkChatOptions options = OpenAiSdkChatOptions.builder()
.model(DEFAULT_CHAT_MODEL)
.temperature(DEFAULT_TEMPERATURE)
.build();

public OpenAiSdkChatOptions getOptions() {
return this.options;
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
/*
* 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.
*/

package org.springframework.ai.model.openaisdk.autoconfigure;

import org.springframework.ai.openaisdk.AbstractOpenAiSdkOptions;
import org.springframework.boot.context.properties.ConfigurationProperties;

@ConfigurationProperties(OpenAiSdkConnectionProperties.CONFIG_PREFIX)
public class OpenAiSdkConnectionProperties extends AbstractOpenAiSdkOptions {

public static final String CONFIG_PREFIX = "spring.ai.openai-sdk";

}
Loading