Refine OpenAI and Microsoft Azure AiClient implementations.
* Apply consistent treatment to the organization of the source code. * Fix Logger statements missing message placeholder formatting. * Simplify logic using Java 17 syntax where applicable. Closes #101
This commit is contained in:
@@ -17,7 +17,11 @@
|
||||
package org.springframework.ai.azure.openai.client;
|
||||
|
||||
import com.azure.ai.openai.OpenAIClient;
|
||||
import com.azure.ai.openai.models.*;
|
||||
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 org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.ai.client.AiClient;
|
||||
@@ -31,83 +35,102 @@ import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Implementation of {@link AiClient} backed by an OpenAiService
|
||||
* {@link AiClient} implementation for {@literal Microsoft Azure AI} backed by
|
||||
* {@link OpenAIClient}.
|
||||
*
|
||||
* @author Mark Pollack
|
||||
* @author Ueibin Kim
|
||||
* @author John Blum
|
||||
* @see org.springframework.ai.client.AiClient
|
||||
* @see com.azure.ai.openai.OpenAIClient
|
||||
*/
|
||||
public class AzureOpenAiClient implements AiClient {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(AzureOpenAiClient.class);
|
||||
|
||||
private final OpenAIClient msoftOpenAiClient;
|
||||
|
||||
private Double temperature = 0.7;
|
||||
|
||||
private String model = "gpt-35-turbo";
|
||||
|
||||
public AzureOpenAiClient(OpenAIClient msoftOpenAiClient) {
|
||||
Assert.notNull(msoftOpenAiClient, "com.azure.ai.openai.OpenAIClient must not be null");
|
||||
this.msoftOpenAiClient = msoftOpenAiClient;
|
||||
}
|
||||
private final Logger logger = LoggerFactory.getLogger(getClass());
|
||||
|
||||
@Override
|
||||
public String generate(String text) {
|
||||
ChatMessage azureChatMessage = new ChatMessage(ChatRole.USER, text);
|
||||
private final OpenAIClient msoftOpenAiClient;
|
||||
|
||||
ChatCompletionsOptions options = new ChatCompletionsOptions(List.of(azureChatMessage));
|
||||
options.setTemperature(this.getTemperature());
|
||||
options.setModel(this.getModel());
|
||||
|
||||
logger.trace("Azure Chat Message: ", azureChatMessage);
|
||||
ChatCompletions chatCompletions = this.msoftOpenAiClient.getChatCompletions(this.getModel(), options);
|
||||
logger.trace("Azure ChatCompletions: ", chatCompletions);
|
||||
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 AiResponse generate(Prompt prompt) {
|
||||
List<Message> messages = prompt.getMessages();
|
||||
List<ChatMessage> azureMessages = new ArrayList<>();
|
||||
for (Message message : messages) {
|
||||
String messageType = message.getMessageTypeValue();
|
||||
ChatRole chatRole = ChatRole.fromString(messageType);
|
||||
azureMessages.add(new ChatMessage(chatRole, message.getContent()));
|
||||
}
|
||||
ChatCompletionsOptions options = new ChatCompletionsOptions(azureMessages);
|
||||
options.setTemperature(this.getTemperature());
|
||||
options.setModel(this.getModel());
|
||||
logger.trace("Azure ChatCompletionsOptions: ", options);
|
||||
ChatCompletions chatCompletions = this.msoftOpenAiClient.getChatCompletions(this.getModel(), options);
|
||||
logger.trace("Azure ChatCompletions: ", chatCompletions);
|
||||
List<Generation> generations = new ArrayList<>();
|
||||
for (ChatChoice choice : chatCompletions.getChoices()) {
|
||||
ChatMessage choiceMessage = choice.getMessage();
|
||||
// TODO investigate mapping of additional metadata/runtime info to the
|
||||
// general model.
|
||||
Generation generation = new Generation(choiceMessage.getContent());
|
||||
generations.add(generation);
|
||||
}
|
||||
return new AiResponse(generations);
|
||||
}
|
||||
|
||||
public Double getTemperature() {
|
||||
return temperature;
|
||||
}
|
||||
|
||||
public void setTemperature(Double temperature) {
|
||||
this.temperature = temperature;
|
||||
public AzureOpenAiClient(OpenAIClient microsoftOpenAiClient) {
|
||||
Assert.notNull(microsoftOpenAiClient, "com.azure.ai.openai.OpenAIClient must not be null");
|
||||
this.msoftOpenAiClient = microsoftOpenAiClient;
|
||||
}
|
||||
|
||||
public String getModel() {
|
||||
return model;
|
||||
return this.model;
|
||||
}
|
||||
|
||||
public void setModel(String model) {
|
||||
this.model = model;
|
||||
}
|
||||
|
||||
public Double getTemperature() {
|
||||
return this.temperature;
|
||||
}
|
||||
|
||||
public void setTemperature(Double temperature) {
|
||||
this.temperature = temperature;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String generate(String text) {
|
||||
|
||||
ChatMessage azureChatMessage = new ChatMessage(ChatRole.USER, text);
|
||||
|
||||
ChatCompletionsOptions options = new ChatCompletionsOptions(List.of(azureChatMessage));
|
||||
options.setTemperature(this.getTemperature());
|
||||
options.setModel(this.getModel());
|
||||
logger.trace("Azure Chat Message: {}", azureChatMessage);
|
||||
|
||||
ChatCompletions chatCompletions = this.msoftOpenAiClient.getChatCompletions(this.getModel(), options);
|
||||
logger.trace("Azure ChatCompletions: {}", chatCompletions);
|
||||
|
||||
StringBuilder stringBuilder = new StringBuilder();
|
||||
|
||||
for (ChatChoice choice : chatCompletions.getChoices()) {
|
||||
ChatMessage message = choice.getMessage();
|
||||
if (message != null && message.getContent() != null) {
|
||||
stringBuilder.append(message.getContent());
|
||||
}
|
||||
}
|
||||
|
||||
return stringBuilder.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public AiResponse generate(Prompt prompt) {
|
||||
|
||||
List<Message> messages = prompt.getMessages();
|
||||
List<ChatMessage> azureMessages = new ArrayList<>();
|
||||
|
||||
for (Message message : messages) {
|
||||
String messageType = message.getMessageTypeValue();
|
||||
ChatRole chatRole = ChatRole.fromString(messageType);
|
||||
azureMessages.add(new ChatMessage(chatRole, message.getContent()));
|
||||
}
|
||||
|
||||
ChatCompletionsOptions options = new ChatCompletionsOptions(azureMessages);
|
||||
options.setTemperature(this.getTemperature());
|
||||
options.setModel(this.getModel());
|
||||
logger.trace("Azure ChatCompletionsOptions: {}", options);
|
||||
|
||||
ChatCompletions chatCompletions = this.msoftOpenAiClient.getChatCompletions(this.getModel(), options);
|
||||
logger.trace("Azure ChatCompletions: {}", chatCompletions);
|
||||
|
||||
List<Generation> generations = new ArrayList<>();
|
||||
|
||||
for (ChatChoice choice : chatCompletions.getChoices()) {
|
||||
ChatMessage choiceMessage = choice.getMessage();
|
||||
// TODO investigate mapping of additional metadata/runtime info to the general
|
||||
// model.
|
||||
Generation generation = new Generation(choiceMessage.getContent());
|
||||
generations.add(generation);
|
||||
}
|
||||
|
||||
return new AiResponse(generations);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -35,18 +35,25 @@ import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Implementation of {@link AiClient} backed by an {@link OpenAiService}.
|
||||
* {@link AiClient} implementation for {@literal OpenAI} backed by {@link OpenAiService}.
|
||||
*
|
||||
* @author Mark Pollack
|
||||
* @author Christian Tzolov
|
||||
* @author Ueibin Kim
|
||||
* @author John Blum
|
||||
* @see org.springframework.ai.client.AiClient
|
||||
* @see com.theokanning.openai.service.OpenAiService
|
||||
*/
|
||||
public class OpenAiClient implements AiClient {
|
||||
|
||||
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.7;
|
||||
|
||||
private String model = "gpt-3.5-turbo";
|
||||
|
||||
private final Logger logger = LoggerFactory.getLogger(getClass());
|
||||
|
||||
private final OpenAiService openAiService;
|
||||
|
||||
public OpenAiClient(OpenAiService openAiService) {
|
||||
@@ -54,22 +61,22 @@ public class OpenAiClient implements AiClient {
|
||||
this.openAiService = openAiService;
|
||||
}
|
||||
|
||||
public Double getTemperature() {
|
||||
return temperature;
|
||||
}
|
||||
|
||||
public void setTemperature(Double temperature) {
|
||||
this.temperature = temperature;
|
||||
}
|
||||
|
||||
public String getModel() {
|
||||
return model;
|
||||
return this.model;
|
||||
}
|
||||
|
||||
public void setModel(String model) {
|
||||
this.model = model;
|
||||
}
|
||||
|
||||
public Double getTemperature() {
|
||||
return this.temperature;
|
||||
}
|
||||
|
||||
public void setTemperature(Double temperature) {
|
||||
this.temperature = temperature;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String generate(String text) {
|
||||
ChatCompletionRequest chatCompletionRequest = getChatCompletionRequest(text);
|
||||
@@ -78,6 +85,7 @@ public class OpenAiClient implements AiClient {
|
||||
|
||||
@Override
|
||||
public AiResponse generate(Prompt prompt) {
|
||||
|
||||
List<Message> messages = prompt.getMessages();
|
||||
|
||||
List<ChatMessage> theoMessages = messages.stream()
|
||||
@@ -89,86 +97,95 @@ public class OpenAiClient implements AiClient {
|
||||
.temperature(this.temperature)
|
||||
.messages(theoMessages)
|
||||
.build();
|
||||
|
||||
return getAiResponse(chatCompletionRequest);
|
||||
}
|
||||
|
||||
private ChatCompletionRequest getChatCompletionRequest(String text) {
|
||||
|
||||
List<ChatMessage> chatMessages = List.of(new ChatMessage("user", text));
|
||||
logger.trace("ChatMessages: ", chatMessages);
|
||||
logger.trace("ChatMessages: {}", chatMessages);
|
||||
|
||||
ChatCompletionRequest chatCompletionRequest = ChatCompletionRequest.builder()
|
||||
.model(this.model)
|
||||
.temperature(this.temperature)
|
||||
.messages(List.of(new ChatMessage("user", text)))
|
||||
.build();
|
||||
logger.trace("ChatCompletionRequest: ", chatCompletionRequest);
|
||||
logger.trace("ChatCompletionRequest: {}", chatCompletionRequest);
|
||||
|
||||
return chatCompletionRequest;
|
||||
}
|
||||
|
||||
private AiResponse getAiResponse(ChatCompletionRequest chatCompletionRequest) {
|
||||
|
||||
List<Generation> generations = new ArrayList<>();
|
||||
logger.trace("ChatMessages: ", chatCompletionRequest.getMessages());
|
||||
logger.trace("ChatMessages: {}", chatCompletionRequest.getMessages());
|
||||
|
||||
List<ChatCompletionChoice> chatCompletionChoices = this.openAiService
|
||||
.createChatCompletion(chatCompletionRequest)
|
||||
.getChoices();
|
||||
logger.trace("ChatCompletionChoice: ", chatCompletionChoices);
|
||||
logger.trace("ChatCompletionChoice: {}", chatCompletionChoices);
|
||||
|
||||
for (ChatCompletionChoice chatCompletionChoice : chatCompletionChoices) {
|
||||
ChatMessage chatMessage = chatCompletionChoice.getMessage();
|
||||
// TODO investigate mapping of additional metadata/runtime info to the
|
||||
// general model.
|
||||
// TODO investigate mapping of additional metadata/runtime info to the general
|
||||
// model.
|
||||
Generation generation = new Generation(chatMessage.getContent(), Map.of("role", chatMessage.getRole()));
|
||||
generations.add(generation);
|
||||
}
|
||||
|
||||
return new AiResponse(generations);
|
||||
}
|
||||
|
||||
private String getResponse(ChatCompletionRequest chatCompletionRequest) {
|
||||
|
||||
StringBuilder builder = new StringBuilder();
|
||||
this.openAiService.createChatCompletion(chatCompletionRequest).getChoices().forEach(choice -> {
|
||||
builder.append(choice.getMessage().getContent());
|
||||
});
|
||||
|
||||
this.openAiService.createChatCompletion(chatCompletionRequest)
|
||||
.getChoices()
|
||||
.forEach(choice -> builder.append(choice.getMessage().getContent()));
|
||||
|
||||
String response = builder.toString();
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
private List<ChatCompletionRequest> getChatCompletionRequest(Prompt prompt) {
|
||||
List<ChatCompletionRequest> chatCompletionRequests = new ArrayList<>();
|
||||
|
||||
List<ChatMessage> chatMessages = convertToChatMessages(prompt.getMessages());
|
||||
List<ChatCompletionRequest> chatCompletionRequests = new ArrayList<>();
|
||||
|
||||
ChatCompletionRequest chatCompletionRequest = ChatCompletionRequest.builder()
|
||||
.model(this.model)
|
||||
.temperature(this.temperature)
|
||||
.messages(chatMessages)
|
||||
.build();
|
||||
|
||||
chatCompletionRequests.add(chatCompletionRequest);
|
||||
|
||||
return chatCompletionRequests;
|
||||
}
|
||||
|
||||
private List<ChatMessage> convertToChatMessages(List<Message> messages) {
|
||||
|
||||
List<ChatMessage> chatMessages = new ArrayList<>();
|
||||
|
||||
for (Message promptMessage : messages) {
|
||||
switch (promptMessage.getMessageType()) {
|
||||
case USER:
|
||||
chatMessages.add(new ChatMessage(MessageType.USER.getValue(), promptMessage.getContent()));
|
||||
break;
|
||||
case ASSISTANT:
|
||||
// TODO - valid?
|
||||
chatMessages.add(new ChatMessage(MessageType.ASSISTANT.getValue(), promptMessage.getContent()));
|
||||
break;
|
||||
case SYSTEM:
|
||||
chatMessages.add(new ChatMessage(MessageType.SYSTEM.getValue(), promptMessage.getContent()));
|
||||
break;
|
||||
case FUNCTION:
|
||||
logger.error(
|
||||
"Can not send a Spring AI Function MessageType to the ChatGPT API, use 'system', 'user' or 'ai' message types.");
|
||||
break;
|
||||
default:
|
||||
logger.error("Unknown Spring AI Chat MessageType. Use 'system', 'human' or 'ai' message types.");
|
||||
break;
|
||||
MessageType promptMessageType = promptMessage.getMessageType();
|
||||
switch (promptMessageType) {
|
||||
case USER, ASSISTANT, SYSTEM -> chatMessages.add(newChatMessage(promptMessage));
|
||||
case FUNCTION -> logger.error(
|
||||
"Cannot send a Spring AI Function MessageType to the ChatGPT API; use 'system', 'user' or 'ai' message types.");
|
||||
default ->
|
||||
logger.error("Unknown Spring AI Chat MessageType; use 'system', 'human' or 'ai' message types.");
|
||||
}
|
||||
}
|
||||
|
||||
return chatMessages;
|
||||
}
|
||||
|
||||
private ChatMessage newChatMessage(Message message) {
|
||||
return new ChatMessage(message.getMessageType().getValue(), message.getContent());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user