diff --git a/java/plugins/semantickernel-plugin-core/src/main/java/com/microsoft/semantickernel/plugins/TextPlugin.java b/java/plugins/semantickernel-plugin-core/src/main/java/com/microsoft/semantickernel/plugins/TextPlugin.java index 0d39f95cbe..69ddf6791e 100644 --- a/java/plugins/semantickernel-plugin-core/src/main/java/com/microsoft/semantickernel/plugins/TextPlugin.java +++ b/java/plugins/semantickernel-plugin-core/src/main/java/com/microsoft/semantickernel/plugins/TextPlugin.java @@ -44,4 +44,19 @@ public class TextPlugin { name = "input") String input) { return input.toLowerCase(Locale.ROOT); } + + + /// + /// Concatenate two strings into one + /// + /// First input to concatenate with + /// Second input to concatenate with + /// Concatenation result from both inputs. + @DefineKernelFunction(description = "Concat two strings into one.", name = "Concat") + public String concat( + @KernelFunctionParameter(description = "First input to concatenate with", name = "input") String input, + @KernelFunctionParameter(description = "Second input to concatenate with", name = "input2") String input2) { + return (input + input2); + } + } diff --git a/java/samples/sample-code/src/main/java/com/microsoft/semantickernel/samples/syntaxexamples/Example05_InlineFunctionDefinition.java b/java/samples/sample-code/src/main/java/com/microsoft/semantickernel/samples/syntaxexamples/Example05_InlineFunctionDefinition.java index 30284af396..d8dd049d6a 100644 --- a/java/samples/sample-code/src/main/java/com/microsoft/semantickernel/samples/syntaxexamples/Example05_InlineFunctionDefinition.java +++ b/java/samples/sample-code/src/main/java/com/microsoft/semantickernel/samples/syntaxexamples/Example05_InlineFunctionDefinition.java @@ -6,7 +6,6 @@ import com.azure.ai.openai.OpenAIAsyncClient; import com.azure.ai.openai.OpenAIClientBuilder; import com.azure.core.credential.AzureKeyCredential; import com.azure.core.credential.KeyCredential; -import com.microsoft.semantickernel.DefaultKernel; import com.microsoft.semantickernel.Kernel; import com.microsoft.semantickernel.exceptions.ConfigurationException; import com.microsoft.semantickernel.orchestration.PromptExecutionSettings; @@ -21,20 +20,21 @@ import java.time.format.DateTimeFormatter; public class Example05_InlineFunctionDefinition { - private static final boolean USE_AZURE_CLIENT = Boolean.parseBoolean( - System.getenv("USE_AZURE_CLIENT")); private static final String CLIENT_KEY = System.getenv("CLIENT_KEY"); + private static final String AZURE_CLIENT_KEY = System.getenv("AZURE_CLIENT_KEY"); - // Only required if USE_AZURE_CLIENT is true + // Only required if AZURE_CLIENT_KEY is set private static final String CLIENT_ENDPOINT = System.getenv("CLIENT_ENDPOINT"); + private static final String MODEL_ID = System.getenv() + .getOrDefault("MODEL_ID", "text-davinci-003"); public static void main(String[] args) throws ConfigurationException { OpenAIAsyncClient client; - if (USE_AZURE_CLIENT) { + if (AZURE_CLIENT_KEY != null) { client = new OpenAIClientBuilder() - .credential(new AzureKeyCredential(CLIENT_KEY)) + .credential(new AzureKeyCredential(AZURE_CLIENT_KEY)) .endpoint(CLIENT_ENDPOINT) .buildAsyncClient(); } else { @@ -45,7 +45,7 @@ public class Example05_InlineFunctionDefinition { TextGenerationService textGenerationService = TextGenerationService.builder() .withOpenAIAsyncClient(client) - .withModelId("text-davinci-003") + .withModelId(MODEL_ID) .build(); Kernel kernel = Kernel.builder() diff --git a/java/samples/sample-code/src/main/java/com/microsoft/semantickernel/samples/syntaxexamples/Example06_TemplateLanguage.java b/java/samples/sample-code/src/main/java/com/microsoft/semantickernel/samples/syntaxexamples/Example06_TemplateLanguage.java index 7aa50a5cda..79aa025f58 100644 --- a/java/samples/sample-code/src/main/java/com/microsoft/semantickernel/samples/syntaxexamples/Example06_TemplateLanguage.java +++ b/java/samples/sample-code/src/main/java/com/microsoft/semantickernel/samples/syntaxexamples/Example06_TemplateLanguage.java @@ -4,7 +4,6 @@ import com.azure.ai.openai.OpenAIAsyncClient; import com.azure.ai.openai.OpenAIClientBuilder; import com.azure.core.credential.AzureKeyCredential; import com.azure.core.credential.KeyCredential; -import com.microsoft.semantickernel.DefaultKernel; import com.microsoft.semantickernel.Kernel; import com.microsoft.semantickernel.aiservices.openai.textcompletion.OpenAITextGenerationService; import com.microsoft.semantickernel.exceptions.ConfigurationException; @@ -19,12 +18,13 @@ import com.microsoft.semantickernel.textcompletion.TextGenerationService; public class Example06_TemplateLanguage { - private static final boolean USE_AZURE_CLIENT = Boolean.parseBoolean( - System.getenv("USE_AZURE_CLIENT")); private static final String CLIENT_KEY = System.getenv("CLIENT_KEY"); + private static final String AZURE_CLIENT_KEY = System.getenv("AZURE_CLIENT_KEY"); - // Only required if USE_AZURE_CLIENT is true + // Only required if AZURE_CLIENT_KEY is set private static final String CLIENT_ENDPOINT = System.getenv("CLIENT_ENDPOINT"); + private static final String MODEL_ID = System.getenv() + .getOrDefault("MODEL_ID", "text-davinci-003"); public static class Time { @@ -45,9 +45,9 @@ public class Example06_TemplateLanguage { OpenAIAsyncClient client; - if (USE_AZURE_CLIENT) { + if (AZURE_CLIENT_KEY != null) { client = new OpenAIClientBuilder() - .credential(new AzureKeyCredential(CLIENT_KEY)) + .credential(new AzureKeyCredential(AZURE_CLIENT_KEY)) .endpoint(CLIENT_ENDPOINT) .buildAsyncClient(); } else { @@ -57,9 +57,9 @@ public class Example06_TemplateLanguage { } TextGenerationService textGenerationService = OpenAITextGenerationService.builder() - .withOpenAIAsyncClient(client) - .withModelId("text-davinci-003") - .build(); + .withOpenAIAsyncClient(client) + .withModelId(MODEL_ID) + .build(); Kernel kernel = Kernel.builder() .withDefaultAIService(TextGenerationService.class, textGenerationService) @@ -85,10 +85,8 @@ public class Example06_TemplateLanguage { // This allows to see the prompt before it's sent to OpenAI System.out.println("--- Rendered Prompt"); - var promptTemplateFactory = new KernelPromptTemplateFactory(); - - var promptTemplate = promptTemplateFactory.tryCreate( - new PromptTemplateConfig(functionDefinition)); + var promptTemplate = new KernelPromptTemplateFactory() + .tryCreate(new PromptTemplateConfig(functionDefinition)); var renderedPrompt = promptTemplate.renderAsync(kernel, null).block(); System.out.println(renderedPrompt); diff --git a/java/samples/sample-code/src/main/java/com/microsoft/semantickernel/samples/syntaxexamples/Example08_RetryHandler.java b/java/samples/sample-code/src/main/java/com/microsoft/semantickernel/samples/syntaxexamples/Example08_RetryHandler.java index 158e5bc54a..630c215181 100644 --- a/java/samples/sample-code/src/main/java/com/microsoft/semantickernel/samples/syntaxexamples/Example08_RetryHandler.java +++ b/java/samples/sample-code/src/main/java/com/microsoft/semantickernel/samples/syntaxexamples/Example08_RetryHandler.java @@ -14,7 +14,8 @@ import com.microsoft.semantickernel.textcompletion.TextGenerationService; import java.time.Duration; public class Example08_RetryHandler { - + private static final String MODEL_ID = System.getenv() + .getOrDefault("MODEL_ID", "text-davinci-003"); public static void main(String[] args) throws ConfigurationException { // Create a Kernel with the HttpClient RetryOptions retryOptions = new RetryOptions(new ExponentialBackoffOptions() @@ -30,7 +31,7 @@ public class Example08_RetryHandler { TextGenerationService textGenerationService = TextGenerationService.builder() .withOpenAIAsyncClient(client) - .withModelId("text-davinci-003") + .withModelId(MODEL_ID) .build(); Kernel kernel = Kernel.builder() diff --git a/java/samples/sample-code/src/main/java/com/microsoft/semantickernel/samples/syntaxexamples/Example09_FunctionTypes.java b/java/samples/sample-code/src/main/java/com/microsoft/semantickernel/samples/syntaxexamples/Example09_FunctionTypes.java index fa69a39119..94c74d2ffa 100644 --- a/java/samples/sample-code/src/main/java/com/microsoft/semantickernel/samples/syntaxexamples/Example09_FunctionTypes.java +++ b/java/samples/sample-code/src/main/java/com/microsoft/semantickernel/samples/syntaxexamples/Example09_FunctionTypes.java @@ -6,7 +6,6 @@ import com.azure.ai.openai.OpenAIAsyncClient; import com.azure.ai.openai.OpenAIClientBuilder; import com.azure.core.credential.AzureKeyCredential; import com.azure.core.credential.KeyCredential; -import com.microsoft.semantickernel.DefaultKernel; import com.microsoft.semantickernel.Kernel; import com.microsoft.semantickernel.aiservices.openai.textcompletion.OpenAITextGenerationService; import com.microsoft.semantickernel.orchestration.contextvariables.ContextVariable; @@ -29,12 +28,13 @@ import reactor.core.publisher.Mono; public class Example09_FunctionTypes { - private static final boolean USE_AZURE_CLIENT = Boolean.parseBoolean( - System.getenv("USE_AZURE_CLIENT")); private static final String CLIENT_KEY = System.getenv("CLIENT_KEY"); + private static final String AZURE_CLIENT_KEY = System.getenv("AZURE_CLIENT_KEY"); - // Only required if USE_AZURE_CLIENT is true + // Only required if AZURE_CLIENT_KEY is set private static final String CLIENT_ENDPOINT = System.getenv("CLIENT_ENDPOINT"); + private static final String MODEL_ID = System.getenv() + .getOrDefault("MODEL_ID", "text-davinci-003"); private static class DateTimeContextVariableTypeConverter extends @@ -72,21 +72,21 @@ public class Example09_FunctionTypes { OpenAIAsyncClient client; - if (USE_AZURE_CLIENT) { + if (AZURE_CLIENT_KEY != null) { client = new OpenAIClientBuilder() - .credential(new AzureKeyCredential(CLIENT_KEY)) - .endpoint(CLIENT_ENDPOINT) - .buildAsyncClient(); + .credential(new AzureKeyCredential(AZURE_CLIENT_KEY)) + .endpoint(CLIENT_ENDPOINT) + .buildAsyncClient(); } else { client = new OpenAIClientBuilder() - .credential(new KeyCredential(CLIENT_KEY)) - .buildAsyncClient(); + .credential(new KeyCredential(CLIENT_KEY)) + .buildAsyncClient(); } TextGenerationService textGenerationService = OpenAITextGenerationService.builder() - .withOpenAIAsyncClient(client) - .withModelId("text-davinci-003") - .build(); + .withOpenAIAsyncClient(client) + .withModelId(MODEL_ID) + .build(); // Load native plugin into the kernel function collection, sharing its functions with prompt templates KernelPlugin plugin = KernelPluginFactory diff --git a/java/samples/sample-code/src/main/java/com/microsoft/semantickernel/samples/syntaxexamples/Example10_DescribeAllPluginsAndFunctions.java b/java/samples/sample-code/src/main/java/com/microsoft/semantickernel/samples/syntaxexamples/Example10_DescribeAllPluginsAndFunctions.java index 5082c849f2..f3d0aa3b21 100644 --- a/java/samples/sample-code/src/main/java/com/microsoft/semantickernel/samples/syntaxexamples/Example10_DescribeAllPluginsAndFunctions.java +++ b/java/samples/sample-code/src/main/java/com/microsoft/semantickernel/samples/syntaxexamples/Example10_DescribeAllPluginsAndFunctions.java @@ -4,7 +4,6 @@ import com.azure.ai.openai.OpenAIAsyncClient; import com.azure.ai.openai.OpenAIClientBuilder; import com.azure.core.credential.AzureKeyCredential; import com.azure.core.credential.KeyCredential; -import com.microsoft.semantickernel.DefaultKernel; import com.microsoft.semantickernel.Kernel; import com.microsoft.semantickernel.aiservices.openai.textcompletion.OpenAITextGenerationService; import com.microsoft.semantickernel.orchestration.KernelFunction; @@ -21,12 +20,14 @@ import java.util.Locale; public class Example10_DescribeAllPluginsAndFunctions { - private static final boolean USE_AZURE_CLIENT = Boolean.parseBoolean( - System.getenv("USE_AZURE_CLIENT")); - private static final String CLIENT_KEY = System.getenv("CLIENT_KEY"); - // Only required if USE_AZURE_CLIENT is true + private static final String CLIENT_KEY = System.getenv("CLIENT_KEY"); + private static final String AZURE_CLIENT_KEY = System.getenv("AZURE_CLIENT_KEY"); + + // Only required if AZURE_CLIENT_KEY is set private static final String CLIENT_ENDPOINT = System.getenv("CLIENT_ENDPOINT"); + private static final String MODEL_ID = System.getenv() + .getOrDefault("MODEL_ID", "text-davinci-003"); public static class StaticTextPlugin { @@ -70,27 +71,25 @@ public class Example10_DescribeAllPluginsAndFunctions { /// See the end of the file for a sample of what the output looks like. /// public static void main(String[] args) { - TextGenerationService textGenerationService; + OpenAIAsyncClient client; - if (USE_AZURE_CLIENT) { - OpenAIAsyncClient client = new OpenAIClientBuilder() - .credential(new AzureKeyCredential(CLIENT_KEY)) + if (AZURE_CLIENT_KEY != null) { + client = new OpenAIClientBuilder() + .credential(new AzureKeyCredential(AZURE_CLIENT_KEY)) .endpoint(CLIENT_ENDPOINT) .buildAsyncClient(); - textGenerationService = OpenAITextGenerationService.builder() - .withOpenAIAsyncClient(client) - .withModelId("text-davinci-003") - .build(); } else { - OpenAIAsyncClient client = new OpenAIClientBuilder() + client = new OpenAIClientBuilder() .credential(new KeyCredential(CLIENT_KEY)) .buildAsyncClient(); - - // TODO: Add support for OpenAI API - textGenerationService = null; } + TextGenerationService textGenerationService = OpenAITextGenerationService.builder() + .withOpenAIAsyncClient(client) + .withModelId(MODEL_ID) + .build(); + Kernel kernel = Kernel.builder() .withDefaultAIService(TextGenerationService.class, textGenerationService) .build(); diff --git a/java/samples/sample-code/src/main/java/com/microsoft/semantickernel/samples/syntaxexamples/Example13_ConversationSummaryPlugin.java b/java/samples/sample-code/src/main/java/com/microsoft/semantickernel/samples/syntaxexamples/Example13_ConversationSummaryPlugin.java index d4d651995d..c9edd73ea7 100644 --- a/java/samples/sample-code/src/main/java/com/microsoft/semantickernel/samples/syntaxexamples/Example13_ConversationSummaryPlugin.java +++ b/java/samples/sample-code/src/main/java/com/microsoft/semantickernel/samples/syntaxexamples/Example13_ConversationSummaryPlugin.java @@ -5,7 +5,6 @@ import com.azure.ai.openai.OpenAIAsyncClient; import com.azure.ai.openai.OpenAIClientBuilder; import com.azure.core.credential.AzureKeyCredential; import com.azure.core.credential.KeyCredential; -import com.microsoft.semantickernel.DefaultKernel; import com.microsoft.semantickernel.Kernel; import com.microsoft.semantickernel.aiservices.openai.textcompletion.OpenAITextGenerationService; import com.microsoft.semantickernel.exceptions.ConfigurationException; @@ -26,12 +25,13 @@ import reactor.core.publisher.Mono; */ public class Example13_ConversationSummaryPlugin { - private static final boolean USE_AZURE_CLIENT = Boolean.parseBoolean( - System.getenv("USE_AZURE_CLIENT")); private static final String CLIENT_KEY = System.getenv("CLIENT_KEY"); + private static final String AZURE_CLIENT_KEY = System.getenv("AZURE_CLIENT_KEY"); - // Only required if USE_AZURE_CLIENT is true + // Only required if AZURE_CLIENT_KEY is set private static final String CLIENT_ENDPOINT = System.getenv("CLIENT_ENDPOINT"); + private static final String MODEL_ID = System.getenv() + .getOrDefault("MODEL_ID", "text-davinci-003"); private static final String ChatTranscript = @@ -219,27 +219,25 @@ public class Example13_ConversationSummaryPlugin { } private static Kernel initializeKernel() { - TextGenerationService textGenerationService; + OpenAIAsyncClient client; - if (USE_AZURE_CLIENT) { - OpenAIAsyncClient client = new OpenAIClientBuilder() - .credential(new AzureKeyCredential(CLIENT_KEY)) + if (AZURE_CLIENT_KEY != null) { + client = new OpenAIClientBuilder() + .credential(new AzureKeyCredential(AZURE_CLIENT_KEY)) .endpoint(CLIENT_ENDPOINT) .buildAsyncClient(); - textGenerationService = OpenAITextGenerationService.builder() - .withOpenAIAsyncClient(client) - .withModelId("text-davinci-003") - .build(); } else { - OpenAIAsyncClient client = new OpenAIClientBuilder() + client = new OpenAIClientBuilder() .credential(new KeyCredential(CLIENT_KEY)) .buildAsyncClient(); - - // TODO: Add support for OpenAI API - textGenerationService = null; } + TextGenerationService textGenerationService = OpenAITextGenerationService.builder() + .withOpenAIAsyncClient(client) + .withModelId(MODEL_ID) + .build(); + return Kernel.builder() .withDefaultAIService(TextGenerationService.class, textGenerationService) .build(); diff --git a/java/samples/sample-code/src/main/java/com/microsoft/semantickernel/samples/syntaxexamples/Example17_ChatGPT.java b/java/samples/sample-code/src/main/java/com/microsoft/semantickernel/samples/syntaxexamples/Example17_ChatGPT.java index 251d0114a6..8f9efb6fa1 100644 --- a/java/samples/sample-code/src/main/java/com/microsoft/semantickernel/samples/syntaxexamples/Example17_ChatGPT.java +++ b/java/samples/sample-code/src/main/java/com/microsoft/semantickernel/samples/syntaxexamples/Example17_ChatGPT.java @@ -10,33 +10,35 @@ import com.microsoft.semantickernel.chatcompletion.ChatHistory; public class Example17_ChatGPT { - private static final boolean USE_AZURE_CLIENT = Boolean.parseBoolean( - System.getenv("USE_AZURE_CLIENT")); private static final String CLIENT_KEY = System.getenv("CLIENT_KEY"); + private static final String AZURE_CLIENT_KEY = System.getenv("AZURE_CLIENT_KEY"); - // Only required if USE_AZURE_CLIENT is true + // Only required if AZURE_CLIENT_KEY is set private static final String CLIENT_ENDPOINT = System.getenv("CLIENT_ENDPOINT"); + private static final String MODEL_ID = System.getenv() + .getOrDefault("MODEL_ID", "gpt-35-turbo"); public static void main(String[] args) { System.out.println("======== Open AI - ChatGPT ========"); OpenAIAsyncClient client; - if (USE_AZURE_CLIENT) { + if (AZURE_CLIENT_KEY != null) { client = new OpenAIClientBuilder() - .credential(new AzureKeyCredential(CLIENT_KEY)) - .endpoint(CLIENT_ENDPOINT) - .buildAsyncClient(); + .credential(new AzureKeyCredential(AZURE_CLIENT_KEY)) + .endpoint(CLIENT_ENDPOINT) + .buildAsyncClient(); + } else { client = new OpenAIClientBuilder() - .credential(new KeyCredential(CLIENT_KEY)) - .buildAsyncClient(); + .credential(new KeyCredential(CLIENT_KEY)) + .buildAsyncClient(); } ChatCompletionService chatGPT = OpenAIChatCompletion.builder() - .withModelId("gpt-35-turbo") - .withOpenAIAsyncClient(client) - .build(); + .withModelId(MODEL_ID) + .withOpenAIAsyncClient(client) + .build(); System.out.println("Chat content:"); System.out.println("------------------------"); @@ -50,7 +52,8 @@ public class Example17_ChatGPT { GPTReply(chatGPT, chatHistory); messageOutput(chatHistory); - chatHistory.addUserMessage("I love history and philosophy, I'd like to learn something new about Greece, any suggestion"); + chatHistory.addUserMessage( + "I love history and philosophy, I'd like to learn something new about Greece, any suggestion"); messageOutput(chatHistory); GPTReply(chatGPT, chatHistory); @@ -65,7 +68,7 @@ public class Example17_ChatGPT { private static void GPTReply(ChatCompletionService chatGPT, ChatHistory chatHistory) { var reply = chatGPT.getChatMessageContentsAsync(chatHistory, null, null) - .block(); + .block(); StringBuilder message = new StringBuilder(); reply.forEach(chatMessageContent -> message.append(chatMessageContent.getContent())); diff --git a/java/samples/sample-code/src/main/java/com/microsoft/semantickernel/samples/syntaxexamples/Example26_AADAuth.java b/java/samples/sample-code/src/main/java/com/microsoft/semantickernel/samples/syntaxexamples/Example26_AADAuth.java index 9f973fe004..4dcb26eea2 100644 --- a/java/samples/sample-code/src/main/java/com/microsoft/semantickernel/samples/syntaxexamples/Example26_AADAuth.java +++ b/java/samples/sample-code/src/main/java/com/microsoft/semantickernel/samples/syntaxexamples/Example26_AADAuth.java @@ -12,6 +12,8 @@ import com.microsoft.semantickernel.exceptions.ConfigurationException; public class Example26_AADAuth { private static final String CLIENT_ENDPOINT = System.getenv("CLIENT_ENDPOINT"); + private static final String MODEL_ID = System.getenv() + .getOrDefault("MODEL_ID", "gpt-35-turbo"); public static void main(String[] args) throws ConfigurationException { @@ -25,7 +27,7 @@ public class Example26_AADAuth { ChatCompletionService chatService = ChatCompletionService.builder() .withOpenAIAsyncClient(client) - .withModelId("gpt-35-turbo-2") + .withModelId(MODEL_ID) .build(); Kernel kernel = Kernel.builder() diff --git a/java/samples/sample-code/src/main/java/com/microsoft/semantickernel/samples/syntaxexamples/Example27_PromptFunctionsUsingChatGPT.java b/java/samples/sample-code/src/main/java/com/microsoft/semantickernel/samples/syntaxexamples/Example27_PromptFunctionsUsingChatGPT.java index e330b58a76..50f3c7c58f 100644 --- a/java/samples/sample-code/src/main/java/com/microsoft/semantickernel/samples/syntaxexamples/Example27_PromptFunctionsUsingChatGPT.java +++ b/java/samples/sample-code/src/main/java/com/microsoft/semantickernel/samples/syntaxexamples/Example27_PromptFunctionsUsingChatGPT.java @@ -13,22 +13,24 @@ import com.microsoft.semantickernel.plugin.KernelFunctionFactory; public class Example27_PromptFunctionsUsingChatGPT { - private static final boolean USE_AZURE_CLIENT = Boolean.parseBoolean( - System.getenv("USE_AZURE_CLIENT")); private static final String CLIENT_KEY = System.getenv("CLIENT_KEY"); + private static final String AZURE_CLIENT_KEY = System.getenv("AZURE_CLIENT_KEY"); - // Only required if USE_AZURE_CLIENT is true + // Only required if AZURE_CLIENT_KEY is set private static final String CLIENT_ENDPOINT = System.getenv("CLIENT_ENDPOINT"); + private static final String MODEL_ID = System.getenv() + .getOrDefault("MODEL_ID", "gpt-35-turbo"); public static void main(String[] args) { OpenAIAsyncClient client; - if (USE_AZURE_CLIENT) { + if (AZURE_CLIENT_KEY != null) { client = new OpenAIClientBuilder() - .credential(new AzureKeyCredential(CLIENT_KEY)) + .credential(new AzureKeyCredential(AZURE_CLIENT_KEY)) .endpoint(CLIENT_ENDPOINT) .buildAsyncClient(); + } else { client = new OpenAIClientBuilder() .credential(new KeyCredential(CLIENT_KEY)) @@ -38,7 +40,7 @@ public class Example27_PromptFunctionsUsingChatGPT { ChatCompletionService openAIChatCompletion = ChatCompletionService.builder() .withOpenAIAsyncClient(client) - .withModelId("gpt-35-turbo-2") + .withModelId(MODEL_ID) .build(); Kernel kernel = Kernel.builder() diff --git a/java/samples/sample-code/src/main/java/com/microsoft/semantickernel/samples/syntaxexamples/Example33_StreamingChat.java b/java/samples/sample-code/src/main/java/com/microsoft/semantickernel/samples/syntaxexamples/Example33_StreamingChat.java index 943e53e5f5..d91f6ee622 100644 --- a/java/samples/sample-code/src/main/java/com/microsoft/semantickernel/samples/syntaxexamples/Example33_StreamingChat.java +++ b/java/samples/sample-code/src/main/java/com/microsoft/semantickernel/samples/syntaxexamples/Example33_StreamingChat.java @@ -13,31 +13,34 @@ import com.microsoft.semantickernel.chatcompletion.StreamingChatMessageContent; public class Example33_StreamingChat { - private static final boolean USE_AZURE_CLIENT = Boolean.parseBoolean( - System.getenv("USE_AZURE_CLIENT")); private static final String CLIENT_KEY = System.getenv("CLIENT_KEY"); + private static final String AZURE_CLIENT_KEY = System.getenv("AZURE_CLIENT_KEY"); - // Only required if USE_AZURE_CLIENT is true + // Only required if AZURE_CLIENT_KEY is set private static final String CLIENT_ENDPOINT = System.getenv("CLIENT_ENDPOINT"); + private static final String MODEL_ID = System.getenv() + .getOrDefault("MODEL_ID", "gpt-35-turbo"); + public static void main(String[] args) { System.out.println("======== Open AI - ChatGPT Streaming ========"); OpenAIAsyncClient client; - if (USE_AZURE_CLIENT) { + if (AZURE_CLIENT_KEY != null) { client = new OpenAIClientBuilder() - .credential(new AzureKeyCredential(CLIENT_KEY)) - .endpoint(CLIENT_ENDPOINT) - .buildAsyncClient(); + .credential(new AzureKeyCredential(AZURE_CLIENT_KEY)) + .endpoint(CLIENT_ENDPOINT) + .buildAsyncClient(); + } else { client = new OpenAIClientBuilder() - .credential(new KeyCredential(CLIENT_KEY)) - .buildAsyncClient(); + .credential(new KeyCredential(CLIENT_KEY)) + .buildAsyncClient(); } ChatCompletionService chatGPT = OpenAIChatCompletion.builder() - .withModelId("gpt-35-turbo") + .withModelId(MODEL_ID) .withOpenAIAsyncClient(client) .build(); diff --git a/java/samples/sample-code/src/main/java/com/microsoft/semantickernel/samples/syntaxexamples/Example55_TextChunker.java b/java/samples/sample-code/src/main/java/com/microsoft/semantickernel/samples/syntaxexamples/Example55_TextChunker.java new file mode 100644 index 0000000000..e2c0cec75b --- /dev/null +++ b/java/samples/sample-code/src/main/java/com/microsoft/semantickernel/samples/syntaxexamples/Example55_TextChunker.java @@ -0,0 +1,54 @@ +package com.microsoft.semantickernel.samples.syntaxexamples; + +import com.microsoft.semantickernel.text.TextChunker; +import java.util.List; + +public class Example55_TextChunker { + + private static final String text = """ + The city of Venice, located in the northeastern part of Italy, + is renowned for its unique geographical features. Built on more than 100 small islands in a lagoon in the + Adriatic Sea, it has no roads, just canals including the Grand Canal thoroughfare lined with Renaissance and + Gothic palaces. The central square, Piazza San Marco, contains St. Mark's Basilica, which is tiled with Byzantine + mosaics, and the Campanile bell tower offering views of the city's red roofs. + + The Amazon Rainforest, also known as Amazonia, is a moist broadleaf tropical rainforest in the Amazon biome that + covers most of the Amazon basin of South America. This basin encompasses 7 million square kilometers, of which + 5.5 million square kilometers are covered by the rainforest. This region includes territory belonging to nine nations + and 3.4 million square kilometers of uncontacted tribes. The Amazon represents over half of the planet's remaining + rainforests and comprises the largest and most biodiverse tract of tropical rainforest in the world. + + The Great Barrier Reef is the world's largest coral reef system composed of over 2,900 individual reefs and 900 islands + stretching for over 2,300 kilometers over an area of approximately 344,400 square kilometers. The reef is located in the + Coral Sea, off the coast of Queensland, Australia. The Great Barrier Reef can be seen from outer space and is the world's + biggest single structure made by living organisms. This reef structure is composed of and built by billions of tiny organisms, + known as coral polyps. + """.stripIndent(); + + public static void main(String[] args) { + RunExample(); + + // TODO: This example should also demo using token counters, not supported in Java atm + } + + + private static void RunExample() { + System.out.println("=== Text chunking ==="); + + var lines = TextChunker.splitPlainTextLines(text, 40); + var paragraphs = TextChunker.splitPlainTextParagraphs(lines, 120); + + writeParagraphsToConsole(paragraphs); + } + + private static void writeParagraphsToConsole(List paragraphs) { + for (var i = 0; i < paragraphs.size(); i++) { + System.out.println(paragraphs.get(i)); + + if (i < paragraphs.size() - 1) { + System.out.println("------------------------"); + } + } + } + +} diff --git a/java/samples/sample-code/src/main/java/com/microsoft/semantickernel/samples/syntaxexamples/Example56_TemplateMethodFunctionsWithMultipleArguments.java b/java/samples/sample-code/src/main/java/com/microsoft/semantickernel/samples/syntaxexamples/Example56_TemplateMethodFunctionsWithMultipleArguments.java new file mode 100644 index 0000000000..d1c92248d3 --- /dev/null +++ b/java/samples/sample-code/src/main/java/com/microsoft/semantickernel/samples/syntaxexamples/Example56_TemplateMethodFunctionsWithMultipleArguments.java @@ -0,0 +1,91 @@ +package com.microsoft.semantickernel.samples.syntaxexamples; + +import com.azure.ai.openai.OpenAIAsyncClient; +import com.azure.ai.openai.OpenAIClientBuilder; +import com.azure.core.credential.AzureKeyCredential; +import com.azure.core.credential.KeyCredential; +import com.microsoft.semantickernel.Kernel; +import com.microsoft.semantickernel.aiservices.openai.chatcompletion.OpenAIChatCompletion; +import com.microsoft.semantickernel.chatcompletion.ChatCompletionService; +import com.microsoft.semantickernel.orchestration.PromptExecutionSettings; +import com.microsoft.semantickernel.orchestration.contextvariables.KernelArguments; +import com.microsoft.semantickernel.plugin.KernelPluginFactory; +import com.microsoft.semantickernel.samples.plugins.text.TextPlugin; +import com.microsoft.semantickernel.semanticfunctions.KernelFunctionFromPrompt; +import com.microsoft.semantickernel.semanticfunctions.KernelPromptTemplateFactory; +import com.microsoft.semantickernel.semanticfunctions.PromptTemplateConfig; + +public class Example56_TemplateMethodFunctionsWithMultipleArguments { + + private static final String CLIENT_KEY = System.getenv("CLIENT_KEY"); + private static final String AZURE_CLIENT_KEY = System.getenv("AZURE_CLIENT_KEY"); + + // Only required if AZURE_CLIENT_KEY is set + private static final String CLIENT_ENDPOINT = System.getenv("CLIENT_ENDPOINT"); + private static final String MODEL_ID = System.getenv() + .getOrDefault("MODEL_ID", "gpt-35-turbo"); + + public static void main(String[] args) { + + OpenAIAsyncClient client; + + if (AZURE_CLIENT_KEY != null) { + client = new OpenAIClientBuilder() + .credential(new AzureKeyCredential(AZURE_CLIENT_KEY)) + .endpoint(CLIENT_ENDPOINT) + .buildAsyncClient(); + + } else { + client = new OpenAIClientBuilder() + .credential(new KeyCredential(CLIENT_KEY)) + .buildAsyncClient(); + } + + ChatCompletionService openAIChatCompletion = OpenAIChatCompletion.builder() + .withModelId(MODEL_ID) + .withOpenAIAsyncClient(client) + .build(); + + Kernel kernel = Kernel.builder() + .withDefaultAIService(ChatCompletionService.class, openAIChatCompletion) + .build(); + + System.out.println("======== TemplateMethodFunctionsWithMultipleArguments ========"); + + var arguments = KernelArguments.builder() + .withVariable("word2", " Potter") + .build(); + + // Load native plugin into the kernel function collection, sharing its functions with prompt templates + // Functions loaded here are available as "text.*" + kernel.getPlugins().add(KernelPluginFactory.createFromObject(new TextPlugin(), "text")); + + // Prompt Function invoking text.Concat method function with named arguments input and input2 where input is a string and input2 is set to a variable from context called word2. + String functionDefinition = "Write a haiku about the following: {{text.Concat input='Harry' input2=$word2}}"; + + // This allows to see the prompt before it's sent to OpenAI + System.out.println("--- Rendered Prompt"); + var promptTemplateFactory = new KernelPromptTemplateFactory(); + var promptTemplate = promptTemplateFactory.tryCreate( + new PromptTemplateConfig(functionDefinition)); + var renderedPrompt = promptTemplate.renderAsync(kernel, arguments).block(); + System.out.println(renderedPrompt); + + // Run the prompt / prompt function + + var haiku = KernelFunctionFromPrompt + .builder() + .withTemplate(functionDefinition) + .withDefaultExecutionSettings( + PromptExecutionSettings.builder() + .withMaxTokens(100) + .build()) + .build(); + + // Show the result + System.out.println("--- Prompt Function result"); + var result = kernel.invokeAsync(haiku, arguments, String.class).block(); + System.out.println(result.getValue()); + } + +} diff --git a/java/samples/sample-code/src/main/java/com/microsoft/semantickernel/samples/syntaxexamples/Example58_ConfigureExecutionSettings.java b/java/samples/sample-code/src/main/java/com/microsoft/semantickernel/samples/syntaxexamples/Example58_ConfigureExecutionSettings.java new file mode 100644 index 0000000000..fdc9d0d9a8 --- /dev/null +++ b/java/samples/sample-code/src/main/java/com/microsoft/semantickernel/samples/syntaxexamples/Example58_ConfigureExecutionSettings.java @@ -0,0 +1,99 @@ +package com.microsoft.semantickernel.samples.syntaxexamples; + +import com.azure.ai.openai.OpenAIAsyncClient; +import com.azure.ai.openai.OpenAIClientBuilder; +import com.azure.core.credential.AzureKeyCredential; +import com.azure.core.credential.KeyCredential; +import com.microsoft.semantickernel.Kernel; +import com.microsoft.semantickernel.aiservices.openai.chatcompletion.OpenAIChatCompletion; +import com.microsoft.semantickernel.chatcompletion.ChatCompletionService; +import com.microsoft.semantickernel.orchestration.PromptExecutionSettings; +import com.microsoft.semantickernel.plugin.KernelFunctionFactory; +import com.microsoft.semantickernel.semanticfunctions.KernelFunctionFromPrompt; +import com.microsoft.semantickernel.semanticfunctions.PromptTemplateConfig; + +public class Example58_ConfigureExecutionSettings { + + private static final String CLIENT_KEY = System.getenv("CLIENT_KEY"); + private static final String AZURE_CLIENT_KEY = System.getenv("AZURE_CLIENT_KEY"); + + // Only required if AZURE_CLIENT_KEY is set + private static final String CLIENT_ENDPOINT = System.getenv("CLIENT_ENDPOINT"); + private static final String MODEL_ID = System.getenv() + .getOrDefault("MODEL_ID", "gpt-35-turbo"); + + public static void main(String[] args) { + System.out.println("======== Example58_ConfigureExecutionSettings ========"); + + OpenAIAsyncClient client; + + if (AZURE_CLIENT_KEY != null) { + client = new OpenAIClientBuilder() + .credential(new AzureKeyCredential(AZURE_CLIENT_KEY)) + .endpoint(CLIENT_ENDPOINT) + .buildAsyncClient(); + + } else { + client = new OpenAIClientBuilder() + .credential(new KeyCredential(CLIENT_KEY)) + .buildAsyncClient(); + } + + ChatCompletionService openAIChatCompletion = OpenAIChatCompletion.builder() + .withModelId(MODEL_ID) + .withOpenAIAsyncClient(client) + .build(); + + System.out.println("======== Using Chat GPT model for text generation ========"); + + var kernel = Kernel.builder() + .withDefaultAIService(ChatCompletionService.class, openAIChatCompletion) + .build(); + + var prompt = "Hello AI, what can you do for me?"; + + // Option 1: + // Invoke the prompt function and pass an OpenAI specific instance containing the execution settings + var result = kernel.invokeAsync( + KernelFunctionFromPrompt.builder() + .withTemplate(prompt) + .withDefaultExecutionSettings( + PromptExecutionSettings.builder() + .withMaxTokens(60) + .withTemperature(0.7) + .build() + ).build(), + null, + String.class + ).block(); + + System.out.println(result.getValue()); + + // Option 2: + // Load prompt template configuration including the execution settings from a JSON payload + // Create the prompt functions using the prompt template and the configuration (loaded in the previous step) + // Invoke the prompt function using the implicitly set execution settings + String configPayload = """ + { + "schema": 1, + "name": "HelloAI", + "description": "Say hello to an AI", + "type": "completion", + "completion": { + "max_tokens": 256, + "temperature": 0.5, + "top_p": 0.0, + "presence_penalty": 0.0, + "frequency_penalty": 0.0 + } + }""".stripIndent(); + + var promptConfig = PromptTemplateConfig.parseFromJson(configPayload); + promptConfig.setTemplate(prompt); + + var func = KernelFunctionFactory.createFromPrompt(promptConfig, null); + + result = kernel.invokeAsync(func, null, String.class).block(); + System.out.println(result.getValue()); + } +} diff --git a/java/samples/sample-code/src/main/java/com/microsoft/semantickernel/samples/syntaxexamples/Example64_MultiplePromptTemplates.java b/java/samples/sample-code/src/main/java/com/microsoft/semantickernel/samples/syntaxexamples/Example64_MultiplePromptTemplates.java index d9f23179ca..dfc162c4be 100644 --- a/java/samples/sample-code/src/main/java/com/microsoft/semantickernel/samples/syntaxexamples/Example64_MultiplePromptTemplates.java +++ b/java/samples/sample-code/src/main/java/com/microsoft/semantickernel/samples/syntaxexamples/Example64_MultiplePromptTemplates.java @@ -14,70 +14,73 @@ import com.microsoft.semantickernel.semanticfunctions.HandlebarsPromptTemplateFa import com.microsoft.semantickernel.semanticfunctions.KernelFunctionFromPrompt; import com.microsoft.semantickernel.semanticfunctions.KernelPromptTemplateFactory; import com.microsoft.semantickernel.semanticfunctions.PromptTemplateFactory; - import java.util.List; public class Example64_MultiplePromptTemplates { - private static final boolean USE_AZURE_CLIENT = Boolean.parseBoolean( - System.getenv("USE_AZURE_CLIENT")); private static final String CLIENT_KEY = System.getenv("CLIENT_KEY"); + private static final String AZURE_CLIENT_KEY = System.getenv("AZURE_CLIENT_KEY"); - // Only required if USE_AZURE_CLIENT is true + // Only required if AZURE_CLIENT_KEY is set private static final String CLIENT_ENDPOINT = System.getenv("CLIENT_ENDPOINT"); + private static final String MODEL_ID = System.getenv() + .getOrDefault("MODEL_ID", "gpt-35-turbo"); + public static void main(String[] args) { System.out.println("======== Example64_MultiplePromptTemplates ========"); OpenAIAsyncClient client; - if (USE_AZURE_CLIENT) { + if (AZURE_CLIENT_KEY != null) { client = new OpenAIClientBuilder() - .credential(new AzureKeyCredential(CLIENT_KEY)) - .endpoint(CLIENT_ENDPOINT) - .buildAsyncClient(); + .credential(new AzureKeyCredential(AZURE_CLIENT_KEY)) + .endpoint(CLIENT_ENDPOINT) + .buildAsyncClient(); + } else { client = new OpenAIClientBuilder() - .credential(new KeyCredential(CLIENT_KEY)) - .buildAsyncClient(); + .credential(new KeyCredential(CLIENT_KEY)) + .buildAsyncClient(); } - ChatCompletionService chatCompletion = OpenAIChatCompletion.builder() - .withModelId("gpt-35-turbo") - .withOpenAIAsyncClient(client) - .build(); + ChatCompletionService openAIChatCompletion = OpenAIChatCompletion.builder() + .withModelId(MODEL_ID) + .withOpenAIAsyncClient(client) + .build(); var kernel = new DefaultKernel.Builder() - .withDefaultAIService(ChatCompletionService.class, chatCompletion) - .build(); + .withDefaultAIService(ChatCompletionService.class, openAIChatCompletion) + .build(); var templateFactory = new AggregatorPromptTemplateFactory( - List.of( - new KernelPromptTemplateFactory(), - new HandlebarsPromptTemplateFactory() - ) + List.of( + new KernelPromptTemplateFactory(), + new HandlebarsPromptTemplateFactory() + ) ); runPrompt(kernel, - "semantic-kernel", - "Hello AI, my name is {{$name}}. What is the origin of my name?", - templateFactory); + "semantic-kernel", + "Hello AI, my name is {{$name}}. What is the origin of my name?", + templateFactory); runPrompt(kernel, - "handlebars", - "Hello AI, my name is {{name}}. What is the origin of my name?", - templateFactory); + "handlebars", + "Hello AI, my name is {{name}}. What is the origin of my name?", + templateFactory); } - public static void runPrompt (Kernel kernel, String templateFormat, String prompt, PromptTemplateFactory templateFactory) { + public static void runPrompt(Kernel kernel, String templateFormat, String prompt, + PromptTemplateFactory templateFactory) { var function = new KernelFunctionFromPrompt.Builder() - .withTemplate(prompt) - .withTemplateFormat(templateFormat) - .withPromptTemplateFactory(templateFactory) - .build(); + .withTemplate(prompt) + .withTemplateFormat(templateFormat) + .withPromptTemplateFactory(templateFactory) + .build(); var arguments = KernelArguments.builder() - .withVariable("name", "Bob") - .build(); + .withVariable("name", "Bob") + .build(); var result = kernel.invokeAsync(function, arguments, String.class).block(); System.out.println(result.getValue()); diff --git a/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/plugin/KernelFunctionFactory.java b/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/plugin/KernelFunctionFactory.java index 135409a36c..b80314c547 100644 --- a/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/plugin/KernelFunctionFactory.java +++ b/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/plugin/KernelFunctionFactory.java @@ -4,6 +4,9 @@ import com.microsoft.semantickernel.orchestration.KernelFunction; import com.microsoft.semantickernel.orchestration.KernelFunctionFromMethod; import com.microsoft.semantickernel.orchestration.PromptExecutionSettings; import com.microsoft.semantickernel.semanticfunctions.KernelFunctionFromPrompt; +import com.microsoft.semantickernel.semanticfunctions.KernelPromptTemplateFactory; +import com.microsoft.semantickernel.semanticfunctions.PromptTemplate; +import com.microsoft.semantickernel.semanticfunctions.PromptTemplateConfig; import com.microsoft.semantickernel.semanticfunctions.PromptTemplateFactory; import java.lang.reflect.Method; import java.util.HashMap; @@ -103,6 +106,26 @@ public class KernelFunctionFactory { promptTemplateFactory); } + public static KernelFunction createFromPrompt( + PromptTemplateConfig promptConfig, + @Nullable PromptTemplateFactory promptTemplateFactory) { + + if (promptTemplateFactory == null) { + promptTemplateFactory = new KernelPromptTemplateFactory(); + } + + return KernelFunctionFactory.create(promptTemplateFactory.tryCreate(promptConfig), + promptConfig); + } + + public static KernelFunction create( + PromptTemplate promptTemplate, + PromptTemplateConfig promptConfig) { + return new KernelFunctionFromPrompt( + promptTemplate, + promptConfig); + } + private static Map createSettingsDictionary( PromptExecutionSettings executionSettings) { HashMap map = new HashMap<>(); diff --git a/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/plugin/KernelPluginFactory.java b/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/plugin/KernelPluginFactory.java index d4d0efd632..6d7ce3190b 100644 --- a/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/plugin/KernelPluginFactory.java +++ b/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/plugin/KernelPluginFactory.java @@ -280,9 +280,9 @@ public class KernelPluginFactory { try { String config = getFileContents(configFileName, clazz); - return new ObjectMapper().readValue(config, PromptTemplateConfig.class); - } catch (IOException e) { - if (e instanceof JsonMappingException) { + return PromptTemplateConfig.parseFromJson(config); + } catch (Exception e) { + if (e instanceof SKException) { LOGGER.error("Failed to parse config file " + configFileName, e); throw new SKException("Failed to parse config file " + configFileName, e); diff --git a/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/semanticfunctions/KernelPromptTemplateFactory.java b/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/semanticfunctions/KernelPromptTemplateFactory.java index 9ac3a195c8..93fb1d17aa 100644 --- a/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/semanticfunctions/KernelPromptTemplateFactory.java +++ b/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/semanticfunctions/KernelPromptTemplateFactory.java @@ -1,21 +1,26 @@ package com.microsoft.semantickernel.semanticfunctions; +import static com.microsoft.semantickernel.semanticfunctions.HandlebarsPromptTemplateFactory.HANDLEBARS_TEMPLATE_FORMAT; +import static com.microsoft.semantickernel.semanticfunctions.PromptTemplateConfig.SEMANTIC_KERNEL_TEMPLATE_FORMAT; + import com.microsoft.semantickernel.templateengine.handlebars.HandlebarsPromptTemplate; import com.microsoft.semantickernel.templateengine.semantickernel.DefaultPromptTemplate; -import reactor.util.annotation.NonNull; - import java.util.Locale; -import static com.microsoft.semantickernel.semanticfunctions.PromptTemplateConfig.SEMANTIC_KERNEL_TEMPLATE_FORMAT; - public class KernelPromptTemplateFactory implements PromptTemplateFactory { - public PromptTemplate tryCreate(@NonNull PromptTemplateConfig templateConfig) { - if (templateConfig.getTemplateFormat() != null && - SEMANTIC_KERNEL_TEMPLATE_FORMAT.equals(templateConfig.getTemplateFormat().toLowerCase(Locale.ROOT))) { + public PromptTemplate tryCreate(PromptTemplateConfig templateConfig) { + if (templateConfig == null || templateConfig.getTemplateFormat() == null) { return new DefaultPromptTemplate(templateConfig); } - throw new UnknownTemplateFormatException(templateConfig.getTemplateFormat()); + switch (templateConfig.getTemplateFormat().toLowerCase(Locale.ROOT)) { + case SEMANTIC_KERNEL_TEMPLATE_FORMAT: + return new DefaultPromptTemplate(templateConfig); + case HANDLEBARS_TEMPLATE_FORMAT: + return new HandlebarsPromptTemplate(templateConfig); + default: + throw new UnknownTemplateFormatException(templateConfig.getTemplateFormat()); + } } } diff --git a/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/semanticfunctions/PromptTemplateConfig.java b/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/semanticfunctions/PromptTemplateConfig.java index 97c4af832f..5e8fdcaf9d 100644 --- a/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/semanticfunctions/PromptTemplateConfig.java +++ b/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/semanticfunctions/PromptTemplateConfig.java @@ -2,7 +2,11 @@ package com.microsoft.semantickernel.semanticfunctions; import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.microsoft.semantickernel.exceptions.SKException; import com.microsoft.semantickernel.orchestration.PromptExecutionSettings; import com.microsoft.semantickernel.plugin.KernelParameterMetadata; import com.microsoft.semantickernel.plugin.KernelReturnParameterMetadata; @@ -13,6 +17,7 @@ import java.util.List; import java.util.Map; import java.util.stream.Collectors; +@JsonIgnoreProperties(ignoreUnknown = true) public class PromptTemplateConfig { public static final int CURRENT_SCHEMA = 1; @@ -271,4 +276,12 @@ public class PromptTemplateConfig { this.executionSettings = executionSettings; } + public static PromptTemplateConfig parseFromJson(String json) throws SKException { + try { + return new ObjectMapper().readValue(json, PromptTemplateConfig.class); + } catch (JsonProcessingException e) { + throw new SKException("Unable to parse prompt template config", e); + } + } + } diff --git a/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/templateengine/semantickernel/TemplateTokenizer.java b/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/templateengine/semantickernel/TemplateTokenizer.java index 5d0e99653c..10baf9606f 100644 --- a/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/templateengine/semantickernel/TemplateTokenizer.java +++ b/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/templateengine/semantickernel/TemplateTokenizer.java @@ -167,13 +167,6 @@ public class TemplateTokenizer { break; case FunctionId: - if (codeBlocks.size() > 2) { - throw new TemplateException( - TemplateException.ErrorCodes.SYNTAX_ERROR, - "Functions support only one parameter: " - + contentWithoutDelimiters); - } - blocks.add(new CodeBlock(codeBlocks, contentWithoutDelimiters)); break; diff --git a/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/templateengine/semantickernel/blocks/CodeBlock.java b/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/templateengine/semantickernel/blocks/CodeBlock.java index c4d6826722..51f4acaab8 100644 --- a/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/templateengine/semantickernel/blocks/CodeBlock.java +++ b/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/templateengine/semantickernel/blocks/CodeBlock.java @@ -37,27 +37,44 @@ public final class CodeBlock extends Block implements CodeRendering { return false; } - if (this.tokens.size() > 1) { - if (this.tokens.get(0).getType() != BlockTypes.FunctionId) { - LOGGER.error("Unexpected second token found: " + this.tokens.get(1).getContent()); - return false; - } - - if (this.tokens.get(1).getType() != BlockTypes.Value - && this.tokens.get(1).getType() != BlockTypes.Variable) { - LOGGER.error("Functions support only one parameter"); - return false; - } + if (this.tokens.size() > 0 && this.tokens.get(0).getType() == BlockTypes.NamedArg) { + LOGGER.error("Unexpected named argument found. Expected function name first."); + return false; } - if (this.tokens.size() > 2) { - LOGGER.error("Unexpected second token found: " + this.tokens.get(1).getContent()); + if (this.tokens.size() > 1 && !this.isValidFunctionCall()) { return false; } return true; } + private boolean isValidFunctionCall() { + if (this.tokens.get(0).getType() != BlockTypes.FunctionId) { + LOGGER.error("Unexpected second token found: " + tokens.get(1).getContent()); + return false; + } + + if (this.tokens.get(1).getType() != BlockTypes.Value && + this.tokens.get(1).getType() != BlockTypes.Variable && + this.tokens.get(1).getType() != BlockTypes.NamedArg) { + LOGGER.error( + "The first arg of a function must be a quoted string, variable or named argument"); + return false; + } + + for (int i = 2; i < this.tokens.size(); i++) { + if (this.tokens.get(i).getType() != BlockTypes.NamedArg) { + LOGGER.error( + "Functions only support named arguments after the first argument. Argument " + i + + " is not named."); + return false; + } + } + + return true; + } + @Override public Mono renderCodeAsync(Kernel kernel, @Nullable KernelArguments arguments) { if (!this.isValid()) { diff --git a/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/templateengine/semantickernel/blocks/NamedArgBlock.java b/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/templateengine/semantickernel/blocks/NamedArgBlock.java index 86fbee71ef..4b914b9e2c 100644 --- a/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/templateengine/semantickernel/blocks/NamedArgBlock.java +++ b/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/templateengine/semantickernel/blocks/NamedArgBlock.java @@ -2,13 +2,16 @@ package com.microsoft.semantickernel.templateengine.semantickernel.blocks; import static com.microsoft.semantickernel.templateengine.semantickernel.blocks.BlockTypes.NamedArg; -import com.microsoft.semantickernel.Todo; import com.microsoft.semantickernel.Verify; import com.microsoft.semantickernel.orchestration.contextvariables.KernelArguments; import javax.annotation.Nullable; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; public class NamedArgBlock extends Block implements TextRendering { + private static final Logger LOGGER = LoggerFactory.getLogger(NamedArgBlock.class); + private final String name; private final String value; private final VarBlock argNameAsVarBlock; @@ -49,7 +52,24 @@ public class NamedArgBlock extends Block implements TextRendering { @Override public boolean isValid() { - throw new Todo(); + if (Verify.isNullOrEmpty(this.name)) { + LOGGER.error("A named argument must have a name"); + return false; + } + + if (this.valBlock != null && !this.valBlock.isValid()) { + LOGGER.error("There was an issue with the named argument value for '" + name); + return false; + } else if (this.varBlock != null && !this.varBlock.isValid()) { + LOGGER.error("There was an issue with the named argument value for '" + name); + return false; + } else if (this.valBlock == null && this.varBlock == null) { + LOGGER.error("A named argument must have a value"); + return false; + } + + // Argument names share the same validation as variables + return this.argNameAsVarBlock.isValid(); } @Nullable