Add Azure OpenAI support
This commit is contained in:
3
pom.xml
3
pom.xml
@@ -14,8 +14,10 @@
|
||||
<modules>
|
||||
<module>spring-ai-core</module>
|
||||
<module>spring-ai-openai</module>
|
||||
<module>spring-ai-azure-openai</module>
|
||||
<module>spring-ai-spring-boot-autoconfigure</module>
|
||||
<module>spring-ai-spring-boot-starters/spring-ai-starter-openai</module>
|
||||
<module>spring-ai-spring-boot-starters/spring-ai-starter-azure-openai</module>
|
||||
<module>spring-ai-docs</module>
|
||||
</modules>
|
||||
|
||||
@@ -63,6 +65,7 @@
|
||||
<spring-boot.version>3.1.2</spring-boot.version>
|
||||
<stringtemplate.version>4.0.2</stringtemplate.version>
|
||||
<open-ai-client.version>0.12.0</open-ai-client.version>
|
||||
<azure-open-ai-client.version>1.0.0-beta.3</azure-open-ai-client.version>
|
||||
|
||||
<!-- documentation dependencies -->
|
||||
<asciidoctorj-pdf.version>1.6.2</asciidoctorj-pdf.version> <!-- FIXME build failure with version 2.3.9 -->
|
||||
|
||||
55
spring-ai-azure-openai/pom.xml
Normal file
55
spring-ai-azure-openai/pom.xml
Normal file
@@ -0,0 +1,55 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 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.experimental.ai</groupId>
|
||||
<artifactId>spring-ai</artifactId>
|
||||
<version>1.0.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
<artifactId>spring-ai-azure-openai</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
<name>Spring AI Azure OpenAI</name>
|
||||
<description>OpenAI support</description>
|
||||
<url>https://github.com/spring-projects-experimental/spring-ai</url>
|
||||
|
||||
<scm>
|
||||
<url>https://github.com/spring-projects-experimental/spring-ai</url>
|
||||
<connection>git://github.com/spring-projects-experimental/spring-ai.git</connection>
|
||||
<developerConnection>git@github.com:spring-projects-experimental/spring-ai.git</developerConnection>
|
||||
</scm>
|
||||
|
||||
<dependencies>
|
||||
|
||||
<!-- production dependencies -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.experimental.ai</groupId>
|
||||
<artifactId>spring-ai-core</artifactId>
|
||||
<version>${project.parent.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.azure</groupId>
|
||||
<artifactId>azure-ai-openai</artifactId>
|
||||
<version>${azure-open-ai-client.version}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- Spring Framework -->
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-context-support</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-logging</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- test dependencies -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-test</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
@@ -0,0 +1,94 @@
|
||||
/*
|
||||
* Copyright 2023 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.azure.openai.llm;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import com.azure.ai.openai.OpenAIClient;
|
||||
import com.azure.ai.openai.models.ChatChoice;
|
||||
import com.azure.ai.openai.models.ChatCompletions;
|
||||
import com.azure.ai.openai.models.ChatCompletionsOptions;
|
||||
import com.azure.ai.openai.models.ChatMessage;
|
||||
import com.azure.ai.openai.models.ChatRole;
|
||||
import com.azure.ai.openai.models.Choice;
|
||||
import com.azure.ai.openai.models.Completions;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import org.springframework.ai.core.llm.LLMResult;
|
||||
import org.springframework.ai.core.llm.LlmClient;
|
||||
import org.springframework.ai.core.prompt.Prompt;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Implementation of {@link LlmClient} backed by an OpenAiService
|
||||
*/
|
||||
public class AzureOpenAiClient implements LlmClient {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(AzureOpenAiClient.class);
|
||||
|
||||
private final OpenAIClient msoftOpenAiClient;
|
||||
|
||||
private Double temperature = 0.5;
|
||||
|
||||
private String model = "gpt-35-turbo";
|
||||
|
||||
public AzureOpenAiClient(OpenAIClient msoftOpenAiClient) {
|
||||
Assert.notNull(msoftOpenAiClient, "OpenAiClient must not be null");
|
||||
this.msoftOpenAiClient = msoftOpenAiClient;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String generate(String text) {
|
||||
ChatMessage chatMessage = new ChatMessage(ChatRole.USER, text);
|
||||
|
||||
ChatCompletionsOptions options = new ChatCompletionsOptions(List.of(chatMessage));
|
||||
options.setTemperature(this.getTemperature());
|
||||
options.setModel(this.getModel());
|
||||
|
||||
ChatCompletions chatCompletions = this.msoftOpenAiClient.getChatCompletions(this.getModel(), options);
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for (ChatChoice choice : chatCompletions.getChoices()) {
|
||||
if (choice.getMessage() != null && choice.getMessage().getContent() != null) {
|
||||
sb.append(choice.getMessage().getContent());
|
||||
}
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public LLMResult generate(Prompt... prompts) {
|
||||
throw new RuntimeException("Method LLMResult generate(Prompt... prompts) not implemented.");
|
||||
}
|
||||
|
||||
public Double getTemperature() {
|
||||
return temperature;
|
||||
}
|
||||
|
||||
public void setTemperature(Double temperature) {
|
||||
this.temperature = temperature;
|
||||
}
|
||||
|
||||
public String getModel() {
|
||||
return model;
|
||||
}
|
||||
|
||||
public void setModel(String model) {
|
||||
this.model = model;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -25,6 +25,7 @@ import org.junit.jupiter.api.Test;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
class PromptTests {
|
||||
|
||||
@Test
|
||||
|
||||
@@ -39,8 +39,9 @@ public class OpenAiClient implements LlmClient {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(OpenAiClient.class);
|
||||
|
||||
// TODO how to set default options for the entire client
|
||||
// TODO expose request options into Prompt API via PromptOptions
|
||||
private Double temperature = 0.3;
|
||||
private Double temperature = 0.5;
|
||||
|
||||
private String model = "gpt-3.5-turbo";
|
||||
|
||||
@@ -51,6 +52,22 @@ public class OpenAiClient implements LlmClient {
|
||||
this.openAiService = openAiService;
|
||||
}
|
||||
|
||||
public Double getTemperature() {
|
||||
return temperature;
|
||||
}
|
||||
|
||||
public void setTemperature(Double temperature) {
|
||||
this.temperature = temperature;
|
||||
}
|
||||
|
||||
public String getModel() {
|
||||
return model;
|
||||
}
|
||||
|
||||
public void setModel(String model) {
|
||||
this.model = model;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String generate(String text) {
|
||||
ChatCompletionRequest chatCompletionRequest = getChatCompletionRequest(text);
|
||||
|
||||
@@ -34,6 +34,13 @@
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.experimental.ai</groupId>
|
||||
<artifactId>spring-ai-azure-openai</artifactId>
|
||||
<version>${project.parent.version}</version>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
|
||||
<!-- test dependencies -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
|
||||
@@ -0,0 +1,63 @@
|
||||
/*
|
||||
* Copyright 2023 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.autoconfigure.azure.openai;
|
||||
|
||||
import com.azure.ai.openai.OpenAIClient;
|
||||
import com.azure.ai.openai.OpenAIClientBuilder;
|
||||
import com.azure.core.credential.AzureKeyCredential;
|
||||
|
||||
import org.springframework.ai.azure.openai.llm.AzureOpenAiClient;
|
||||
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.context.annotation.Bean;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
@AutoConfiguration
|
||||
@ConditionalOnClass(OpenAIClientBuilder.class)
|
||||
@EnableConfigurationProperties(AzureOpenAiProperties.class)
|
||||
public class AzureOpenAiAutoConfiguration {
|
||||
|
||||
private final AzureOpenAiProperties azureOpenAiProperties;
|
||||
|
||||
public AzureOpenAiAutoConfiguration(AzureOpenAiProperties azureOpenAiProperties) {
|
||||
this.azureOpenAiProperties = azureOpenAiProperties;
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
public OpenAIClient msoftSdkOpenAiClient(AzureOpenAiProperties azureOpenAiProperties) {
|
||||
if (!StringUtils.hasText(azureOpenAiProperties.getApiKey())) {
|
||||
throw new IllegalArgumentException("You must provide an API key with the property name "
|
||||
+ AzureOpenAiProperties.CONFIG_PREFIX + ".api-key");
|
||||
}
|
||||
OpenAIClient msoftSdkOpenAiClient = new OpenAIClientBuilder().endpoint(this.azureOpenAiProperties.getEndpoint())
|
||||
.credential(new AzureKeyCredential(this.azureOpenAiProperties.getApiKey()))
|
||||
.buildClient();
|
||||
return msoftSdkOpenAiClient;
|
||||
}
|
||||
|
||||
@Bean
|
||||
public AzureOpenAiClient azureOpenAiClient(OpenAIClient msoftSdkOpenAiClient) {
|
||||
AzureOpenAiClient azureOpenAiClient = new AzureOpenAiClient(msoftSdkOpenAiClient);
|
||||
azureOpenAiClient.setTemperature(this.azureOpenAiProperties.getTemperature());
|
||||
azureOpenAiClient.setModel(this.azureOpenAiProperties.getModel());
|
||||
return azureOpenAiClient;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
/*
|
||||
* Copyright 2023 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.autoconfigure.azure.openai;
|
||||
|
||||
import java.net.URI;
|
||||
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
|
||||
@ConfigurationProperties(AzureOpenAiProperties.CONFIG_PREFIX)
|
||||
public class AzureOpenAiProperties {
|
||||
|
||||
// TODO look into Spring Cloud Azure project for credentials as well as
|
||||
// e.g. com.azure.core.credential.AzureKeyCredential
|
||||
public static final String CONFIG_PREFIX = "spring.ai.azure.openai";
|
||||
|
||||
private String apiKey;
|
||||
|
||||
private String endpoint;
|
||||
|
||||
private Double temperature = 0.5;
|
||||
|
||||
private String model = "gpt-35-turbo";
|
||||
|
||||
public String getEndpoint() {
|
||||
return endpoint;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the service endpoint that will be connected to by clients.
|
||||
* @param endpoint The URL of the service endpoint
|
||||
*/
|
||||
public void setEndpoint(String endpoint) {
|
||||
this.endpoint = endpoint;
|
||||
}
|
||||
|
||||
public Double getTemperature() {
|
||||
return temperature;
|
||||
}
|
||||
|
||||
public void setTemperature(Double temperature) {
|
||||
this.temperature = temperature;
|
||||
}
|
||||
|
||||
public String getModel() {
|
||||
return model;
|
||||
}
|
||||
|
||||
public void setModel(String model) {
|
||||
this.model = model;
|
||||
}
|
||||
|
||||
public void setApiKey(String apiKey) {
|
||||
this.apiKey = apiKey;
|
||||
}
|
||||
|
||||
public String getApiKey() {
|
||||
return apiKey;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -25,6 +25,8 @@ import org.springframework.boot.context.properties.EnableConfigurationProperties
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import static org.springframework.ai.autoconfigure.openai.OpenAiProperties.CONFIG_PREFIX;
|
||||
|
||||
@AutoConfiguration
|
||||
@ConditionalOnClass(OpenAiService.class)
|
||||
@EnableConfigurationProperties(OpenAiProperties.class)
|
||||
@@ -40,10 +42,13 @@ public class OpenAiAutoConfiguration {
|
||||
public OpenAiClient openAiClient(OpenAiProperties openAiProperties) {
|
||||
if (!StringUtils.hasText(openAiProperties.getApiKey())) {
|
||||
throw new IllegalArgumentException(
|
||||
"You must provide an API key with the property name spring.ai.openai.api-key");
|
||||
"You must provide an API key with the property name " + CONFIG_PREFIX + ".api-key");
|
||||
}
|
||||
OpenAiService openAiService = new OpenAiService(openAiProperties.getApiKey());
|
||||
return new OpenAiClient(openAiService);
|
||||
OpenAiService theoOpenAiService = new OpenAiService(openAiProperties.getApiKey());
|
||||
OpenAiClient openAiClient = new OpenAiClient(theoOpenAiService);
|
||||
openAiClient.setTemperature(openAiProperties.getTemperature());
|
||||
openAiClient.setModel(openAiProperties.getModel());
|
||||
return openAiClient;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -18,11 +18,19 @@ package org.springframework.ai.autoconfigure.openai;
|
||||
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
|
||||
@ConfigurationProperties(prefix = "spring.ai.openai")
|
||||
import static org.springframework.ai.autoconfigure.openai.OpenAiProperties.CONFIG_PREFIX;
|
||||
|
||||
@ConfigurationProperties(CONFIG_PREFIX)
|
||||
public class OpenAiProperties {
|
||||
|
||||
public static final String CONFIG_PREFIX = "spring.ai.openai";
|
||||
|
||||
private String apiKey;
|
||||
|
||||
private Double temperature = 0.5;
|
||||
|
||||
private String model = "gpt-3.5-turbo";
|
||||
|
||||
public String getApiKey() {
|
||||
return apiKey;
|
||||
}
|
||||
@@ -31,4 +39,20 @@ public class OpenAiProperties {
|
||||
this.apiKey = apiKey;
|
||||
}
|
||||
|
||||
public String getModel() {
|
||||
return model;
|
||||
}
|
||||
|
||||
public void setModel(String model) {
|
||||
this.model = model;
|
||||
}
|
||||
|
||||
public Double getTemperature() {
|
||||
return temperature;
|
||||
}
|
||||
|
||||
public void setTemperature(Double temperature) {
|
||||
this.temperature = temperature;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1 +1,2 @@
|
||||
org.springframework.ai.autoconfigure.openai.OpenAiAutoConfiguration
|
||||
org.springframework.ai.autoconfigure.openai.OpenAiAutoConfiguration
|
||||
org.springframework.ai.autoconfigure.azure.openai.AzureOpenAiAutoConfiguration
|
||||
@@ -0,0 +1,50 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 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.experimental.ai</groupId>
|
||||
<artifactId>spring-ai</artifactId>
|
||||
<version>1.0.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
<artifactId>spring-ai-azure-openai-spring-boot-starter</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
<name>Spring AI Starter - Azure OpenAI</name>
|
||||
<description>Spring AI Azure OpenAI Auto Configuration</description>
|
||||
<url>https://github.com/spring-projects-experimental/spring-ai</url>
|
||||
|
||||
<scm>
|
||||
<url>https://github.com/spring-projects-experimental/spring-ai</url>
|
||||
<connection>git://github.com/spring-projects-experimental/spring-ai.git</connection>
|
||||
<developerConnection>git@github.com:spring-projects-experimental/spring-ai.git</developerConnection>
|
||||
</scm>
|
||||
|
||||
<dependencies>
|
||||
|
||||
<!-- production dependencies -->
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.experimental.ai</groupId>
|
||||
<artifactId>spring-ai-spring-boot-autoconfigure</artifactId>
|
||||
<version>${project.parent.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.experimental.ai</groupId>
|
||||
<artifactId>spring-ai-azure-openai</artifactId>
|
||||
<version>${project.parent.version}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- test dependencies -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-test</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
@@ -8,8 +8,8 @@
|
||||
</parent>
|
||||
<artifactId>spring-ai-openai-spring-boot-starter</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
<name>Spring AI Starters</name>
|
||||
<description>Spring AI Auto Configuration</description>
|
||||
<name>Spring AI Starter - OpenAI</name>
|
||||
<description>Spring AI Open AI Auto Configuration</description>
|
||||
<url>https://github.com/spring-projects-experimental/spring-ai</url>
|
||||
|
||||
<scm>
|
||||
|
||||
Reference in New Issue
Block a user