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 index ae123174d..6629d0ffc 100644 --- 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 @@ -16,24 +16,20 @@ 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 com.azure.ai.openai.models.*; 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.Generation; import org.springframework.ai.core.prompt.Prompt; +import org.springframework.ai.core.prompt.messages.Message; import org.springframework.util.Assert; +import java.util.ArrayList; +import java.util.List; + /** * Implementation of {@link LlmClient} backed by an OpenAiService */ @@ -54,9 +50,9 @@ public class AzureOpenAiClient implements LlmClient { @Override public String generate(String text) { - ChatMessage chatMessage = new ChatMessage(ChatRole.USER, text); + ChatMessage azureChatMessage = new ChatMessage(ChatRole.USER, text); - ChatCompletionsOptions options = new ChatCompletionsOptions(List.of(chatMessage)); + ChatCompletionsOptions options = new ChatCompletionsOptions(List.of(azureChatMessage)); options.setTemperature(this.getTemperature()); options.setModel(this.getModel()); @@ -72,7 +68,31 @@ public class AzureOpenAiClient implements LlmClient { @Override public LLMResult generate(Prompt... prompts) { - throw new RuntimeException("Method LLMResult generate(Prompt... prompts) not implemented."); + List> generationsList = new ArrayList<>(); + for (Prompt prompt : prompts) { + + List messages = prompt.getMessages(); + List azureMessages = new ArrayList<>(); + for (Message message : messages) { + String messageType = message.getMessageType().getValue(); + 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()); + ChatCompletions chatCompletions = this.msoftOpenAiClient.getChatCompletions(this.getModel(), options); + List 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); + } + generationsList.add(generations); + } + return new LLMResult(generationsList); } public Double getTemperature() { diff --git a/spring-ai-core/pom.xml b/spring-ai-core/pom.xml index ad196f8cc..4424348d0 100644 --- a/spring-ai-core/pom.xml +++ b/spring-ai-core/pom.xml @@ -33,6 +33,7 @@ spring-boot-starter-test test + diff --git a/spring-ai-core/src/main/java/org/springframework/ai/core/llm/LLMResult.java b/spring-ai-core/src/main/java/org/springframework/ai/core/llm/LLMResult.java index a6e67bb59..32d91fd21 100644 --- a/spring-ai-core/src/main/java/org/springframework/ai/core/llm/LLMResult.java +++ b/spring-ai-core/src/main/java/org/springframework/ai/core/llm/LLMResult.java @@ -1,41 +1,71 @@ -package org.springframework.ai.core.llm;/* - * 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. - */ - -import java.util.List; -import java.util.Map; +/* + * 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.core.llm; import org.springframework.ai.core.prompt.Generation; -public interface LLMResult { +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public class LLMResult { + + private final List> generations; + + private Map providerOutput = new HashMap<>(); + + private Map runInfo = new HashMap<>(); + + public LLMResult(List> generations) { + this.generations = generations; + } + + public LLMResult(List> generations, Map providerOutput) { + this.generations = generations; + this.providerOutput = providerOutput; + } + + public LLMResult(List> generations, Map providerOutput, + Map runInfo) { + this.generations = generations; + this.providerOutput = providerOutput; + this.runInfo = runInfo; + } /** * The list of generated outputs. It iss a list of lists because a single input could * have multiple outputs, and multiple inputs could be passed in. * @return */ - List> getGenerations(); + public List> getGenerations() { + return this.generations; + } /** * Arbitrary LLM-provider specific output */ - Map getProviderOutput(); + public Map getProviderOutput() { + return null; + } /** * The run metadata information */ - Map getRunInfo(); + public Map getRunInfo() { + return null; + } } diff --git a/spring-ai-core/src/main/java/org/springframework/ai/core/prompt/AiPromptTemplate.java b/spring-ai-core/src/main/java/org/springframework/ai/core/prompt/AssistantPromptTemplate.java similarity index 85% rename from spring-ai-core/src/main/java/org/springframework/ai/core/prompt/AiPromptTemplate.java rename to spring-ai-core/src/main/java/org/springframework/ai/core/prompt/AssistantPromptTemplate.java index a9c80a5f1..d30e3302d 100644 --- a/spring-ai-core/src/main/java/org/springframework/ai/core/prompt/AiPromptTemplate.java +++ b/spring-ai-core/src/main/java/org/springframework/ai/core/prompt/AssistantPromptTemplate.java @@ -16,19 +16,19 @@ package org.springframework.ai.core.prompt; -import java.util.Map; - import org.springframework.ai.core.prompt.messages.AssistantMessage; -public class AiPromptTemplate extends PromptTemplate { +import java.util.Map; + +public class AssistantPromptTemplate extends PromptTemplate { private boolean example = false; - public AiPromptTemplate(String template) { + public AssistantPromptTemplate(String template) { super(template); } - public AiPromptTemplate(String template, boolean example) { + public AssistantPromptTemplate(String template, boolean example) { super(template); this.example = example; } diff --git a/spring-ai-core/src/main/java/org/springframework/ai/core/prompt/ChatPromptTemplate.java b/spring-ai-core/src/main/java/org/springframework/ai/core/prompt/ChatPromptTemplate.java index 143babed9..2130ad92d 100644 --- a/spring-ai-core/src/main/java/org/springframework/ai/core/prompt/ChatPromptTemplate.java +++ b/spring-ai-core/src/main/java/org/springframework/ai/core/prompt/ChatPromptTemplate.java @@ -16,31 +16,32 @@ package org.springframework.ai.core.prompt; +import org.springframework.ai.core.prompt.messages.ChatMessage; +import org.springframework.ai.core.prompt.messages.MessageType; + import java.util.Map; -import org.springframework.ai.core.prompt.messages.ChatMessage; - +/** + * A PromptTemplate that lets you specify the role as a string should the current + * implementations and their roles not suffice for your needs. + */ public class ChatPromptTemplate extends PromptTemplate { - private String role; + private MessageType messageType; - public ChatPromptTemplate(String template) { + public ChatPromptTemplate(MessageType messageType, String template) { super(template); - } - - public ChatPromptTemplate(String template, String role) { - super(template); - this.role = role; + this.messageType = messageType; } @Override public Prompt create() { - return new Prompt(new ChatMessage(render(), this.role)); + return new Prompt(new ChatMessage(this.messageType, render())); } @Override public Prompt create(Map model) { - return new Prompt(new ChatMessage(render(model), this.role)); + return new Prompt(new ChatMessage(this.messageType, render(model))); } } diff --git a/spring-ai-core/src/main/java/org/springframework/ai/core/prompt/Generation.java b/spring-ai-core/src/main/java/org/springframework/ai/core/prompt/Generation.java index 072c77972..d2777bf34 100644 --- a/spring-ai-core/src/main/java/org/springframework/ai/core/prompt/Generation.java +++ b/spring-ai-core/src/main/java/org/springframework/ai/core/prompt/Generation.java @@ -16,16 +16,30 @@ package org.springframework.ai.core.prompt; +import java.util.HashMap; import java.util.Map; public class Generation { private final String text; - private Map info; + private Map info = new HashMap<>(); public Generation(String text) { this.text = text; } + public Generation(String text, Map info) { + this.text = text; + this.info = info; + } + + public String getText() { + return text; + } + + public Map getInfo() { + return info; + } + } diff --git a/spring-ai-core/src/main/java/org/springframework/ai/core/prompt/PromptTemplate.java b/spring-ai-core/src/main/java/org/springframework/ai/core/prompt/PromptTemplate.java index acd7b24ee..565a0dc73 100644 --- a/spring-ai-core/src/main/java/org/springframework/ai/core/prompt/PromptTemplate.java +++ b/spring-ai-core/src/main/java/org/springframework/ai/core/prompt/PromptTemplate.java @@ -16,6 +16,11 @@ package org.springframework.ai.core.prompt; +import org.antlr.runtime.Token; +import org.antlr.runtime.TokenStream; +import org.stringtemplate.v4.ST; +import org.stringtemplate.v4.compiler.STLexer; + import java.util.HashMap; import java.util.HashSet; import java.util.Map; @@ -24,11 +29,6 @@ import java.util.Set; import java.util.stream.Collectors; import java.util.stream.IntStream; -import org.antlr.runtime.Token; -import org.antlr.runtime.TokenStream; -import org.stringtemplate.v4.ST; -import org.stringtemplate.v4.compiler.STLexer; - public class PromptTemplate extends AbstractPromptTemplate { private ST st; diff --git a/spring-ai-core/src/main/java/org/springframework/ai/core/prompt/UserPromptTemplate.java b/spring-ai-core/src/main/java/org/springframework/ai/core/prompt/UserPromptTemplate.java new file mode 100644 index 000000000..98d577478 --- /dev/null +++ b/spring-ai-core/src/main/java/org/springframework/ai/core/prompt/UserPromptTemplate.java @@ -0,0 +1,40 @@ +/* + * 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.core.prompt; + +import org.springframework.ai.core.prompt.messages.SystemMessage; +import org.springframework.ai.core.prompt.messages.UserMessage; + +import java.util.Map; + +public class UserPromptTemplate extends PromptTemplate { + + public UserPromptTemplate(String template) { + super(template); + } + + @Override + public Prompt create() { + return new Prompt(new SystemMessage(render())); + } + + @Override + public Prompt create(Map model) { + return new Prompt(new UserMessage(render(model))); + } + +} diff --git a/spring-ai-core/src/main/java/org/springframework/ai/core/prompt/messages/AbstractMessage.java b/spring-ai-core/src/main/java/org/springframework/ai/core/prompt/messages/AbstractMessage.java index d9bbbe427..1b2a49cec 100644 --- a/spring-ai-core/src/main/java/org/springframework/ai/core/prompt/messages/AbstractMessage.java +++ b/spring-ai-core/src/main/java/org/springframework/ai/core/prompt/messages/AbstractMessage.java @@ -21,11 +21,15 @@ import java.util.Map; public abstract class AbstractMessage implements Message { - private String content; + protected String content; - private Map properties = new HashMap<>(); + protected Map properties = new HashMap<>(); - private MessageType messageType; + protected MessageType messageType; + + protected AbstractMessage() { + + } protected AbstractMessage(MessageType messageType, String content) { this.messageType = messageType; diff --git a/spring-ai-core/src/main/java/org/springframework/ai/core/prompt/messages/ChatMessage.java b/spring-ai-core/src/main/java/org/springframework/ai/core/prompt/messages/ChatMessage.java index a2c12f042..528190fff 100644 --- a/spring-ai-core/src/main/java/org/springframework/ai/core/prompt/messages/ChatMessage.java +++ b/spring-ai-core/src/main/java/org/springframework/ai/core/prompt/messages/ChatMessage.java @@ -20,20 +20,23 @@ import java.util.Map; public class ChatMessage extends AbstractMessage { - private String role; - - public ChatMessage(String content, String role) { - super(MessageType.SYSTEM, content); - this.role = role; + public ChatMessage(String role, String content) { + this.messageType = MessageType.valueOf(role); + this.content = content; } - public ChatMessage(String content, String role, Map properties) { - super(MessageType.SYSTEM, content, properties); - this.role = role; + public ChatMessage(String role, String content, Map properties) { + this.messageType = MessageType.valueOf(role); + this.content = content; + this.properties = properties; } - public String getRole() { - return role; + public ChatMessage(MessageType messageType, String content) { + super(messageType, content); + } + + public ChatMessage(MessageType messageType, String content, Map properties) { + super(messageType, content, properties); } } 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 803a09bef..006aa93de 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 @@ -16,13 +16,13 @@ package org.springframework.ai.core.prompt; +import org.assertj.core.api.Assertions; +import org.junit.jupiter.api.Test; + import java.util.HashMap; import java.util.Map; import java.util.Set; -import org.assertj.core.api.Assertions; -import org.junit.jupiter.api.Test; - import static org.assertj.core.api.Assertions.assertThat; @SuppressWarnings("unchecked")