Java: Add documentation examples (#5595)

### Motivation and Context

Fixes https://github.com/microsoft/semantic-kernel/issues/5205
Fixes https://github.com/microsoft/semantic-kernel/issues/5209
Fixes https://github.com/microsoft/semantic-kernel/issues/5212
Fixes https://github.com/microsoft/semantic-kernel/issues/5213
Fixes https://github.com/microsoft/semantic-kernel/issues/5203

<!-- Thank you for your contribution to the semantic-kernel repo!
Please help reviewers and future users, providing the following
information:
  1. Why is this change required?
  2. What problem does it solve?
  3. What scenario does it contribute to?
  4. If it fixes an open issue, please link to the issue here.
-->

### Description

<!-- Describe your changes, the overall approach, the underlying design.
These notes will help understanding how your code works. Thanks! -->

### Contribution Checklist

<!-- Before submitting this PR, please make sure: -->

- [ ] The code builds clean without any errors or warnings
- [ ] The PR follows the [SK Contribution
Guidelines](https://github.com/microsoft/semantic-kernel/blob/main/CONTRIBUTING.md)
and the [pre-submission formatting
script](https://github.com/microsoft/semantic-kernel/blob/main/CONTRIBUTING.md#development-scripts)
raises no violations
- [ ] All unit tests pass, and I have added new tests where possible
- [ ] I didn't break anyone 😄

---------

Co-authored-by: John Oliver <[email protected]>
This commit is contained in:
Milder Hernandez
2024-03-25 15:37:02 +00:00
committed by GitHub
co-authored by John Oliver
parent 9b181aba9a
commit fa94b269e1
20 changed files with 712 additions and 200 deletions
@@ -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.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.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 class CreatingFunctions {
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();
}
// <RunNativeFunction>
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() + ".");
// </RunNativeFunction>
// 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("------------------------");
// <Conversation>
ChatHistory history = new ChatHistory();
// Start the conversation
@@ -77,26 +81,25 @@ public class CreatingFunctions {
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();
// Get the response from the AI
var reply = chat.getChatMessageContentsAsync(history, kernel, invocationContext)
.block();
String message = reply.get(reply.size() - 1).getContent();
System.out.println("Assistant" + " > " + message);
// Add the message from the agent to the chat history
history.addAssistantMessage(message.toString());
// Get user input again
System.out.print("User > ");
}
}
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("------------------------");
// </Conversation>
}
}
@@ -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) {
// <build_client>
client = new OpenAIClientBuilder()
.credential(new AzureKeyCredential(AZURE_CLIENT_KEY))
.endpoint(CLIENT_ENDPOINT)
.buildAsyncClient();
// </build_client>
} else if (CLIENT_KEY != null) {
client = new OpenAIClientBuilder()
.credential(new KeyCredential(CLIENT_KEY))
.buildAsyncClient();
} else {
System.out.println("No client key found");
return;
}
// <KernelCreation>
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();
// </KernelCreation>
// <Chat>
// 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);
}
// </Chat>
}
// <LightPlugin>
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;
}
}
// </LightPlugin>
}
@@ -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 class Prompts {
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();
}
// <KernelCreation>
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();
// </KernelCreation>
// 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
* // <InitialPrompt>
* 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);
* // </InitialPrompt>
/* Uncomment this block to make this example interactive
// <InitialPrompt>
System.out.println("Your request: ");
String request = new Scanner(System.in).nextLine();
String prompt = "What is the intent of this request? %s".formatted(request);
// </InitialPrompt>
*/
System.out.println("0.0 Initial prompt");
// <InvokeInitialPrompt>
System.out.println(kernel.invokePromptAsync(prompt).block().getResult());
var result = kernel.invokePromptAsync(prompt).block().getResult();
System.out.println(result);
// </InvokeInitialPrompt>
// 1.0 Make the prompt more specific
@@ -73,7 +66,8 @@ public class Prompts {
""".formatted(request);
// </MoreSpecificPrompt>
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 class Prompts {
""".formatted(request);
// </StructuredPrompt>
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 class Prompts {
## Intent
""".formatted(request);
// </FormattedPrompt>
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 class Prompts {
""".formatted(request);
// </FewShotPrompt>
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 class Prompts {
""".formatted(request);
// </AvoidPrompt>
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.";
// <ContextPrompt>
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 class Prompts {
""".formatted(history, request);
// </ContextPrompt>
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 = "<message role=\"user\">I hate sending emails, no one ever reads them.</message>\n<message role=\"assistant\">I'm sorry to hear that. Messages may be a better way to communicate.</message>";
// <RolePrompt>
history = "<message role=\"user\">I hate sending emails, no one ever reads them.</message>\n<message role=\"assistant\">I'm sorry to hear that. Messages may be a better way to communicate.</message>";
prompt = """
<message role="system">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 class Prompts {
""".formatted(history, request);
// </RolePrompt>
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 = "<message role=\"user\">I hate sending emails, no one ever reads them.</message>\n<message role=\"assistant\">I'm sorry to hear that. Messages may be a better way to communicate.</message>";
// <BonusPrompt>
history = "<message role=\"user\">I hate sending emails, no one ever reads them.</message>\n<message role=\"assistant\">I'm sorry to hear that. Messages may be a better way to communicate.</message>";
prompt = """
<message role="system">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 class Prompts {
""".formatted(history, request);
// </BonusPrompt>
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);
}
}
@@ -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.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.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<ChatHistory> fewShotExamples = List.of(continueConversation, endConversation);
// <InvokeSerializedPrompts>
// 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
// <LoadPromptFromYAML>
var getIntent = KernelFunctionYaml.fromPromptYaml(
Files.readString(Path.of(PLUGINS_DIR, "Prompts", "getIntent.prompt.yaml")),
new HandlebarsPromptTemplateFactory());
// </LoadPromptFromYAML>
// Create choices
List<String> 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 class SerializingPrompts {
String userInput;
while (!(userInput = scanner.nextLine()).isEmpty()) {
// Invoke handlebars prompt
// This part is omitted as it requires a specific implementation to invoke a prompt
// <InvokePromptFromYaml>
var intent = kernel.invokeAsync(getIntent)
.withArguments(KernelFunctionArguments.builder()
.withVariable("request", userInput)
.withVariable("choices", choices)
.withVariable("history", history)
.withVariable("fewShotExamples", fewShotExamples)
.build())
.block();
// </InvokePromptFromYaml>
// 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 > ");
}
// </InvokeSerializedPrompts>
}
}
@@ -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) {
// <build_client>
client = new OpenAIClientBuilder()
.credential(new AzureKeyCredential(AZURE_CLIENT_KEY))
.endpoint(CLIENT_ENDPOINT)
.buildAsyncClient();
// </build_client>
} else if (CLIENT_KEY != null) {
client = new OpenAIClientBuilder()
.credential(new KeyCredential(CLIENT_KEY))
.buildAsyncClient();
} else {
System.out.println("No client key found");
return;
}
// <build_kernel>
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();
// </build_kernel>
// <run_poem>
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());
// </run_poem>
// <run_math>
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());
// </run_math>
}
}
@@ -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;
// <KernelPlugin>
public class MathPlugin {
// </KernelPlugin>
// <KernelFunction>
@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);
}
// </KernelFunction>
@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);
}
}
@@ -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."
}
]
}
@@ -0,0 +1,42 @@
name: getIntent
description: Gets the intent of the user.
template: |
<message role="system">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}}.</message>
BEGIN EXAMPLES
{{#each fewShotExamples}}
{{#each this}}
<message role="{{role}}">{{content}}</message>
{{/each}}
{{/each}}
END EXAMPLES
{{ConversationSummaryPlugin.SummarizeConversation history}}
<message role="user">{{request}}</message>
<message role="system">Intent:</message>
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
@@ -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": ""
}
]
}
}
@@ -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}}
@@ -9,6 +9,7 @@ import com.microsoft.semantickernel.contextvariables.converters.CompletionUsageC
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<ContextVariableTypeConverter<?>> 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,
@@ -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 @@ import javax.annotation.Nullable;
* @see ContextVariableTypes#getGlobalVariableTypeForClass(Class)
*/
public class NumberVariableContextVariableTypeConverter<T extends Number> extends
ContextVariableTypeConverter<T> {
private final Function<Number, T> fromNumber;
PrimitiveVariableContextVariableTypeConverter<T> {
/**
* Creates a new instance of the {@link NumberVariableContextVariableTypeConverter} class.
@@ -34,58 +29,15 @@ public class NumberVariableContextVariableTypeConverter<T extends Number> extend
Function<Number, T> fromNumber) {
super(
clazz,
s -> convert(s, clazz),
Number::toString,
fromPromptString);
this.fromNumber = fromNumber;
}
@Override
@Nullable
public <U> U toObject(@Nullable Object t, Class<U> 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);
}
}
@@ -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();
});
}
}
@@ -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<T> extends
ContextVariableTypeConverter<T> {
private final Function<Object, T> 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<T> clazz,
Function<String, T> fromPromptString,
Function<Object, T> fromObject,
Function<T, String> toPromptString) {
super(
clazz,
s -> convert(s, clazz),
toPromptString,
fromPromptString);
this.fromObject = fromObject;
}
@Override
@Nullable
public <U> U toObject(@Nullable Object t, Class<U> 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;
}
}
@@ -180,6 +180,17 @@ public class FunctionInvocation<T> extends Mono<FunctionResult<T>> {
.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 <U> The type of the result of the function invocation.
* @return A new {@code FunctionInvocation} for fluent chaining.
*/
public <U> FunctionInvocation<U> withResultType(Class<U> resultType) {
return withResultType(ContextVariableTypes.getGlobalVariableTypeForClass(resultType));
}
/**
* Add a kernel hook to the function invocation.
*
@@ -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 class 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;
@@ -274,7 +274,7 @@ public class HandlebarsPromptTemplate implements PromptTemplate {
String functionName = kernelFunction.getName();
String pluginName = plugin.getName();
handlebars.registerHelper(
ToolCallBehavior.formFullFunctionName(pluginName, functionName),
String.format("%s.%s", pluginName, functionName),
functionInvokeHelper(kernel, kernelFunction, context));
});
@@ -3,6 +3,7 @@ package com.microsoft.semantickernel.contextvariables.converters;
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 @@ public class NumberVariableContextVariableTypeConverterTest {
assertEquals(expResult, result);
}
@Test
void testFromStringToDouble() {
Object s = "12.0";
double expResult = 12.0d;
ContextVariable<Double> result = ContextVariable.convert(s, double.class, null);
assertEquals(expResult, result.getValue());
}
}
@@ -3,9 +3,17 @@ package com.microsoft.semantickernel.semanticfunctions;
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<String> 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);