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 6703c646e6f0..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 @@ -1,4 +1,3 @@ -// Copyright (c) Microsoft. All rights reserved. package com.microsoft.semantickernel.samples.documentationexamples; import java.util.Scanner; @@ -9,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; @@ -23,8 +25,7 @@ public class CreatingFunctions { // 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"); + 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 ========"); @@ -32,42 +33,45 @@ 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()) - .withPlugin(KernelPluginFactory.createFromObject(new MathPlugin(), "MathPlugin")) - .build(); + .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(); + .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(); + .withModelId(MODEL_ID) + .withOpenAIAsyncClient(client) + .build(); System.out.println("Chat content:"); System.out.println("------------------------"); + // ChatHistory history = new ChatHistory(); // Start the conversation @@ -77,26 +81,25 @@ public static void main(String[] args) { while (!(userInput = scanner.nextLine()).isEmpty()) { history.addUserMessage(userInput); - reply(chat, history); - messageOutput(history); - } - } + // Enable auto function calling + var invocationContext = InvocationContext.builder() + .withToolCallBehavior( + ToolCallBehavior.allowAllKernelFunctions(true)) + .build(); - private static void reply(ChatCompletionService chatGPT, ChatHistory chatHistory) { - // Enable auto function calling - var toolCallBehavior = ToolCallBehavior.allowAllKernelFunctions(true); + // Get the response from the AI + var reply = chat.getChatMessageContentsAsync(history, kernel, invocationContext) + .block(); - var reply = chatGPT.getChatMessageContentsAsync(chatHistory, null, null) - .block(); + String message = reply.get(reply.size() - 1).getContent(); + 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 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..7542d801c71e --- /dev/null +++ b/java/samples/sample-code/src/main/java/com/microsoft/semantickernel/samples/documentationexamples/Plugin.java @@ -0,0 +1,126 @@ +// 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 { + // 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 + 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 ========"); + + 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(); + // + + // + // Create chat history + var history = new ChatHistory(); + + // 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() + .withToolCallBehavior( + ToolCallBehavior.allowAllKernelFunctions(true)) + .build(); + + // Get the response from the AI + var result = chatCompletionService + .getChatMessageContentsAsync( + history, + kernel, + invocationContext) + .block(); + + String message = result.get(result.size() - 1).getContent(); + System.out.printf("Assistant > %s%n", message); + + // Add the message from the agent to the chat history + history.addAssistantMessage(message); + } + // + } + + // + public static class LightPlugin { + + public boolean isOn = false; + + @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) { + + this.isOn = newState; + String state = getState(); + + // Print the state to the console + System.out.printf("[Light is now %s]%n", state); + return state; + } + } + // +} 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 607fa94f00bb..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 @@ -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,45 +21,40 @@ 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 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); - * // + 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".formatted(request); + // */ 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 @@ -73,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 ///////////////////////////////////////////////////////////////// @@ -86,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) ///////////////////////////////////////////////////////////////// @@ -120,9 +115,9 @@ 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(kernel.invokePromptAsync(prompt).block().getResult()); + System.out.println("2.1 Add structure to the output with formatting (using Markdown and JSON)"); + result = kernel.invokePromptAsync(prompt).block().getResult(); + System.out.println(result); // 3.0 Provide examples with few-shot prompting ///////////////////////////////////////////////////////////////// @@ -142,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 ///////////////////////////////////////////////////////////////// @@ -163,12 +159,13 @@ 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 ///////////////////////////////////////////////////////////////// - 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,12 +183,13 @@ 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 ///////////////////////////////////////////////////////////////// - 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". @@ -211,11 +209,12 @@ 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 - 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". @@ -236,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 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..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 @@ -1,4 +1,3 @@ -// Copyright (c) Microsoft. All rights reserved. package com.microsoft.semantickernel.samples.documentationexamples; import com.azure.ai.openai.OpenAIAsyncClient; @@ -6,8 +5,22 @@ 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; +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; @@ -18,45 +31,63 @@ public class SerializingPrompts { // 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"); + 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 PLUGINS_DIR = "java/samples/sample-code/src/main/resources/Plugins"; + + public static void main(String[] args) throws IOException { 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(); + .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(); } + // 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) - .withOpenAIAsyncClient(client) - .build()) - .build(); + .withAIService(ChatCompletionService.class, ChatCompletionService.builder() + .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(PLUGINS_DIR), "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(PLUGINS_DIR, "Prompts", "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 - // 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); @@ -64,22 +95,41 @@ 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 + if (intent.getResult().equals("EndConversation")) { + break; + } - // Get chat response - // This part is omitted as it requires a specific implementation to get the chat response + 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(); - // Stream the response - // This part is omitted as it requires a specific implementation to stream the response + System.out.println("Assistant" + " > " + reply); // Append to history - // This part is omitted as it requires a specific implementation to append to the 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/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/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); } } 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 79% 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 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/resources/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 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/resources/Plugins/Prompts/getIntent.prompt.yaml b/java/samples/sample-code/src/main/resources/Plugins/Prompts/getIntent.prompt.yaml new file mode 100644 index 000000000000..96393916479e --- /dev/null +++ b/java/samples/sample-code/src/main/resources/Plugins/Prompts/getIntent.prompt.yaml @@ -0,0 +1,42 @@ +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}}. + + BEGIN EXAMPLES + {{#each fewShotExamples}} + {{#each this}} + {{content}} + {{/each}} + {{/each}} + END EXAMPLES + + {{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 + gpt-4: + model_id: gpt-4-1106-preview + max_tokens: 10 + temperature: 0.2 \ 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 319e7289c316..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,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.PrimitiveBooleanVariableContextVariableTypeConverter; 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 PrimitiveBooleanVariableContextVariableTypeConverter(), + 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..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 @@ -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; + PrimitiveVariableContextVariableTypeConverter { /** * 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/PrimitiveBooleanVariableContextVariableTypeConverter.java b/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/contextvariables/converters/PrimitiveBooleanVariableContextVariableTypeConverter.java new file mode 100644 index 000000000000..0124fe346ded --- /dev/null +++ b/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/contextvariables/converters/PrimitiveBooleanVariableContextVariableTypeConverter.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 PrimitiveBooleanVariableContextVariableTypeConverter extends + PrimitiveVariableContextVariableTypeConverter { + + /** + * Initializes a new instance of the + * {@link PrimitiveBooleanVariableContextVariableTypeConverter} class. + */ + public PrimitiveBooleanVariableContextVariableTypeConverter() { + 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/PrimitiveVariableContextVariableTypeConverter.java b/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/contextvariables/converters/PrimitiveVariableContextVariableTypeConverter.java new file mode 100644 index 000000000000..01c9a1d6c463 --- /dev/null +++ b/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/contextvariables/converters/PrimitiveVariableContextVariableTypeConverter.java @@ -0,0 +1,96 @@ +// 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 java.util.function.Function; +import javax.annotation.Nullable; + +/** + * A {@link ContextVariableTypeConverter} for primative variables. + */ +public class PrimitiveVariableContextVariableTypeConverter extends + ContextVariableTypeConverter { + + private final Function fromObject; + + /** + * 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 + */ + public PrimitiveVariableContextVariableTypeConverter( + 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/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; 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)); }); 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);