From 2989d99a4ac82ca2c6cd75e2d78fc0a53eddc22a Mon Sep 17 00:00:00 2001 From: Milder Hernandez Cagua Date: Mon, 18 Mar 2024 02:18:58 -0700 Subject: [PATCH 01/15] Add Prompts learn documentation --- .../samples/documentation/Prompts.java | 237 ++++++++++++++++++ .../com/microsoft/semantickernel/Kernel.java | 13 + 2 files changed, 250 insertions(+) create mode 100644 java/samples/sample-code/src/main/java/com/microsoft/semantickernel/samples/documentation/Prompts.java diff --git a/java/samples/sample-code/src/main/java/com/microsoft/semantickernel/samples/documentation/Prompts.java b/java/samples/sample-code/src/main/java/com/microsoft/semantickernel/samples/documentation/Prompts.java new file mode 100644 index 000000000000..448bb0847c10 --- /dev/null +++ b/java/samples/sample-code/src/main/java/com/microsoft/semantickernel/samples/documentation/Prompts.java @@ -0,0 +1,237 @@ +package com.microsoft.semantickernel.samples.documentation; + +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.services.chatcompletion.ChatCompletionService; + +import java.util.Scanner; + +public class Prompts { + 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-2"); + + public static void main(String[] args) { + System.out.println("======== Prompts ========"); + 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(); + } + + // + Kernel kernel = Kernel.builder() + .withAIService(ChatCompletionService.class, ChatCompletionService.builder() + .withModelId(MODEL_ID) + .withOpenAIAsyncClient(client) + .build()) + .build(); + // + + // 0.0 Initial prompt + String request = "I want to send an email to the marketing team celebrating their recent milestone."; + String prompt = """ + What is the intent of this request? %s + """.formatted(request); + + /* Uncomment this block to make this example interactive + // + System.out.println("Your request: "); + String request = new Scanner(System.in).nextLine(); + String prompt = """ + What is the intent of this request? %s + You can choose between SendEmail, SendMessage, CompleteTask, CreateDocument. + """.formatted(request); + // + */ + + System.out.println("0.0 Initial prompt"); + System.out.println(kernel.invokePromptAsync(prompt).block().getResult()); + + // 1.0 Make the prompt more specific + ///////////////////////////////////////////////////////////////// + // + prompt = """ + What is the intent of this request? %s + You can choose between SendEmail, SendMessage, CompleteTask, CreateDocument. + """.formatted(request); + // + System.out.println("1.0 Make the prompt more specific"); + System.out.println(kernel.invokePromptAsync(prompt).block().getResult()); + + // 2.0 Add structure to the output with formatting + ///////////////////////////////////////////////////////////////// + // + prompt = """ + Instructions: What is the intent of this request? + Choices: SendEmail, SendMessage, CompleteTask, CreateDocument. + User Input: %s + Intent: + """.formatted(request); + // + System.out.println("2.0 Add structure to the output with formatting"); + System.out.println(kernel.invokePromptAsync(prompt).block().getResult()); + + // 2.1 Add structure to the output with formatting (using Markdown and JSON) + ///////////////////////////////////////////////////////////////// + // + prompt = """ + ## Instructions + Provide the intent of the request using the following format: + + ```json + { + "intent": {intent} + } + ``` + + ## Choices + You can choose between the following intents: + + ```json + ["SendEmail", "SendMessage", "CompleteTask", "CreateDocument"] + ``` + + ## User Input + The user input is: + + ```json + { + "request": "%s" + } + ``` + + ## Intent + """.formatted(request); + // + System.out.println("2.1 Add structure to the output with formatting (using Markdown and JSON)"); + System.out.println(kernel.invokePromptAsync(prompt).block().getResult()); + + // 3.0 Provide examples with few-shot prompting + ///////////////////////////////////////////////////////////////// + // + prompt = """ + Instructions: What is the intent of this request? + Choices: SendEmail, SendMessage, CompleteTask, CreateDocument. + + User Input: Can you send a very quick approval to the marketing team? + Intent: SendMessage + + User Input: Can you send the full update to the marketing team? + Intent: SendEmail + + User Input: %s + Intent: + """.formatted(request); + // + System.out.println("3.0 Provide examples with few-shot prompting"); + System.out.println(kernel.invokePromptAsync(prompt).block().getResult()); + + // 4.0 Tell the AI what to do to avoid doing something wrong + ///////////////////////////////////////////////////////////////// + // + prompt = """ + Instructions: What is the intent of this request? + If you don't know the intent, don't guess; instead respond with "Unknown". + Choices: SendEmail, SendMessage, CompleteTask, CreateDocument, Unknown. + + User Input: Can you send a very quick approval to the marketing team? + Intent: SendMessage + + User Input: Can you send the full update to the marketing team? + Intent: SendEmail + + User Input: %s + Intent: + """.formatted(request); + // + System.out.println("4.0 Tell the AI what to do to avoid doing something wrong"); + System.out.println(kernel.invokePromptAsync(prompt).block().getResult()); + + // 5.0 Provide context to the AI + ///////////////////////////////////////////////////////////////// + String history = "User input: I hate sending emails, no one ever reads them.\nAI response: I'm sorry to hear that. Messages may be a better way to communicate."; + // + prompt = """ + Instructions: What is the intent of this request? + If you don't know the intent, don't guess; instead respond with "Unknown". + Choices: SendEmail, SendMessage, CompleteTask, CreateDocument, Unknown. + + User Input: Can you send a very quick approval to the marketing team? + Intent: SendMessage + + User Input: Can you send the full update to the marketing team? + Intent: SendEmail + + %s + User Input: %s + Intent: + """.formatted(history, request); + // + System.out.println("5.0 Provide context to the AI"); + System.out.println(kernel.invokePromptAsync(prompt).block().getResult()); + + // 6.0 Using message roles in chat completion prompts + ///////////////////////////////////////////////////////////////// + history = "I hate sending emails, no one ever reads them.\nI'm sorry to hear that. Messages may be a better way to communicate."; + // + prompt = """ + Instructions: What is the intent of this request? + If you don't know the intent, don't guess; instead respond with "Unknown". + Choices: SendEmail, SendMessage, CompleteTask, CreateDocument, Unknown. + + Can you send a very quick approval to the marketing team? + Intent: + SendMessage + + Can you send the full update to the marketing team? + Intent: + SendEmail + + %s + %s + Intent: + """.formatted(history, request); + // + System.out.println("6.0 Using message roles in chat completion prompts"); + System.out.println(kernel.invokePromptAsync(prompt).block().getResult()); + + // 7.0 Give your AI words of encouragement + history = "I hate sending emails, no one ever reads them.\nI'm sorry to hear that. Messages may be a better way to communicate."; + // + prompt = """ + Instructions: What is the intent of this request? + If you don't know the intent, don't guess; instead respond with "Unknown". + Choices: SendEmail, SendMessage, CompleteTask, CreateDocument, Unknown. + Bonus: You'll get $20 if you get this right. + + Can you send a very quick approval to the marketing team? + Intent: + SendMessage + + Can you send the full update to the marketing team? + Intent: + SendEmail + + %s + %s + Intent: + """.formatted(history, request); + // + System.out.println("7.0 Give your AI words of encouragement"); + System.out.println(kernel.invokePromptAsync(prompt).block().getResult()); + } +} \ No newline at end of file diff --git a/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/Kernel.java b/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/Kernel.java index 3b385d30ac94..9095b7250c56 100644 --- a/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/Kernel.java +++ b/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/Kernel.java @@ -7,6 +7,7 @@ import java.util.List; import java.util.function.Function; +import javax.annotation.Nonnull; import javax.annotation.Nullable; import com.microsoft.semantickernel.builders.SemanticKernelBuilder; @@ -151,6 +152,18 @@ public FunctionResult invoke( return this.invokeAsync(pluginName, functionName).block(); } + /** + * Invokes a Prompt. + * + * @param The return type of the prompt. + * @param prompt The prompt to invoke. + * @return The result of the prompt invocation. + * @see KernelFunction#invokeAsync(Kernel) + */ + public FunctionInvocation invokePromptAsync(@Nonnull String prompt) { + return invokeAsync(KernelFunction.createFromPrompt(prompt).build()); + } + /** * Invokes a {@code KernelFunction}. * From 8710b91805c692335a385f23338c64133ac4fbb8 Mon Sep 17 00:00:00 2001 From: Milder Hernandez Cagua Date: Mon, 18 Mar 2024 23:33:25 -0700 Subject: [PATCH 02/15] Update folder name --- .../{documentation => documentationexamples}/Prompts.java | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) rename java/samples/sample-code/src/main/java/com/microsoft/semantickernel/samples/{documentation => documentationexamples}/Prompts.java (99%) diff --git a/java/samples/sample-code/src/main/java/com/microsoft/semantickernel/samples/documentation/Prompts.java b/java/samples/sample-code/src/main/java/com/microsoft/semantickernel/samples/documentationexamples/Prompts.java similarity index 99% rename from java/samples/sample-code/src/main/java/com/microsoft/semantickernel/samples/documentation/Prompts.java rename to java/samples/sample-code/src/main/java/com/microsoft/semantickernel/samples/documentationexamples/Prompts.java index 448bb0847c10..5fe024028c6f 100644 --- a/java/samples/sample-code/src/main/java/com/microsoft/semantickernel/samples/documentation/Prompts.java +++ b/java/samples/sample-code/src/main/java/com/microsoft/semantickernel/samples/documentationexamples/Prompts.java @@ -1,4 +1,4 @@ -package com.microsoft.semantickernel.samples.documentation; +package com.microsoft.semantickernel.samples.documentationexamples; import com.azure.ai.openai.OpenAIAsyncClient; import com.azure.ai.openai.OpenAIClientBuilder; @@ -7,8 +7,6 @@ import com.microsoft.semantickernel.Kernel; import com.microsoft.semantickernel.services.chatcompletion.ChatCompletionService; -import java.util.Scanner; - public class Prompts { private static final String CLIENT_KEY = System.getenv("CLIENT_KEY"); private static final String AZURE_CLIENT_KEY = System.getenv("AZURE_CLIENT_KEY"); From be3868450e1d343c2acae55422fae3dd3c5f85ab Mon Sep 17 00:00:00 2001 From: Milder Hernandez Cagua Date: Wed, 20 Mar 2024 08:54:48 -0700 Subject: [PATCH 03/15] Add CreatingFunctions and MathPlugin --- .../CreatingFunctions.java | 101 +++++++++++++++ .../documentationexamples/Prompts.java | 2 + .../SerializingPrompts.java | 83 +++++++++++++ .../plugins/MathPlugin.java | 116 ++++++++++++++++++ .../plugins/Prompts/chat/config.json | 33 +++++ .../plugins/Prompts/chat/skprompt.txt | 3 + 6 files changed, 338 insertions(+) create mode 100644 java/samples/sample-code/src/main/java/com/microsoft/semantickernel/samples/documentationexamples/CreatingFunctions.java create mode 100644 java/samples/sample-code/src/main/java/com/microsoft/semantickernel/samples/documentationexamples/SerializingPrompts.java create mode 100644 java/samples/sample-code/src/main/java/com/microsoft/semantickernel/samples/documentationexamples/plugins/MathPlugin.java create mode 100644 java/samples/sample-code/src/main/java/com/microsoft/semantickernel/samples/documentationexamples/plugins/Prompts/chat/config.json create mode 100644 java/samples/sample-code/src/main/java/com/microsoft/semantickernel/samples/documentationexamples/plugins/Prompts/chat/skprompt.txt diff --git a/java/samples/sample-code/src/main/java/com/microsoft/semantickernel/samples/documentationexamples/CreatingFunctions.java b/java/samples/sample-code/src/main/java/com/microsoft/semantickernel/samples/documentationexamples/CreatingFunctions.java new file mode 100644 index 000000000000..c00739109e53 --- /dev/null +++ b/java/samples/sample-code/src/main/java/com/microsoft/semantickernel/samples/documentationexamples/CreatingFunctions.java @@ -0,0 +1,101 @@ +package com.microsoft.semantickernel.samples.documentationexamples; + +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.contextvariables.ContextVariableTypes; +import com.microsoft.semantickernel.orchestration.ToolCallBehavior; +import com.microsoft.semantickernel.plugin.KernelPluginFactory; +import com.microsoft.semantickernel.samples.documentationexamples.plugins.MathPlugin; +import com.microsoft.semantickernel.semanticfunctions.KernelFunctionArguments; +import com.microsoft.semantickernel.services.chatcompletion.ChatCompletionService; +import com.microsoft.semantickernel.services.chatcompletion.ChatHistory; + +import java.util.Scanner; + +public class CreatingFunctions { + + 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-2"); + + public static void main(String[] args) { + System.out.println("======== Prompts ========"); + 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(); + } + + Kernel kernel = Kernel.builder() + .withAIService(ChatCompletionService.class, ChatCompletionService.builder() + .withModelId(MODEL_ID) + .withOpenAIAsyncClient(client) + .build()) + .withPlugin(KernelPluginFactory.createFromObject(new MathPlugin(), "MathPlugin")) + .build(); + + // Test the math plugin + var answer = kernel + .invokeAsync(kernel.getFunction("MathPlugin", "sqrt")) + .withArguments(KernelFunctionArguments + .builder() + .withVariable("number1", 12.0) + .build()) + .block(); + System.out.println("The square root of 12 is " + answer.getResult() + "."); + + // Create chat history + ChatCompletionService chat = OpenAIChatCompletion.builder() + .withModelId(MODEL_ID) + .withOpenAIAsyncClient(client) + .build(); + + System.out.println("Chat content:"); + System.out.println("------------------------"); + + ChatHistory history = new ChatHistory(); + + // Start the conversation + System.out.print("User > "); + Scanner scanner = new Scanner(System.in); + String userInput; + while (!(userInput = scanner.nextLine()).isEmpty()) { + history.addUserMessage(userInput); + + reply(chat, history); + messageOutput(history); + } + } + + private static void reply(ChatCompletionService chatGPT, ChatHistory chatHistory) { + // Enable auto function calling + var toolCallBehavior = ToolCallBehavior.allowAllKernelFunctions(true); + + var reply = chatGPT.getChatMessageContentsAsync(chatHistory, null, null) + .block(); + + StringBuilder message = new StringBuilder(); + reply.forEach(chatMessageContent -> message.append(chatMessageContent.getContent())); + chatHistory.addAssistantMessage(message.toString()); + } + + private static void messageOutput(ChatHistory chatHistory) { + var message = chatHistory.getLastMessage().get(); + System.out.println(message.getAuthorRole() + ": " + message.getContent()); + System.out.println("------------------------"); + } +} \ No newline at end of file diff --git a/java/samples/sample-code/src/main/java/com/microsoft/semantickernel/samples/documentationexamples/Prompts.java b/java/samples/sample-code/src/main/java/com/microsoft/semantickernel/samples/documentationexamples/Prompts.java index 5fe024028c6f..cf8549f6e483 100644 --- a/java/samples/sample-code/src/main/java/com/microsoft/semantickernel/samples/documentationexamples/Prompts.java +++ b/java/samples/sample-code/src/main/java/com/microsoft/semantickernel/samples/documentationexamples/Prompts.java @@ -57,7 +57,9 @@ public static void main(String[] args) { */ System.out.println("0.0 Initial prompt"); + // System.out.println(kernel.invokePromptAsync(prompt).block().getResult()); + // // 1.0 Make the prompt more specific ///////////////////////////////////////////////////////////////// diff --git a/java/samples/sample-code/src/main/java/com/microsoft/semantickernel/samples/documentationexamples/SerializingPrompts.java b/java/samples/sample-code/src/main/java/com/microsoft/semantickernel/samples/documentationexamples/SerializingPrompts.java new file mode 100644 index 000000000000..fab949a2aaf7 --- /dev/null +++ b/java/samples/sample-code/src/main/java/com/microsoft/semantickernel/samples/documentationexamples/SerializingPrompts.java @@ -0,0 +1,83 @@ +package com.microsoft.semantickernel.samples.documentationexamples; + +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.services.chatcompletion.ChatCompletionService; + +import java.util.Arrays; +import java.util.List; +import java.util.Scanner; + +public class SerializingPrompts { + 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-2"); + + public static void main(String[] args) { + System.out.println("======== Serializing Prompts ========"); + 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(); + } + + Kernel kernel = Kernel.builder() + .withAIService(ChatCompletionService.class, ChatCompletionService.builder() + .withModelId(MODEL_ID) + .withOpenAIAsyncClient(client) + .build()) + .build(); + + // Load prompts + // This part is omitted as it requires a specific implementation to load prompts from a directory + + // Load prompt from YAML + // This part is omitted as it requires a specific implementation to load prompts from a YAML file + + // Create choices + List choices = Arrays.asList("ContinueConversation", "EndConversation"); + + // Create few-shot examples + // This part is omitted as it requires a specific implementation to create few-shot examples + + // Create chat history + // This part is omitted as it requires a specific implementation to create chat history + + // Start the chat loop + Scanner scanner = new Scanner(System.in); + System.out.print("User > "); + String userInput; + while (!(userInput = scanner.nextLine()).isEmpty()) { + // Invoke handlebars prompt + // This part is omitted as it requires a specific implementation to invoke a prompt + + // End the chat if the intent is "Stop" + // This part is omitted as it requires a specific implementation to handle the intent + + // Get chat response + // This part is omitted as it requires a specific implementation to get the chat response + + // Stream the response + // This part is omitted as it requires a specific implementation to stream the response + + // Append to history + // This part is omitted as it requires a specific implementation to append to the history + + // Get user input again + System.out.print("User > "); + } + } +} \ No newline at end of file diff --git a/java/samples/sample-code/src/main/java/com/microsoft/semantickernel/samples/documentationexamples/plugins/MathPlugin.java b/java/samples/sample-code/src/main/java/com/microsoft/semantickernel/samples/documentationexamples/plugins/MathPlugin.java new file mode 100644 index 000000000000..54013d995992 --- /dev/null +++ b/java/samples/sample-code/src/main/java/com/microsoft/semantickernel/samples/documentationexamples/plugins/MathPlugin.java @@ -0,0 +1,116 @@ +package com.microsoft.semantickernel.samples.documentationexamples.plugins; + +import com.microsoft.semantickernel.semanticfunctions.annotations.DefineKernelFunction; +import com.microsoft.semantickernel.semanticfunctions.annotations.KernelFunctionParameter; + +public class MathPlugin { + + @DefineKernelFunction(name = "sqrt", description = "Take the square root of a number") + public static double sqrt( + @KernelFunctionParameter(name = "number1", description = "The number to take a square root of", type = double.class) double number1) { + return Math.sqrt(number1); + } + + @DefineKernelFunction(name = "add", description = "Add two numbers") + public static double add( + @KernelFunctionParameter(name = "number1", description = "The first number to add", type = double.class) double number1, + @KernelFunctionParameter(name = "number2", description = "The second number to add", type = double.class) double number2) { + return number1 + number2; + } + + @DefineKernelFunction(name = "subtract", description = "Subtract two numbers") + public static double subtract( + @KernelFunctionParameter(name = "number1", description = "The first number to subtract from", type = double.class) double number1, + @KernelFunctionParameter(name = "number2", description = "The second number to subtract away", type = double.class) double number2) { + return number1 - number2; + } + + @DefineKernelFunction(name = "multiply", description = "Multiply two numbers. When increasing by a percentage, don't forget to add 1 to the percentage.") + public static double multiply( + @KernelFunctionParameter(name = "number1", description = "The first number to multiply", type = double.class) double number1, + @KernelFunctionParameter(name = "number2", description = "The second number to multiply", type = double.class) double number2) { + return number1 * number2; + } + + @DefineKernelFunction(name = "divide", description = "Divide two numbers") + public static double divide( + @KernelFunctionParameter(name = "number1", description = "The first number to divide from", type = double.class) double number1, + @KernelFunctionParameter(name = "number2", description = "The second number to divide by", type = double.class) double number2) { + return number1 / number2; + } + + @DefineKernelFunction(name = "power", description = "Raise a number to a power") + public static double power( + @KernelFunctionParameter(name = "number1", description = "The number to raise", type = double.class) double number1, + @KernelFunctionParameter(name = "number2", description = "The power to raise the number to", type = double.class) double number2) { + return Math.pow(number1, number2); + } + + @DefineKernelFunction(name = "log", description = "Take the log of a number") + public static double log( + @KernelFunctionParameter(name = "number1", description = "The number to take the log of", type = double.class) double number1, + @KernelFunctionParameter(name = "number2", description = "The base of the log", type = double.class) double number2) { + return Math.log(number1) / Math.log(number2); + } + + @DefineKernelFunction(name = "round", description = "Round a number to the target number of decimal places") + public static double round( + @KernelFunctionParameter(name = "number1", description = "The number to round", type = double.class) double number1, + @KernelFunctionParameter(name = "number2", description = "The number of decimal places to round to", type = double.class) int number2) { + return Math.round(number1 * Math.pow(10, number2)) / Math.pow(10, number2); + } + + @DefineKernelFunction(name = "abs", description = "Take the absolute value of a number") + public static double abs( + @KernelFunctionParameter(name = "number1", description = "The number to take the absolute value of", type = double.class) double number1) { + return Math.abs(number1); + } + + @DefineKernelFunction(name = "floor", description = "Take the floor of a number") + public static double floor( + @KernelFunctionParameter(name = "number1", description = "The number to take the floor of", type = double.class) double number1) { + return Math.floor(number1); + } + + @DefineKernelFunction(name = "ceiling", description = "Take the ceiling of a number") + public static double ceiling( + @KernelFunctionParameter(name = "number1", description = "The number to take the ceiling of", type = double.class) double number1) { + return Math.ceil(number1); + } + + @DefineKernelFunction(name = "sin", description = "Take the sine of a number") + public static double sin( + @KernelFunctionParameter(name = "number1", description = "The number to take the sine of", type = double.class) double number1) { + return Math.sin(number1); + } + + @DefineKernelFunction(name = "cos", description = "Take the cosine of a number") + public static double cos( + @KernelFunctionParameter(name = "number1", description = "The number to take the cosine of", type = double.class) double number1) { + return Math.cos(number1); + } + + @DefineKernelFunction(name = "tan", description = "Take the tangent of a number") + public static double tan( + @KernelFunctionParameter(name = "number1", description = "The number to take the tangent of", type = double.class) double number1) { + return Math.tan(number1); + } + + @DefineKernelFunction(name = "asin", description = "Take the arcsine of a number") + public static double asin( + @KernelFunctionParameter(name = "number1", description = "The number to take the arcsine of", type = double.class) double number1) { + return Math.asin(number1); + } + + @DefineKernelFunction(name = "acos", description = "Take the arccosine of a number") + public static double acos( + @KernelFunctionParameter(name = "number1", description = "The number to take the arccosine of", type = double.class) double number1) { + return Math.acos(number1); + } + + @DefineKernelFunction(name = "atan", description = "Take the arctangent of a number") + public static double atan( + @KernelFunctionParameter(name = "number1", description = "The number to take the arctangent of", type = double.class) double number1) { + return Math.atan(number1); + } +} diff --git a/java/samples/sample-code/src/main/java/com/microsoft/semantickernel/samples/documentationexamples/plugins/Prompts/chat/config.json b/java/samples/sample-code/src/main/java/com/microsoft/semantickernel/samples/documentationexamples/plugins/Prompts/chat/config.json new file mode 100644 index 000000000000..28c9e2374c5b --- /dev/null +++ b/java/samples/sample-code/src/main/java/com/microsoft/semantickernel/samples/documentationexamples/plugins/Prompts/chat/config.json @@ -0,0 +1,33 @@ +{ + "schema": 1, + "type": "completion", + "description": "Creates a chat response to the user", + "execution_settings": { + "default": { + "max_tokens": 1000, + "temperature": 0 + }, + "gpt-3.5-turbo": { + "model_id": "gpt-3.5-turbo-0613", + "max_tokens": 4000, + "temperature": 0.1 + }, + "gpt-4": { + "model_id": "gpt-4-1106-preview", + "max_tokens": 8000, + "temperature": 0.3 + } + }, + "input_variables": [ + { + "name": "request", + "description": "The user's request.", + "required": true + }, + { + "name": "history", + "description": "The history of the conversation.", + "required": true + } + ] +} \ No newline at end of file diff --git a/java/samples/sample-code/src/main/java/com/microsoft/semantickernel/samples/documentationexamples/plugins/Prompts/chat/skprompt.txt b/java/samples/sample-code/src/main/java/com/microsoft/semantickernel/samples/documentationexamples/plugins/Prompts/chat/skprompt.txt new file mode 100644 index 000000000000..c4f39134698e --- /dev/null +++ b/java/samples/sample-code/src/main/java/com/microsoft/semantickernel/samples/documentationexamples/plugins/Prompts/chat/skprompt.txt @@ -0,0 +1,3 @@ +{{ConversationSummaryPlugin.SummarizeConversation $history}} +User: {{$request}} +Assistant: \ No newline at end of file From c22c8bb9289b75a84b76b140db0724f19004b876 Mon Sep 17 00:00:00 2001 From: Milder Hernandez Cagua Date: Thu, 21 Mar 2024 03:31:51 -0700 Subject: [PATCH 04/15] Update --- .../chatcompletion/OpenAIChatCompletion.java | 31 ++++- .../CreatingFunctions.java | 17 ++- .../documentationexamples/Prompts.java | 6 +- .../SerializingPrompts.java | 60 ++++++--- .../getIntent.prompt.yaml | 36 ++++++ .../plugins/MathPlugin.java | 116 ------------------ .../plugins/Prompts/chat/config.json | 33 ----- .../plugins/Prompts/chat/skprompt.txt | 3 - .../samples/plugins/Prompts/chat/config.json | 6 +- 9 files changed, 128 insertions(+), 180 deletions(-) create mode 100644 java/samples/sample-code/src/main/java/com/microsoft/semantickernel/samples/documentationexamples/getIntent.prompt.yaml delete mode 100644 java/samples/sample-code/src/main/java/com/microsoft/semantickernel/samples/documentationexamples/plugins/MathPlugin.java delete mode 100644 java/samples/sample-code/src/main/java/com/microsoft/semantickernel/samples/documentationexamples/plugins/Prompts/chat/config.json delete mode 100644 java/samples/sample-code/src/main/java/com/microsoft/semantickernel/samples/documentationexamples/plugins/Prompts/chat/skprompt.txt diff --git a/java/aiservices/openai/src/main/java/com/microsoft/semantickernel/aiservices/openai/chatcompletion/OpenAIChatCompletion.java b/java/aiservices/openai/src/main/java/com/microsoft/semantickernel/aiservices/openai/chatcompletion/OpenAIChatCompletion.java index 72858d38baee..a67c74a6c512 100644 --- a/java/aiservices/openai/src/main/java/com/microsoft/semantickernel/aiservices/openai/chatcompletion/OpenAIChatCompletion.java +++ b/java/aiservices/openai/src/main/java/com/microsoft/semantickernel/aiservices/openai/chatcompletion/OpenAIChatCompletion.java @@ -20,7 +20,10 @@ import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import com.microsoft.semantickernel.Kernel; +import com.microsoft.semantickernel.contextvariables.CaseInsensitiveMap; import com.microsoft.semantickernel.contextvariables.ContextVariable; +import com.microsoft.semantickernel.contextvariables.ContextVariableType; +import com.microsoft.semantickernel.contextvariables.ContextVariableTypeConverter; import com.microsoft.semantickernel.contextvariables.ContextVariableTypes; import com.microsoft.semantickernel.exceptions.AIException; import com.microsoft.semantickernel.exceptions.SKException; @@ -39,9 +42,11 @@ import com.microsoft.semantickernel.services.chatcompletion.ChatHistory; import com.microsoft.semantickernel.services.chatcompletion.ChatMessageContent; import java.util.ArrayList; +import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Objects; +import java.util.function.Function; import java.util.stream.Collectors; import javax.annotation.Nullable; import org.slf4j.Logger; @@ -230,6 +235,30 @@ private Mono>> internalChatMessageContentsAsync( return result.map(op -> (List>) op); } + private KernelFunctionArguments correctFunctionArgumentsTypes(KernelFunction function, KernelFunctionArguments arguments) { + Map> updatedArguments = new CaseInsensitiveMap<>(); + + // TODO: Where to put this? + Map, Function> parsers = new HashMap<>(); + parsers.put(double.class, Double::parseDouble); + parsers.put(int.class, Integer::parseInt); + parsers.put(Double.class, Double::parseDouble); + parsers.put(Integer.class, Integer::parseInt); + + function.getMetadata().getParameters().forEach(parameter -> { + if (arguments.containsKey(parameter.getName())) { + if (parsers.containsKey(parameter.getTypeClass())) { + ContextVariable arg = arguments.get(parameter.getName()); + Object value = parsers.get(parameter.getTypeClass()).apply(arg.getValue().toString()); + Class clazz = parameter.getTypeClass(); + updatedArguments.put(parameter.getName(), ContextVariable.of(value, new ContextVariableTypeConverter.NoopConverter<>(clazz))); + } + } + }); + + return KernelFunctionArguments.builder().withVariables(updatedArguments).build(); + } + @SuppressWarnings("StringSplitter") private Mono> invokeFunctionTool( Kernel kernel, @@ -249,7 +278,7 @@ private Mono> invokeFunctionTool( return function .invokeAsync(kernel) - .withArguments(openAIFunctionToolCall.getArguments()) + .withArguments(correctFunctionArgumentsTypes(function, openAIFunctionToolCall.getArguments())) .withResultType(ContextVariableTypes.getGlobalVariableTypeForClass( String.class)); } catch (JsonProcessingException e) { diff --git a/java/samples/sample-code/src/main/java/com/microsoft/semantickernel/samples/documentationexamples/CreatingFunctions.java b/java/samples/sample-code/src/main/java/com/microsoft/semantickernel/samples/documentationexamples/CreatingFunctions.java index 0672b1ecf752..202f6e3f0020 100644 --- a/java/samples/sample-code/src/main/java/com/microsoft/semantickernel/samples/documentationexamples/CreatingFunctions.java +++ b/java/samples/sample-code/src/main/java/com/microsoft/semantickernel/samples/documentationexamples/CreatingFunctions.java @@ -8,6 +8,9 @@ import com.azure.core.credential.KeyCredential; import com.microsoft.semantickernel.Kernel; import com.microsoft.semantickernel.aiservices.openai.chatcompletion.OpenAIChatCompletion; +import com.microsoft.semantickernel.contextvariables.ContextVariableType; +import com.microsoft.semantickernel.contextvariables.ContextVariableTypes; +import com.microsoft.semantickernel.orchestration.InvocationContext; import com.microsoft.semantickernel.orchestration.ToolCallBehavior; import com.microsoft.semantickernel.plugin.KernelPluginFactory; import com.microsoft.semantickernel.samples.plugins.MathPlugin; @@ -69,22 +72,26 @@ public static void main(String[] args) { ChatHistory history = new ChatHistory(); // Start the conversation - System.out.print("User > "); + System.out.print("user > "); Scanner scanner = new Scanner(System.in); String userInput; while (!(userInput = scanner.nextLine()).isEmpty()) { history.addUserMessage(userInput); - reply(chat, history); + reply(kernel, chat, history); messageOutput(history); + + System.out.print("user > "); } } - private static void reply(ChatCompletionService chatGPT, ChatHistory chatHistory) { + private static void reply(Kernel kernel, ChatCompletionService chatGPT, ChatHistory chatHistory) { // Enable auto function calling var toolCallBehavior = ToolCallBehavior.allowAllKernelFunctions(true); - var reply = chatGPT.getChatMessageContentsAsync(chatHistory, null, null) + var reply = chatGPT.getChatMessageContentsAsync(chatHistory, kernel, InvocationContext.builder() + .withToolCallBehavior(toolCallBehavior) + .build()) .block(); StringBuilder message = new StringBuilder(); @@ -94,7 +101,7 @@ private static void reply(ChatCompletionService chatGPT, ChatHistory chatHistory private static void messageOutput(ChatHistory chatHistory) { var message = chatHistory.getLastMessage().get(); - System.out.println(message.getAuthorRole() + ": " + message.getContent()); + System.out.println(message.getAuthorRole() + " > " + message.getContent()); System.out.println("------------------------"); } } \ No newline at end of file diff --git a/java/samples/sample-code/src/main/java/com/microsoft/semantickernel/samples/documentationexamples/Prompts.java b/java/samples/sample-code/src/main/java/com/microsoft/semantickernel/samples/documentationexamples/Prompts.java index cf8549f6e483..bec173e9365e 100644 --- a/java/samples/sample-code/src/main/java/com/microsoft/semantickernel/samples/documentationexamples/Prompts.java +++ b/java/samples/sample-code/src/main/java/com/microsoft/semantickernel/samples/documentationexamples/Prompts.java @@ -163,8 +163,8 @@ public static void main(String[] args) { // 5.0 Provide context to the AI ///////////////////////////////////////////////////////////////// - String history = "User input: I hate sending emails, no one ever reads them.\nAI response: I'm sorry to hear that. Messages may be a better way to communicate."; // + String history = "User input: I hate sending emails, no one ever reads them.\nAI response: I'm sorry to hear that. Messages may be a better way to communicate."; prompt = """ Instructions: What is the intent of this request? If you don't know the intent, don't guess; instead respond with "Unknown". @@ -186,8 +186,8 @@ public static void main(String[] args) { // 6.0 Using message roles in chat completion prompts ///////////////////////////////////////////////////////////////// - history = "I hate sending emails, no one ever reads them.\nI'm sorry to hear that. Messages may be a better way to communicate."; // + history = "I hate sending emails, no one ever reads them.\nI'm sorry to hear that. Messages may be a better way to communicate."; prompt = """ Instructions: What is the intent of this request? If you don't know the intent, don't guess; instead respond with "Unknown". @@ -210,8 +210,8 @@ public static void main(String[] args) { System.out.println(kernel.invokePromptAsync(prompt).block().getResult()); // 7.0 Give your AI words of encouragement - history = "I hate sending emails, no one ever reads them.\nI'm sorry to hear that. Messages may be a better way to communicate."; // + history = "I hate sending emails, no one ever reads them.\nI'm sorry to hear that. Messages may be a better way to communicate."; prompt = """ Instructions: What is the intent of this request? If you don't know the intent, don't guess; instead respond with "Unknown". diff --git a/java/samples/sample-code/src/main/java/com/microsoft/semantickernel/samples/documentationexamples/SerializingPrompts.java b/java/samples/sample-code/src/main/java/com/microsoft/semantickernel/samples/documentationexamples/SerializingPrompts.java index fab949a2aaf7..c047d6e1bca0 100644 --- a/java/samples/sample-code/src/main/java/com/microsoft/semantickernel/samples/documentationexamples/SerializingPrompts.java +++ b/java/samples/sample-code/src/main/java/com/microsoft/semantickernel/samples/documentationexamples/SerializingPrompts.java @@ -5,8 +5,21 @@ import com.azure.core.credential.AzureKeyCredential; import com.azure.core.credential.KeyCredential; import com.microsoft.semantickernel.Kernel; +import com.microsoft.semantickernel.plugin.KernelPluginFactory; +import com.microsoft.semantickernel.samples.plugins.ConversationSummaryPlugin; +import com.microsoft.semantickernel.semanticfunctions.HandlebarsPromptTemplateFactory; +import com.microsoft.semantickernel.semanticfunctions.KernelFunction; +import com.microsoft.semantickernel.semanticfunctions.KernelFunctionArguments; +import com.microsoft.semantickernel.semanticfunctions.KernelFunctionYaml; +import com.microsoft.semantickernel.services.chatcompletion.AuthorRole; import com.microsoft.semantickernel.services.chatcompletion.ChatCompletionService; +import com.microsoft.semantickernel.services.chatcompletion.ChatHistory; +import java.io.File; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; import java.util.Arrays; import java.util.List; import java.util.Scanner; @@ -19,7 +32,9 @@ public class SerializingPrompts { private static final String CLIENT_ENDPOINT = System.getenv("CLIENT_ENDPOINT"); private static final String MODEL_ID = System.getenv().getOrDefault("MODEL_ID", "gpt-35-turbo-2"); - public static void main(String[] args) { + private static final String SAMPLES_DIR = "java/samples/sample-code/src/main/java/com/microsoft/semantickernel/samples"; + + public static void main(String[] args) throws IOException { System.out.println("======== Serializing Prompts ========"); OpenAIAsyncClient client; @@ -39,22 +54,35 @@ public static void main(String[] args) { .withModelId(MODEL_ID) .withOpenAIAsyncClient(client) .build()) + .withPlugin(KernelPluginFactory.createFromObject(new ConversationSummaryPlugin(), "ConversationSummaryPlugin")) .build(); // Load prompts - // This part is omitted as it requires a specific implementation to load prompts from a directory + var prompts = KernelPluginFactory.importPluginFromDirectory( + Path.of(SAMPLES_DIR, "plugins"), "Prompts", null); // Load prompt from YAML - // This part is omitted as it requires a specific implementation to load prompts from a YAML file + var getIntent = KernelFunctionYaml.fromPromptYaml( + Files.readString(Path.of(SAMPLES_DIR, "documentationexamples", "getIntent.prompt.yaml")), + new HandlebarsPromptTemplateFactory()); // Create choices List choices = Arrays.asList("ContinueConversation", "EndConversation"); // Create few-shot examples - // This part is omitted as it requires a specific implementation to create few-shot examples + ChatHistory continueConversation = new ChatHistory(); + continueConversation.addMessage(AuthorRole.USER, "Can you send a very quick approval to the marketing team?"); + continueConversation.addMessage(AuthorRole.SYSTEM, "Intent:"); + continueConversation.addMessage(AuthorRole.ASSISTANT, "ContinueConversation"); + ChatHistory endConversation = new ChatHistory(); + endConversation.addMessage(AuthorRole.USER, "Can you send the full update to the marketing team?"); + endConversation.addMessage(AuthorRole.SYSTEM, "Intent:"); + endConversation.addMessage(AuthorRole.ASSISTANT, "EndConversation"); + + List fewShotExamples = List.of(continueConversation, endConversation); // Create chat history - // This part is omitted as it requires a specific implementation to create chat history + ChatHistory history = new ChatHistory(); // Start the chat loop Scanner scanner = new Scanner(System.in); @@ -62,19 +90,21 @@ public static void main(String[] args) { String userInput; while (!(userInput = scanner.nextLine()).isEmpty()) { // Invoke handlebars prompt - // This part is omitted as it requires a specific implementation to invoke a prompt + var intent = kernel.invokeAsync(getIntent) + .withArguments(KernelFunctionArguments.builder() + .withVariable("request", userInput) + .withVariable("choices", choices) + .withVariable("history", history) + .withVariable("fewShotExamples", fewShotExamples) + .build()) + .block(); // End the chat if the intent is "Stop" - // This part is omitted as it requires a specific implementation to handle the intent - - // Get chat response - // This part is omitted as it requires a specific implementation to get the chat response - - // Stream the response - // This part is omitted as it requires a specific implementation to stream the response + if (intent.getResult().equals("EndConversation")) { + break; + } - // Append to history - // This part is omitted as it requires a specific implementation to append to the history + // WIP // Get user input again System.out.print("User > "); diff --git a/java/samples/sample-code/src/main/java/com/microsoft/semantickernel/samples/documentationexamples/getIntent.prompt.yaml b/java/samples/sample-code/src/main/java/com/microsoft/semantickernel/samples/documentationexamples/getIntent.prompt.yaml new file mode 100644 index 000000000000..1c16c2e49055 --- /dev/null +++ b/java/samples/sample-code/src/main/java/com/microsoft/semantickernel/samples/documentationexamples/getIntent.prompt.yaml @@ -0,0 +1,36 @@ +name: getIntent +description: Gets the intent of the user. +template: | + Instructions: What is the intent of this request? + Do not explain the reasoning, just reply back with the intent. If you are unsure, reply with {{choices.[0]}}. + Choices: {{choices}}. + + {{#each fewShotExamples}} + {{#each this}} + {{content}} + {{/each}} + {{/each}} + + {{ConversationSummaryPlugin.SummarizeConversation history}} + + {{request}} + Intent: +template_format: handlebars +input_variables: + - name: choices + description: The choices for the AI to choose from + default: ContinueConversation, EndConversation + - name: fewShotExamples + description: Few shot examples for the AI to learn from + is_required: true + - name: request + description: The user's request + is_required: true +execution_settings: + default: + max_tokens: 10 + temperature: 0 + gpt-3.5-turbo: + model_id: gpt-35-turbo-2 + max_tokens: 10 + temperature: 0.2 \ No newline at end of file diff --git a/java/samples/sample-code/src/main/java/com/microsoft/semantickernel/samples/documentationexamples/plugins/MathPlugin.java b/java/samples/sample-code/src/main/java/com/microsoft/semantickernel/samples/documentationexamples/plugins/MathPlugin.java deleted file mode 100644 index 54013d995992..000000000000 --- a/java/samples/sample-code/src/main/java/com/microsoft/semantickernel/samples/documentationexamples/plugins/MathPlugin.java +++ /dev/null @@ -1,116 +0,0 @@ -package com.microsoft.semantickernel.samples.documentationexamples.plugins; - -import com.microsoft.semantickernel.semanticfunctions.annotations.DefineKernelFunction; -import com.microsoft.semantickernel.semanticfunctions.annotations.KernelFunctionParameter; - -public class MathPlugin { - - @DefineKernelFunction(name = "sqrt", description = "Take the square root of a number") - public static double sqrt( - @KernelFunctionParameter(name = "number1", description = "The number to take a square root of", type = double.class) double number1) { - return Math.sqrt(number1); - } - - @DefineKernelFunction(name = "add", description = "Add two numbers") - public static double add( - @KernelFunctionParameter(name = "number1", description = "The first number to add", type = double.class) double number1, - @KernelFunctionParameter(name = "number2", description = "The second number to add", type = double.class) double number2) { - return number1 + number2; - } - - @DefineKernelFunction(name = "subtract", description = "Subtract two numbers") - public static double subtract( - @KernelFunctionParameter(name = "number1", description = "The first number to subtract from", type = double.class) double number1, - @KernelFunctionParameter(name = "number2", description = "The second number to subtract away", type = double.class) double number2) { - return number1 - number2; - } - - @DefineKernelFunction(name = "multiply", description = "Multiply two numbers. When increasing by a percentage, don't forget to add 1 to the percentage.") - public static double multiply( - @KernelFunctionParameter(name = "number1", description = "The first number to multiply", type = double.class) double number1, - @KernelFunctionParameter(name = "number2", description = "The second number to multiply", type = double.class) double number2) { - return number1 * number2; - } - - @DefineKernelFunction(name = "divide", description = "Divide two numbers") - public static double divide( - @KernelFunctionParameter(name = "number1", description = "The first number to divide from", type = double.class) double number1, - @KernelFunctionParameter(name = "number2", description = "The second number to divide by", type = double.class) double number2) { - return number1 / number2; - } - - @DefineKernelFunction(name = "power", description = "Raise a number to a power") - public static double power( - @KernelFunctionParameter(name = "number1", description = "The number to raise", type = double.class) double number1, - @KernelFunctionParameter(name = "number2", description = "The power to raise the number to", type = double.class) double number2) { - return Math.pow(number1, number2); - } - - @DefineKernelFunction(name = "log", description = "Take the log of a number") - public static double log( - @KernelFunctionParameter(name = "number1", description = "The number to take the log of", type = double.class) double number1, - @KernelFunctionParameter(name = "number2", description = "The base of the log", type = double.class) double number2) { - return Math.log(number1) / Math.log(number2); - } - - @DefineKernelFunction(name = "round", description = "Round a number to the target number of decimal places") - public static double round( - @KernelFunctionParameter(name = "number1", description = "The number to round", type = double.class) double number1, - @KernelFunctionParameter(name = "number2", description = "The number of decimal places to round to", type = double.class) int number2) { - return Math.round(number1 * Math.pow(10, number2)) / Math.pow(10, number2); - } - - @DefineKernelFunction(name = "abs", description = "Take the absolute value of a number") - public static double abs( - @KernelFunctionParameter(name = "number1", description = "The number to take the absolute value of", type = double.class) double number1) { - return Math.abs(number1); - } - - @DefineKernelFunction(name = "floor", description = "Take the floor of a number") - public static double floor( - @KernelFunctionParameter(name = "number1", description = "The number to take the floor of", type = double.class) double number1) { - return Math.floor(number1); - } - - @DefineKernelFunction(name = "ceiling", description = "Take the ceiling of a number") - public static double ceiling( - @KernelFunctionParameter(name = "number1", description = "The number to take the ceiling of", type = double.class) double number1) { - return Math.ceil(number1); - } - - @DefineKernelFunction(name = "sin", description = "Take the sine of a number") - public static double sin( - @KernelFunctionParameter(name = "number1", description = "The number to take the sine of", type = double.class) double number1) { - return Math.sin(number1); - } - - @DefineKernelFunction(name = "cos", description = "Take the cosine of a number") - public static double cos( - @KernelFunctionParameter(name = "number1", description = "The number to take the cosine of", type = double.class) double number1) { - return Math.cos(number1); - } - - @DefineKernelFunction(name = "tan", description = "Take the tangent of a number") - public static double tan( - @KernelFunctionParameter(name = "number1", description = "The number to take the tangent of", type = double.class) double number1) { - return Math.tan(number1); - } - - @DefineKernelFunction(name = "asin", description = "Take the arcsine of a number") - public static double asin( - @KernelFunctionParameter(name = "number1", description = "The number to take the arcsine of", type = double.class) double number1) { - return Math.asin(number1); - } - - @DefineKernelFunction(name = "acos", description = "Take the arccosine of a number") - public static double acos( - @KernelFunctionParameter(name = "number1", description = "The number to take the arccosine of", type = double.class) double number1) { - return Math.acos(number1); - } - - @DefineKernelFunction(name = "atan", description = "Take the arctangent of a number") - public static double atan( - @KernelFunctionParameter(name = "number1", description = "The number to take the arctangent of", type = double.class) double number1) { - return Math.atan(number1); - } -} diff --git a/java/samples/sample-code/src/main/java/com/microsoft/semantickernel/samples/documentationexamples/plugins/Prompts/chat/config.json b/java/samples/sample-code/src/main/java/com/microsoft/semantickernel/samples/documentationexamples/plugins/Prompts/chat/config.json deleted file mode 100644 index 28c9e2374c5b..000000000000 --- a/java/samples/sample-code/src/main/java/com/microsoft/semantickernel/samples/documentationexamples/plugins/Prompts/chat/config.json +++ /dev/null @@ -1,33 +0,0 @@ -{ - "schema": 1, - "type": "completion", - "description": "Creates a chat response to the user", - "execution_settings": { - "default": { - "max_tokens": 1000, - "temperature": 0 - }, - "gpt-3.5-turbo": { - "model_id": "gpt-3.5-turbo-0613", - "max_tokens": 4000, - "temperature": 0.1 - }, - "gpt-4": { - "model_id": "gpt-4-1106-preview", - "max_tokens": 8000, - "temperature": 0.3 - } - }, - "input_variables": [ - { - "name": "request", - "description": "The user's request.", - "required": true - }, - { - "name": "history", - "description": "The history of the conversation.", - "required": true - } - ] -} \ No newline at end of file diff --git a/java/samples/sample-code/src/main/java/com/microsoft/semantickernel/samples/documentationexamples/plugins/Prompts/chat/skprompt.txt b/java/samples/sample-code/src/main/java/com/microsoft/semantickernel/samples/documentationexamples/plugins/Prompts/chat/skprompt.txt deleted file mode 100644 index c4f39134698e..000000000000 --- a/java/samples/sample-code/src/main/java/com/microsoft/semantickernel/samples/documentationexamples/plugins/Prompts/chat/skprompt.txt +++ /dev/null @@ -1,3 +0,0 @@ -{{ConversationSummaryPlugin.SummarizeConversation $history}} -User: {{$request}} -Assistant: \ No newline at end of file diff --git a/java/samples/sample-code/src/main/java/com/microsoft/semantickernel/samples/plugins/Prompts/chat/config.json b/java/samples/sample-code/src/main/java/com/microsoft/semantickernel/samples/plugins/Prompts/chat/config.json index 28c9e2374c5b..6b3f3495a145 100644 --- a/java/samples/sample-code/src/main/java/com/microsoft/semantickernel/samples/plugins/Prompts/chat/config.json +++ b/java/samples/sample-code/src/main/java/com/microsoft/semantickernel/samples/plugins/Prompts/chat/config.json @@ -21,13 +21,11 @@ "input_variables": [ { "name": "request", - "description": "The user's request.", - "required": true + "description": "The user's request." }, { "name": "history", - "description": "The history of the conversation.", - "required": true + "description": "The history of the conversation." } ] } \ No newline at end of file From 983bb603c26e2b8c6bdb53844b364491f13267c5 Mon Sep 17 00:00:00 2001 From: Milder Hernandez Cagua Date: Thu, 21 Mar 2024 03:48:19 -0700 Subject: [PATCH 05/15] Fix --- .../openai/chatcompletion/OpenAIChatCompletion.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/java/aiservices/openai/src/main/java/com/microsoft/semantickernel/aiservices/openai/chatcompletion/OpenAIChatCompletion.java b/java/aiservices/openai/src/main/java/com/microsoft/semantickernel/aiservices/openai/chatcompletion/OpenAIChatCompletion.java index a67c74a6c512..74c7eefde6fc 100644 --- a/java/aiservices/openai/src/main/java/com/microsoft/semantickernel/aiservices/openai/chatcompletion/OpenAIChatCompletion.java +++ b/java/aiservices/openai/src/main/java/com/microsoft/semantickernel/aiservices/openai/chatcompletion/OpenAIChatCompletion.java @@ -247,11 +247,13 @@ private KernelFunctionArguments correctFunctionArgumentsTypes(KernelFunction function.getMetadata().getParameters().forEach(parameter -> { if (arguments.containsKey(parameter.getName())) { + ContextVariable arg = arguments.get(parameter.getName()); if (parsers.containsKey(parameter.getTypeClass())) { - ContextVariable arg = arguments.get(parameter.getName()); Object value = parsers.get(parameter.getTypeClass()).apply(arg.getValue().toString()); Class clazz = parameter.getTypeClass(); updatedArguments.put(parameter.getName(), ContextVariable.of(value, new ContextVariableTypeConverter.NoopConverter<>(clazz))); + } else { + updatedArguments.put(parameter.getName(), arg); } } }); From f26e3ec1aaac247319da0a94af819bbb5a3abfca Mon Sep 17 00:00:00 2001 From: John Oliver <1615532+johnoliver@users.noreply.github.com> Date: Thu, 21 Mar 2024 17:44:46 +0000 Subject: [PATCH 06/15] Add Plugin sample --- .../samples/documentationexamples/Plugin.java | 122 ++++++++++++++++++ .../ContextVariableTypes.java | 5 +- ...rVariableContextVariableTypeConverter.java | 68 ++-------- ...nVariableContextVariableTypeConverter.java | 61 +++++++++ ...eVariableContextVariableTypeConverter.java | 102 +++++++++++++++ ...iableContextVariableTypeConverterTest.java | 8 ++ .../KernelFunctionFromMethodTest.java | 37 ++++++ 7 files changed, 344 insertions(+), 59 deletions(-) create mode 100644 java/samples/sample-code/src/main/java/com/microsoft/semantickernel/samples/documentationexamples/Plugin.java create mode 100644 java/semantickernel-api/src/main/java/com/microsoft/semantickernel/contextvariables/converters/PrimativeBooleanVariableContextVariableTypeConverter.java create mode 100644 java/semantickernel-api/src/main/java/com/microsoft/semantickernel/contextvariables/converters/PrimativeVariableContextVariableTypeConverter.java diff --git a/java/samples/sample-code/src/main/java/com/microsoft/semantickernel/samples/documentationexamples/Plugin.java b/java/samples/sample-code/src/main/java/com/microsoft/semantickernel/samples/documentationexamples/Plugin.java new file mode 100644 index 000000000000..541832e2c599 --- /dev/null +++ b/java/samples/sample-code/src/main/java/com/microsoft/semantickernel/samples/documentationexamples/Plugin.java @@ -0,0 +1,122 @@ +// Copyright (c) Microsoft. All rights reserved. +package com.microsoft.semantickernel.samples.documentationexamples; + +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.orchestration.InvocationContext; +import com.microsoft.semantickernel.orchestration.ToolCallBehavior; +import com.microsoft.semantickernel.plugin.KernelPlugin; +import com.microsoft.semantickernel.plugin.KernelPluginFactory; +import com.microsoft.semantickernel.semanticfunctions.annotations.DefineKernelFunction; +import com.microsoft.semantickernel.semanticfunctions.annotations.KernelFunctionParameter; +import com.microsoft.semantickernel.services.chatcompletion.ChatCompletionService; +import com.microsoft.semantickernel.services.chatcompletion.ChatHistory; +import java.io.InputStream; +import java.util.Scanner; + +public class Plugin { + + public static InputStream INPUT = System.in; + + // CLIENT_KEY is for an OpenAI client + private static final String CLIENT_KEY = System.getenv("CLIENT_KEY"); + + // AZURE_CLIENT_KEY and CLIENT_ENDPOINT are for an Azure client + // CLIENT_ENDPOINT required if AZURE_CLIENT_KEY is set + private static final String AZURE_CLIENT_KEY = System.getenv("AZURE_CLIENT_KEY"); + private static final String CLIENT_ENDPOINT = System.getenv("CLIENT_ENDPOINT"); + + private static final String MODEL_ID = System.getenv() + .getOrDefault("MODEL_ID", "gpt-3.5-turbo"); + + public static void main(String[] args) { + System.out.println("======== Plugin ========"); + + OpenAIAsyncClient client; + + if (AZURE_CLIENT_KEY != null && CLIENT_ENDPOINT != null) { + client = new OpenAIClientBuilder() + .credential(new AzureKeyCredential(AZURE_CLIENT_KEY)) + .endpoint(CLIENT_ENDPOINT) + .buildAsyncClient(); + } else if (CLIENT_KEY != null) { + client = new OpenAIClientBuilder() + .credential(new KeyCredential(CLIENT_KEY)) + .buildAsyncClient(); + } else { + System.out.println("No client key found"); + return; + } + + ChatCompletionService chatCompletionService = ChatCompletionService.builder() + .withModelId(MODEL_ID) + .withOpenAIAsyncClient(client) + .build(); + + KernelPlugin plugin = KernelPluginFactory.createFromObject( + new LightPlugin(), "LightPlugin"); + + Kernel kernel = Kernel.builder() + .withAIService(ChatCompletionService.class, chatCompletionService) + .withPlugin(plugin) + .build(); + + Scanner scanner = new Scanner(INPUT); + + var history = new ChatHistory(); + + // Start the chat loop + while (true) { + // + // Get user input + System.out.print("User > "); + String request = scanner.nextLine(); + history.addUserMessage(request); + + // Enable auto function calling + var invocationContext = InvocationContext.builder() + .withToolCallBehavior( + ToolCallBehavior.allowAllKernelFunctions(true)) + .build(); + + var result = chatCompletionService + .getChatMessageContentsAsync( + history, + kernel, + invocationContext) + .block(); + + String message = result.get(result.size() - 1).getContent(); + System.out.printf("Assistant > %s%n", message); + + // Append to history + history.addAssistantMessage(message); + } + } + + public static class LightPlugin { + + public boolean isOn = false; + + @DefineKernelFunction(name = "changeState", description = "Changes the state of the light.'") + + public String changeState( + @KernelFunctionParameter(name = "newState", description = "The new state of the light, boolean true==on, false==off.", type = boolean.class) boolean newState) { + this.isOn = newState; + String state = getState(); + + // Print the state to the console + System.out.printf("[Light is now %s]%n", state); + + return state; + } + + String getState() { + return isOn ? "on" : "off"; + + } + } +} diff --git a/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/contextvariables/ContextVariableTypes.java b/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/contextvariables/ContextVariableTypes.java index 319e7289c316..8736cb1154c2 100644 --- a/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/contextvariables/ContextVariableTypes.java +++ b/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/contextvariables/ContextVariableTypes.java @@ -9,6 +9,7 @@ import com.microsoft.semantickernel.contextvariables.converters.DateTimeContextVariableTypeConverter; import com.microsoft.semantickernel.contextvariables.converters.InstantContextVariableTypeConverter; import com.microsoft.semantickernel.contextvariables.converters.NumberVariableContextVariableTypeConverter; +import com.microsoft.semantickernel.contextvariables.converters.PrimativeBooleanVariableContextVariableTypeConverter; import com.microsoft.semantickernel.contextvariables.converters.StringVariableContextVariableTypeConverter; import com.microsoft.semantickernel.contextvariables.converters.TextContentVariableContextVariableTypeConverter; import com.microsoft.semantickernel.contextvariables.converters.VoidVariableContextVariableTypeConverter; @@ -29,7 +30,6 @@ public class ContextVariableTypes { static { List> types = Arrays.asList( new CharacterVariableContextVariableTypeConverter(), - new BooleanVariableContextVariableTypeConverter(), new ChatHistoryVariableContextVariableTypeConverter(), new TextContentVariableContextVariableTypeConverter(), new StringVariableContextVariableTypeConverter(), @@ -40,6 +40,9 @@ public class ContextVariableTypes { new InstantContextVariableTypeConverter(), new CompletionUsageContextVariableTypeConverter(), + new BooleanVariableContextVariableTypeConverter(), + new PrimativeBooleanVariableContextVariableTypeConverter(), + new NumberVariableContextVariableTypeConverter<>(Byte.class, Byte::parseByte, Number::byteValue), new NumberVariableContextVariableTypeConverter<>(byte.class, Byte::parseByte, diff --git a/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/contextvariables/converters/NumberVariableContextVariableTypeConverter.java b/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/contextvariables/converters/NumberVariableContextVariableTypeConverter.java index 1e4fb45727f5..f6bd188025ef 100644 --- a/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/contextvariables/converters/NumberVariableContextVariableTypeConverter.java +++ b/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/contextvariables/converters/NumberVariableContextVariableTypeConverter.java @@ -1,12 +1,9 @@ // Copyright (c) Microsoft. All rights reserved. package com.microsoft.semantickernel.contextvariables.converters; -import static com.microsoft.semantickernel.contextvariables.ContextVariableTypes.convert; - import com.microsoft.semantickernel.contextvariables.ContextVariableTypeConverter; import com.microsoft.semantickernel.contextvariables.ContextVariableTypes; import java.util.function.Function; -import javax.annotation.Nullable; /** * A {@link ContextVariableTypeConverter} for {@code java.lang.Number} type variables. Use, for @@ -17,9 +14,7 @@ * @see ContextVariableTypes#getGlobalVariableTypeForClass(Class) */ public class NumberVariableContextVariableTypeConverter extends - ContextVariableTypeConverter { - - private final Function fromNumber; + PrimativeVariableContextVariableTypeConverter { /** * Creates a new instance of the {@link NumberVariableContextVariableTypeConverter} class. @@ -34,58 +29,15 @@ public NumberVariableContextVariableTypeConverter( Function fromNumber) { super( clazz, - s -> convert(s, clazz), - Number::toString, - fromPromptString); - this.fromNumber = fromNumber; - } - - @Override - @Nullable - public U toObject(@Nullable Object t, Class clazz) { - if (t == null) { - return null; - } - - // Let the parent class have a crack at it first - // since someone may have installed a special converter. - U obj = super.toObject(t, clazz); - if (obj != null) { - return obj; - } - // If the type is a string, and the object is of the same type as the - // converter, then we can convert with the toPromptString method. - if (clazz == String.class && t.getClass() == super.getType()) { - return clazz.cast(toPromptString(getType().cast(t))); - } - return null; - } - - @Override - @Nullable - public T fromObject(@Nullable Object s) { - T obj = super.fromObject(s); - if (obj != null) { - return obj; - } - - if (s instanceof Number) { - return fromNumber.apply((Number) s); - } - - if (s instanceof String) { - return fromPromptString((String) s); - } - return null; - } - - @Override - @Nullable - public T fromPromptString(@Nullable String s) { - if (s != null && !s.isEmpty()) { - return super.fromPromptString(s); - } - return null; + fromPromptString, + num -> { + if (num instanceof Number) { + return fromNumber.apply((Number) num); + } else { + return null; + } + }, + Number::toString); } } diff --git a/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/contextvariables/converters/PrimativeBooleanVariableContextVariableTypeConverter.java b/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/contextvariables/converters/PrimativeBooleanVariableContextVariableTypeConverter.java new file mode 100644 index 000000000000..70d06b2ac877 --- /dev/null +++ b/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/contextvariables/converters/PrimativeBooleanVariableContextVariableTypeConverter.java @@ -0,0 +1,61 @@ +// Copyright (c) Microsoft. All rights reserved. +package com.microsoft.semantickernel.contextvariables.converters; + +import com.microsoft.semantickernel.contextvariables.ContextVariableTypeConverter; +import com.microsoft.semantickernel.contextvariables.ContextVariableTypes; +import java.util.Locale; + +/** + * A {@link ContextVariableTypeConverter} for {@link Boolean} variables. Use + * {@code ContextVariableTypes.getDefaultVariableTypeForClass(Boolean.class)} to get an instance of + * this class. + * + * @see ContextVariableTypes#getGlobalVariableTypeForClass(Class) + */ +public class PrimativeBooleanVariableContextVariableTypeConverter extends + PrimativeVariableContextVariableTypeConverter { + + /** + * Initializes a new instance of the + * {@link PrimativeBooleanVariableContextVariableTypeConverter} class. + */ + public PrimativeBooleanVariableContextVariableTypeConverter() { + super( + boolean.class, + s -> { + switch (((String) s).toLowerCase(Locale.ROOT).trim()) { + case "true": + case "on": + case "1": + case "yes": + case "y": + case "t": + case "enable": + return true; + case "false": + case "off": + case "0": + case "no": + case "n": + case "f": + case "disable": + return false; + default: + return null; + } + }, + s -> { + if (s instanceof Boolean || boolean.class.isInstance(s)) { + return s; + } + return null; + }, + s -> { + if (s == null) { + return null; + } + return ((Boolean) s).toString(); + }); + } + +} diff --git a/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/contextvariables/converters/PrimativeVariableContextVariableTypeConverter.java b/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/contextvariables/converters/PrimativeVariableContextVariableTypeConverter.java new file mode 100644 index 000000000000..de66d131266e --- /dev/null +++ b/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/contextvariables/converters/PrimativeVariableContextVariableTypeConverter.java @@ -0,0 +1,102 @@ +// Copyright (c) Microsoft. All rights reserved. +package com.microsoft.semantickernel.contextvariables.converters; + +import static com.microsoft.semantickernel.contextvariables.ContextVariableTypes.convert; + +import com.microsoft.semantickernel.contextvariables.ContextVariable; +import com.microsoft.semantickernel.contextvariables.ContextVariableTypeConverter; +import com.microsoft.semantickernel.contextvariables.ContextVariableTypes; +import java.util.function.Function; +import javax.annotation.Nullable; + +/** + * A {@link ContextVariableTypeConverter} for {@code java.lang.Number} type variables. Use, for + * example, {@code ContextVariableTypes.getGlobalVariableTypeForClass(Integer.class)} to get an + * instance of this class that works with the {@code Integer} type. + * + * @param the type of the number + * @see ContextVariableTypes#getGlobalVariableTypeForClass(Class) + */ +public class PrimativeVariableContextVariableTypeConverter extends + ContextVariableTypeConverter { + + private final Function fromObject; + + /** + * Creates a new instance of the {@link PrimativeVariableContextVariableTypeConverter} class. + * + * @param clazz the class + * @param fromPromptString the function to convert from a prompt string + * @param fromObject the function to convert from an object to primative + */ + public PrimativeVariableContextVariableTypeConverter( + Class clazz, + Function fromPromptString, + Function fromObject, + Function toPromptString) { + super( + clazz, + s -> convert(s, clazz), + toPromptString, + fromPromptString); + this.fromObject = fromObject; + } + + @Override + @Nullable + public U toObject(@Nullable Object t, Class clazz) { + if (t == null) { + return null; + } + + // Let the parent class have a crack at it first + // since someone may have installed a special converter. + U obj = super.toObject(t, clazz); + if (obj != null) { + return obj; + } + // If the type is a string, and the object is of the same type as the + // converter, then we can convert with the toPromptString method. + if (clazz == String.class && t.getClass() == super.getType()) { + return clazz.cast(toPromptString(getType().cast(t))); + } + return null; + } + + @Override + @Nullable + public T fromObject(@Nullable Object s) { + if (s instanceof ContextVariable) { + s = ((ContextVariable) s).getValue(); + } + + T obj = super.fromObject(s); + if (obj != null) { + return obj; + } + + try { + T result = fromObject.apply(s); + if (result != null) { + return result; + } + } catch (Exception e) { + // ignore + } + + if (s instanceof String) { + return fromPromptString((String) s); + } + return null; + } + + @Override + @Nullable + public T fromPromptString(@Nullable String s) { + if (s != null && !s.isEmpty()) { + return super.fromPromptString(s); + } + return null; + } + +} diff --git a/java/semantickernel-api/src/test/java/com/microsoft/semantickernel/contextvariables/converters/NumberVariableContextVariableTypeConverterTest.java b/java/semantickernel-api/src/test/java/com/microsoft/semantickernel/contextvariables/converters/NumberVariableContextVariableTypeConverterTest.java index b8852870fbf9..7a73dbdf262c 100644 --- a/java/semantickernel-api/src/test/java/com/microsoft/semantickernel/contextvariables/converters/NumberVariableContextVariableTypeConverterTest.java +++ b/java/semantickernel-api/src/test/java/com/microsoft/semantickernel/contextvariables/converters/NumberVariableContextVariableTypeConverterTest.java @@ -3,6 +3,7 @@ import static org.junit.jupiter.api.Assertions.assertEquals; +import com.microsoft.semantickernel.contextvariables.ContextVariable; import com.microsoft.semantickernel.contextvariables.ContextVariableType; import com.microsoft.semantickernel.contextvariables.ContextVariableTypeConverter; import com.microsoft.semantickernel.contextvariables.ContextVariableTypes; @@ -92,4 +93,11 @@ void testToPromptStringReturnsEmptyStringForNull() { assertEquals(expResult, result); } + @Test + void testFromStringToDouble() { + Object s = "12.0"; + double expResult = 12.0d; + ContextVariable result = ContextVariable.convert(s, double.class, null); + assertEquals(expResult, result.getValue()); + } } \ No newline at end of file diff --git a/java/semantickernel-api/src/test/java/com/microsoft/semantickernel/semanticfunctions/KernelFunctionFromMethodTest.java b/java/semantickernel-api/src/test/java/com/microsoft/semantickernel/semanticfunctions/KernelFunctionFromMethodTest.java index d234431f56f2..8ff2a5eb2869 100644 --- a/java/semantickernel-api/src/test/java/com/microsoft/semantickernel/semanticfunctions/KernelFunctionFromMethodTest.java +++ b/java/semantickernel-api/src/test/java/com/microsoft/semantickernel/semanticfunctions/KernelFunctionFromMethodTest.java @@ -3,9 +3,17 @@ import static org.junit.jupiter.api.Assertions.assertEquals; +import com.microsoft.semantickernel.Kernel; +import com.microsoft.semantickernel.contextvariables.ContextVariableTypes; +import com.microsoft.semantickernel.orchestration.FunctionResult; +import com.microsoft.semantickernel.plugin.KernelPlugin; +import com.microsoft.semantickernel.plugin.KernelPluginFactory; +import com.microsoft.semantickernel.semanticfunctions.annotations.DefineKernelFunction; +import com.microsoft.semantickernel.semanticfunctions.annotations.KernelFunctionParameter; import java.lang.reflect.Method; import java.util.Collections; import java.util.List; +import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; import reactor.core.publisher.Mono; @@ -15,6 +23,35 @@ public class KernelFunctionFromMethodTest { public KernelFunctionFromMethodTest() { } + public static class ExamplePlugin { + + @DefineKernelFunction(name = "sqrt", description = "Take the square root of a number") + public static double sqrt( + @KernelFunctionParameter(name = "number1", description = "The number to take a square root of", type = double.class) double number1) { + return Math.sqrt(number1); + } + } + + @Test + void typeConversionOnMethodCall() { + KernelPlugin plugin = KernelPluginFactory.createFromObject( + new ExamplePlugin(), "ExamplePlugin"); + + Kernel kernel = Kernel.builder().build(); + + FunctionResult result = plugin + .get("sqrt") + .invokeAsync(kernel) + .withResultType(ContextVariableTypes.getGlobalVariableTypeForClass(String.class)) + .withArguments( + KernelFunctionArguments.builder() + .withVariable("number1", "12.0") + .build()) + .block(); + + Assertions.assertEquals("3.4641016151377544", result.getResult()); + } + @Test void testCreate() throws Exception { Method method = String.class.getMethod("concat", String.class); From e48a75445c3344b0e8d558c68381cef920875d6d Mon Sep 17 00:00:00 2001 From: John Oliver <1615532+johnoliver@users.noreply.github.com> Date: Thu, 21 Mar 2024 18:55:25 +0000 Subject: [PATCH 07/15] Add UsingTheKernel --- .../samples/documentationexamples/Plugin.java | 6 + .../SerializingPrompts.java | 118 +++++++++++------- .../documentationexamples/UsingTheKernel.java | 98 +++++++++++++++ .../Plugins/Prompts/Chat/config.json | 33 +++++ .../Plugins/Prompts/Chat/skprompt.txt | 3 + .../WriterPlugin/ShortPoem/config.json | 21 ++++ .../WriterPlugin/ShortPoem/skprompt.txt | 2 + .../ContextVariableTypes.java | 4 +- ...rVariableContextVariableTypeConverter.java | 2 +- ...VariableContextVariableTypeConverter.java} | 8 +- ...VariableContextVariableTypeConverter.java} | 16 +-- .../orchestration/FunctionInvocation.java | 11 ++ .../semanticfunctions/InputVariable.java | 3 +- 13 files changed, 264 insertions(+), 61 deletions(-) create mode 100644 java/samples/sample-code/src/main/java/com/microsoft/semantickernel/samples/documentationexamples/UsingTheKernel.java create mode 100644 java/samples/sample-code/src/main/resources/Plugins/Prompts/Chat/config.json create mode 100644 java/samples/sample-code/src/main/resources/Plugins/Prompts/Chat/skprompt.txt create mode 100644 java/samples/sample-code/src/main/resources/Plugins/WriterPlugin/ShortPoem/config.json create mode 100644 java/samples/sample-code/src/main/resources/Plugins/WriterPlugin/ShortPoem/skprompt.txt rename java/semantickernel-api/src/main/java/com/microsoft/semantickernel/contextvariables/converters/{PrimativeBooleanVariableContextVariableTypeConverter.java => PrimitiveBooleanVariableContextVariableTypeConverter.java} (87%) rename java/semantickernel-api/src/main/java/com/microsoft/semantickernel/contextvariables/converters/{PrimativeVariableContextVariableTypeConverter.java => PrimitiveVariableContextVariableTypeConverter.java} (78%) diff --git a/java/samples/sample-code/src/main/java/com/microsoft/semantickernel/samples/documentationexamples/Plugin.java b/java/samples/sample-code/src/main/java/com/microsoft/semantickernel/samples/documentationexamples/Plugin.java index 541832e2c599..8cabc738ce2c 100644 --- a/java/samples/sample-code/src/main/java/com/microsoft/semantickernel/samples/documentationexamples/Plugin.java +++ b/java/samples/sample-code/src/main/java/com/microsoft/semantickernel/samples/documentationexamples/Plugin.java @@ -38,10 +38,12 @@ public static void main(String[] args) { OpenAIAsyncClient client; if (AZURE_CLIENT_KEY != null && CLIENT_ENDPOINT != null) { + // client = new OpenAIClientBuilder() .credential(new AzureKeyCredential(AZURE_CLIENT_KEY)) .endpoint(CLIENT_ENDPOINT) .buildAsyncClient(); + // } else if (CLIENT_KEY != null) { client = new OpenAIClientBuilder() .credential(new KeyCredential(CLIENT_KEY)) @@ -51,6 +53,8 @@ public static void main(String[] args) { return; } + // + ChatCompletionService chatCompletionService = ChatCompletionService.builder() .withModelId(MODEL_ID) .withOpenAIAsyncClient(client) @@ -64,6 +68,8 @@ public static void main(String[] args) { .withPlugin(plugin) .build(); + // + Scanner scanner = new Scanner(INPUT); var history = new ChatHistory(); diff --git a/java/samples/sample-code/src/main/java/com/microsoft/semantickernel/samples/documentationexamples/SerializingPrompts.java b/java/samples/sample-code/src/main/java/com/microsoft/semantickernel/samples/documentationexamples/SerializingPrompts.java index f914b689d363..7602ecf9378f 100644 --- a/java/samples/sample-code/src/main/java/com/microsoft/semantickernel/samples/documentationexamples/SerializingPrompts.java +++ b/java/samples/sample-code/src/main/java/com/microsoft/semantickernel/samples/documentationexamples/SerializingPrompts.java @@ -6,80 +6,114 @@ import com.azure.core.credential.AzureKeyCredential; import com.azure.core.credential.KeyCredential; import com.microsoft.semantickernel.Kernel; +import com.microsoft.semantickernel.contextvariables.ContextVariableTypes; +import com.microsoft.semantickernel.plugin.KernelPluginFactory; +import com.microsoft.semantickernel.samples.plugins.ConversationSummaryPlugin; +import com.microsoft.semantickernel.semanticfunctions.KernelFunctionArguments; import com.microsoft.semantickernel.services.chatcompletion.ChatCompletionService; - -import java.util.Arrays; -import java.util.List; +import com.microsoft.semantickernel.services.chatcompletion.ChatHistory; +import java.io.InputStream; import java.util.Scanner; public class SerializingPrompts { + + public static InputStream INPUT = System.in; + + // CLIENT_KEY is for an OpenAI 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 AZURE_CLIENT_KEY is set + // AZURE_CLIENT_KEY and CLIENT_ENDPOINT are for an Azure client + // CLIENT_ENDPOINT required if AZURE_CLIENT_KEY is set + private static final String AZURE_CLIENT_KEY = System.getenv("AZURE_CLIENT_KEY"); private static final String CLIENT_ENDPOINT = System.getenv("CLIENT_ENDPOINT"); - private static final String MODEL_ID = System.getenv().getOrDefault("MODEL_ID", - "gpt-35-turbo-2"); + + private static final String MODEL_ID = System.getenv() + .getOrDefault("MODEL_ID", "gpt-3.5-turbo"); public static void main(String[] args) { - System.out.println("======== Serializing Prompts ========"); + + System.out.println("======== Serializing ========"); + OpenAIAsyncClient client; - if (AZURE_CLIENT_KEY != null) { + if (AZURE_CLIENT_KEY != null && CLIENT_ENDPOINT != null) { client = new OpenAIClientBuilder() .credential(new AzureKeyCredential(AZURE_CLIENT_KEY)) .endpoint(CLIENT_ENDPOINT) .buildAsyncClient(); - } else { + } else if (CLIENT_KEY != null) { client = new OpenAIClientBuilder() .credential(new KeyCredential(CLIENT_KEY)) .buildAsyncClient(); + } else { + System.out.println("No client key found"); + return; } - Kernel kernel = Kernel.builder() - .withAIService(ChatCompletionService.class, ChatCompletionService.builder() - .withModelId(MODEL_ID) - .withOpenAIAsyncClient(client) - .build()) + var chatCompletionService = ChatCompletionService.builder() + .withModelId(MODEL_ID) + .withOpenAIAsyncClient(client) .build(); - // Load prompts - // This part is omitted as it requires a specific implementation to load prompts from a directory + var conversationSummaryPlugin = KernelPluginFactory + .createFromObject(new ConversationSummaryPlugin(), "ConversationSummaryPlugin"); - // Load prompt from YAML - // This part is omitted as it requires a specific implementation to load prompts from a YAML file + var chatPlugin = KernelPluginFactory.importPluginFromResourcesDirectory( + "Plugins", + "Prompts", + "Chat", + null, + String.class); - // Create choices - List choices = Arrays.asList("ContinueConversation", "EndConversation"); + var kernel = Kernel.builder() + .withAIService(ChatCompletionService.class, chatCompletionService) + .withPlugin(conversationSummaryPlugin) + .withPlugin(chatPlugin) + .build(); - // Create few-shot examples - // This part is omitted as it requires a specific implementation to create few-shot examples + ChatHistory chatHistory = new ChatHistory(); - // Create chat history - // This part is omitted as it requires a specific implementation to create chat history + chatCompletionService + .getChatMessageContentsAsync( + chatHistory, + kernel, + null) + .block(); - // Start the chat loop - Scanner scanner = new Scanner(System.in); - System.out.print("User > "); - String userInput; - while (!(userInput = scanner.nextLine()).isEmpty()) { - // Invoke handlebars prompt - // This part is omitted as it requires a specific implementation to invoke a prompt + Scanner scanner = new Scanner(INPUT); - // End the chat if the intent is "Stop" - // This part is omitted as it requires a specific implementation to handle the intent + ChatHistory history = new ChatHistory( + "This example is a chat bot that answers questions. Ask a question to get started. Type 'exit' to quit."); - // Get chat response - // This part is omitted as it requires a specific implementation to get the chat response + System.out.println( + "This example is a chat bot that answers questions. Ask a question to get started. Type 'exit' to quit."); - // Stream the response - // This part is omitted as it requires a specific implementation to stream the response + // Start the chat loop + while (true) { + // Get user input + System.out.println("User > "); + var request = scanner.nextLine(); + if ("exit".equalsIgnoreCase(request)) { + break; + } + + var chatResult = kernel + .invokeAsync(chatPlugin.get("chat")) + .withArguments( + KernelFunctionArguments.builder() + .withVariable("request", request) + .withVariable("history", history) + .build()) + .withResultType(ContextVariableTypes.getGlobalVariableTypeForClass(String.class)) + .block(); + + String message = chatResult.getResult(); + System.out.println("Assistant: " + message); + System.out.println(); // Append to history - // This part is omitted as it requires a specific implementation to append to the history - - // Get user input again - System.out.print("User > "); + history.addUserMessage(request); + history.addAssistantMessage(message); } } } \ No newline at end of file diff --git a/java/samples/sample-code/src/main/java/com/microsoft/semantickernel/samples/documentationexamples/UsingTheKernel.java b/java/samples/sample-code/src/main/java/com/microsoft/semantickernel/samples/documentationexamples/UsingTheKernel.java new file mode 100644 index 000000000000..e19a2ad14552 --- /dev/null +++ b/java/samples/sample-code/src/main/java/com/microsoft/semantickernel/samples/documentationexamples/UsingTheKernel.java @@ -0,0 +1,98 @@ +// Copyright (c) Microsoft. All rights reserved. +package com.microsoft.semantickernel.samples.documentationexamples; + +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.plugin.KernelPlugin; +import com.microsoft.semantickernel.plugin.KernelPluginFactory; +import com.microsoft.semantickernel.samples.plugins.MathPlugin; +import com.microsoft.semantickernel.semanticfunctions.KernelFunctionArguments; +import com.microsoft.semantickernel.services.chatcompletion.ChatCompletionService; + +public class UsingTheKernel { + + // CLIENT_KEY is for an OpenAI client + private static final String CLIENT_KEY = System.getenv("CLIENT_KEY"); + + // AZURE_CLIENT_KEY and CLIENT_ENDPOINT are for an Azure client + // CLIENT_ENDPOINT required if AZURE_CLIENT_KEY is set + private static final String AZURE_CLIENT_KEY = System.getenv("AZURE_CLIENT_KEY"); + private static final String CLIENT_ENDPOINT = System.getenv("CLIENT_ENDPOINT"); + + private static final String MODEL_ID = System.getenv() + .getOrDefault("MODEL_ID", "gpt-3.5-turbo"); + + public static void main(String[] args) { + System.out.println("======== UsingTheKernel ========"); + + OpenAIAsyncClient client; + + if (AZURE_CLIENT_KEY != null && CLIENT_ENDPOINT != null) { + // + client = new OpenAIClientBuilder() + .credential(new AzureKeyCredential(AZURE_CLIENT_KEY)) + .endpoint(CLIENT_ENDPOINT) + .buildAsyncClient(); + // + } else if (CLIENT_KEY != null) { + client = new OpenAIClientBuilder() + .credential(new KeyCredential(CLIENT_KEY)) + .buildAsyncClient(); + } else { + System.out.println("No client key found"); + return; + } + + // + + ChatCompletionService chatCompletionService = ChatCompletionService.builder() + .withModelId(MODEL_ID) + .withOpenAIAsyncClient(client) + .build(); + + KernelPlugin mathPlugin = KernelPluginFactory.createFromObject( + new MathPlugin(), "MathPlugin"); + + var poemPlugin = KernelPluginFactory.importPluginFromResourcesDirectory( + "Plugins", + "WriterPlugin", + "ShortPoem", + null, + String.class); + + Kernel kernel = Kernel.builder() + .withAIService(ChatCompletionService.class, chatCompletionService) + .withPlugin(mathPlugin) + .withPlugin(poemPlugin) + .build(); + + // + + // + var result = poemPlugin.get("ShortPoem") + .invokeAsync(kernel) + .withArguments( + KernelFunctionArguments.builder() + .withInput("The cat sat on a mat") + .build()) + .withResultType(String.class) + .block(); + System.out.println(result.getResult()); + // + + // + var root = mathPlugin.get("sqrt") + .invokeAsync(kernel) + .withArguments( + KernelFunctionArguments.builder() + .withInput(12) + .build()) + .withResultType(Double.class) + .block(); + System.out.println("Square root of 12 is: " + root.getResult()); + // + } +} diff --git a/java/samples/sample-code/src/main/resources/Plugins/Prompts/Chat/config.json b/java/samples/sample-code/src/main/resources/Plugins/Prompts/Chat/config.json new file mode 100644 index 000000000000..93e812d1841d --- /dev/null +++ b/java/samples/sample-code/src/main/resources/Plugins/Prompts/Chat/config.json @@ -0,0 +1,33 @@ +{ + "schema": 1, + "type": "completion", + "description": "Creates a chat response to the user", + "execution_settings": { + "default": { + "max_tokens": 1000, + "temperature": 0 + }, + "gpt-3.5-turbo": { + "model_id": "gpt-3.5-turbo-0613", + "max_tokens": 4000, + "temperature": 0.1 + }, + "gpt-4": { + "model_id": "gpt-4-1106-preview", + "max_tokens": 8000, + "temperature": 0.3 + } + }, + "input_variables": [ + { + "name": "request", + "description": "The user's request.", + "required": true + }, + { + "name": "history", + "description": "The history of the conversation.", + "required": true + } + ] +} \ No newline at end of file diff --git a/java/samples/sample-code/src/main/resources/Plugins/Prompts/Chat/skprompt.txt b/java/samples/sample-code/src/main/resources/Plugins/Prompts/Chat/skprompt.txt new file mode 100644 index 000000000000..82ae0242ca86 --- /dev/null +++ b/java/samples/sample-code/src/main/resources/Plugins/Prompts/Chat/skprompt.txt @@ -0,0 +1,3 @@ +{{ConversationSummaryPlugin.SummarizeConversation $history}} +User: {{$request}} +Assistant: \ No newline at end of file diff --git a/java/samples/sample-code/src/main/resources/Plugins/WriterPlugin/ShortPoem/config.json b/java/samples/sample-code/src/main/resources/Plugins/WriterPlugin/ShortPoem/config.json new file mode 100644 index 000000000000..c03553d76657 --- /dev/null +++ b/java/samples/sample-code/src/main/resources/Plugins/WriterPlugin/ShortPoem/config.json @@ -0,0 +1,21 @@ +{ + "schema": 1, + "type": "completion", + "description": "Turn a scenario into a short and entertaining poem.", + "completion": { + "max_tokens": 200, + "temperature": 0.5, + "top_p": 0.0, + "presence_penalty": 0.0, + "frequency_penalty": 0.0 + }, + "input": { + "parameters": [ + { + "name": "input", + "description": "The scenario to turn into a poem.", + "defaultValue": "" + } + ] + } +} diff --git a/java/samples/sample-code/src/main/resources/Plugins/WriterPlugin/ShortPoem/skprompt.txt b/java/samples/sample-code/src/main/resources/Plugins/WriterPlugin/ShortPoem/skprompt.txt new file mode 100644 index 000000000000..d85146e03127 --- /dev/null +++ b/java/samples/sample-code/src/main/resources/Plugins/WriterPlugin/ShortPoem/skprompt.txt @@ -0,0 +1,2 @@ +Generate a short funny poem or limerick to explain the given event. Be creative and be funny. Let your imagination run wild. +Event: {{$input}} diff --git a/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/contextvariables/ContextVariableTypes.java b/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/contextvariables/ContextVariableTypes.java index 8736cb1154c2..f6e8da171632 100644 --- a/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/contextvariables/ContextVariableTypes.java +++ b/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/contextvariables/ContextVariableTypes.java @@ -9,7 +9,7 @@ import com.microsoft.semantickernel.contextvariables.converters.DateTimeContextVariableTypeConverter; import com.microsoft.semantickernel.contextvariables.converters.InstantContextVariableTypeConverter; import com.microsoft.semantickernel.contextvariables.converters.NumberVariableContextVariableTypeConverter; -import com.microsoft.semantickernel.contextvariables.converters.PrimativeBooleanVariableContextVariableTypeConverter; +import com.microsoft.semantickernel.contextvariables.converters.PrimitiveBooleanVariableContextVariableTypeConverter; import com.microsoft.semantickernel.contextvariables.converters.StringVariableContextVariableTypeConverter; import com.microsoft.semantickernel.contextvariables.converters.TextContentVariableContextVariableTypeConverter; import com.microsoft.semantickernel.contextvariables.converters.VoidVariableContextVariableTypeConverter; @@ -41,7 +41,7 @@ public class ContextVariableTypes { new CompletionUsageContextVariableTypeConverter(), new BooleanVariableContextVariableTypeConverter(), - new PrimativeBooleanVariableContextVariableTypeConverter(), + new PrimitiveBooleanVariableContextVariableTypeConverter(), new NumberVariableContextVariableTypeConverter<>(Byte.class, Byte::parseByte, Number::byteValue), diff --git a/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/contextvariables/converters/NumberVariableContextVariableTypeConverter.java b/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/contextvariables/converters/NumberVariableContextVariableTypeConverter.java index f6bd188025ef..2720d1765535 100644 --- a/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/contextvariables/converters/NumberVariableContextVariableTypeConverter.java +++ b/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/contextvariables/converters/NumberVariableContextVariableTypeConverter.java @@ -14,7 +14,7 @@ * @see ContextVariableTypes#getGlobalVariableTypeForClass(Class) */ public class NumberVariableContextVariableTypeConverter extends - PrimativeVariableContextVariableTypeConverter { + PrimitiveVariableContextVariableTypeConverter { /** * Creates a new instance of the {@link NumberVariableContextVariableTypeConverter} class. diff --git a/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/contextvariables/converters/PrimativeBooleanVariableContextVariableTypeConverter.java b/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/contextvariables/converters/PrimitiveBooleanVariableContextVariableTypeConverter.java similarity index 87% rename from java/semantickernel-api/src/main/java/com/microsoft/semantickernel/contextvariables/converters/PrimativeBooleanVariableContextVariableTypeConverter.java rename to java/semantickernel-api/src/main/java/com/microsoft/semantickernel/contextvariables/converters/PrimitiveBooleanVariableContextVariableTypeConverter.java index 70d06b2ac877..0124fe346ded 100644 --- a/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/contextvariables/converters/PrimativeBooleanVariableContextVariableTypeConverter.java +++ b/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/contextvariables/converters/PrimitiveBooleanVariableContextVariableTypeConverter.java @@ -12,14 +12,14 @@ * * @see ContextVariableTypes#getGlobalVariableTypeForClass(Class) */ -public class PrimativeBooleanVariableContextVariableTypeConverter extends - PrimativeVariableContextVariableTypeConverter { +public class PrimitiveBooleanVariableContextVariableTypeConverter extends + PrimitiveVariableContextVariableTypeConverter { /** * Initializes a new instance of the - * {@link PrimativeBooleanVariableContextVariableTypeConverter} class. + * {@link PrimitiveBooleanVariableContextVariableTypeConverter} class. */ - public PrimativeBooleanVariableContextVariableTypeConverter() { + public PrimitiveBooleanVariableContextVariableTypeConverter() { super( boolean.class, s -> { diff --git a/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/contextvariables/converters/PrimativeVariableContextVariableTypeConverter.java b/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/contextvariables/converters/PrimitiveVariableContextVariableTypeConverter.java similarity index 78% rename from java/semantickernel-api/src/main/java/com/microsoft/semantickernel/contextvariables/converters/PrimativeVariableContextVariableTypeConverter.java rename to java/semantickernel-api/src/main/java/com/microsoft/semantickernel/contextvariables/converters/PrimitiveVariableContextVariableTypeConverter.java index de66d131266e..01c9a1d6c463 100644 --- a/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/contextvariables/converters/PrimativeVariableContextVariableTypeConverter.java +++ b/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/contextvariables/converters/PrimitiveVariableContextVariableTypeConverter.java @@ -5,31 +5,25 @@ import com.microsoft.semantickernel.contextvariables.ContextVariable; import com.microsoft.semantickernel.contextvariables.ContextVariableTypeConverter; -import com.microsoft.semantickernel.contextvariables.ContextVariableTypes; import java.util.function.Function; import javax.annotation.Nullable; /** - * A {@link ContextVariableTypeConverter} for {@code java.lang.Number} type variables. Use, for - * example, {@code ContextVariableTypes.getGlobalVariableTypeForClass(Integer.class)} to get an - * instance of this class that works with the {@code Integer} type. - * - * @param the type of the number - * @see ContextVariableTypes#getGlobalVariableTypeForClass(Class) + * A {@link ContextVariableTypeConverter} for primative variables. */ -public class PrimativeVariableContextVariableTypeConverter extends +public class PrimitiveVariableContextVariableTypeConverter extends ContextVariableTypeConverter { private final Function fromObject; /** - * Creates a new instance of the {@link PrimativeVariableContextVariableTypeConverter} class. + * Creates a new instance of the {@link PrimitiveVariableContextVariableTypeConverter} class. * * @param clazz the class * @param fromPromptString the function to convert from a prompt string - * @param fromObject the function to convert from an object to primative + * @param fromObject the function to convert from an object to primative */ - public PrimativeVariableContextVariableTypeConverter( + public PrimitiveVariableContextVariableTypeConverter( Class clazz, Function fromPromptString, Function fromObject, diff --git a/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/orchestration/FunctionInvocation.java b/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/orchestration/FunctionInvocation.java index 38864bcae8fd..bfeaa62bfde1 100644 --- a/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/orchestration/FunctionInvocation.java +++ b/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/orchestration/FunctionInvocation.java @@ -180,6 +180,17 @@ public FunctionInvocation withResultType(ContextVariableType resultTyp .withTypes(contextVariableTypes); } + /** + * Supply the result type of function invocation. Uses the global context variable types. + * + * @param resultType The arguments to supply to the function invocation. + * @param The type of the result of the function invocation. + * @return A new {@code FunctionInvocation} for fluent chaining. + */ + public FunctionInvocation withResultType(Class resultType) { + return withResultType(ContextVariableTypes.getGlobalVariableTypeForClass(resultType)); + } + /** * Add a kernel hook to the function invocation. * diff --git a/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/semanticfunctions/InputVariable.java b/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/semanticfunctions/InputVariable.java index 33e94baceb7b..e0c6a1a62f35 100644 --- a/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/semanticfunctions/InputVariable.java +++ b/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/semanticfunctions/InputVariable.java @@ -1,6 +1,7 @@ // Copyright (c) Microsoft. All rights reserved. package com.microsoft.semantickernel.semanticfunctions; +import com.fasterxml.jackson.annotation.JsonAlias; import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonProperty; import com.microsoft.semantickernel.plugin.KernelPluginFactory; @@ -35,7 +36,7 @@ public InputVariable( @JsonProperty("type") String type, @JsonProperty("description") @Nullable String description, @JsonProperty("default") @Nullable String defaultValue, - @JsonProperty("is_required") boolean isRequired) { + @JsonAlias({ "required", "is_required" }) @JsonProperty("is_required") boolean isRequired) { this.name = name; this.description = description; this.defaultValue = defaultValue; From c2fc5aa9cbfd47012b656de12d584032539d591e Mon Sep 17 00:00:00 2001 From: Milder Hernandez Cagua Date: Fri, 22 Mar 2024 00:02:02 -0700 Subject: [PATCH 08/15] Update CreatingFunctions.java --- .../CreatingFunctions.java | 41 ++++++++----------- 1 file changed, 18 insertions(+), 23 deletions(-) diff --git a/java/samples/sample-code/src/main/java/com/microsoft/semantickernel/samples/documentationexamples/CreatingFunctions.java b/java/samples/sample-code/src/main/java/com/microsoft/semantickernel/samples/documentationexamples/CreatingFunctions.java index bba6f367be39..eb4ccf44f2ff 100644 --- a/java/samples/sample-code/src/main/java/com/microsoft/semantickernel/samples/documentationexamples/CreatingFunctions.java +++ b/java/samples/sample-code/src/main/java/com/microsoft/semantickernel/samples/documentationexamples/CreatingFunctions.java @@ -28,7 +28,7 @@ public class CreatingFunctions { private static final String MODEL_ID = System.getenv().getOrDefault("MODEL_ID", "gpt-35-turbo-2"); public static void main(String[] args) { - System.out.println("======== Prompts ========"); + System.out.println("======== Creating Functions ========"); OpenAIAsyncClient client; if (AZURE_CLIENT_KEY != null) { @@ -42,6 +42,7 @@ public static void main(String[] args) { .buildAsyncClient(); } + // Kernel kernel = Kernel.builder() .withAIService(ChatCompletionService.class, ChatCompletionService.builder() .withModelId(MODEL_ID) @@ -55,10 +56,11 @@ public static void main(String[] args) { .invokeAsync(kernel.getFunction("MathPlugin", "sqrt")) .withArguments(KernelFunctionArguments .builder() - .withVariable("number1", "12.0") + .withVariable("number1", 12.0) .build()) .block(); System.out.println("The square root of 12 is " + answer.getResult() + "."); + // // Create chat history ChatCompletionService chat = OpenAIChatCompletion.builder() @@ -72,36 +74,29 @@ public static void main(String[] args) { ChatHistory history = new ChatHistory(); // Start the conversation - System.out.print("user > "); + System.out.print("User > "); Scanner scanner = new Scanner(System.in); String userInput; while (!(userInput = scanner.nextLine()).isEmpty()) { history.addUserMessage(userInput); - reply(kernel, chat, history); - messageOutput(history); + var toolCallBehavior = ToolCallBehavior.allowAllKernelFunctions(true); - System.out.print("user > "); - } - } + var reply = chat.getChatMessageContentsAsync(history, kernel, InvocationContext.builder() + .withToolCallBehavior(toolCallBehavior) + .build()) + .block(); - private static void reply(Kernel kernel, ChatCompletionService chatGPT, ChatHistory chatHistory) { - // Enable auto function calling - var toolCallBehavior = ToolCallBehavior.allowAllKernelFunctions(true); + StringBuilder message = new StringBuilder(); + reply.forEach(chatMessageContent -> message.append(chatMessageContent.getContent())); - var reply = chatGPT.getChatMessageContentsAsync(chatHistory, kernel, InvocationContext.builder() - .withToolCallBehavior(toolCallBehavior) - .build()) - .block(); + System.out.println("Assistant" + " > " + message); - StringBuilder message = new StringBuilder(); - reply.forEach(chatMessageContent -> message.append(chatMessageContent.getContent())); - chatHistory.addAssistantMessage(message.toString()); - } + // Add the message from the agent to the chat history + history.addAssistantMessage(message.toString()); - private static void messageOutput(ChatHistory chatHistory) { - var message = chatHistory.getLastMessage().get(); - System.out.println(message.getAuthorRole() + " > " + message.getContent()); - System.out.println("------------------------"); + // Get user input again + System.out.print("User > "); + } } } \ No newline at end of file From 85055c36878d9146d8130159d438fc47adb4b206 Mon Sep 17 00:00:00 2001 From: Milder Hernandez Cagua Date: Fri, 22 Mar 2024 02:32:17 -0700 Subject: [PATCH 09/15] Update doc files --- .../CreatingFunctions.java | 6 ++- .../documentationexamples/Prompts.java | 46 ++++++++-------- .../samples/plugins/MathPlugin.java | 53 ++++++++++--------- 3 files changed, 53 insertions(+), 52 deletions(-) diff --git a/java/samples/sample-code/src/main/java/com/microsoft/semantickernel/samples/documentationexamples/CreatingFunctions.java b/java/samples/sample-code/src/main/java/com/microsoft/semantickernel/samples/documentationexamples/CreatingFunctions.java index eb4ccf44f2ff..7920bddcc38c 100644 --- a/java/samples/sample-code/src/main/java/com/microsoft/semantickernel/samples/documentationexamples/CreatingFunctions.java +++ b/java/samples/sample-code/src/main/java/com/microsoft/semantickernel/samples/documentationexamples/CreatingFunctions.java @@ -28,7 +28,7 @@ public class CreatingFunctions { private static final String MODEL_ID = System.getenv().getOrDefault("MODEL_ID", "gpt-35-turbo-2"); public static void main(String[] args) { - System.out.println("======== Creating Functions ========"); + System.out.println("======== Prompts ========"); OpenAIAsyncClient client; if (AZURE_CLIENT_KEY != null) { @@ -71,6 +71,7 @@ public static void main(String[] args) { System.out.println("Chat content:"); System.out.println("------------------------"); + // ChatHistory history = new ChatHistory(); // Start the conversation @@ -90,7 +91,7 @@ public static void main(String[] args) { StringBuilder message = new StringBuilder(); reply.forEach(chatMessageContent -> message.append(chatMessageContent.getContent())); - System.out.println("Assistant" + " > " + message); + System.out.println("Assistant" + " > " + message.toString()); // Add the message from the agent to the chat history history.addAssistantMessage(message.toString()); @@ -98,5 +99,6 @@ public static void main(String[] args) { // Get user input again System.out.print("User > "); } + // } } \ No newline at end of file diff --git a/java/samples/sample-code/src/main/java/com/microsoft/semantickernel/samples/documentationexamples/Prompts.java b/java/samples/sample-code/src/main/java/com/microsoft/semantickernel/samples/documentationexamples/Prompts.java index 25a1753e1186..bec173e9365e 100644 --- a/java/samples/sample-code/src/main/java/com/microsoft/semantickernel/samples/documentationexamples/Prompts.java +++ b/java/samples/sample-code/src/main/java/com/microsoft/semantickernel/samples/documentationexamples/Prompts.java @@ -1,4 +1,3 @@ -// Copyright (c) Microsoft. All rights reserved. package com.microsoft.semantickernel.samples.documentationexamples; import com.azure.ai.openai.OpenAIAsyncClient; @@ -14,8 +13,7 @@ public class Prompts { // 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-3.5-turbo"); + private static final String MODEL_ID = System.getenv().getOrDefault("MODEL_ID", "gpt-35-turbo-2"); public static void main(String[] args) { System.out.println("======== Prompts ========"); @@ -23,22 +21,22 @@ public static void main(String[] args) { if (AZURE_CLIENT_KEY != null) { client = new OpenAIClientBuilder() - .credential(new AzureKeyCredential(AZURE_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(); } // Kernel kernel = Kernel.builder() - .withAIService(ChatCompletionService.class, ChatCompletionService.builder() - .withModelId(MODEL_ID) - .withOpenAIAsyncClient(client) - .build()) - .build(); + .withAIService(ChatCompletionService.class, ChatCompletionService.builder() + .withModelId(MODEL_ID) + .withOpenAIAsyncClient(client) + .build()) + .build(); // // 0.0 Initial prompt @@ -47,16 +45,15 @@ public static void main(String[] args) { What is the intent of this request? %s """.formatted(request); - /* - * Uncomment this block to make this example interactive - * // - * System.out.println("Your request: "); - * String request = new Scanner(System.in).nextLine(); - * String prompt = """ - * What is the intent of this request? %s - * You can choose between SendEmail, SendMessage, CompleteTask, CreateDocument. - * """.formatted(request); - * // + /* Uncomment this block to make this example interactive + // + System.out.println("Your request: "); + String request = new Scanner(System.in).nextLine(); + String prompt = """ + What is the intent of this request? %s + You can choose between SendEmail, SendMessage, CompleteTask, CreateDocument. + """.formatted(request); + // */ System.out.println("0.0 Initial prompt"); @@ -120,8 +117,7 @@ public static void main(String[] args) { ## Intent """.formatted(request); // - System.out - .println("2.1 Add structure to the output with formatting (using Markdown and JSON)"); + System.out.println("2.1 Add structure to the output with formatting (using Markdown and JSON)"); System.out.println(kernel.invokePromptAsync(prompt).block().getResult()); // 3.0 Provide examples with few-shot prompting diff --git a/java/samples/sample-code/src/main/java/com/microsoft/semantickernel/samples/plugins/MathPlugin.java b/java/samples/sample-code/src/main/java/com/microsoft/semantickernel/samples/plugins/MathPlugin.java index c939019b5095..8dab3bf5e099 100644 --- a/java/samples/sample-code/src/main/java/com/microsoft/semantickernel/samples/plugins/MathPlugin.java +++ b/java/samples/sample-code/src/main/java/com/microsoft/semantickernel/samples/plugins/MathPlugin.java @@ -1,117 +1,120 @@ -// Copyright (c) Microsoft. All rights reserved. package com.microsoft.semantickernel.samples.plugins; import com.microsoft.semantickernel.semanticfunctions.annotations.DefineKernelFunction; import com.microsoft.semantickernel.semanticfunctions.annotations.KernelFunctionParameter; +// public class MathPlugin { +// + // @DefineKernelFunction(name = "sqrt", description = "Take the square root of a number") public static double sqrt( - @KernelFunctionParameter(name = "number1", description = "The number to take a square root of", type = double.class) double number1) { + @KernelFunctionParameter(name = "number1", description = "The number to take a square root of", type = double.class) double number1) { return Math.sqrt(number1); } + // @DefineKernelFunction(name = "add", description = "Add two numbers") public static double add( - @KernelFunctionParameter(name = "number1", description = "The first number to add", type = double.class) double number1, - @KernelFunctionParameter(name = "number2", description = "The second number to add", type = double.class) double number2) { + @KernelFunctionParameter(name = "number1", description = "The first number to add", type = double.class) double number1, + @KernelFunctionParameter(name = "number2", description = "The second number to add", type = double.class) double number2) { return number1 + number2; } @DefineKernelFunction(name = "subtract", description = "Subtract two numbers") public static double subtract( - @KernelFunctionParameter(name = "number1", description = "The first number to subtract from", type = double.class) double number1, - @KernelFunctionParameter(name = "number2", description = "The second number to subtract away", type = double.class) double number2) { + @KernelFunctionParameter(name = "number1", description = "The first number to subtract from", type = double.class) double number1, + @KernelFunctionParameter(name = "number2", description = "The second number to subtract away", type = double.class) double number2) { return number1 - number2; } @DefineKernelFunction(name = "multiply", description = "Multiply two numbers. When increasing by a percentage, don't forget to add 1 to the percentage.") public static double multiply( - @KernelFunctionParameter(name = "number1", description = "The first number to multiply", type = double.class) double number1, - @KernelFunctionParameter(name = "number2", description = "The second number to multiply", type = double.class) double number2) { + @KernelFunctionParameter(name = "number1", description = "The first number to multiply", type = double.class) double number1, + @KernelFunctionParameter(name = "number2", description = "The second number to multiply", type = double.class) double number2) { return number1 * number2; } @DefineKernelFunction(name = "divide", description = "Divide two numbers") public static double divide( - @KernelFunctionParameter(name = "number1", description = "The first number to divide from", type = double.class) double number1, - @KernelFunctionParameter(name = "number2", description = "The second number to divide by", type = double.class) double number2) { + @KernelFunctionParameter(name = "number1", description = "The first number to divide from", type = double.class) double number1, + @KernelFunctionParameter(name = "number2", description = "The second number to divide by", type = double.class) double number2) { return number1 / number2; } @DefineKernelFunction(name = "power", description = "Raise a number to a power") public static double power( - @KernelFunctionParameter(name = "number1", description = "The number to raise", type = double.class) double number1, - @KernelFunctionParameter(name = "number2", description = "The power to raise the number to", type = double.class) double number2) { + @KernelFunctionParameter(name = "number1", description = "The number to raise", type = double.class) double number1, + @KernelFunctionParameter(name = "number2", description = "The power to raise the number to", type = double.class) double number2) { return Math.pow(number1, number2); } @DefineKernelFunction(name = "log", description = "Take the log of a number") public static double log( - @KernelFunctionParameter(name = "number1", description = "The number to take the log of", type = double.class) double number1, - @KernelFunctionParameter(name = "number2", description = "The base of the log", type = double.class) double number2) { + @KernelFunctionParameter(name = "number1", description = "The number to take the log of", type = double.class) double number1, + @KernelFunctionParameter(name = "number2", description = "The base of the log", type = double.class) double number2) { return Math.log(number1) / Math.log(number2); } @DefineKernelFunction(name = "round", description = "Round a number to the target number of decimal places") public static double round( - @KernelFunctionParameter(name = "number1", description = "The number to round", type = double.class) double number1, - @KernelFunctionParameter(name = "number2", description = "The number of decimal places to round to", type = double.class) int number2) { + @KernelFunctionParameter(name = "number1", description = "The number to round", type = double.class) double number1, + @KernelFunctionParameter(name = "number2", description = "The number of decimal places to round to", type = double.class) int number2) { return Math.round(number1 * Math.pow(10, number2)) / Math.pow(10, number2); } @DefineKernelFunction(name = "abs", description = "Take the absolute value of a number") public static double abs( - @KernelFunctionParameter(name = "number1", description = "The number to take the absolute value of", type = double.class) double number1) { + @KernelFunctionParameter(name = "number1", description = "The number to take the absolute value of", type = double.class) double number1) { return Math.abs(number1); } @DefineKernelFunction(name = "floor", description = "Take the floor of a number") public static double floor( - @KernelFunctionParameter(name = "number1", description = "The number to take the floor of", type = double.class) double number1) { + @KernelFunctionParameter(name = "number1", description = "The number to take the floor of", type = double.class) double number1) { return Math.floor(number1); } @DefineKernelFunction(name = "ceiling", description = "Take the ceiling of a number") public static double ceiling( - @KernelFunctionParameter(name = "number1", description = "The number to take the ceiling of", type = double.class) double number1) { + @KernelFunctionParameter(name = "number1", description = "The number to take the ceiling of", type = double.class) double number1) { return Math.ceil(number1); } @DefineKernelFunction(name = "sin", description = "Take the sine of a number") public static double sin( - @KernelFunctionParameter(name = "number1", description = "The number to take the sine of", type = double.class) double number1) { + @KernelFunctionParameter(name = "number1", description = "The number to take the sine of", type = double.class) double number1) { return Math.sin(number1); } @DefineKernelFunction(name = "cos", description = "Take the cosine of a number") public static double cos( - @KernelFunctionParameter(name = "number1", description = "The number to take the cosine of", type = double.class) double number1) { + @KernelFunctionParameter(name = "number1", description = "The number to take the cosine of", type = double.class) double number1) { return Math.cos(number1); } @DefineKernelFunction(name = "tan", description = "Take the tangent of a number") public static double tan( - @KernelFunctionParameter(name = "number1", description = "The number to take the tangent of", type = double.class) double number1) { + @KernelFunctionParameter(name = "number1", description = "The number to take the tangent of", type = double.class) double number1) { return Math.tan(number1); } @DefineKernelFunction(name = "asin", description = "Take the arcsine of a number") public static double asin( - @KernelFunctionParameter(name = "number1", description = "The number to take the arcsine of", type = double.class) double number1) { + @KernelFunctionParameter(name = "number1", description = "The number to take the arcsine of", type = double.class) double number1) { return Math.asin(number1); } @DefineKernelFunction(name = "acos", description = "Take the arccosine of a number") public static double acos( - @KernelFunctionParameter(name = "number1", description = "The number to take the arccosine of", type = double.class) double number1) { + @KernelFunctionParameter(name = "number1", description = "The number to take the arccosine of", type = double.class) double number1) { return Math.acos(number1); } @DefineKernelFunction(name = "atan", description = "Take the arctangent of a number") public static double atan( - @KernelFunctionParameter(name = "number1", description = "The number to take the arctangent of", type = double.class) double number1) { + @KernelFunctionParameter(name = "number1", description = "The number to take the arctangent of", type = double.class) double number1) { return Math.atan(number1); } } From b3d406d67358bada90a7aedece1b6c617a9c77e4 Mon Sep 17 00:00:00 2001 From: Milder Hernandez Cagua Date: Fri, 22 Mar 2024 04:16:31 -0700 Subject: [PATCH 10/15] Update doc files --- .../CreatingFunctions.java | 2 +- .../SerializingPrompts.java | 54 +++++++++++++------ .../Plugins/Prompts/Chat/config.json | 33 ------------ .../Plugins/Prompts/Chat/skprompt.txt | 3 -- .../Plugins}/Prompts/chat/config.json | 0 .../Plugins}/Prompts/chat/skprompt.txt | 0 .../Plugins/Prompts}/getIntent.prompt.yaml | 2 + .../handlebars/HandlebarsPromptTemplate.java | 2 +- 8 files changed, 42 insertions(+), 54 deletions(-) delete mode 100644 java/samples/sample-code/src/main/resources/Plugins/Prompts/Chat/config.json delete mode 100644 java/samples/sample-code/src/main/resources/Plugins/Prompts/Chat/skprompt.txt rename java/samples/sample-code/src/main/{java/com/microsoft/semantickernel/samples/plugins => resources/Plugins}/Prompts/chat/config.json (100%) rename java/samples/sample-code/src/main/{java/com/microsoft/semantickernel/samples/plugins => resources/Plugins}/Prompts/chat/skprompt.txt (100%) rename java/samples/sample-code/src/main/{java/com/microsoft/semantickernel/samples/documentationexamples => resources/Plugins/Prompts}/getIntent.prompt.yaml (97%) diff --git a/java/samples/sample-code/src/main/java/com/microsoft/semantickernel/samples/documentationexamples/CreatingFunctions.java b/java/samples/sample-code/src/main/java/com/microsoft/semantickernel/samples/documentationexamples/CreatingFunctions.java index 7920bddcc38c..8562c8808c79 100644 --- a/java/samples/sample-code/src/main/java/com/microsoft/semantickernel/samples/documentationexamples/CreatingFunctions.java +++ b/java/samples/sample-code/src/main/java/com/microsoft/semantickernel/samples/documentationexamples/CreatingFunctions.java @@ -91,7 +91,7 @@ public static void main(String[] args) { StringBuilder message = new StringBuilder(); reply.forEach(chatMessageContent -> message.append(chatMessageContent.getContent())); - System.out.println("Assistant" + " > " + message.toString()); + System.out.println("Assistant" + " > " + message); // Add the message from the agent to the chat history history.addAssistantMessage(message.toString()); diff --git a/java/samples/sample-code/src/main/java/com/microsoft/semantickernel/samples/documentationexamples/SerializingPrompts.java b/java/samples/sample-code/src/main/java/com/microsoft/semantickernel/samples/documentationexamples/SerializingPrompts.java index c047d6e1bca0..0c2cd8c019c5 100644 --- a/java/samples/sample-code/src/main/java/com/microsoft/semantickernel/samples/documentationexamples/SerializingPrompts.java +++ b/java/samples/sample-code/src/main/java/com/microsoft/semantickernel/samples/documentationexamples/SerializingPrompts.java @@ -5,6 +5,7 @@ import com.azure.core.credential.AzureKeyCredential; import com.azure.core.credential.KeyCredential; import com.microsoft.semantickernel.Kernel; +import com.microsoft.semantickernel.orchestration.InvocationContext; import com.microsoft.semantickernel.plugin.KernelPluginFactory; import com.microsoft.semantickernel.samples.plugins.ConversationSummaryPlugin; import com.microsoft.semantickernel.semanticfunctions.HandlebarsPromptTemplateFactory; @@ -32,7 +33,7 @@ public class SerializingPrompts { private static final String CLIENT_ENDPOINT = System.getenv("CLIENT_ENDPOINT"); private static final String MODEL_ID = System.getenv().getOrDefault("MODEL_ID", "gpt-35-turbo-2"); - private static final String SAMPLES_DIR = "java/samples/sample-code/src/main/java/com/microsoft/semantickernel/samples"; + private static final String PLUGINS_DIR = "java/samples/sample-code/src/main/resources/Plugins"; public static void main(String[] args) throws IOException { System.out.println("======== Serializing Prompts ========"); @@ -49,6 +50,20 @@ public static void main(String[] args) throws IOException { .buildAsyncClient(); } + // Create few-shot examples + ChatHistory continueConversation = new ChatHistory(false); + continueConversation.addMessage(AuthorRole.USER, "Can you send a very quick approval to the marketing team?"); + continueConversation.addMessage(AuthorRole.SYSTEM, "Intent:"); + continueConversation.addMessage(AuthorRole.ASSISTANT, "ContinueConversation"); + ChatHistory endConversation = new ChatHistory(false); + endConversation.addMessage(AuthorRole.USER, "Can you send the full update to the marketing team?"); + endConversation.addMessage(AuthorRole.SYSTEM, "Intent:"); + endConversation.addMessage(AuthorRole.ASSISTANT, "EndConversation"); + + List fewShotExamples = List.of(continueConversation, endConversation); + + // + // Create Kernel Kernel kernel = Kernel.builder() .withAIService(ChatCompletionService.class, ChatCompletionService.builder() .withModelId(MODEL_ID) @@ -59,28 +74,18 @@ public static void main(String[] args) throws IOException { // Load prompts var prompts = KernelPluginFactory.importPluginFromDirectory( - Path.of(SAMPLES_DIR, "plugins"), "Prompts", null); + Path.of(PLUGINS_DIR), "Prompts", null); // Load prompt from YAML + // var getIntent = KernelFunctionYaml.fromPromptYaml( - Files.readString(Path.of(SAMPLES_DIR, "documentationexamples", "getIntent.prompt.yaml")), + Files.readString(Path.of(PLUGINS_DIR, "Prompts", "getIntent.prompt.yaml")), new HandlebarsPromptTemplateFactory()); + // // Create choices List choices = Arrays.asList("ContinueConversation", "EndConversation"); - // Create few-shot examples - ChatHistory continueConversation = new ChatHistory(); - continueConversation.addMessage(AuthorRole.USER, "Can you send a very quick approval to the marketing team?"); - continueConversation.addMessage(AuthorRole.SYSTEM, "Intent:"); - continueConversation.addMessage(AuthorRole.ASSISTANT, "ContinueConversation"); - ChatHistory endConversation = new ChatHistory(); - endConversation.addMessage(AuthorRole.USER, "Can you send the full update to the marketing team?"); - endConversation.addMessage(AuthorRole.SYSTEM, "Intent:"); - endConversation.addMessage(AuthorRole.ASSISTANT, "EndConversation"); - - List fewShotExamples = List.of(continueConversation, endConversation); - // Create chat history ChatHistory history = new ChatHistory(); @@ -90,6 +95,8 @@ public static void main(String[] args) throws IOException { String userInput; while (!(userInput = scanner.nextLine()).isEmpty()) { // Invoke handlebars prompt + + // var intent = kernel.invokeAsync(getIntent) .withArguments(KernelFunctionArguments.builder() .withVariable("request", userInput) @@ -98,16 +105,31 @@ public static void main(String[] args) throws IOException { .withVariable("fewShotExamples", fewShotExamples) .build()) .block(); + // // End the chat if the intent is "Stop" if (intent.getResult().equals("EndConversation")) { break; } - // WIP + var reply = kernel.invokeAsync(prompts.get("Chat")) + .withArguments(KernelFunctionArguments.builder() + .withVariable("request", userInput) + .withVariable("history", + String.join("\n", history.getMessages().stream().map(m -> m.getAuthorRole() + " > " + m.getContent()).toList())) + .build()) + .withResultType(String.class) + .block().getResult(); + + System.out.println("Assistant" + " > " + reply); + + // Append to history + history.addUserMessage(userInput); + history.addAssistantMessage(reply); // Get user input again System.out.print("User > "); } + // } } \ No newline at end of file diff --git a/java/samples/sample-code/src/main/resources/Plugins/Prompts/Chat/config.json b/java/samples/sample-code/src/main/resources/Plugins/Prompts/Chat/config.json deleted file mode 100644 index 93e812d1841d..000000000000 --- a/java/samples/sample-code/src/main/resources/Plugins/Prompts/Chat/config.json +++ /dev/null @@ -1,33 +0,0 @@ -{ - "schema": 1, - "type": "completion", - "description": "Creates a chat response to the user", - "execution_settings": { - "default": { - "max_tokens": 1000, - "temperature": 0 - }, - "gpt-3.5-turbo": { - "model_id": "gpt-3.5-turbo-0613", - "max_tokens": 4000, - "temperature": 0.1 - }, - "gpt-4": { - "model_id": "gpt-4-1106-preview", - "max_tokens": 8000, - "temperature": 0.3 - } - }, - "input_variables": [ - { - "name": "request", - "description": "The user's request.", - "required": true - }, - { - "name": "history", - "description": "The history of the conversation.", - "required": true - } - ] -} \ No newline at end of file diff --git a/java/samples/sample-code/src/main/resources/Plugins/Prompts/Chat/skprompt.txt b/java/samples/sample-code/src/main/resources/Plugins/Prompts/Chat/skprompt.txt deleted file mode 100644 index 82ae0242ca86..000000000000 --- a/java/samples/sample-code/src/main/resources/Plugins/Prompts/Chat/skprompt.txt +++ /dev/null @@ -1,3 +0,0 @@ -{{ConversationSummaryPlugin.SummarizeConversation $history}} -User: {{$request}} -Assistant: \ No newline at end of file diff --git a/java/samples/sample-code/src/main/java/com/microsoft/semantickernel/samples/plugins/Prompts/chat/config.json b/java/samples/sample-code/src/main/resources/Plugins/Prompts/chat/config.json similarity index 100% rename from java/samples/sample-code/src/main/java/com/microsoft/semantickernel/samples/plugins/Prompts/chat/config.json rename to java/samples/sample-code/src/main/resources/Plugins/Prompts/chat/config.json diff --git a/java/samples/sample-code/src/main/java/com/microsoft/semantickernel/samples/plugins/Prompts/chat/skprompt.txt b/java/samples/sample-code/src/main/resources/Plugins/Prompts/chat/skprompt.txt similarity index 100% rename from java/samples/sample-code/src/main/java/com/microsoft/semantickernel/samples/plugins/Prompts/chat/skprompt.txt rename to java/samples/sample-code/src/main/resources/Plugins/Prompts/chat/skprompt.txt diff --git a/java/samples/sample-code/src/main/java/com/microsoft/semantickernel/samples/documentationexamples/getIntent.prompt.yaml b/java/samples/sample-code/src/main/resources/Plugins/Prompts/getIntent.prompt.yaml similarity index 97% rename from java/samples/sample-code/src/main/java/com/microsoft/semantickernel/samples/documentationexamples/getIntent.prompt.yaml rename to java/samples/sample-code/src/main/resources/Plugins/Prompts/getIntent.prompt.yaml index 1c16c2e49055..f897333689b4 100644 --- a/java/samples/sample-code/src/main/java/com/microsoft/semantickernel/samples/documentationexamples/getIntent.prompt.yaml +++ b/java/samples/sample-code/src/main/resources/Plugins/Prompts/getIntent.prompt.yaml @@ -5,11 +5,13 @@ template: | Do not explain the reasoning, just reply back with the intent. If you are unsure, reply with {{choices.[0]}}. Choices: {{choices}}. + BEGIN EXAMPLES {{#each fewShotExamples}} {{#each this}} {{content}} {{/each}} {{/each}} + END EXAMPLES {{ConversationSummaryPlugin.SummarizeConversation history}} diff --git a/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/templateengine/handlebars/HandlebarsPromptTemplate.java b/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/templateengine/handlebars/HandlebarsPromptTemplate.java index 2681526f07c4..f67ae40cab41 100644 --- a/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/templateengine/handlebars/HandlebarsPromptTemplate.java +++ b/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/templateengine/handlebars/HandlebarsPromptTemplate.java @@ -274,7 +274,7 @@ private static void addFunctionHelpers(Kernel kernel, Handlebars handlebars, String functionName = kernelFunction.getName(); String pluginName = plugin.getName(); handlebars.registerHelper( - ToolCallBehavior.formFullFunctionName(pluginName, functionName), + String.format("%s.%s", pluginName, functionName), functionInvokeHelper(kernel, kernelFunction, context)); }); From 6d31de4e0f8f6f85e25e6d200f6f809982452450 Mon Sep 17 00:00:00 2001 From: Milder Hernandez Cagua Date: Fri, 22 Mar 2024 11:43:33 -0700 Subject: [PATCH 11/15] Update Prompts.java --- .../samples/documentationexamples/Prompts.java | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/java/samples/sample-code/src/main/java/com/microsoft/semantickernel/samples/documentationexamples/Prompts.java b/java/samples/sample-code/src/main/java/com/microsoft/semantickernel/samples/documentationexamples/Prompts.java index bec173e9365e..734739bf6db5 100644 --- a/java/samples/sample-code/src/main/java/com/microsoft/semantickernel/samples/documentationexamples/Prompts.java +++ b/java/samples/sample-code/src/main/java/com/microsoft/semantickernel/samples/documentationexamples/Prompts.java @@ -41,18 +41,13 @@ public static void main(String[] args) { // 0.0 Initial prompt String request = "I want to send an email to the marketing team celebrating their recent milestone."; - String prompt = """ - What is the intent of this request? %s - """.formatted(request); + String prompt = "What is the intent of this request? %s".formatted(request); /* Uncomment this block to make this example interactive // System.out.println("Your request: "); String request = new Scanner(System.in).nextLine(); - String prompt = """ - What is the intent of this request? %s - You can choose between SendEmail, SendMessage, CompleteTask, CreateDocument. - """.formatted(request); + String prompt = "What is the intent of this request? %s".formatted(request); // */ From 7e66bf7e6496f4c14f49e9cff6955f6f8e56307f Mon Sep 17 00:00:00 2001 From: Milder Hernandez Cagua Date: Fri, 22 Mar 2024 18:05:48 -0700 Subject: [PATCH 12/15] Update getIntent.prompt.yaml --- .../main/resources/Plugins/Prompts/getIntent.prompt.yaml | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/java/samples/sample-code/src/main/resources/Plugins/Prompts/getIntent.prompt.yaml b/java/samples/sample-code/src/main/resources/Plugins/Prompts/getIntent.prompt.yaml index f897333689b4..96393916479e 100644 --- a/java/samples/sample-code/src/main/resources/Plugins/Prompts/getIntent.prompt.yaml +++ b/java/samples/sample-code/src/main/resources/Plugins/Prompts/getIntent.prompt.yaml @@ -35,4 +35,8 @@ execution_settings: gpt-3.5-turbo: model_id: gpt-35-turbo-2 max_tokens: 10 - temperature: 0.2 \ No newline at end of file + temperature: 0.2 + gpt-4: + model_id: gpt-4-1106-preview + max_tokens: 10 + temperature: 0.2 \ No newline at end of file From ddd803a9142ad3da0d68b1865f7c5f3cc7884c80 Mon Sep 17 00:00:00 2001 From: Milder Hernandez Cagua Date: Sat, 23 Mar 2024 00:46:54 -0700 Subject: [PATCH 13/15] Update Plugin.java --- .../samples/documentationexamples/Plugin.java | 58 +++++++++---------- 1 file changed, 28 insertions(+), 30 deletions(-) diff --git a/java/samples/sample-code/src/main/java/com/microsoft/semantickernel/samples/documentationexamples/Plugin.java b/java/samples/sample-code/src/main/java/com/microsoft/semantickernel/samples/documentationexamples/Plugin.java index 8cabc738ce2c..7542d801c71e 100644 --- a/java/samples/sample-code/src/main/java/com/microsoft/semantickernel/samples/documentationexamples/Plugin.java +++ b/java/samples/sample-code/src/main/java/com/microsoft/semantickernel/samples/documentationexamples/Plugin.java @@ -18,19 +18,13 @@ import java.util.Scanner; public class Plugin { - - public static InputStream INPUT = System.in; - // CLIENT_KEY is for an OpenAI client private static final String CLIENT_KEY = System.getenv("CLIENT_KEY"); - - // AZURE_CLIENT_KEY and CLIENT_ENDPOINT are for an Azure client - // CLIENT_ENDPOINT required if AZURE_CLIENT_KEY is set private static final String AZURE_CLIENT_KEY = System.getenv("AZURE_CLIENT_KEY"); - private static final String CLIENT_ENDPOINT = System.getenv("CLIENT_ENDPOINT"); - private static final String MODEL_ID = System.getenv() - .getOrDefault("MODEL_ID", "gpt-3.5-turbo"); + // 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-2"); public static void main(String[] args) { System.out.println("======== Plugin ========"); @@ -53,8 +47,8 @@ public static void main(String[] args) { return; } - // + // ChatCompletionService chatCompletionService = ChatCompletionService.builder() .withModelId(MODEL_ID) .withOpenAIAsyncClient(client) @@ -67,20 +61,19 @@ public static void main(String[] args) { .withAIService(ChatCompletionService.class, chatCompletionService) .withPlugin(plugin) .build(); + // - // - - Scanner scanner = new Scanner(INPUT); - + // + // Create chat history var history = new ChatHistory(); - // Start the chat loop - while (true) { - // - // Get user input - System.out.print("User > "); - String request = scanner.nextLine(); - history.addUserMessage(request); + // Start the conversation + System.out.print("User > "); + Scanner scanner = new Scanner(System.in); + String userInput; + while (!(userInput = scanner.nextLine()).isEmpty()) { + // Add user input to history + history.addUserMessage(userInput); // Enable auto function calling var invocationContext = InvocationContext.builder() @@ -88,6 +81,7 @@ public static void main(String[] args) { ToolCallBehavior.allowAllKernelFunctions(true)) .build(); + // Get the response from the AI var result = chatCompletionService .getChatMessageContentsAsync( history, @@ -98,31 +92,35 @@ public static void main(String[] args) { String message = result.get(result.size() - 1).getContent(); System.out.printf("Assistant > %s%n", message); - // Append to history + // Add the message from the agent to the chat history history.addAssistantMessage(message); } + // } + // public static class LightPlugin { public boolean isOn = false; - @DefineKernelFunction(name = "changeState", description = "Changes the state of the light.'") + @DefineKernelFunction(name = "getState", description = "Gets the state of the light.'") + String getState() { + return isOn ? "on" : "off"; + } + @DefineKernelFunction(name = "changeState", description = "Changes the state of the light.'") public String changeState( - @KernelFunctionParameter(name = "newState", description = "The new state of the light, boolean true==on, false==off.", type = boolean.class) boolean newState) { + @KernelFunctionParameter(name = "newState", + description = "The new state of the light, boolean true==on, false==off.", + type = boolean.class) boolean newState) { + this.isOn = newState; String state = getState(); // Print the state to the console System.out.printf("[Light is now %s]%n", state); - return state; } - - String getState() { - return isOn ? "on" : "off"; - - } } + // } From c3257bb1560045d12388d14769e357e3c7d7f337 Mon Sep 17 00:00:00 2001 From: Milder Hernandez Cagua Date: Sat, 23 Mar 2024 00:51:58 -0700 Subject: [PATCH 14/15] Update CreatingFunctions.java --- .../CreatingFunctions.java | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/java/samples/sample-code/src/main/java/com/microsoft/semantickernel/samples/documentationexamples/CreatingFunctions.java b/java/samples/sample-code/src/main/java/com/microsoft/semantickernel/samples/documentationexamples/CreatingFunctions.java index 8562c8808c79..201d972ab1ea 100644 --- a/java/samples/sample-code/src/main/java/com/microsoft/semantickernel/samples/documentationexamples/CreatingFunctions.java +++ b/java/samples/sample-code/src/main/java/com/microsoft/semantickernel/samples/documentationexamples/CreatingFunctions.java @@ -81,16 +81,17 @@ public static void main(String[] args) { while (!(userInput = scanner.nextLine()).isEmpty()) { history.addUserMessage(userInput); - var toolCallBehavior = ToolCallBehavior.allowAllKernelFunctions(true); - - var reply = chat.getChatMessageContentsAsync(history, kernel, InvocationContext.builder() - .withToolCallBehavior(toolCallBehavior) - .build()) + // Enable auto function calling + var invocationContext = InvocationContext.builder() + .withToolCallBehavior( + ToolCallBehavior.allowAllKernelFunctions(true)) + .build(); + + // Get the response from the AI + var reply = chat.getChatMessageContentsAsync(history, kernel, invocationContext) .block(); - StringBuilder message = new StringBuilder(); - reply.forEach(chatMessageContent -> message.append(chatMessageContent.getContent())); - + String message = reply.get(reply.size() - 1).getContent(); System.out.println("Assistant" + " > " + message); // Add the message from the agent to the chat history From 20a11b78916b78c9faab853a0cc5bdef54b260a1 Mon Sep 17 00:00:00 2001 From: Milder Hernandez Cagua Date: Sat, 23 Mar 2024 01:02:00 -0700 Subject: [PATCH 15/15] Add suggestions --- .../documentationexamples/Prompts.java | 27 ++++++++++++------- 1 file changed, 18 insertions(+), 9 deletions(-) diff --git a/java/samples/sample-code/src/main/java/com/microsoft/semantickernel/samples/documentationexamples/Prompts.java b/java/samples/sample-code/src/main/java/com/microsoft/semantickernel/samples/documentationexamples/Prompts.java index 734739bf6db5..a96a9783e288 100644 --- a/java/samples/sample-code/src/main/java/com/microsoft/semantickernel/samples/documentationexamples/Prompts.java +++ b/java/samples/sample-code/src/main/java/com/microsoft/semantickernel/samples/documentationexamples/Prompts.java @@ -53,7 +53,8 @@ public static void main(String[] args) { System.out.println("0.0 Initial prompt"); // - System.out.println(kernel.invokePromptAsync(prompt).block().getResult()); + var result = kernel.invokePromptAsync(prompt).block().getResult(); + System.out.println(result); // // 1.0 Make the prompt more specific @@ -65,7 +66,8 @@ public static void main(String[] args) { """.formatted(request); // System.out.println("1.0 Make the prompt more specific"); - System.out.println(kernel.invokePromptAsync(prompt).block().getResult()); + result = kernel.invokePromptAsync(prompt).block().getResult(); + System.out.println(result); // 2.0 Add structure to the output with formatting ///////////////////////////////////////////////////////////////// @@ -78,7 +80,8 @@ public static void main(String[] args) { """.formatted(request); // System.out.println("2.0 Add structure to the output with formatting"); - System.out.println(kernel.invokePromptAsync(prompt).block().getResult()); + result = kernel.invokePromptAsync(prompt).block().getResult(); + System.out.println(result); // 2.1 Add structure to the output with formatting (using Markdown and JSON) ///////////////////////////////////////////////////////////////// @@ -113,7 +116,8 @@ public static void main(String[] args) { """.formatted(request); // System.out.println("2.1 Add structure to the output with formatting (using Markdown and JSON)"); - System.out.println(kernel.invokePromptAsync(prompt).block().getResult()); + result = kernel.invokePromptAsync(prompt).block().getResult(); + System.out.println(result); // 3.0 Provide examples with few-shot prompting ///////////////////////////////////////////////////////////////// @@ -133,7 +137,8 @@ public static void main(String[] args) { """.formatted(request); // System.out.println("3.0 Provide examples with few-shot prompting"); - System.out.println(kernel.invokePromptAsync(prompt).block().getResult()); + result = kernel.invokePromptAsync(prompt).block().getResult(); + System.out.println(result); // 4.0 Tell the AI what to do to avoid doing something wrong ///////////////////////////////////////////////////////////////// @@ -154,7 +159,8 @@ public static void main(String[] args) { """.formatted(request); // System.out.println("4.0 Tell the AI what to do to avoid doing something wrong"); - System.out.println(kernel.invokePromptAsync(prompt).block().getResult()); + result = kernel.invokePromptAsync(prompt).block().getResult(); + System.out.println(result); // 5.0 Provide context to the AI ///////////////////////////////////////////////////////////////// @@ -177,7 +183,8 @@ public static void main(String[] args) { """.formatted(history, request); // System.out.println("5.0 Provide context to the AI"); - System.out.println(kernel.invokePromptAsync(prompt).block().getResult()); + result = kernel.invokePromptAsync(prompt).block().getResult(); + System.out.println(result); // 6.0 Using message roles in chat completion prompts ///////////////////////////////////////////////////////////////// @@ -202,7 +209,8 @@ public static void main(String[] args) { """.formatted(history, request); // System.out.println("6.0 Using message roles in chat completion prompts"); - System.out.println(kernel.invokePromptAsync(prompt).block().getResult()); + result = kernel.invokePromptAsync(prompt).block().getResult(); + System.out.println(result); // 7.0 Give your AI words of encouragement // @@ -227,6 +235,7 @@ public static void main(String[] args) { """.formatted(history, request); // System.out.println("7.0 Give your AI words of encouragement"); - System.out.println(kernel.invokePromptAsync(prompt).block().getResult()); + result = kernel.invokePromptAsync(prompt).block().getResult(); + System.out.println(result); } } \ No newline at end of file