diff --git a/pom.xml b/pom.xml
index 7037a6bc5..d78e5124b 100644
--- a/pom.xml
+++ b/pom.xml
@@ -14,8 +14,10 @@
spring-ai-core
spring-ai-openai
+ spring-ai-azure-openai
spring-ai-spring-boot-autoconfigure
spring-ai-spring-boot-starters/spring-ai-starter-openai
+ spring-ai-spring-boot-starters/spring-ai-starter-azure-openai
spring-ai-docs
@@ -63,6 +65,7 @@
3.1.2
4.0.2
0.12.0
+ 1.0.0-beta.3
1.6.2
diff --git a/spring-ai-azure-openai/pom.xml b/spring-ai-azure-openai/pom.xml
new file mode 100644
index 000000000..0e9dfdf75
--- /dev/null
+++ b/spring-ai-azure-openai/pom.xml
@@ -0,0 +1,55 @@
+
+
+ 4.0.0
+
+ org.springframework.experimental.ai
+ spring-ai
+ 1.0.0-SNAPSHOT
+
+ spring-ai-azure-openai
+ jar
+ Spring AI Azure OpenAI
+ OpenAI support
+ https://github.com/spring-projects-experimental/spring-ai
+
+
+ https://github.com/spring-projects-experimental/spring-ai
+ git://github.com/spring-projects-experimental/spring-ai.git
+ git@github.com:spring-projects-experimental/spring-ai.git
+
+
+
+
+
+
+ org.springframework.experimental.ai
+ spring-ai-core
+ ${project.parent.version}
+
+
+
+ com.azure
+ azure-ai-openai
+ ${azure-open-ai-client.version}
+
+
+
+
+ org.springframework
+ spring-context-support
+
+
+
+ org.springframework.boot
+ spring-boot-starter-logging
+
+
+
+
+ org.springframework.boot
+ spring-boot-starter-test
+ test
+
+
+
+
diff --git a/spring-ai-azure-openai/src/main/java/org/springframework/ai/azure/openai/llm/AzureOpenAiClient.java b/spring-ai-azure-openai/src/main/java/org/springframework/ai/azure/openai/llm/AzureOpenAiClient.java
new file mode 100644
index 000000000..ae123174d
--- /dev/null
+++ b/spring-ai-azure-openai/src/main/java/org/springframework/ai/azure/openai/llm/AzureOpenAiClient.java
@@ -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;
+ }
+
+}
diff --git a/spring-ai-core/src/test/java/org/springframework/ai/core/prompt/PromptTests.java b/spring-ai-core/src/test/java/org/springframework/ai/core/prompt/PromptTests.java
index 15d192c9c..803a09bef 100644
--- a/spring-ai-core/src/test/java/org/springframework/ai/core/prompt/PromptTests.java
+++ b/spring-ai-core/src/test/java/org/springframework/ai/core/prompt/PromptTests.java
@@ -25,6 +25,7 @@ import org.junit.jupiter.api.Test;
import static org.assertj.core.api.Assertions.assertThat;
+@SuppressWarnings("unchecked")
class PromptTests {
@Test
diff --git a/spring-ai-openai/src/main/java/org/springframework/ai/openai/llm/OpenAiClient.java b/spring-ai-openai/src/main/java/org/springframework/ai/openai/llm/OpenAiClient.java
index 666a1a947..bfc8200cf 100644
--- a/spring-ai-openai/src/main/java/org/springframework/ai/openai/llm/OpenAiClient.java
+++ b/spring-ai-openai/src/main/java/org/springframework/ai/openai/llm/OpenAiClient.java
@@ -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);
diff --git a/spring-ai-spring-boot-autoconfigure/pom.xml b/spring-ai-spring-boot-autoconfigure/pom.xml
index 0aec2cc4e..59feb9016 100644
--- a/spring-ai-spring-boot-autoconfigure/pom.xml
+++ b/spring-ai-spring-boot-autoconfigure/pom.xml
@@ -34,6 +34,13 @@
true
+
+ org.springframework.experimental.ai
+ spring-ai-azure-openai
+ ${project.parent.version}
+ true
+
+
org.springframework.boot
diff --git a/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/autoconfigure/azure/openai/AzureOpenAiAutoConfiguration.java b/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/autoconfigure/azure/openai/AzureOpenAiAutoConfiguration.java
new file mode 100644
index 000000000..de500e057
--- /dev/null
+++ b/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/autoconfigure/azure/openai/AzureOpenAiAutoConfiguration.java
@@ -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;
+ }
+
+}
diff --git a/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/autoconfigure/azure/openai/AzureOpenAiProperties.java b/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/autoconfigure/azure/openai/AzureOpenAiProperties.java
new file mode 100644
index 000000000..890a07c99
--- /dev/null
+++ b/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/autoconfigure/azure/openai/AzureOpenAiProperties.java
@@ -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;
+ }
+
+}
diff --git a/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/autoconfigure/openai/OpenAiAutoConfiguration.java b/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/autoconfigure/openai/OpenAiAutoConfiguration.java
index 3278ce18e..23138f984 100644
--- a/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/autoconfigure/openai/OpenAiAutoConfiguration.java
+++ b/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/autoconfigure/openai/OpenAiAutoConfiguration.java
@@ -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;
}
}
diff --git a/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/autoconfigure/openai/OpenAiProperties.java b/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/autoconfigure/openai/OpenAiProperties.java
index ae61e1040..095ac5433 100644
--- a/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/autoconfigure/openai/OpenAiProperties.java
+++ b/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/autoconfigure/openai/OpenAiProperties.java
@@ -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;
+ }
+
}
diff --git a/spring-ai-spring-boot-autoconfigure/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports b/spring-ai-spring-boot-autoconfigure/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
index 337264244..343f10957 100644
--- a/spring-ai-spring-boot-autoconfigure/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
+++ b/spring-ai-spring-boot-autoconfigure/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
@@ -1 +1,2 @@
-org.springframework.ai.autoconfigure.openai.OpenAiAutoConfiguration
\ No newline at end of file
+org.springframework.ai.autoconfigure.openai.OpenAiAutoConfiguration
+org.springframework.ai.autoconfigure.azure.openai.AzureOpenAiAutoConfiguration
\ No newline at end of file
diff --git a/spring-ai-spring-boot-starters/spring-ai-starter-azure-openai/pom.xml b/spring-ai-spring-boot-starters/spring-ai-starter-azure-openai/pom.xml
new file mode 100644
index 000000000..65af3d774
--- /dev/null
+++ b/spring-ai-spring-boot-starters/spring-ai-starter-azure-openai/pom.xml
@@ -0,0 +1,50 @@
+
+
+ 4.0.0
+
+ org.springframework.experimental.ai
+ spring-ai
+ 1.0.0-SNAPSHOT
+
+ spring-ai-azure-openai-spring-boot-starter
+ jar
+ Spring AI Starter - Azure OpenAI
+ Spring AI Azure OpenAI Auto Configuration
+ https://github.com/spring-projects-experimental/spring-ai
+
+
+ https://github.com/spring-projects-experimental/spring-ai
+ git://github.com/spring-projects-experimental/spring-ai.git
+ git@github.com:spring-projects-experimental/spring-ai.git
+
+
+
+
+
+
+
+ org.springframework.boot
+ spring-boot-starter
+
+
+
+ org.springframework.experimental.ai
+ spring-ai-spring-boot-autoconfigure
+ ${project.parent.version}
+
+
+
+ org.springframework.experimental.ai
+ spring-ai-azure-openai
+ ${project.parent.version}
+
+
+
+
+ org.springframework.boot
+ spring-boot-starter-test
+ test
+
+
+
+
diff --git a/spring-ai-spring-boot-starters/spring-ai-starter-openai/pom.xml b/spring-ai-spring-boot-starters/spring-ai-starter-openai/pom.xml
index 4a336226f..d4245a0a1 100644
--- a/spring-ai-spring-boot-starters/spring-ai-starter-openai/pom.xml
+++ b/spring-ai-spring-boot-starters/spring-ai-starter-openai/pom.xml
@@ -8,8 +8,8 @@
spring-ai-openai-spring-boot-starter
jar
- Spring AI Starters
- Spring AI Auto Configuration
+ Spring AI Starter - OpenAI
+ Spring AI Open AI Auto Configuration
https://github.com/spring-projects-experimental/spring-ai