Java: Add Plugin sample (#5601)

### Motivation and Context

<!-- 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 😄
This commit is contained in:
John Oliver
2024-03-27 12:46:58 +00:00
committed by GitHub
parent eeaf397560
commit a0d6073ef3
33 changed files with 381 additions and 234 deletions
@@ -253,7 +253,7 @@ public class ToolCallBehaviourTest {
private static MappingBuilder buildResponse(String bodyMatcher, String responseBody) {
return post(urlEqualTo(
"//openai/deployments/gpt-35-turbo-2/chat/completions?api-version=2023-12-01-preview"))
"//openai/deployments/gpt-35-turbo-2/chat/completions?api-version=2024-02-15-preview"))
.withRequestBody(new ContainsPattern(bodyMatcher))
.willReturn(
aResponse()
@@ -2,7 +2,7 @@
"priority": 1,
"request": {
"method": "POST",
"url": "//openai/deployments/gpt-35-turbo-2/chat/completions?api-version=2023-12-01-preview",
"url": "//openai/deployments/gpt-35-turbo-2/chat/completions?api-version=2024-02-15-preview",
"bodyPatterns": [
{
"contains": "That is all"
@@ -1,7 +1,7 @@
{
"request": {
"method": "POST",
"url": "//openai/deployments/gpt-35-turbo-2/chat/completions?api-version=2023-12-01-preview"
"url": "//openai/deployments/gpt-35-turbo-2/chat/completions?api-version=2024-02-15-preview"
},
"response": {
"body": "{\"id\":\"chatcmpl-xxx\",\"object\":\"chat.completion\",\"created\":1707253061,\"model\":\"gpt-35-turbo\",\"prompt_filter_results\":[{\"prompt_index\":0,\"content_filter_results\":{\"hate\":{\"filtered\":false,\"severity\":\"safe\"},\"self_harm\":{\"filtered\":false,\"severity\":\"safe\"},\"sexual\":{\"filtered\":false,\"severity\":\"safe\"},\"violence\":{\"filtered\":false,\"severity\":\"safe\"}}}],\"choices\":[{\"finish_reason\":\"stop\",\"index\":0,\"message\":{\"role\":\"assistant\",\"content\":\"1979b4ce-5463-4cfb-8ec8-1d05c4b44ccf\"},\"content_filter_results\":{\"hate\":{\"filtered\":false,\"severity\":\"safe\"},\"self_harm\":{\"filtered\":false,\"severity\":\"safe\"},\"sexual\":{\"filtered\":false,\"severity\":\"safe\"},\"violence\":{\"filtered\":false,\"severity\":\"safe\"}}}],\"usage\":{\"prompt_tokens\":17,\"completion_tokens\":67,\"total_tokens\":84}}",
@@ -1,7 +1,7 @@
{
"request": {
"method": "POST",
"url": "//openai/deployments/gpt-35-turbo/chat/completions?api-version=2023-12-01-preview"
"url": "//openai/deployments/gpt-35-turbo/chat/completions?api-version=2024-02-15-preview"
},
"response": {
"body": "{\"id\":\"chatcmpl-xxx\",\"object\":\"chat.completion\",\"created\":1707253039,\"model\":\"gpt-35-turbo\",\"prompt_filter_results\":[{\"prompt_index\":0,\"content_filter_results\":{\"hate\":{\"filtered\":false,\"severity\":\"safe\"},\"self_harm\":{\"filtered\":false,\"severity\":\"safe\"},\"sexual\":{\"filtered\":false,\"severity\":\"safe\"},\"violence\":{\"filtered\":false,\"severity\":\"safe\"}}}],\"choices\":[{\"finish_reason\":\"stop\",\"index\":0,\"message\":{\"role\":\"assistant\",\"content\":\"ac9817bc-7e1a-48e4-b06c-0ff7618b88c6\"},\"content_filter_results\":{\"hate\":{\"filtered\":false,\"severity\":\"safe\"},\"self_harm\":{\"filtered\":false,\"severity\":\"safe\"},\"sexual\":{\"filtered\":false,\"severity\":\"safe\"},\"violence\":{\"filtered\":false,\"severity\":\"safe\"}}}],\"usage\":{\"prompt_tokens\":26,\"completion_tokens\":131,\"total_tokens\":157}}",
@@ -1,7 +1,7 @@
{
"request": {
"method": "POST",
"url": "//openai/deployments/text-davinci-003/completions?api-version=2023-12-01-preview"
"url": "//openai/deployments/text-davinci-003/completions?api-version=2024-02-15-preview"
},
"response": {
"body": "{\"id\":\"cmpl-xxx\",\"object\":\"text_completion\",\"created\":1707253062,\"model\":\"text-davinci-003\",\"prompt_filter_results\":[{\"prompt_index\":0,\"content_filter_results\":{\"hate\":{\"filtered\":false,\"severity\":\"safe\"},\"self_harm\":{\"filtered\":false,\"severity\":\"safe\"},\"sexual\":{\"filtered\":false,\"severity\":\"safe\"},\"violence\":{\"filtered\":false,\"severity\":\"safe\"}}}],\"choices\":[{\"text\":\"7949e981-c632-422f-9b76-335a2379cd83\",\"index\":0,\"finish_reason\":\"stop\",\"logprobs\":null,\"content_filter_results\":{\"hate\":{\"filtered\":false,\"severity\":\"safe\"},\"self_harm\":{\"filtered\":false,\"severity\":\"safe\"},\"sexual\":{\"filtered\":false,\"severity\":\"safe\"},\"violence\":{\"filtered\":false,\"severity\":\"safe\"}}}],\"usage\":{\"prompt_tokens\":10,\"completion_tokens\":26,\"total_tokens\":36}}",
+1 -1
View File
@@ -14,7 +14,7 @@
<description>Parent pom for the Semantic Kernel Project</description>
<properties>
<azure-ai-openai.version>1.0.0-beta.6</azure-ai-openai.version>
<azure-ai-openai.version>1.0.0-beta.7</azure-ai-openai.version>
<checkstyle.version>10.13.0</checkstyle.version>
<com.uber.nullaway.version>0.10.21</com.uber.nullaway.version>
<google.errorprone.core.version>2.19.1</google.errorprone.core.version>
@@ -42,14 +42,14 @@ public class AIServices {
System.out.println("No client key found");
return;
}
// <CreateChatCompletionService>
ChatCompletionService chatCompletionService = ChatCompletionService.builder()
.withModelId(CHAT_MODEL_ID)
.withOpenAIAsyncClient(client)
.build();
// </CreateChatCompletionService>
// <CreateTextGenerationService>
TextGenerationService textGenerationService = TextGenerationService.builder()
.withModelId(TEXT_MODEL_ID)
@@ -1,15 +1,12 @@
// Copyright (c) Microsoft. All rights reserved.
package com.microsoft.semantickernel.samples.documentationexamples;
import java.util.Scanner;
import com.azure.ai.openai.OpenAIAsyncClient;
import com.azure.ai.openai.OpenAIClientBuilder;
import com.azure.core.credential.AzureKeyCredential;
import com.azure.core.credential.KeyCredential;
import com.microsoft.semantickernel.Kernel;
import com.microsoft.semantickernel.aiservices.openai.chatcompletion.OpenAIChatCompletion;
import com.microsoft.semantickernel.contextvariables.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;
@@ -17,6 +14,7 @@ import com.microsoft.semantickernel.samples.plugins.MathPlugin;
import com.microsoft.semantickernel.semanticfunctions.KernelFunctionArguments;
import com.microsoft.semantickernel.services.chatcompletion.ChatCompletionService;
import com.microsoft.semantickernel.services.chatcompletion.ChatHistory;
import java.util.Scanner;
public class CreatingFunctions {
@@ -25,7 +23,8 @@ 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 ========");
@@ -33,40 +32,40 @@ 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("------------------------");
@@ -83,16 +82,16 @@ public class CreatingFunctions {
// Enable auto function calling
var invocationContext = InvocationContext.builder()
.withToolCallBehavior(
ToolCallBehavior.allowAllKernelFunctions(true))
.build();
.withToolCallBehavior(
ToolCallBehavior.allowAllKernelFunctions(true))
.build();
// Get the response from the AI
var reply = chat.getChatMessageContentsAsync(history, kernel, invocationContext)
.block();
.block();
String message = reply.get(reply.size() - 1).getContent();
System.out.println("Assistant" + " > " + message);
System.out.println("Assistant > " + message);
// Add the message from the agent to the chat history
history.addAssistantMessage(message.toString());
@@ -18,13 +18,19 @@ import java.io.InputStream;
import java.util.Scanner;
public class Plugin {
public static InputStream INPUT = System.in;
// CLIENT_KEY is for an OpenAI client
private static final String CLIENT_KEY = System.getenv("CLIENT_KEY");
private static final String AZURE_CLIENT_KEY = System.getenv("AZURE_CLIENT_KEY");
// Only required if AZURE_CLIENT_KEY is set
// AZURE_CLIENT_KEY and CLIENT_ENDPOINT are for an Azure client
// CLIENT_ENDPOINT required if AZURE_CLIENT_KEY is set
private static final String AZURE_CLIENT_KEY = System.getenv("AZURE_CLIENT_KEY");
private static final String CLIENT_ENDPOINT = System.getenv("CLIENT_ENDPOINT");
private static final String MODEL_ID = System.getenv().getOrDefault("MODEL_ID", "gpt-35-turbo-2");
private static final String MODEL_ID = System.getenv()
.getOrDefault("MODEL_ID", "gpt-3.5-turbo");
public static void main(String[] args) {
System.out.println("======== Plugin ========");
@@ -47,7 +53,6 @@ public class Plugin {
return;
}
// <KernelCreation>
ChatCompletionService chatCompletionService = ChatCompletionService.builder()
.withModelId(MODEL_ID)
@@ -69,7 +74,7 @@ public class Plugin {
// Start the conversation
System.out.print("User > ");
Scanner scanner = new Scanner(System.in);
Scanner scanner = new Scanner(INPUT);
String userInput;
while (!(userInput = scanner.nextLine()).isEmpty()) {
// Add user input to history
@@ -94,6 +99,7 @@ public class Plugin {
// Add the message from the agent to the chat history
history.addAssistantMessage(message);
System.out.print("User > ");
}
// </Chat>
}
@@ -101,7 +107,7 @@ public class Plugin {
// <LightPlugin>
public static class LightPlugin {
public boolean isOn = false;
private boolean isOn = false;
@DefineKernelFunction(name = "getState", description = "Gets the state of the light.'")
String getState() {
@@ -110,9 +116,7 @@ public class Plugin {
@DefineKernelFunction(name = "changeState", description = "Changes the state of the light.'")
public String changeState(
@KernelFunctionParameter(name = "newState",
description = "The new state of the light, boolean true==on, false==off.",
type = boolean.class) boolean newState) {
@KernelFunctionParameter(name = "newState", description = "The new state of the light, boolean true==on, false==off.", type = boolean.class) boolean newState) {
this.isOn = newState;
String state = getState();
@@ -1,3 +1,4 @@
// Copyright (c) Microsoft. All rights reserved.
package com.microsoft.semantickernel.samples.documentationexamples;
import com.azure.ai.openai.OpenAIAsyncClient;
@@ -13,7 +14,8 @@ 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-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 ========");
@@ -21,34 +23,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".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
* You can choose between SendEmail, SendMessage, CompleteTask, CreateDocument.
* """.formatted(request);
* // </InitialPrompt>
*/
System.out.println("0.0 Initial prompt");
@@ -115,7 +123,8 @@ 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("2.1 Add structure to the output with formatting (using Markdown and JSON)");
result = kernel.invokePromptAsync(prompt).block().getResult();
System.out.println(result);
@@ -1,3 +1,4 @@
// Copyright (c) Microsoft. All rights reserved.
package com.microsoft.semantickernel.samples.documentationexamples;
import com.azure.ai.openai.OpenAIAsyncClient;
@@ -5,33 +6,39 @@ 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.contextvariables.ContextVariableTypeConverter;
import com.microsoft.semantickernel.contextvariables.converters.CollectionVariableContextVariableTypeConverter;
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.io.InputStream;
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;
import java.util.stream.Collectors;
public class SerializingPrompts {
private static final String CLIENT_KEY = System.getenv("CLIENT_KEY");
private static final String AZURE_CLIENT_KEY = System.getenv("AZURE_CLIENT_KEY");
// Only required if AZURE_CLIENT_KEY is set
public static InputStream INPUT = System.in;
// CLIENT_KEY is for an OpenAI client
private static final String CLIENT_KEY = System.getenv("CLIENT_KEY");
// AZURE_CLIENT_KEY and CLIENT_ENDPOINT are for an Azure client
// CLIENT_ENDPOINT required if AZURE_CLIENT_KEY is set
private static final String AZURE_CLIENT_KEY = System.getenv("AZURE_CLIENT_KEY");
private static final String CLIENT_ENDPOINT = System.getenv("CLIENT_ENDPOINT");
private static final String MODEL_ID = System.getenv().getOrDefault("MODEL_ID", "gpt-35-turbo-2");
private static final String MODEL_ID = System.getenv()
.getOrDefault("MODEL_ID", "gpt-3.5-turbo");
private static final String PLUGINS_DIR = "java/samples/sample-code/src/main/resources/Plugins";
@@ -39,48 +46,57 @@ public class SerializingPrompts {
System.out.println("======== Serializing Prompts ========");
OpenAIAsyncClient client;
if (AZURE_CLIENT_KEY != null) {
if (AZURE_CLIENT_KEY != null && CLIENT_ENDPOINT != null) {
client = new OpenAIClientBuilder()
.credential(new AzureKeyCredential(AZURE_CLIENT_KEY))
.endpoint(CLIENT_ENDPOINT)
.buildAsyncClient();
.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 {
client = new OpenAIClientBuilder()
.credential(new KeyCredential(CLIENT_KEY))
.buildAsyncClient();
System.out.println("No client key found");
return;
}
// 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.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.USER, "Thats all");
endConversation.addMessage(AuthorRole.SYSTEM, "Intent:");
endConversation.addMessage(AuthorRole.ASSISTANT, "EndConversation");
List<ChatHistory> fewShotExamples = List.of(continueConversation, endConversation);
// Customise the type converters toPromptString for ChatHistory to serialize the messages as "author: content"
CollectionVariableContextVariableTypeConverter collectionConverter = new CollectionVariableContextVariableTypeConverter(
"\n");
// <InvokeSerializedPrompts>
// Create Kernel
Kernel kernel = Kernel.builder()
.withAIService(ChatCompletionService.class, ChatCompletionService.builder()
.withModelId(MODEL_ID)
.withOpenAIAsyncClient(client)
.build())
.withPlugin(KernelPluginFactory.createFromObject(new ConversationSummaryPlugin(), "ConversationSummaryPlugin"))
.build();
.withAIService(ChatCompletionService.class, ChatCompletionService.builder()
.withModelId(MODEL_ID)
.withOpenAIAsyncClient(client)
.build())
.withPlugin(KernelPluginFactory.createFromObject(new ConversationSummaryPlugin(),
"ConversationSummaryPlugin"))
.build();
// Load prompts
var prompts = KernelPluginFactory.importPluginFromDirectory(
Path.of(PLUGINS_DIR), "Prompts", null);
Path.of(PLUGINS_DIR), "Prompts", null);
// Load prompt from YAML
// <LoadPromptFromYAML>
var getIntent = KernelFunctionYaml.fromPromptYaml(
Files.readString(Path.of(PLUGINS_DIR, "Prompts", "getIntent.prompt.yaml")),
new HandlebarsPromptTemplateFactory());
Files.readString(Path.of(PLUGINS_DIR, "Prompts", "getIntent.prompt.yaml")),
new HandlebarsPromptTemplateFactory());
// </LoadPromptFromYAML>
// Create choices
@@ -90,21 +106,26 @@ public class SerializingPrompts {
ChatHistory history = new ChatHistory();
// Start the chat loop
Scanner scanner = new Scanner(System.in);
Scanner scanner = new Scanner(INPUT);
System.out.print("User > ");
String userInput;
while (!(userInput = scanner.nextLine()).isEmpty()) {
// Invoke handlebars prompt
String historyString = history.getMessages()
.stream()
.map(m -> m.getAuthorRole() + " > " + m.getContent())
.collect(Collectors.joining("\n"));
// <InvokePromptFromYaml>
var intent = kernel.invokeAsync(getIntent)
.withArguments(KernelFunctionArguments.builder()
.withVariable("request", userInput)
.withVariable("choices", choices)
.withVariable("history", history)
.withVariable("fewShotExamples", fewShotExamples)
.build())
.block();
.withArguments(KernelFunctionArguments.builder()
.withVariable("request", userInput)
.withVariable("choices", choices)
.withVariable("history", historyString)
.withVariable("fewShotExamples", fewShotExamples, collectionConverter)
.build())
.block();
// </InvokePromptFromYaml>
// End the chat if the intent is "Stop"
@@ -113,15 +134,17 @@ public class SerializingPrompts {
}
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();
.withArguments(KernelFunctionArguments.builder()
.withVariable("request", userInput)
.withVariable("history",
String.join("\n",
history.getMessages().stream()
.map(m -> m.getAuthorRole() + " > " + m.getContent()).toList()))
.build())
.withResultType(String.class)
.block().getResult();
System.out.println("Assistant" + " > " + reply);
System.out.println("Assistant > " + reply);
// Append to history
history.addUserMessage(userInput);
@@ -1,3 +1,4 @@
// Copyright (c) Microsoft. All rights reserved.
package com.microsoft.semantickernel.samples.plugins;
import com.microsoft.semantickernel.semanticfunctions.annotations.DefineKernelFunction;
@@ -5,116 +6,116 @@ import com.microsoft.semantickernel.semanticfunctions.annotations.KernelFunction
// <KernelPlugin>
public class MathPlugin {
// </KernelPlugin>
// </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);
}
}
@@ -0,0 +1,33 @@
{
"schema": 1,
"type": "completion",
"description": "Creates a chat response to the user",
"execution_settings": {
"default": {
"max_tokens": 1000,
"temperature": 0
},
"gpt-3.5-turbo": {
"model_id": "gpt-3.5-turbo-0613",
"max_tokens": 4000,
"temperature": 0.1
},
"gpt-4": {
"model_id": "gpt-4-1106-preview",
"max_tokens": 8000,
"temperature": 0.3
}
},
"input_variables": [
{
"name": "request",
"description": "The user's request.",
"required": true
},
{
"name": "history",
"description": "The history of the conversation.",
"required": true
}
]
}
@@ -1,3 +1,3 @@
{{ConversationSummaryPlugin.SummarizeConversation $history}}
User: {{$request}}
Assistant:
Assistant:
@@ -1,31 +0,0 @@
{
"schema": 1,
"type": "completion",
"description": "Creates a chat response to the user",
"execution_settings": {
"default": {
"max_tokens": 1000,
"temperature": 0
},
"gpt-3.5-turbo": {
"model_id": "gpt-3.5-turbo-0613",
"max_tokens": 4000,
"temperature": 0.1
},
"gpt-4": {
"model_id": "gpt-4-1106-preview",
"max_tokens": 8000,
"temperature": 0.3
}
},
"input_variables": [
{
"name": "request",
"description": "The user's request."
},
{
"name": "history",
"description": "The history of the conversation."
}
]
}
@@ -5,15 +5,15 @@ template: |
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="assistant">Summary of conversation so far:
{{ConversationSummaryPlugin-SummarizeConversation history}}
</message>
<message role="user">{{request}}</message>
<message role="system">Intent:</message>
@@ -39,4 +39,4 @@ execution_settings:
gpt-4:
model_id: gpt-4-1106-preview
max_tokens: 10
temperature: 0.2
temperature: 0.2
@@ -127,7 +127,8 @@ public class ContextVariable<T> {
if (typeOfActualReturnedType != null) {
// Try the to object
T converted = typeOfActualReturnedType.getConverter().toObject(it, requestedResultType);
T converted = typeOfActualReturnedType.getConverter().toObject(contextVariableTypes, it,
requestedResultType);
if (converted != null) {
return contextVariableTypes.getVariableTypeForClass(requestedResultType)
@@ -137,7 +138,9 @@ public class ContextVariable<T> {
if (requestedResultType.isAssignableFrom(String.class)) {
// Try using toPromptString
String str = typeOfActualReturnedType.getConverter()
.toPromptString(typeOfActualReturnedType.getClazz().cast(it));
.toPromptString(
contextVariableTypes,
typeOfActualReturnedType.getClazz().cast(it));
return requestedResultTypeVariable.of(str);
}
@@ -295,19 +298,29 @@ public class ContextVariable<T> {
* @param converter the converter to use when converting the value
* @return the value of this {@code ContextVariable} as a prompt string
*/
public String toPromptString(ContextVariableTypeConverter<T> converter) {
return converter.toPromptString(value);
public String toPromptString(ContextVariableTypes types,
ContextVariableTypeConverter<T> converter) {
return converter.toPromptString(types, value);
}
public String toPromptString(ContextVariableTypes types) {
return toPromptString(types, type.getConverter());
}
/**
* Use the convert of the type of this {@code ContextVariable} to convert the value of this
* {@code ContextVariable} to a prompt string.
* Use the given {@code ContextVariableTypeConverter} to convert the value of this
* {@code ContextVariable} to a prompt string. This method is useful when the convert of this
* {@code ContextVariableType} does not create the expected prompt string.
*
* @param converter the converter to use when converting the value
* @return the value of this {@code ContextVariable} as a prompt string
*/
public String toPromptString() {
return toPromptString(type.getConverter());
public String toPromptString(ContextVariableTypeConverter<T> converter) {
return converter.toPromptString(value);
}
public String toPromptString() {
return toPromptString(type.getConverter());
}
*/
/**
* Returns true if the value of this {@code ContextVariable} is {@code null} or empty.
@@ -23,9 +23,14 @@ public class ContextVariableTypeConverter<T> {
private static final Logger LOGGER = LoggerFactory.getLogger(
ContextVariableTypeConverter.class);
public interface ToPromptStringFunction<T> {
String toPromptString(ContextVariableTypes types, T t);
}
private final Class<T> clazz;
private final Function<Object, T> fromObject;
private final Function<T, String> toPromptString;
private final ToPromptStringFunction<T> toPromptString;
private final Function<String, T> fromPromptString;
private final List<Converter<T, ?>> toObjects;
@@ -45,6 +50,44 @@ public class ContextVariableTypeConverter<T> {
this(clazz, fromObject, toPromptString, fromPromptString, Collections.emptyList());
}
/**
* Create a new context variable type converter.
*
* @param clazz the class of the type
* @param fromObject a function to convert an object to the type
* @param toPromptString a function to convert the type to a prompt string
* @param fromPromptString a function to convert a prompt string to the type
*/
public ContextVariableTypeConverter(
Class<T> clazz,
Function<Object, T> fromObject,
ToPromptStringFunction<T> toPromptString,
Function<String, T> fromPromptString) {
this(clazz, fromObject, toPromptString, fromPromptString, Collections.emptyList());
}
/**
* Create a new context variable type converter.
*
* @param clazz the class of the type
* @param fromObject a function to convert an object to the type
* @param toPromptString a function to convert the type to a prompt string
* @param fromPromptString a function to convert a prompt string to the type
* @param toObjects a list of converters to convert the type to other types
*/
public ContextVariableTypeConverter(
Class<T> clazz,
Function<Object, T> fromObject,
ToPromptStringFunction<T> toPromptString,
Function<String, T> fromPromptString,
List<Converter<T, ?>> toObjects) {
this.clazz = clazz;
this.fromObject = fromObject;
this.toPromptString = toPromptString;
this.fromPromptString = fromPromptString;
this.toObjects = new ArrayList<>(toObjects);
}
/**
* Create a new context variable type converter.
*
@@ -62,7 +105,7 @@ public class ContextVariableTypeConverter<T> {
List<Converter<T, ?>> toObjects) {
this.clazz = clazz;
this.fromObject = fromObject;
this.toPromptString = toPromptString;
this.toPromptString = (types, t) -> toPromptString.apply(t);
this.fromPromptString = fromPromptString;
this.toObjects = new ArrayList<>(toObjects);
}
@@ -77,7 +120,7 @@ public class ContextVariableTypeConverter<T> {
*/
@Nullable
@SuppressWarnings("unchecked")
public <U> U toObject(@Nullable Object t, Class<U> clazz) {
public <U> U toObject(ContextVariableTypes types, @Nullable Object t, Class<U> clazz) {
if (t == null) {
return null;
}
@@ -129,11 +172,11 @@ public class ContextVariableTypeConverter<T> {
* @param t the type to convert
* @return the prompt string
*/
public String toPromptString(@Nullable T t) {
public String toPromptString(ContextVariableTypes types, @Nullable T t) {
if (t == null) {
return "";
}
return toPromptString.apply(t);
return toPromptString.toPromptString(types, t);
}
/**
@@ -235,9 +278,8 @@ public class ContextVariableTypeConverter<T> {
/**
* Create a new builder for a context variable type converter.
*
* @param <T> the type of the context variable
* @param <T> the type of the context variable
* @param clazz the class of the type
*
* @return the builder
*/
public static <T> Builder<T> builder(Class<T> clazz) {
@@ -251,7 +293,7 @@ public class ContextVariableTypeConverter<T> {
private final Class<T> clazz;
private Function<Object, T> fromObject;
private Function<T, String> toPromptString;
private ToPromptStringFunction<T> toPromptString;
private Function<String, T> fromPromptString;
/**
@@ -265,7 +307,7 @@ public class ContextVariableTypeConverter<T> {
fromObject = x -> {
throw new UnsupportedOperationException("fromObject not implemented");
};
toPromptString = x -> {
toPromptString = (a, b) -> {
throw new UnsupportedOperationException("toPromptString not implemented");
};
fromPromptString = x -> {
@@ -320,7 +362,7 @@ public class ContextVariableTypeConverter<T> {
* @return this builder
*/
public Builder<T> toPromptString(Function<T, String> toPromptString) {
this.toPromptString = toPromptString;
this.toPromptString = (ignore, a) -> toPromptString.apply(a);
return this;
}
@@ -341,7 +383,10 @@ public class ContextVariableTypeConverter<T> {
* @return the context variable type converter
*/
public ContextVariableTypeConverter<T> build() {
return new ContextVariableTypeConverter<>(clazz, fromObject, toPromptString,
return new ContextVariableTypeConverter<>(
clazz,
fromObject,
toPromptString,
fromPromptString);
}
}
@@ -4,8 +4,10 @@ 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.ContextVariableType;
import com.microsoft.semantickernel.contextvariables.ContextVariableTypeConverter;
import com.microsoft.semantickernel.contextvariables.ContextVariableTypes;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import java.util.Collection;
import java.util.stream.Collectors;
@@ -19,28 +21,46 @@ import java.util.stream.Collectors;
public class CollectionVariableContextVariableTypeConverter extends
ContextVariableTypeConverter<Collection> {
/**
* Creates a new instance of the {@link CollectionVariableContextVariableTypeConverter} class.
*/
public CollectionVariableContextVariableTypeConverter() {
@SuppressFBWarnings("CT_CONSTRUCTOR_THROW")
public CollectionVariableContextVariableTypeConverter(String delimiter) {
super(
Collection.class,
s -> convert(s, Collection.class),
CollectionVariableContextVariableTypeConverter::getString,
getString(delimiter),
s -> {
throw new UnsupportedOperationException();
});
}
private static String getString(Collection<?> c) {
return c.stream()
.map(t -> {
if (t instanceof ContextVariable) {
return ((ContextVariable<?>) t).toPromptString();
} else {
return t.toString();
}
})
.collect(Collectors.joining(","));
/**
* Creates a new instance of the {@link CollectionVariableContextVariableTypeConverter} class.
*/
@SuppressFBWarnings("CT_CONSTRUCTOR_THROW")
public CollectionVariableContextVariableTypeConverter() {
this(",");
}
public static ToPromptStringFunction<Collection> getString(String delimiter) {
return (types, collection) -> {
return (String) collection
.stream()
.map(t -> getString(types, t))
.collect(Collectors.joining(delimiter));
};
}
private static String getString(ContextVariableTypes types, Object t) {
if (t instanceof ContextVariable) {
return ((ContextVariable<?>) t).toPromptString(types);
} else {
ContextVariableType converter = types.getVariableTypeForClass(
t.getClass());
if (converter != null) {
return converter.getConverter().toPromptString(types, t);
}
return t.toString();
}
}
}
@@ -5,11 +5,14 @@ import static com.microsoft.semantickernel.contextvariables.ContextVariableTypes
import com.microsoft.semantickernel.contextvariables.ContextVariable;
import com.microsoft.semantickernel.contextvariables.ContextVariableTypeConverter;
import com.microsoft.semantickernel.contextvariables.ContextVariableTypes;
import java.util.function.Function;
import javax.annotation.Nullable;
/**
* A {@link ContextVariableTypeConverter} for primative variables.
*
* @param <T> the type of the variable
*/
public class PrimitiveVariableContextVariableTypeConverter<T> extends
ContextVariableTypeConverter<T> {
@@ -38,21 +41,21 @@ public class PrimitiveVariableContextVariableTypeConverter<T> extends
@Override
@Nullable
public <U> U toObject(@Nullable Object t, Class<U> clazz) {
public <U> U toObject(ContextVariableTypes types, @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);
U obj = super.toObject(types, 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 clazz.cast(toPromptString(types, getType().cast(t)));
}
return null;
}
@@ -2,6 +2,7 @@
package com.microsoft.semantickernel.implementation.templateengine.tokenizer;
import com.microsoft.semantickernel.Kernel;
import com.microsoft.semantickernel.contextvariables.ContextVariableTypes;
import com.microsoft.semantickernel.exceptions.SKException;
import com.microsoft.semantickernel.implementation.Verify;
import com.microsoft.semantickernel.implementation.templateengine.tokenizer.blocks.Block;
@@ -11,8 +12,8 @@ import com.microsoft.semantickernel.implementation.templateengine.tokenizer.bloc
import com.microsoft.semantickernel.implementation.templateengine.tokenizer.blocks.TextRendering;
import com.microsoft.semantickernel.implementation.templateengine.tokenizer.blocks.VarBlock;
import com.microsoft.semantickernel.orchestration.InvocationContext;
import com.microsoft.semantickernel.semanticfunctions.KernelFunctionArguments;
import com.microsoft.semantickernel.semanticfunctions.InputVariable;
import com.microsoft.semantickernel.semanticfunctions.KernelFunctionArguments;
import com.microsoft.semantickernel.semanticfunctions.PromptTemplate;
import com.microsoft.semantickernel.semanticfunctions.PromptTemplateConfig;
import com.microsoft.semantickernel.templateengine.semantickernel.TemplateException;
@@ -139,12 +140,19 @@ public class DefaultPromptTemplate implements PromptTemplate {
@Nullable KernelFunctionArguments arguments,
@Nullable InvocationContext context) {
ContextVariableTypes types;
if (context != null) {
types = context.getContextVariableTypes();
} else {
types = new ContextVariableTypes();
}
return Flux
.fromIterable(blocks)
.concatMap(block -> {
if (block instanceof TextRendering) {
return Mono.just(
((TextRendering) block).render(arguments));
((TextRendering) block).render(types, arguments));
} else if (block instanceof CodeRendering) {
return ((CodeRendering) block).renderCodeAsync(kernel, arguments, context);
} else {
@@ -104,7 +104,8 @@ public final class CodeBlock extends Block implements CodeRendering {
case VALUE:
case VARIABLE:
return Mono.just(
((TextRendering) this.tokens.get(0)).render(arguments));
((TextRendering) this.tokens.get(0)).render(context.getContextVariableTypes(),
arguments));
case FUNCTION_ID:
return this
@@ -207,7 +208,8 @@ public final class CodeBlock extends Block implements CodeRendering {
}
if (firstPositionalInputValue == null) {
firstPositionalInputValue = ((TextRendering) tokens.get(1)).render(arguments);
firstPositionalInputValue = ((TextRendering) tokens.get(1)).render(types,
arguments);
firstPositionalInputValue = ContextVariable
.convert(
firstPositionalInputValue,
@@ -244,7 +246,7 @@ public final class CodeBlock extends Block implements CodeRendering {
"Ambiguity found as a named parameter '{arg.Name}' cannot be set for the first parameter when there is also a positional value: '{firstPositionalInputValue}' provided. Function: {fBlock.PluginName}.{fBlock.FunctionName}");
}
arguments.put(arg.getName(), types.contextVariableOf(arg.getValue(arguments)));
arguments.put(arg.getName(), types.contextVariableOf(arg.getValue(types, arguments)));
}
return arguments;
@@ -1,6 +1,7 @@
// Copyright (c) Microsoft. All rights reserved.
package com.microsoft.semantickernel.implementation.templateengine.tokenizer.blocks;
import com.microsoft.semantickernel.contextvariables.ContextVariableTypes;
import com.microsoft.semantickernel.semanticfunctions.KernelFunctionArguments;
import javax.annotation.Nullable;
@@ -48,7 +49,7 @@ public final class FunctionIdBlock extends Block implements TextRendering {
@Override
@Nullable
public String render(@Nullable KernelFunctionArguments variables) {
public String render(ContextVariableTypes types, @Nullable KernelFunctionArguments variables) {
return this.getContent();
}
@@ -3,6 +3,7 @@ package com.microsoft.semantickernel.implementation.templateengine.tokenizer.blo
import static com.microsoft.semantickernel.implementation.templateengine.tokenizer.blocks.BlockTypes.NAMED_ARG;
import com.microsoft.semantickernel.contextvariables.ContextVariableTypes;
import com.microsoft.semantickernel.exceptions.SKException;
import com.microsoft.semantickernel.implementation.Verify;
import com.microsoft.semantickernel.semanticfunctions.KernelFunctionArguments;
@@ -128,7 +129,7 @@ public class NamedArgBlock extends Block implements TextRendering {
}
@Override
public String render(@Nullable KernelFunctionArguments variables) {
public String render(ContextVariableTypes types, @Nullable KernelFunctionArguments variables) {
return getContent();
}
@@ -151,15 +152,15 @@ public class NamedArgBlock extends Block implements TextRendering {
}
@SuppressWarnings("NullAway")
public String getValue(KernelFunctionArguments arguments) {
public String getValue(ContextVariableTypes types, KernelFunctionArguments arguments) {
boolean valueIsValidValBlock = this.valBlock != null && this.valBlock.isValid();
if (valueIsValidValBlock) {
return this.valBlock.render(arguments);
return this.valBlock.render(types, arguments);
}
boolean valueIsValidVarBlock = this.varBlock != null && this.varBlock.isValid();
if (valueIsValidVarBlock) {
return this.varBlock.render(arguments);
return this.varBlock.render(types, arguments);
}
return "";
@@ -1,6 +1,7 @@
// Copyright (c) Microsoft. All rights reserved.
package com.microsoft.semantickernel.implementation.templateengine.tokenizer.blocks;
import com.microsoft.semantickernel.contextvariables.ContextVariableTypes;
import com.microsoft.semantickernel.semanticfunctions.KernelFunctionArguments;
import javax.annotation.Nullable;
@@ -20,7 +21,7 @@ public final class TextBlock extends Block implements TextRendering {
}
@Override
public String render(@Nullable KernelFunctionArguments variables) {
public String render(ContextVariableTypes types, @Nullable KernelFunctionArguments variables) {
return super.getContent();
}
}
@@ -1,6 +1,7 @@
// Copyright (c) Microsoft. All rights reserved.
package com.microsoft.semantickernel.implementation.templateengine.tokenizer.blocks;
import com.microsoft.semantickernel.contextvariables.ContextVariableTypes;
import com.microsoft.semantickernel.semanticfunctions.KernelFunctionArguments;
import javax.annotation.Nullable;
@@ -16,5 +17,5 @@ public interface TextRendering {
* @return Rendered content
*/
@Nullable
String render(@Nullable KernelFunctionArguments variables);
String render(ContextVariableTypes types, @Nullable KernelFunctionArguments variables);
}
@@ -1,6 +1,7 @@
// Copyright (c) Microsoft. All rights reserved.
package com.microsoft.semantickernel.implementation.templateengine.tokenizer.blocks;
import com.microsoft.semantickernel.contextvariables.ContextVariableTypes;
import com.microsoft.semantickernel.semanticfunctions.KernelFunctionArguments;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -38,7 +39,7 @@ public final class ValBlock extends Block implements TextRendering {
@Override
@Nullable
public String render(@Nullable KernelFunctionArguments variables) {
public String render(ContextVariableTypes types, @Nullable KernelFunctionArguments variables) {
return value;
}
@@ -2,6 +2,7 @@
package com.microsoft.semantickernel.implementation.templateengine.tokenizer.blocks;
import com.microsoft.semantickernel.contextvariables.ContextVariable;
import com.microsoft.semantickernel.contextvariables.ContextVariableTypes;
import com.microsoft.semantickernel.semanticfunctions.KernelFunctionArguments;
import com.microsoft.semantickernel.templateengine.semantickernel.TemplateException;
import javax.annotation.Nullable;
@@ -24,7 +25,7 @@ public final class VarBlock extends Block implements TextRendering {
}
@Override
public String render(@Nullable KernelFunctionArguments variables) {
public String render(ContextVariableTypes types, @Nullable KernelFunctionArguments variables) {
if (variables == null) {
return "";
}
@@ -42,7 +43,7 @@ public final class VarBlock extends Block implements TextRendering {
LOGGER.warn("Variable `{}{}` not found", Symbols.VarPrefix, name);
}
return value != null ? value.toPromptString() : "";
return value != null ? value.toPromptString(types) : "";
}
@Override
@@ -443,7 +443,8 @@ public class KernelFunctionFromMethod<T> extends KernelFunction<T> {
ContextVariableTypeConverter<?> c = arg.getType().getConverter();
Object converted = c.toObject(arg.getValue(), parameter.getType());
Object converted = c.toObject(invocationContext.getContextVariableTypes(),
arg.getValue(), parameter.getType());
if (converted != null) {
return converted;
}
@@ -470,7 +471,7 @@ public class KernelFunctionFromMethod<T> extends KernelFunction<T> {
if (value == null && type.equals(String.class) && arg != null) {
ContextVariableTypeConverter c = arg.getType().getConverter();
value = c.toPromptString(arg.getValue());
value = c.toPromptString(invocationContext.getContextVariableTypes(), arg.getValue());
}
return value;
@@ -128,7 +128,7 @@ public class HandlebarsPromptTemplate implements PromptTemplate {
Object value = null;
if (context instanceof KernelFunctionArguments) {
ContextVariable<?> variable = ((KernelFunctionArguments) context).get(name);
value = variable != null ? variable.getValue() : null;
value = variable != null ? variable.getValue() : UNRESOLVED;
}
if (context instanceof ContextVariable) {
value = ((ContextVariable<?>) context).getValue();
@@ -187,6 +187,11 @@ public class HandlebarsPromptTemplate implements PromptTemplate {
private static Helper<Object> handleEach(InvocationContext invocationContext) {
return (context, options) -> {
if (context instanceof ContextVariable) {
return ((ContextVariable<?>) context)
.toPromptString(invocationContext.getContextVariableTypes());
}
if (context instanceof Iterable) {
StringBuilder sb = new StringBuilder();
Iterator<?> iterator = ((Iterable<?>) context).iterator();
@@ -209,7 +214,8 @@ public class HandlebarsPromptTemplate implements PromptTemplate {
ContextVariableType type = invocationContext.getContextVariableTypes()
.getVariableTypeForClass(context.getClass());
if (type != null) {
return type.getConverter().toPromptString(context);
return type.getConverter()
.toPromptString(invocationContext.getContextVariableTypes(), context);
}
return null;
};
@@ -274,7 +280,7 @@ public class HandlebarsPromptTemplate implements PromptTemplate {
String functionName = kernelFunction.getName();
String pluginName = plugin.getName();
handlebars.registerHelper(
String.format("%s.%s", pluginName, functionName),
ToolCallBehavior.formFullFunctionName(pluginName, functionName),
functionInvokeHelper(kernel, kernelFunction, context));
});
@@ -20,7 +20,7 @@ public class NumberVariableContextVariableTypeConverterTest {
.getGlobalVariableTypeForClass(Integer.class);
ContextVariableTypeConverter<Integer> instance = type.getConverter();
Object expResult = "1";
Object result = instance.toObject(1, String.class);
Object result = instance.toObject(new ContextVariableTypes(), 1, String.class);
assertEquals(expResult, result);
}
@@ -78,7 +78,7 @@ public class NumberVariableContextVariableTypeConverterTest {
.getGlobalVariableTypeForClass(Integer.class);
ContextVariableTypeConverter<Integer> instance = type.getConverter();
String expResult = "123";
String result = instance.toPromptString(num);
String result = instance.toPromptString(new ContextVariableTypes(), num);
assertEquals(expResult, result);
}
@@ -89,7 +89,7 @@ public class NumberVariableContextVariableTypeConverterTest {
.getGlobalVariableTypeForClass(Integer.class);
ContextVariableTypeConverter<Integer> instance = type.getConverter();
String expResult = "";
String result = instance.toPromptString(num);
String result = instance.toPromptString(new ContextVariableTypes(), num);
assertEquals(expResult, result);
}
@@ -1,6 +1,7 @@
// Copyright (c) Microsoft. All rights reserved.
package com.microsoft.semantickernel.templateengine.semantickernel;
import com.microsoft.semantickernel.contextvariables.ContextVariableTypes;
import com.microsoft.semantickernel.implementation.templateengine.tokenizer.CodeTokenizer;
import com.microsoft.semantickernel.implementation.templateengine.tokenizer.blocks.Block;
import com.microsoft.semantickernel.implementation.templateengine.tokenizer.blocks.FunctionIdBlock;
@@ -26,6 +27,7 @@ public class CodeTokenizerTest {
NamedArgBlock namedArgBlock = (NamedArgBlock) tokens.get(1);
Assertions.assertEquals("street", namedArgBlock.getName());
Assertions.assertEquals("123 Main St", namedArgBlock.getValue(
new ContextVariableTypes(),
new KernelFunctionArguments.Builder()
.withVariable("street", "123 Main St")
.build()));
@@ -33,11 +35,13 @@ public class CodeTokenizerTest {
namedArgBlock = (NamedArgBlock) tokens.get(2);
Assertions.assertEquals("zip", namedArgBlock.getName());
Assertions.assertEquals("98123", namedArgBlock.getValue(
new ContextVariableTypes(),
new KernelFunctionArguments.Builder().build()));
namedArgBlock = (NamedArgBlock) tokens.get(3);
Assertions.assertEquals("city", namedArgBlock.getName());
Assertions.assertEquals("Seattle", namedArgBlock.getValue(
new ContextVariableTypes(),
new KernelFunctionArguments.Builder().build()));
}
@@ -55,6 +59,7 @@ public class CodeTokenizerTest {
NamedArgBlock namedArgBlock = (NamedArgBlock) tokens.get(1);
Assertions.assertEquals("recall", namedArgBlock.getName());
Assertions.assertEquals("where did I grow up?", namedArgBlock.getValue(
new ContextVariableTypes(),
new KernelFunctionArguments.Builder()
.build()));
}
+3 -3
View File
@@ -81,18 +81,18 @@
<dependency>
<groupId>com.azure</groupId>
<artifactId>azure-ai-openai</artifactId>
<version>1.0.0-beta.6</version>
<version>1.0.0-beta.7</version>
</dependency>
<dependency>
<groupId>com.azure</groupId>
<artifactId>azure-core-http-netty</artifactId>
<version>1.13.11</version>
<version>1.14.1</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>com.azure</groupId>
<artifactId>azure-identity</artifactId>
<version>1.11.2</version>
<version>1.11.4</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>