From 11b242165164fd185c8a6aeca1661cf191295ec4 Mon Sep 17 00:00:00 2001 From: John Oliver <1615532+johnoliver@users.noreply.github.com> Date: Wed, 31 Jan 2024 20:51:34 +0000 Subject: [PATCH] Java: Fix linting issues (#4800) Fix linting issues --- .../chatcompletion/OpenAIChatCompletion.java | 72 ++++-- .../openai/chatcompletion/ParsedPrompt.java | 6 + .../chatcompletion/XMLPromptParser.java | 8 +- .../OpenAITextGenerationService.java | 40 +++- .../Example03_ArgumentsTest.java | 4 +- ...xample05_InlineFunctionDefinitionTest.java | 3 +- java/samples/pom.xml | 4 + java/samples/sample-code/pom.xml | 24 ++ .../plugins/ConversationSummaryPlugin.java | 22 +- .../syntaxexamples/Example03_Arguments.java | 15 +- .../Example05_InlineFunctionDefinition.java | 6 +- .../Example06_TemplateLanguage.java | 2 +- .../Example09_FunctionTypes.java | 144 ++++++++++-- .../Example13_ConversationSummaryPlugin.java | 10 +- ...Example27_PromptFunctionsUsingChatGPT.java | 2 +- ...eMethodFunctionsWithMultipleArguments.java | 2 +- .../Example58_ConfigureExecutionSettings.java | 4 +- .../Example61_MultipleLLMs.java | 6 +- .../Example62_CustomAIServiceSelector.java | 2 +- .../Example63_ChatCompletionPrompts.java | 4 +- .../Example64_MultiplePromptTemplates.java | 2 +- .../Example69_MutableKernelPlugin.java | 4 +- .../samples/syntaxexamples/RunAll.java | 67 ++++++ .../microsoft/semantickernel/AIService.java | 53 +++-- .../com/microsoft/semantickernel/Kernel.java | 40 +++- .../semantickernel/KernelContent.java | 16 +- .../StreamingKernelContent.java | 16 +- .../chatcompletion/AuthorRole.java | 11 +- .../chatcompletion/ChatCompletionService.java | 8 + .../chatcompletion/ChatMessageContent.java | 8 +- .../StreamingChatMessageContent.java | 37 ++- .../exceptions/SkillsNotFoundException.java | 42 ---- .../hooks/FunctionInvokedEvent.java | 7 +- .../hooks/FunctionInvokingEvent.java | 5 +- .../orchestration/DefaultKernelFunction.java | 13 +- .../orchestration/FunctionResult.java | 9 +- .../orchestration/FunctionResultMetadata.java | 26 ++- .../orchestration/KernelFunction.java | 32 +-- .../orchestration/KernelFunctionMetadata.java | 18 +- .../orchestration/MethodDetails.java | 16 +- .../contextvariables/ContextVariable.java | 131 ++++++++++- .../contextvariables/ContextVariableType.java | 15 +- .../ContextVariableTypeConverter.java | 14 +- .../ContextVariableTypes.java | 28 ++- .../DefaultKernelArguments.java | 6 +- .../contextvariables/NullContextVariable.java | 13 ++ .../WritableKernelArguments.java | 2 - .../DateTimeContextVariableTypeConverter.java | 12 +- .../InstantContextVariableTypeConverter.java | 26 +++ ...rVariableContextVariableTypeConverter.java | 25 +- .../plugin/KernelFunctionFactory.java | 39 +--- .../plugin/KernelParameterMetadata.java | 11 +- .../semantickernel/plugin/KernelPlugin.java | 13 +- .../plugin/KernelPluginFactory.java | 85 ++++--- .../plugin/KernelReturnParameterMetadata.java | 5 + .../AggregatorPromptTemplateFactory.java | 10 +- .../HandlebarsPromptTemplateFactory.java | 7 +- .../semanticfunctions/InputVariable.java | 28 ++- .../KernelFunctionFromMethod.java | 219 +++++++++--------- .../KernelFunctionFromPrompt.java | 136 ++++++++--- .../KernelPromptTemplateFactory.java | 3 +- .../PromptTemplateConfig.java | 44 +++- .../PromptTemplateFactory.java | 4 +- .../services/AIServiceSelection.java | 5 +- .../services/OrderedAIServiceSelector.java | 27 ++- .../handlebars/HandlebarsPromptTemplate.java | 36 ++- .../semantickernel/CodeTokenizer.java | 10 +- .../semantickernel/DefaultPromptTemplate.java | 7 +- .../semantickernel/TemplateException.java | 37 ++- .../semantickernel/blocks/Block.java | 13 +- .../semantickernel/blocks/CodeBlock.java | 24 +- .../blocks/FunctionIdBlock.java | 2 +- .../semantickernel/blocks/NamedArgBlock.java | 49 +++- .../semantickernel/blocks/TextBlock.java | 3 +- .../semantickernel/blocks/ValBlock.java | 3 +- .../semantickernel/blocks/VarBlock.java | 3 +- .../textcompletion/StreamingTextContent.java | 4 - .../textcompletion/TextContent.java | 3 +- .../textcompletion/TextGenerationService.java | 4 + 79 files changed, 1315 insertions(+), 601 deletions(-) create mode 100644 java/samples/sample-code/src/main/java/com/microsoft/semantickernel/samples/syntaxexamples/RunAll.java delete mode 100644 java/semantickernel-api/src/main/java/com/microsoft/semantickernel/exceptions/SkillsNotFoundException.java create mode 100644 java/semantickernel-api/src/main/java/com/microsoft/semantickernel/orchestration/contextvariables/NullContextVariable.java create mode 100644 java/semantickernel-api/src/main/java/com/microsoft/semantickernel/orchestration/contextvariables/converters/InstantContextVariableTypeConverter.java diff --git a/java/aiservices/openai/src/main/java/com/microsoft/semantickernel/aiservices/openai/chatcompletion/OpenAIChatCompletion.java b/java/aiservices/openai/src/main/java/com/microsoft/semantickernel/aiservices/openai/chatcompletion/OpenAIChatCompletion.java index 7db347c3fd..95cfd0594f 100644 --- a/java/aiservices/openai/src/main/java/com/microsoft/semantickernel/aiservices/openai/chatcompletion/OpenAIChatCompletion.java +++ b/java/aiservices/openai/src/main/java/com/microsoft/semantickernel/aiservices/openai/chatcompletion/OpenAIChatCompletion.java @@ -26,6 +26,7 @@ import com.microsoft.semantickernel.chatcompletion.ChatHistory; import com.microsoft.semantickernel.chatcompletion.ChatMessageContent; import com.microsoft.semantickernel.chatcompletion.StreamingChatMessageContent; import com.microsoft.semantickernel.exceptions.AIException; +import com.microsoft.semantickernel.exceptions.SKException; import com.microsoft.semantickernel.hooks.KernelHooks; import com.microsoft.semantickernel.hooks.PreChatCompletionEvent; import com.microsoft.semantickernel.orchestration.FunctionResult; @@ -56,9 +57,15 @@ public class OpenAIChatCompletion implements ChatCompletionService { private static final Logger LOGGER = LoggerFactory.getLogger(OpenAIChatCompletion.class); private final OpenAIAsyncClient client; private final Map> attributes; + + @Nullable private final String serviceId; - public OpenAIChatCompletion(OpenAIAsyncClient client, String modelId, String serviceId) { + public OpenAIChatCompletion( + OpenAIAsyncClient client, + String modelId, + @Nullable + String serviceId) { this.serviceId = serviceId; this.client = client; this.attributes = new HashMap<>(); @@ -75,6 +82,7 @@ public class OpenAIChatCompletion implements ChatCompletionService { } @Override + @Nullable public String getServiceId() { return serviceId; } @@ -82,8 +90,11 @@ public class OpenAIChatCompletion implements ChatCompletionService { @Override public Mono> getChatMessageContentsAsync( ChatHistory chatHistory, + @Nullable PromptExecutionSettings promptExecutionSettings, + @Nullable Kernel kernel, + @Nullable KernelHooks kernelHooks) { List chatRequestMessages = getChatRequestMessages(chatHistory); @@ -112,6 +123,7 @@ public class OpenAIChatCompletion implements ChatCompletionService { @Override public Mono> getChatMessageContentsAsync( String prompt, + @Nullable PromptExecutionSettings promptExecutionSettings, Kernel kernel, @Nullable @@ -138,7 +150,9 @@ public class OpenAIChatCompletion implements ChatCompletionService { private Mono> internalChatMessageContentsAsync( List chatRequestMessages, + @Nullable List functions, + @Nullable PromptExecutionSettings promptExecutionSettings, KernelHooks kernelHooks) { @@ -242,7 +256,8 @@ public class OpenAIChatCompletion implements ChatCompletionService { * {"type":"function", "function": {"name":"search-search", "parameters": {"query":"Banksy"}}} * where 'name' is . */ - private Mono invokeTool(Kernel kernel, String json) { + @SuppressWarnings("UnusedMethod") + private Mono> invokeTool(Kernel kernel, String json) { try { ObjectMapper mapper = new ObjectMapper(); JsonNode jsonNode = mapper.readTree(json); @@ -254,8 +269,10 @@ public class OpenAIChatCompletion implements ChatCompletionService { if (result != null) { return result.map(contextVariable -> { String content = contextVariable.getResult(); - return new StreamingChatMessageContent(AuthorRole.TOOL, content).setModelId( - id); + if (content == null) { + throw new SKException("Function result must not be null"); + } + return new StreamingChatMessageContent<>(AuthorRole.TOOL, content, id); }); } } @@ -268,6 +285,7 @@ public class OpenAIChatCompletion implements ChatCompletionService { /* * The jsonNode should represent: {"name":"search-search", "parameters": {"query":"Banksy"}}} */ + @SuppressWarnings("StringSplitter") private Mono> invokeFunction(Kernel kernel, JsonNode jsonNode) { String name = jsonNode.get("name").asText(); String[] parts = name.split("-"); @@ -298,23 +316,28 @@ public class OpenAIChatCompletion implements ChatCompletionService { private static ChatCompletionsOptions getCompletionsOptions( ChatCompletionService chatCompletionService, List chatRequestMessages, + @Nullable List functions, + @Nullable PromptExecutionSettings promptExecutionSettings) { - + ChatCompletionsOptions options = new ChatCompletionsOptions(chatRequestMessages) .setModel(chatCompletionService.getModelId()); if (promptExecutionSettings == null) { return options; } - + if (promptExecutionSettings.getResultsPerPrompt() < 1 - || promptExecutionSettings.getResultsPerPrompt() > MAX_RESULTS_PER_PROMPT) { - throw new AIException(AIException.ErrorCodes.INVALID_REQUEST, String.format("Results per prompt must be in range between 1 and %d, inclusive.", MAX_RESULTS_PER_PROMPT)); + || promptExecutionSettings.getResultsPerPrompt() > MAX_RESULTS_PER_PROMPT) { + throw new AIException(AIException.ErrorCodes.INVALID_REQUEST, + String.format("Results per prompt must be in range between 1 and %d, inclusive.", + MAX_RESULTS_PER_PROMPT)); } - List toolDefinitions = - chatCompletionsToolDefinitions(promptExecutionSettings.getToolCallBehavior(), functions); + List toolDefinitions = + chatCompletionsToolDefinitions(promptExecutionSettings.getToolCallBehavior(), + functions); if (toolDefinitions != null && !toolDefinitions.isEmpty()) { options.setTools(toolDefinitions); // TODO: options.setToolChoices(toolChoices); @@ -351,18 +374,22 @@ public class OpenAIChatCompletion implements ChatCompletionService { return options; } + @SuppressWarnings("StringSplitter") private static List chatCompletionsToolDefinitions( + @Nullable ToolCallBehavior toolCallBehavior, + @Nullable List functions) { if (functions == null || functions.isEmpty()) { return Collections.emptyList(); } - - if (toolCallBehavior == null || !(toolCallBehavior.kernelFunctionsEnabled() || toolCallBehavior.autoInvokeEnabled())) { + + if (toolCallBehavior == null || !(toolCallBehavior.kernelFunctionsEnabled() + || toolCallBehavior.autoInvokeEnabled())) { return Collections.emptyList(); } - + return functions.stream() .filter(function -> { String[] parts = function.getName().split("-"); @@ -384,6 +411,9 @@ public class OpenAIChatCompletion implements ChatCompletionService { .map(message -> { AuthorRole authorRole = message.getAuthorRole(); String content = message.getContent(); + if (content == null) { + throw new SKException("ChatMessageContent content must not be null"); + } return getChatRequestMessage(authorRole, content); }) .collect(Collectors.toList()); @@ -405,7 +435,7 @@ public class OpenAIChatCompletion implements ChatCompletionService { return new ChatRequestToolMessage(content, null); default: LOGGER.debug("Unexpected author role: " + authorRole); - return null; + throw new SKException("Unexpected author role: " + authorRole); } } @@ -459,7 +489,10 @@ public class OpenAIChatCompletion implements ChatCompletionService { private static class ToolContentBuffer implements ContentBuffer { + @Nullable private String id = null; + + @Nullable private String name = null; private List arguments = new ArrayList<>(); @@ -575,6 +608,17 @@ public class OpenAIChatCompletion implements ChatCompletionService { @Override public OpenAIChatCompletion build() { + + if (this.client == null) { + throw new AIException(AIException.ErrorCodes.INVALID_REQUEST, + "OpenAI client must be provided"); + } + + if (this.modelId == null || modelId.isEmpty()) { + throw new AIException(AIException.ErrorCodes.INVALID_REQUEST, + "OpenAI model id must be provided"); + } + return new OpenAIChatCompletion(client, modelId, serviceId); } } diff --git a/java/aiservices/openai/src/main/java/com/microsoft/semantickernel/aiservices/openai/chatcompletion/ParsedPrompt.java b/java/aiservices/openai/src/main/java/com/microsoft/semantickernel/aiservices/openai/chatcompletion/ParsedPrompt.java index 68f18b1b67..11955aee04 100644 --- a/java/aiservices/openai/src/main/java/com/microsoft/semantickernel/aiservices/openai/chatcompletion/ParsedPrompt.java +++ b/java/aiservices/openai/src/main/java/com/microsoft/semantickernel/aiservices/openai/chatcompletion/ParsedPrompt.java @@ -2,7 +2,9 @@ package com.microsoft.semantickernel.aiservices.openai.chatcompletion; import com.azure.ai.openai.models.ChatRequestMessage; import com.azure.ai.openai.models.FunctionDefinition; +import java.util.ArrayList; import java.util.List; +import javax.annotation.Nullable; class ParsedPrompt { @@ -10,8 +12,12 @@ class ParsedPrompt { private final List functions; protected ParsedPrompt(List parsedMessages, + @Nullable List parsedFunctions) { this.chatRequestMessages = parsedMessages; + if (parsedFunctions == null) { + parsedFunctions = new ArrayList<>(); + } this.functions = parsedFunctions; } diff --git a/java/aiservices/openai/src/main/java/com/microsoft/semantickernel/aiservices/openai/chatcompletion/XMLPromptParser.java b/java/aiservices/openai/src/main/java/com/microsoft/semantickernel/aiservices/openai/chatcompletion/XMLPromptParser.java index 08d45667fe..176b1837b1 100644 --- a/java/aiservices/openai/src/main/java/com/microsoft/semantickernel/aiservices/openai/chatcompletion/XMLPromptParser.java +++ b/java/aiservices/openai/src/main/java/com/microsoft/semantickernel/aiservices/openai/chatcompletion/XMLPromptParser.java @@ -144,7 +144,9 @@ class XMLPromptParser { // }, // }, //} - assert functionDefinition != null; + if (functionDefinition == null) { + throw new SKException("Failed to parse function definition"); + } if (!parameters.isEmpty()) { StringBuilder sb = new StringBuilder( "{\"type\": \"object\", \"properties\": {"); @@ -207,11 +209,11 @@ class XMLPromptParser { String role, String content) { try { - AuthorRole authorRole = AuthorRole.valueOf(role.toUpperCase()); + AuthorRole authorRole = AuthorRole.valueOf(role.toUpperCase(Locale.ROOT)); return OpenAIChatCompletion.getChatRequestMessage(authorRole, content); } catch (IllegalArgumentException e) { LOGGER.debug("Unknown author role: " + role); - return null; + throw new SKException("Unknown author role: " + role); } } } \ No newline at end of file diff --git a/java/aiservices/openai/src/main/java/com/microsoft/semantickernel/aiservices/openai/textcompletion/OpenAITextGenerationService.java b/java/aiservices/openai/src/main/java/com/microsoft/semantickernel/aiservices/openai/textcompletion/OpenAITextGenerationService.java index b1fea06b4e..c0db75c824 100644 --- a/java/aiservices/openai/src/main/java/com/microsoft/semantickernel/aiservices/openai/textcompletion/OpenAITextGenerationService.java +++ b/java/aiservices/openai/src/main/java/com/microsoft/semantickernel/aiservices/openai/textcompletion/OpenAITextGenerationService.java @@ -25,6 +25,7 @@ public class OpenAITextGenerationService implements TextGenerationService { private final OpenAIAsyncClient client; private final Map> attributes; + @Nullable private final String serviceId; /// @@ -38,7 +39,9 @@ public class OpenAITextGenerationService implements TextGenerationService { /// The to use for logging. If null, no logging will be performed. public OpenAITextGenerationService( OpenAIAsyncClient client, - String modelId, String serviceId) { + String modelId, + @Nullable + String serviceId) { this.serviceId = serviceId; this.client = client; attributes = new HashMap<>(); @@ -55,13 +58,16 @@ public class OpenAITextGenerationService implements TextGenerationService { } @Override + @Nullable public String getServiceId() { return serviceId; } @Override - public Mono> getTextContentsAsync(String prompt, - @Nullable PromptExecutionSettings executionSettings, @Nullable Kernel kernel) { + public Mono> getTextContentsAsync( + String prompt, + @Nullable PromptExecutionSettings executionSettings, + @Nullable Kernel kernel) { return this.internalCompleteTextAsync(prompt, executionSettings); } @@ -78,6 +84,7 @@ public class OpenAITextGenerationService implements TextGenerationService { protected Mono> internalCompleteTextAsync( String text, + @Nullable PromptExecutionSettings requestSettings) { CompletionsOptions completionsOptions = getCompletionsOptions(text, requestSettings); @@ -118,7 +125,9 @@ public class OpenAITextGenerationService implements TextGenerationService { } private CompletionsOptions getCompletionsOptions( - String text, PromptExecutionSettings requestSettings) { + String text, + @Nullable + PromptExecutionSettings requestSettings) { if (requestSettings == null) { return new CompletionsOptions(Collections.singletonList(text)) .setMaxTokens(PromptExecutionSettings.DEFAULT_MAX_TOKENS); @@ -127,8 +136,10 @@ public class OpenAITextGenerationService implements TextGenerationService { throw new AIException(AIException.ErrorCodes.INVALID_REQUEST, "Max tokens must be >0"); } if (requestSettings.getResultsPerPrompt() < 1 - || requestSettings.getResultsPerPrompt() > MAX_RESULTS_PER_PROMPT) { - throw new AIException(AIException.ErrorCodes.INVALID_REQUEST, String.format("Results per prompt must be in range between 1 and %d, inclusive.", MAX_RESULTS_PER_PROMPT)); + || requestSettings.getResultsPerPrompt() > MAX_RESULTS_PER_PROMPT) { + throw new AIException(AIException.ErrorCodes.INVALID_REQUEST, + String.format("Results per prompt must be in range between 1 and %d, inclusive.", + MAX_RESULTS_PER_PROMPT)); } CompletionsOptions options = @@ -143,12 +154,6 @@ public class OpenAITextGenerationService implements TextGenerationService { .setUser(requestSettings.getUser()) .setBestOf(requestSettings.getBestOf()) .setLogitBias(new HashMap<>()); -/* - if (requestSettings instanceof ChatRequestSettings) { - options = options.setStop(requestSettings.getStopSequences()); - } - - */ return options; } @@ -157,7 +162,18 @@ public class OpenAITextGenerationService implements TextGenerationService { */ public static class Builder extends TextGenerationService.Builder { + @Override public TextGenerationService build() { + + if (this.client == null) { + throw new AIException(AIException.ErrorCodes.INVALID_REQUEST, + "OpenAI client must be provided"); + } + if (this.modelId == null) { + throw new AIException(AIException.ErrorCodes.INVALID_REQUEST, + "OpenAI model id must be provided"); + } + return new OpenAITextGenerationService( this.client, this.modelId, diff --git a/java/api-test/integration-tests/src/test/java/com/microsoft/semantickernel/syntaxexamples/Example03_ArgumentsTest.java b/java/api-test/integration-tests/src/test/java/com/microsoft/semantickernel/syntaxexamples/Example03_ArgumentsTest.java index 2367d35524..18196685a2 100644 --- a/java/api-test/integration-tests/src/test/java/com/microsoft/semantickernel/syntaxexamples/Example03_ArgumentsTest.java +++ b/java/api-test/integration-tests/src/test/java/com/microsoft/semantickernel/syntaxexamples/Example03_ArgumentsTest.java @@ -6,7 +6,7 @@ import com.microsoft.semantickernel.orchestration.FunctionResult; import com.microsoft.semantickernel.orchestration.contextvariables.KernelArguments; import com.microsoft.semantickernel.plugin.KernelPlugin; import com.microsoft.semantickernel.plugin.KernelPluginFactory; -import com.microsoft.semantickernel.samples.syntaxexamples.Example03_Arguments.StaticTextSkill; +import com.microsoft.semantickernel.samples.syntaxexamples.Example03_Arguments.StaticTextPlugin; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; @@ -22,7 +22,7 @@ public class Example03_ArgumentsTest { // Load native skill KernelPlugin functionCollection = - KernelPluginFactory.createFromObject(new StaticTextSkill(), "text"); + KernelPluginFactory.createFromObject(new StaticTextPlugin(), "text"); KernelArguments arguments = KernelArguments.builder() .withInput("Today is: ") diff --git a/java/api-test/integration-tests/src/test/java/com/microsoft/semantickernel/syntaxexamples/Example05_InlineFunctionDefinitionTest.java b/java/api-test/integration-tests/src/test/java/com/microsoft/semantickernel/syntaxexamples/Example05_InlineFunctionDefinitionTest.java index 1ac1d29664..a35b7f8c58 100644 --- a/java/api-test/integration-tests/src/test/java/com/microsoft/semantickernel/syntaxexamples/Example05_InlineFunctionDefinitionTest.java +++ b/java/api-test/integration-tests/src/test/java/com/microsoft/semantickernel/syntaxexamples/Example05_InlineFunctionDefinitionTest.java @@ -29,7 +29,7 @@ public class Example05_InlineFunctionDefinitionTest { .endpoint("http://localhost:" + wmRuntimeInfo.getHttpPort()) .buildAsyncClient(); - TextGenerationService textGenerationService = OpenAITextGenerationService.builder() + TextGenerationService textGenerationService = TextGenerationService.builder() .withOpenAIAsyncClient(client) .withModelId("text-davinci-003") .build(); @@ -55,6 +55,7 @@ public class Example05_InlineFunctionDefinitionTest { """.stripIndent(); var excuseFunction = new KernelFunctionFromPrompt.Builder() + .withName("Excuse") .withTemplate(promptTemplate) .withDefaultExecutionSettings( new PromptExecutionSettings.Builder() diff --git a/java/samples/pom.xml b/java/samples/pom.xml index 192cf6abfb..ae72b8025c 100644 --- a/java/samples/pom.xml +++ b/java/samples/pom.xml @@ -126,6 +126,10 @@ ${maven.compiler.release} ${project.build.sourceEncoding} true + + + + diff --git a/java/samples/sample-code/pom.xml b/java/samples/sample-code/pom.xml index fb8ad75d3b..db7e040172 100644 --- a/java/samples/sample-code/pom.xml +++ b/java/samples/sample-code/pom.xml @@ -75,6 +75,30 @@ + + + bug-check + + + false + + + + + + + com.github.spotbugs + spotbugs-maven-plugin + ${maven.spotbugs-plugin.version} + + true + + + + + + + diff --git a/java/samples/sample-code/src/main/java/com/microsoft/semantickernel/samples/plugins/ConversationSummaryPlugin.java b/java/samples/sample-code/src/main/java/com/microsoft/semantickernel/samples/plugins/ConversationSummaryPlugin.java index d0093a1cd9..ac48ef0060 100644 --- a/java/samples/sample-code/src/main/java/com/microsoft/semantickernel/samples/plugins/ConversationSummaryPlugin.java +++ b/java/samples/sample-code/src/main/java/com/microsoft/semantickernel/samples/plugins/ConversationSummaryPlugin.java @@ -69,7 +69,8 @@ public class ConversationSummaryPlugin { /// The containing services, plugins, and other state for use throughout the operation. @DefineKernelFunction( description = "Given a long conversation transcript, summarize the conversation.", - name = "SummarizeConversation" + name = "SummarizeConversation", + returnType = "java.lang.String" ) public Mono SummarizeConversationAsync( @KernelFunctionParameter( @@ -88,7 +89,8 @@ public class ConversationSummaryPlugin { /// The containing services, plugins, and other state for use throughout the operation. @DefineKernelFunction( description = "Given a long conversation transcript, identify action items.", - name = "GetConversationActionItems" + name = "GetConversationActionItems", + returnType = "java.lang.String" ) public Mono GetConversationActionItemsAsync( @KernelFunctionParameter( @@ -107,7 +109,8 @@ public class ConversationSummaryPlugin { /// The containing services, plugins, and other state for use throughout the operation. @DefineKernelFunction( description = "Given a long conversation transcript, identify topics worth remembering.", - name = "GetConversationTopics" + name = "GetConversationTopics", + returnType = "java.lang.String" ) public Mono GetConversationTopicsAsync( @@ -127,14 +130,15 @@ public class ConversationSummaryPlugin { return Flux.fromIterable(paragraphs) .concatMap(paragraph -> { // The first parameter is the input text. - return func.invokeAsync(kernel, - new DefaultKernelArguments.Builder() - .withInput(paragraph) - .build(), - ContextVariableTypes.getDefaultVariableTypeForClass(String.class)); + return func + .invokeAsync(kernel, + new DefaultKernelArguments.Builder() + .withInput(paragraph) + .build(), + ContextVariableTypes.getDefaultVariableTypeForClass(String.class)); }) .reduce("", (acc, next) -> { - return acc + "\n" + next.getResultVariable(); + return acc + "\n" + next.getResult(); }); } } diff --git a/java/samples/sample-code/src/main/java/com/microsoft/semantickernel/samples/syntaxexamples/Example03_Arguments.java b/java/samples/sample-code/src/main/java/com/microsoft/semantickernel/samples/syntaxexamples/Example03_Arguments.java index 051c3e415c..6b3b5e14a8 100644 --- a/java/samples/sample-code/src/main/java/com/microsoft/semantickernel/samples/syntaxexamples/Example03_Arguments.java +++ b/java/samples/sample-code/src/main/java/com/microsoft/semantickernel/samples/syntaxexamples/Example03_Arguments.java @@ -17,10 +17,12 @@ import reactor.core.publisher.Mono; */ public class Example03_Arguments { - public static class StaticTextSkill { + public static class StaticTextPlugin { + @DefineKernelFunction( description = "Change all string chars to uppercase.", - name = "Uppercase") + name = "Uppercase", + returnType = "java.lang.String") public static Mono uppercase( @KernelFunctionParameter( description = "Text to uppercase", @@ -29,7 +31,10 @@ public class Example03_Arguments { return Mono.just(text.toUpperCase(Locale.ROOT)); } - @DefineKernelFunction(description = "Append the day variable", name = "appendDay") + @DefineKernelFunction( + description = "Append the day variable", + name = "appendDay", + returnType = "java.lang.String") public Mono appendDay( @KernelFunctionParameter(description = "Text to append to", name = "input") String input, @@ -43,7 +48,7 @@ public class Example03_Arguments { // Load native skill KernelPlugin functionCollection = - KernelPluginFactory.createFromObject(new StaticTextSkill(), "text"); + KernelPluginFactory.createFromObject(new StaticTextPlugin(), "text"); KernelArguments arguments = KernelArguments.builder() .withInput("Today is: ") @@ -57,6 +62,6 @@ public class Example03_Arguments { String.class) .block(); - System.out.println(resultValue.getResultVariable()); + System.out.println(resultValue.getResult()); } } diff --git a/java/samples/sample-code/src/main/java/com/microsoft/semantickernel/samples/syntaxexamples/Example05_InlineFunctionDefinition.java b/java/samples/sample-code/src/main/java/com/microsoft/semantickernel/samples/syntaxexamples/Example05_InlineFunctionDefinition.java index 6bf7f45a6d..26d6b29d02 100644 --- a/java/samples/sample-code/src/main/java/com/microsoft/semantickernel/samples/syntaxexamples/Example05_InlineFunctionDefinition.java +++ b/java/samples/sample-code/src/main/java/com/microsoft/semantickernel/samples/syntaxexamples/Example05_InlineFunctionDefinition.java @@ -85,7 +85,7 @@ public class Example05_InlineFunctionDefinition { .build(), String.class) .block(); - System.out.println(result.getResultVariable()); + System.out.println(result.getResult()); result = kernel.invokeAsync(excuseFunction, KernelArguments.builder() @@ -93,7 +93,7 @@ public class Example05_InlineFunctionDefinition { .build(), String.class) .block(); - System.out.println(result.getResultVariable()); + System.out.println(result.getResult()); var fixedFunction = KernelFunctionFactory.createFromPrompt( "Translate this date " + DateTimeFormatter @@ -112,7 +112,7 @@ public class Example05_InlineFunctionDefinition { FunctionResult fixedFunctionResult = kernel .invokeAsync(fixedFunction, null, String.class) .block(); - System.out.println(fixedFunctionResult.getResultVariable()); + System.out.println(fixedFunctionResult.getResult()); } } diff --git a/java/samples/sample-code/src/main/java/com/microsoft/semantickernel/samples/syntaxexamples/Example06_TemplateLanguage.java b/java/samples/sample-code/src/main/java/com/microsoft/semantickernel/samples/syntaxexamples/Example06_TemplateLanguage.java index a4baef2c33..53f2c0a5f6 100644 --- a/java/samples/sample-code/src/main/java/com/microsoft/semantickernel/samples/syntaxexamples/Example06_TemplateLanguage.java +++ b/java/samples/sample-code/src/main/java/com/microsoft/semantickernel/samples/syntaxexamples/Example06_TemplateLanguage.java @@ -105,6 +105,6 @@ public class Example06_TemplateLanguage { // Show the result System.out.println("--- Prompt Function result"); var result = kernel.invokeAsync(kindOfDay, null, String.class).block(); - System.out.println(result.getResultVariable()); + System.out.println(result.getResult()); } } diff --git a/java/samples/sample-code/src/main/java/com/microsoft/semantickernel/samples/syntaxexamples/Example09_FunctionTypes.java b/java/samples/sample-code/src/main/java/com/microsoft/semantickernel/samples/syntaxexamples/Example09_FunctionTypes.java index fbd8ee80df..1049ccf35b 100644 --- a/java/samples/sample-code/src/main/java/com/microsoft/semantickernel/samples/syntaxexamples/Example09_FunctionTypes.java +++ b/java/samples/sample-code/src/main/java/com/microsoft/semantickernel/samples/syntaxexamples/Example09_FunctionTypes.java @@ -8,6 +8,7 @@ import com.azure.core.credential.AzureKeyCredential; import com.azure.core.credential.KeyCredential; import com.microsoft.semantickernel.Kernel; import com.microsoft.semantickernel.aiservices.openai.textcompletion.OpenAITextGenerationService; +import com.microsoft.semantickernel.orchestration.FunctionResult; import com.microsoft.semantickernel.orchestration.contextvariables.ContextVariable; import com.microsoft.semantickernel.orchestration.contextvariables.ContextVariableTypeConverter; import com.microsoft.semantickernel.orchestration.contextvariables.ContextVariableTypeConverter.NoopConverter; @@ -18,7 +19,8 @@ import com.microsoft.semantickernel.plugin.annotations.DefineKernelFunction; import com.microsoft.semantickernel.plugin.annotations.KernelFunctionParameter; import com.microsoft.semantickernel.textcompletion.TextGenerationService; import java.nio.file.Path; -import java.time.LocalDateTime; +import java.time.Instant; +import java.time.OffsetDateTime; import java.time.ZonedDateTime; import java.time.format.DateTimeFormatter; import java.time.temporal.Temporal; @@ -115,10 +117,14 @@ public class Example09_FunctionTypes { .build(); // Different ways to invoke a function (not limited to these examples) - kernel.invokeAsync(plugin.get("NoInputWithVoidResult"), null, String.class).block(); - kernel.invokeAsync(plugin.get("NoInputTaskWithVoidResult"), null, String.class).block(); + FunctionResult result = kernel.invokeAsync(plugin.get("NoInputWithVoidResult"), null, + String.class).block(); + System.out.println(result.getResult()); + result = kernel.invokeAsync(plugin.get("NoInputTaskWithVoidResult"), null, String.class) + .block(); + System.out.println(result.getResult()); - kernel + result = kernel .invokeAsync( plugin.get("InputDateTimeWithStringResult"), KernelArguments @@ -132,10 +138,13 @@ public class Example09_FunctionTypes { .build(), String.class ).block(); + System.out.println(result.getResult()); - kernel.invokeAsync(plugin.get("NoInputTaskWithStringResult"), null, String.class).block(); + result = kernel.invokeAsync(plugin.get("NoInputTaskWithStringResult"), null, String.class) + .block(); + System.out.println(result.getResult()); - kernel.invokeAsync(plugin.get("MultipleInputsWithVoidResult"), + result = kernel.invokeAsync(plugin.get("MultipleInputsWithVoidResult"), KernelArguments .builder() .withVariable("x", "x string") @@ -144,8 +153,9 @@ public class Example09_FunctionTypes { .build(), String.class) .block(); + System.out.println(result.getResult()); - kernel + result = kernel .invokeAsync(plugin.get("ComplexInputWithStringResult"), KernelArguments .builder() @@ -164,8 +174,9 @@ public class Example09_FunctionTypes { .build(), String.class) .block(); + System.out.println(result.getResult()); - kernel + result = kernel .invokeAsync(plugin.get("InputStringTaskWithStringResult"), KernelArguments .builder() @@ -173,8 +184,9 @@ public class Example09_FunctionTypes { .build(), String.class) .block(); + System.out.println(result.getResult()); - kernel + result = kernel .invokeAsync(plugin.get("InputStringTaskWithVoidResult"), KernelArguments .builder() @@ -182,24 +194,80 @@ public class Example09_FunctionTypes { .build(), Void.class) .block(); + System.out.println(result.getResult()); - kernel + result = kernel .invokeAsync(plugin.get("noInputComplexReturnTypeAsync"), null, Temporal.class) .block(); + System.out.println(result.getResult()); - kernel + result = kernel .invokeAsync(plugin.get("noInputComplexReturnType"), null, Temporal.class) .block(); + System.out.println(result.getResult()); - kernel + // Possibilities for return type combinations: + // | Method return type | Declared Function Return Type | Invocation Return type | + // |--------------------|-------------------------------|------------------------| + // | T | T | T | + // | T | T | V converted from T | + + // | T | U extends T | T | + // | T | U extends T | U | + // | T | U extends T | V converted from U | + + result = kernel + .invokeAsync(plugin.get("conversionScenarioA"), + null, + OffsetDateTime.class) + .block(); + System.out.println(result.getResult()); + + result = kernel + .invokeAsync(plugin.get("conversionScenarioA"), + null, + Instant.class) + .block(); + System.out.println(result.getResult()); + + result = kernel + .invokeAsync(plugin.get("conversionScenarioB"), + null, + OffsetDateTime.class) + .block(); + System.out.println(result.getResult()); + + result = kernel + .invokeAsync(plugin.get("conversionScenarioB"), + null, + Temporal.class) + .block(); + System.out.println(result.getResult()); + + result = kernel + .invokeAsync(plugin.get("conversionScenarioB"), + null, + Instant.class) + .block(); + System.out.println(result.getResult()); + + result = kernel + .invokeAsync(plugin.get("noInputComplexReturnType"), + null, + String.class) + .block(); + System.out.println(result.getResult()); + + result = kernel .invokeAsync(plugin.get("withDefaultValue"), null, String.class) .block(); + System.out.println(result.getResult()); /* TODO: support FunctionResult @@ -254,7 +322,10 @@ public class Example09_FunctionTypes { /// /// Example of using a void task function with no input /// - @DefineKernelFunction(name = "NoInputTaskWithVoidResult") + @DefineKernelFunction( + name = "NoInputTaskWithVoidResult", + returnType = "java.lang.Void" + ) public Mono NoInputTaskWithVoidResult() { return Mono.fromRunnable( () -> System.out.println("Running this.NoInputTaskWithVoidResult) -> No input")); @@ -263,7 +334,9 @@ public class Example09_FunctionTypes { /// /// Example of using a function with a DateTime input and a string result /// - @DefineKernelFunction(name = "InputDateTimeWithStringResult") + @DefineKernelFunction( + name = "InputDateTimeWithStringResult", + returnType = "java.lang.String") public String InputDateTimeWithStringResult( @KernelFunctionParameter( name = "currentDate", @@ -281,7 +354,9 @@ public class Example09_FunctionTypes { /// /// Example of using a Task function with no input and a string result /// - @DefineKernelFunction(name = "NoInputTaskWithStringResult") + @DefineKernelFunction( + name = "NoInputTaskWithStringResult", + returnType = "java.lang.String") public Mono NoInputTaskWithStringResult() { return Mono.fromCallable(() -> { var result = "string result"; @@ -341,7 +416,10 @@ public class Example09_FunctionTypes { /// /// Example using an async task function echoing the input /// - @DefineKernelFunction(name = "InputStringTaskWithStringResult") + @DefineKernelFunction( + name = "InputStringTaskWithStringResult", + returnType = "java.lang.String" + ) public Mono InputStringTaskWithStringResult( @KernelFunctionParameter( @@ -360,7 +438,10 @@ public class Example09_FunctionTypes { /// /// Example using an async void task with string input /// - @DefineKernelFunction(name = "InputStringTaskWithVoidResult") + @DefineKernelFunction( + name = "InputStringTaskWithVoidResult", + returnType = "java.lang.Void" + ) public Mono InputStringTaskWithVoidResult( @KernelFunctionParameter( name = "x" @@ -377,9 +458,9 @@ public class Example09_FunctionTypes { name = "noInputComplexReturnTypeAsync", returnType = "java.time.temporal.Temporal" ) - public Mono noInputComplexReturnTypeAsync() { + public Mono noInputComplexReturnTypeAsync() { return Mono.just( - LocalDateTime.now() + OffsetDateTime.now() ); } @@ -387,8 +468,8 @@ public class Example09_FunctionTypes { name = "noInputComplexReturnType", returnType = "java.time.temporal.Temporal" ) - public LocalDateTime noInputComplexReturnType() { - return LocalDateTime.now(); + public OffsetDateTime noInputComplexReturnType() { + return OffsetDateTime.now(); } @DefineKernelFunction( @@ -406,6 +487,27 @@ public class Example09_FunctionTypes { return Integer.toString(x); } + // Possibilities for return type combinations: + // | Method return type | Declared Function Return Type | + // |--------------------|-------------------------------| + // | T | T | + // | T | U extends T | + @DefineKernelFunction( + name = "conversionScenarioA", + returnType = "java.time.OffsetDateTime" + ) + public OffsetDateTime conversionScenarioA() { + return OffsetDateTime.now(); + } + + @DefineKernelFunction( + name = "conversionScenarioB", + returnType = "java.time.temporal.Temporal" + ) + public OffsetDateTime conversionScenarioB() { + return OffsetDateTime.now(); + } + /* /// /// Example using a function to return the result of another inner function diff --git a/java/samples/sample-code/src/main/java/com/microsoft/semantickernel/samples/syntaxexamples/Example13_ConversationSummaryPlugin.java b/java/samples/sample-code/src/main/java/com/microsoft/semantickernel/samples/syntaxexamples/Example13_ConversationSummaryPlugin.java index 46564cf8d2..4373a9be09 100644 --- a/java/samples/sample-code/src/main/java/com/microsoft/semantickernel/samples/syntaxexamples/Example13_ConversationSummaryPlugin.java +++ b/java/samples/sample-code/src/main/java/com/microsoft/semantickernel/samples/syntaxexamples/Example13_ConversationSummaryPlugin.java @@ -170,7 +170,7 @@ public class Example13_ConversationSummaryPlugin { ); System.out.println("Generated Action Items:"); - System.out.println(summary.block().getResultVariable()); + System.out.println(summary.block().getResult()); } private static void getConversationTopicsAsync() { @@ -191,7 +191,7 @@ public class Example13_ConversationSummaryPlugin { ); System.out.println("Generated Topics:"); - System.out.println(summary.block().getResultVariable()); + System.out.println(summary.block().getResult()); } @@ -203,7 +203,7 @@ public class Example13_ConversationSummaryPlugin { KernelPlugin conversationSummaryPlugin = KernelPluginFactory.createFromObject( new ConversationSummaryPlugin(), null); - Mono> summary = kernel + FunctionResult summary = kernel .invokeAsync( conversationSummaryPlugin .getFunctions() @@ -212,10 +212,10 @@ public class Example13_ConversationSummaryPlugin { .withInput(ChatTranscript) .build(), String.class - ); + ).block(); System.out.println("Generated Summary (This may take some time):"); - System.out.println(summary.block().getResultVariable()); + System.out.println(summary.getResult()); } private static Kernel initializeKernel() { diff --git a/java/samples/sample-code/src/main/java/com/microsoft/semantickernel/samples/syntaxexamples/Example27_PromptFunctionsUsingChatGPT.java b/java/samples/sample-code/src/main/java/com/microsoft/semantickernel/samples/syntaxexamples/Example27_PromptFunctionsUsingChatGPT.java index d6c24a9483..9adaa3ebc0 100644 --- a/java/samples/sample-code/src/main/java/com/microsoft/semantickernel/samples/syntaxexamples/Example27_PromptFunctionsUsingChatGPT.java +++ b/java/samples/sample-code/src/main/java/com/microsoft/semantickernel/samples/syntaxexamples/Example27_PromptFunctionsUsingChatGPT.java @@ -58,7 +58,7 @@ public class Example27_PromptFunctionsUsingChatGPT { ContextVariableTypes.getDefaultVariableTypeForClass(String.class) ) .block(); - System.out.println(result.getResultVariable()); + System.out.println(result.getResult()); } } diff --git a/java/samples/sample-code/src/main/java/com/microsoft/semantickernel/samples/syntaxexamples/Example56_TemplateMethodFunctionsWithMultipleArguments.java b/java/samples/sample-code/src/main/java/com/microsoft/semantickernel/samples/syntaxexamples/Example56_TemplateMethodFunctionsWithMultipleArguments.java index 2f2bace055..fd02a2e432 100644 --- a/java/samples/sample-code/src/main/java/com/microsoft/semantickernel/samples/syntaxexamples/Example56_TemplateMethodFunctionsWithMultipleArguments.java +++ b/java/samples/sample-code/src/main/java/com/microsoft/semantickernel/samples/syntaxexamples/Example56_TemplateMethodFunctionsWithMultipleArguments.java @@ -85,7 +85,7 @@ public class Example56_TemplateMethodFunctionsWithMultipleArguments { // Show the result System.out.println("--- Prompt Function result"); var result = kernel.invokeAsync(haiku, arguments, String.class).block(); - System.out.println(result.getResultVariable()); + System.out.println(result.getResult()); } } diff --git a/java/samples/sample-code/src/main/java/com/microsoft/semantickernel/samples/syntaxexamples/Example58_ConfigureExecutionSettings.java b/java/samples/sample-code/src/main/java/com/microsoft/semantickernel/samples/syntaxexamples/Example58_ConfigureExecutionSettings.java index da4ce682e6..e78f012cb2 100644 --- a/java/samples/sample-code/src/main/java/com/microsoft/semantickernel/samples/syntaxexamples/Example58_ConfigureExecutionSettings.java +++ b/java/samples/sample-code/src/main/java/com/microsoft/semantickernel/samples/syntaxexamples/Example58_ConfigureExecutionSettings.java @@ -65,7 +65,7 @@ public class Example58_ConfigureExecutionSettings { String.class ).block(); - System.out.println(result.getResultVariable()); + System.out.println(result.getResult()); // Option 2: // Load prompt template configuration including the execution settings from a JSON payload @@ -92,6 +92,6 @@ public class Example58_ConfigureExecutionSettings { var func = KernelFunctionFactory.createFromPrompt(promptConfig, null); result = kernel.invokeAsync(func, null, String.class).block(); - System.out.println(result.getResultVariable()); + System.out.println(result.getResult()); } } diff --git a/java/samples/sample-code/src/main/java/com/microsoft/semantickernel/samples/syntaxexamples/Example61_MultipleLLMs.java b/java/samples/sample-code/src/main/java/com/microsoft/semantickernel/samples/syntaxexamples/Example61_MultipleLLMs.java index bcec3f8bec..4dcbd05b00 100644 --- a/java/samples/sample-code/src/main/java/com/microsoft/semantickernel/samples/syntaxexamples/Example61_MultipleLLMs.java +++ b/java/samples/sample-code/src/main/java/com/microsoft/semantickernel/samples/syntaxexamples/Example61_MultipleLLMs.java @@ -85,7 +85,7 @@ public class Example61_MultipleLLMs { .build(); var result = kernel.invokeAsync(func, arguments, String.class).block(); - System.out.println(result.getResultVariable()); + System.out.println(result.getResult()); } @@ -108,7 +108,7 @@ public class Example61_MultipleLLMs { String.class ).block(); - System.out.println(result.getResultVariable()); + System.out.println(result.getResult()); } @@ -141,7 +141,7 @@ public class Example61_MultipleLLMs { String.class) .block(); - System.out.println(result.getResultVariable()); + System.out.println(result.getResult()); } } diff --git a/java/samples/sample-code/src/main/java/com/microsoft/semantickernel/samples/syntaxexamples/Example62_CustomAIServiceSelector.java b/java/samples/sample-code/src/main/java/com/microsoft/semantickernel/samples/syntaxexamples/Example62_CustomAIServiceSelector.java index 1ed731b17c..460e26a871 100644 --- a/java/samples/sample-code/src/main/java/com/microsoft/semantickernel/samples/syntaxexamples/Example62_CustomAIServiceSelector.java +++ b/java/samples/sample-code/src/main/java/com/microsoft/semantickernel/samples/syntaxexamples/Example62_CustomAIServiceSelector.java @@ -78,7 +78,7 @@ public class Example62_CustomAIServiceSelector { .build(); var result = kernel.invokeAsync(func, arguments, String.class).block(); - System.out.println(result.getResultVariable()); + System.out.println(result.getResult()); } // A dumb AIServiceSelector that just returns the first service and execution settings it finds diff --git a/java/samples/sample-code/src/main/java/com/microsoft/semantickernel/samples/syntaxexamples/Example63_ChatCompletionPrompts.java b/java/samples/sample-code/src/main/java/com/microsoft/semantickernel/samples/syntaxexamples/Example63_ChatCompletionPrompts.java index db2b4d527a..d00e706bd5 100644 --- a/java/samples/sample-code/src/main/java/com/microsoft/semantickernel/samples/syntaxexamples/Example63_ChatCompletionPrompts.java +++ b/java/samples/sample-code/src/main/java/com/microsoft/semantickernel/samples/syntaxexamples/Example63_ChatCompletionPrompts.java @@ -55,14 +55,14 @@ public class Example63_ChatCompletionPrompts { System.out.println("Chat Prompt:"); System.out.println(chatPrompt); System.out.println("Chat Prompt Result:"); - System.out.println(chatPromptResult.getResultVariable()); + System.out.println(chatPromptResult.getResult()); CountDownLatch cdl = new CountDownLatch(1); System.out.println("Chat Prompt Result:"); kernel .invokeAsync(chatSemanticFunction, null, String.class) .doFinally(x -> cdl.countDown()) - .subscribe(result -> System.out.println(result.getResultVariable())); + .subscribe(result -> System.out.println(result.getResult())); cdl.await(); } diff --git a/java/samples/sample-code/src/main/java/com/microsoft/semantickernel/samples/syntaxexamples/Example64_MultiplePromptTemplates.java b/java/samples/sample-code/src/main/java/com/microsoft/semantickernel/samples/syntaxexamples/Example64_MultiplePromptTemplates.java index 79782a1918..3a2020682f 100644 --- a/java/samples/sample-code/src/main/java/com/microsoft/semantickernel/samples/syntaxexamples/Example64_MultiplePromptTemplates.java +++ b/java/samples/sample-code/src/main/java/com/microsoft/semantickernel/samples/syntaxexamples/Example64_MultiplePromptTemplates.java @@ -82,6 +82,6 @@ public class Example64_MultiplePromptTemplates { .build(); var result = kernel.invokeAsync(function, arguments, String.class).block(); - System.out.println(result.getResultVariable()); + System.out.println(result.getResult()); } } diff --git a/java/samples/sample-code/src/main/java/com/microsoft/semantickernel/samples/syntaxexamples/Example69_MutableKernelPlugin.java b/java/samples/sample-code/src/main/java/com/microsoft/semantickernel/samples/syntaxexamples/Example69_MutableKernelPlugin.java index f8e8b69c2e..e5f7156969 100644 --- a/java/samples/sample-code/src/main/java/com/microsoft/semantickernel/samples/syntaxexamples/Example69_MutableKernelPlugin.java +++ b/java/samples/sample-code/src/main/java/com/microsoft/semantickernel/samples/syntaxexamples/Example69_MutableKernelPlugin.java @@ -25,8 +25,8 @@ public class Example69_MutableKernelPlugin { System.out.println("======== Example69_MutableKernelPlugin ========"); KernelPlugin plugin = new KernelPlugin("Plugin", "Mutable plugin", null); - plugin.getFunctions().put("dateFunction", KernelFunctionFactory.createFromMethod( - Time.class.getMethod("date"), new Time(), "date", null, null, null)); + plugin.addFunction(KernelFunctionFactory.createFromMethod( + Time.class.getMethod("date"), new Time(), "dateFunction", null, null, null)); Kernel kernel = Kernel.builder().build(); kernel.getPlugins().add(plugin); diff --git a/java/samples/sample-code/src/main/java/com/microsoft/semantickernel/samples/syntaxexamples/RunAll.java b/java/samples/sample-code/src/main/java/com/microsoft/semantickernel/samples/syntaxexamples/RunAll.java new file mode 100644 index 0000000000..0f35bda833 --- /dev/null +++ b/java/samples/sample-code/src/main/java/com/microsoft/semantickernel/samples/syntaxexamples/RunAll.java @@ -0,0 +1,67 @@ +package com.microsoft.semantickernel.samples.syntaxexamples; + +import java.util.Arrays; +import java.util.List; +import java.util.Scanner; + +/** + * Run all the syntax examples. + *

+ * Refer to the + * README for configuring your environment to run the examples. + */ +public class RunAll { + + public interface MainMethod { + + void run(String[] args) throws Exception; + } + + public static void main(String[] args) { + List mains = Arrays.asList( + Example01_NativeFunctions::main, + Example03_Arguments::main, + Example05_InlineFunctionDefinition::main, + Example06_TemplateLanguage::main, + Example08_RetryHandler::main, + Example09_FunctionTypes::main, + Example10_DescribeAllPluginsAndFunctions::main, + //Example11_WebSearchQueries::main, + Example13_ConversationSummaryPlugin::main, + Example17_ChatGPT::main, + //Example26_AADAuth::main, + + Example27_PromptFunctionsUsingChatGPT::main, + Example30_ChatWithPrompts::main, + Example33_Chat::main, + Example41_HttpClientUsage::main, + Example43_GetModelResult::main, + Example44_MultiChatCompletion::main, + Example49_LogitBias::main, + Example55_TextChunker::main, + Example56_TemplateMethodFunctionsWithMultipleArguments::main, + Example57_KernelHooks::main, + Example58_ConfigureExecutionSettings::main, + Example61_MultipleLLMs::main, + Example62_CustomAIServiceSelector::main, + Example63_ChatCompletionPrompts::main, + Example64_MultiplePromptTemplates::main, + Example69_MutableKernelPlugin::main + ); + + Scanner scanner = new Scanner(System.in); + mains.forEach(mainMethod -> { + try { + + System.out.println("========================================"); + mainMethod.run(args); + + System.out.println("Press any key to continue..."); + scanner.nextLine(); + } catch (Exception e) { + throw new RuntimeException(e); + } + }); + } +} diff --git a/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/AIService.java b/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/AIService.java index cf07463ebd..614f251137 100644 --- a/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/AIService.java +++ b/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/AIService.java @@ -2,62 +2,75 @@ package com.microsoft.semantickernel; import com.microsoft.semantickernel.orchestration.contextvariables.ContextVariable; -import javax.annotation.Nullable; import java.util.Map; +import javax.annotation.Nullable; /** - * Marker interface for AI services + * Marker interface for AI services */ public interface AIService { Map> getAttributes(); - + /** * Key used to store the model identifier in the {@code attributes} map. - */ - static final String MODEL_ID_KEY = "ModelId"; + */ + String MODEL_ID_KEY = "ModelId"; /** * Key used to store the endpoint in the {@code attributes} map. - */ - static final String ENDPOINT_KEY = "Endpoint"; + */ + String ENDPOINT_KEY = "Endpoint"; /** * Key used to store the API version in the {@code attributes} map. - */ - - static final String API_VERSION_KEY = "ApiVersion"; - - /** - * Gets the model identifier from the {@code attributes} map. - * @return The model identifier if it was specified in the service's attributes; otherwise, null. */ - default String getModelId() { return getAttribute(this, MODEL_ID_KEY); } + + String API_VERSION_KEY = "ApiVersion"; + + /** + * Gets the model identifier from the {@code attributes} map. + * + * @return The model identifier if it was specified in the service's attributes; otherwise, + * null. + */ + @Nullable + default String getModelId() { + return getAttribute(this, MODEL_ID_KEY); + } /** * Gets the endpoint from the {@code attributes} map. + * * @return The endpoint if it was specified in the service's attributes; otherwise, null. */ - default String getEndpoint() { return getAttribute(this, ENDPOINT_KEY); } + @Nullable + default String getEndpoint() { + return getAttribute(this, ENDPOINT_KEY); + } /** * Gets the API version from the {@code attributes} map. + * * @return The API version if it was specified in the service's attributes; otherwise, null. */ - default String getApiVersion() { return getAttribute(this, API_VERSION_KEY); } + @Nullable + default String getApiVersion() { + return getAttribute(this, API_VERSION_KEY); + } + @Nullable String getServiceId(); ///

/// Gets the specified attribute. /// @Nullable - static String getAttribute(AIService service, String key) - { + static String getAttribute(AIService service, String key) { if (service.getAttributes() != null) { ContextVariable contextVariable = service.getAttributes().get(key); if (contextVariable != null) { - return contextVariable.getValue().toString(); + return contextVariable.getValue(String.class); } } return null; diff --git a/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/Kernel.java b/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/Kernel.java index 1b596d8aa7..cd44b23307 100644 --- a/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/Kernel.java +++ b/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/Kernel.java @@ -5,11 +5,14 @@ import com.microsoft.semantickernel.builders.Buildable; import com.microsoft.semantickernel.builders.SemanticKernelBuilder; import com.microsoft.semantickernel.hooks.KernelHooks; import com.microsoft.semantickernel.orchestration.FunctionResult; +import com.microsoft.semantickernel.orchestration.FunctionResultMetadata; import com.microsoft.semantickernel.orchestration.KernelFunction; +import com.microsoft.semantickernel.orchestration.contextvariables.ContextVariable; import com.microsoft.semantickernel.orchestration.contextvariables.ContextVariableType; import com.microsoft.semantickernel.orchestration.contextvariables.ContextVariableTypeConverter.NoopConverter; import com.microsoft.semantickernel.orchestration.contextvariables.ContextVariableTypes; import com.microsoft.semantickernel.orchestration.contextvariables.KernelArguments; +import com.microsoft.semantickernel.orchestration.contextvariables.NullContextVariable; import com.microsoft.semantickernel.plugin.KernelPlugin; import com.microsoft.semantickernel.plugin.KernelPluginCollection; import com.microsoft.semantickernel.semanticfunctions.PromptTemplate; @@ -98,20 +101,41 @@ public class Kernel implements Buildable { kernelHooks = mergeInGlobalHooks(kernelHooks); - ContextVariableType contextVariable; + ContextVariableType contextVariable = getContextVariableType(function, resultType); + + return function + .invokeAsync(this, arguments, kernelHooks, contextVariable) + .map(it -> { + return new FunctionResult<>( + ContextVariable.convert(it.getResult(), resultType), + it.getMetadata() + ); + }) + .defaultIfEmpty( + new FunctionResult<>( + new NullContextVariable<>(resultType), + new FunctionResultMetadata())); + } + + @Nullable + private static ContextVariableType getContextVariableType( + KernelFunction function, + Class resultType) { + + Class functionReturnType = function.getMetadata().getReturnParameter().getParameterType(); try { - contextVariable = ContextVariableTypes.getDefaultVariableTypeForClass(resultType); + return ContextVariableTypes.getDefaultVariableTypeForClass( + functionReturnType); } catch (Exception e) { - if (resultType.isAssignableFrom( - function.getMetadata().getReturnParameter().getParameterType())) { - contextVariable = new ContextVariableType<>(new NoopConverter<>(resultType), - resultType); + if (functionReturnType.isAssignableFrom(resultType)) { + return new ContextVariableType<>( + new NoopConverter<>(functionReturnType), + functionReturnType); } else { - throw e; + return null; } } - return function.invokeAsync(this, arguments, kernelHooks, contextVariable); } public Mono> invokeAsync( diff --git a/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/KernelContent.java b/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/KernelContent.java index 2f5b046cb3..51bd1fe9c8 100644 --- a/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/KernelContent.java +++ b/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/KernelContent.java @@ -1,9 +1,6 @@ package com.microsoft.semantickernel; import com.microsoft.semantickernel.orchestration.FunctionResultMetadata; -import com.microsoft.semantickernel.orchestration.contextvariables.ContextVariable; -import java.util.Map; -import javax.annotation.CheckForNull; import javax.annotation.Nullable; public abstract class KernelContent> { @@ -11,7 +8,7 @@ public abstract class KernelContent> { @Nullable private Object innerContent; @Nullable - private String modelId; + private final String modelId; @Nullable private FunctionResultMetadata metadata; @@ -30,25 +27,26 @@ public abstract class KernelContent> { } } - T setInnerContent(@Nullable Object innerContent) { + KernelContent setInnerContent(@Nullable Object innerContent) { this.innerContent = innerContent; - return (T) this; + return this; } - @CheckForNull + @Nullable public Object getInnerContent() { return innerContent; } - @CheckForNull + @Nullable public String getModelId() { return modelId; } - @CheckForNull + @Nullable public FunctionResultMetadata getMetadata() { return metadata; } + @Nullable public abstract String getContent(); } diff --git a/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/StreamingKernelContent.java b/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/StreamingKernelContent.java index 41b69f64c6..198b64e866 100644 --- a/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/StreamingKernelContent.java +++ b/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/StreamingKernelContent.java @@ -24,7 +24,7 @@ public abstract class StreamingKernelContent { /// The model ID used to generate the content. ///
@Nullable - private final String modelId; + private String modelId; /// /// The metadata associated with the content. @@ -32,18 +32,14 @@ public abstract class StreamingKernelContent { @Nullable private final Map> metadata; - protected StreamingKernelContent(@Nullable T innerContent, int choiceIndex, - @Nullable String modelId, @Nullable Map> metadata) { + protected StreamingKernelContent( + @Nullable T innerContent, + int choiceIndex, + @Nullable String modelId, + @Nullable Map> metadata) { this.innerContent = innerContent; this.choiceIndex = choiceIndex; this.modelId = modelId; this.metadata = metadata; } - - - public abstract byte[] toByteArray(); - - public Object getInnerContent() { - return innerContent; - } } diff --git a/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/chatcompletion/AuthorRole.java b/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/chatcompletion/AuthorRole.java index 53fe82eb2c..07913ab888 100644 --- a/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/chatcompletion/AuthorRole.java +++ b/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/chatcompletion/AuthorRole.java @@ -16,18 +16,9 @@ public enum AuthorRole { return role; } - public AuthorRole fromString(String role) { - for (AuthorRole authorRole : AuthorRole.values()) { - if (authorRole.role.equalsIgnoreCase(role)) { - return authorRole; - } - } - return null; - } - private final String role; - private AuthorRole(String role) { + AuthorRole(String role) { this.role = role; } } diff --git a/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/chatcompletion/ChatCompletionService.java b/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/chatcompletion/ChatCompletionService.java index c0f694c935..7e94d13b79 100644 --- a/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/chatcompletion/ChatCompletionService.java +++ b/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/chatcompletion/ChatCompletionService.java @@ -11,6 +11,7 @@ import com.microsoft.semantickernel.builders.ServiceLoadUtil; import com.microsoft.semantickernel.hooks.KernelHooks; import com.microsoft.semantickernel.orchestration.PromptExecutionSettings; import java.util.List; +import javax.annotation.Nullable; import reactor.core.publisher.Mono; public interface ChatCompletionService extends Buildable, TextAIService { @@ -36,8 +37,10 @@ public interface ChatCompletionService extends Buildable, TextAIService { Mono> getChatMessageContentsAsync( String prompt, + @Nullable PromptExecutionSettings promptExecutionSettings, Kernel kernel, + @Nullable KernelHooks kernelHooks ); @@ -49,8 +52,13 @@ public interface ChatCompletionService extends Buildable, TextAIService { abstract class Builder implements SemanticKernelBuilder { + @Nullable protected OpenAIAsyncClient client; + + @Nullable protected String modelId; + + @Nullable protected String serviceId; diff --git a/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/chatcompletion/ChatMessageContent.java b/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/chatcompletion/ChatMessageContent.java index 62f3f98593..20b96d6975 100644 --- a/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/chatcompletion/ChatMessageContent.java +++ b/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/chatcompletion/ChatMessageContent.java @@ -13,6 +13,7 @@ import javax.annotation.Nullable; public class ChatMessageContent extends KernelContent { private AuthorRole authorRole; + @Nullable private String content; @Nullable private List items; @@ -52,13 +53,14 @@ public class ChatMessageContent extends KernelContent { public ChatMessageContent( AuthorRole authorRole, - List items, + List> items, String modelId, String innerContent, Charset encoding, FunctionResultMetadata metadata ) { super(innerContent, modelId, metadata); + this.content = null; this.authorRole = authorRole; this.encoding = encoding != null ? encoding : StandardCharsets.UTF_8; this.items = new ArrayList<>(items); @@ -72,11 +74,13 @@ public class ChatMessageContent extends KernelContent { this.authorRole = authorRole; } + @Nullable + @Override public String getContent() { return content; } - public void setContent(String content) { + public void setContent(@Nullable String content) { this.content = content; } diff --git a/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/chatcompletion/StreamingChatMessageContent.java b/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/chatcompletion/StreamingChatMessageContent.java index 05f14b2640..b534e0aac8 100644 --- a/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/chatcompletion/StreamingChatMessageContent.java +++ b/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/chatcompletion/StreamingChatMessageContent.java @@ -1,33 +1,38 @@ // Copyright (c) Microsoft. All rights reserved. package com.microsoft.semantickernel.chatcompletion; +import com.microsoft.semantickernel.StreamingKernelContent; +import com.microsoft.semantickernel.orchestration.contextvariables.ContextVariable; import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; import java.util.Map; +import javax.annotation.Nullable; -import com.microsoft.semantickernel.StreamingKernelContent; -import com.microsoft.semantickernel.orchestration.contextvariables.ContextVariable; - -public class StreamingChatMessageContent extends StreamingKernelContent { +public class StreamingChatMessageContent extends StreamingKernelContent { private final String content; private final AuthorRole role; private Charset encoding; - private String modelId; public StreamingChatMessageContent( AuthorRole role, - String content) { - this(role, content, null, 0, null, null, null); + String content, + @Nullable + String modelId) { + this(role, content, null, 0, modelId, null, null); } public StreamingChatMessageContent( AuthorRole role, String content, - Object innerContent, + @Nullable + T innerContent, int choiceIndex, + @Nullable String modelId, + @Nullable Charset encoding, + @Nullable Map> metadata) { super(innerContent, choiceIndex, modelId, metadata); this.role = role; @@ -40,11 +45,6 @@ public class StreamingChatMessageContent extends StreamingKernelContent { return content != null ? content : ""; } - @Override - public byte[] toByteArray() { - return toString().getBytes(encoding); - } - public String getContent() { return content; } @@ -57,18 +57,9 @@ public class StreamingChatMessageContent extends StreamingKernelContent { return encoding; } - public StreamingChatMessageContent setEncoding(Charset encoding) { + public StreamingChatMessageContent setEncoding(Charset encoding) { this.encoding = encoding; return this; } - - public StreamingChatMessageContent setModelId(String modelId) { - this.modelId = modelId; - return this; - } - - public String getModelId() { - return modelId; - } } diff --git a/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/exceptions/SkillsNotFoundException.java b/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/exceptions/SkillsNotFoundException.java deleted file mode 100644 index 692497226d..0000000000 --- a/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/exceptions/SkillsNotFoundException.java +++ /dev/null @@ -1,42 +0,0 @@ -// Copyright (c) Microsoft. All rights reserved. -package com.microsoft.semantickernel.exceptions; - -import javax.annotation.Nonnull; -import javax.annotation.Nullable; - -public class SkillsNotFoundException extends SKException { - - private final ErrorCodes errorCode; - - public SkillsNotFoundException(@Nonnull ErrorCodes errorCodes) { - this(errorCodes, null, null); - } - - public SkillsNotFoundException(@Nonnull ErrorCodes errorCodes, @Nullable String message) { - this(errorCodes, message, null); - } - - public SkillsNotFoundException( - @Nonnull ErrorCodes errorCodes, @Nullable String message, @Nullable Throwable cause) { - super(formatDefaultMessage(errorCodes.getMessage(), message), cause); - this.errorCode = errorCodes; - } - - public ErrorCodes getErrorCode() { - return errorCode; - } - - public enum ErrorCodes { - SKILLS_NOT_FOUND("Skills not found"); - - final String message; - - ErrorCodes(String message) { - this.message = message; - } - - String getMessage() { - return message; - } - } -} diff --git a/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/hooks/FunctionInvokedEvent.java b/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/hooks/FunctionInvokedEvent.java index abd5e40490..66b75a0599 100644 --- a/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/hooks/FunctionInvokedEvent.java +++ b/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/hooks/FunctionInvokedEvent.java @@ -3,14 +3,18 @@ package com.microsoft.semantickernel.hooks; import com.microsoft.semantickernel.orchestration.FunctionResult; import com.microsoft.semantickernel.orchestration.KernelFunction; import com.microsoft.semantickernel.orchestration.contextvariables.KernelArguments; +import javax.annotation.Nullable; public class FunctionInvokedEvent implements KernelHookEvent { private final KernelFunction function; + @Nullable private final KernelArguments arguments; private final FunctionResult result; - public FunctionInvokedEvent(KernelFunction function, KernelArguments arguments, + public FunctionInvokedEvent( + KernelFunction function, + @Nullable KernelArguments arguments, FunctionResult result) { this.function = function; this.arguments = arguments; @@ -21,6 +25,7 @@ public class FunctionInvokedEvent implements KernelHookEvent { return function; } + @Nullable public KernelArguments getArguments() { return arguments; } diff --git a/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/hooks/FunctionInvokingEvent.java b/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/hooks/FunctionInvokingEvent.java index 4cb8f100da..5df282bbe6 100644 --- a/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/hooks/FunctionInvokingEvent.java +++ b/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/hooks/FunctionInvokingEvent.java @@ -2,13 +2,15 @@ package com.microsoft.semantickernel.hooks; import com.microsoft.semantickernel.orchestration.KernelFunction; import com.microsoft.semantickernel.orchestration.contextvariables.KernelArguments; +import javax.annotation.Nullable; public class FunctionInvokingEvent implements KernelHookEvent { private final KernelFunction function; + @Nullable private final KernelArguments arguments; - public FunctionInvokingEvent(KernelFunction function, KernelArguments arguments) { + public FunctionInvokingEvent(KernelFunction function, @Nullable KernelArguments arguments) { this.function = function; this.arguments = arguments; } @@ -17,6 +19,7 @@ public class FunctionInvokingEvent implements KernelHookEvent { return function; } + @Nullable public KernelArguments getArguments() { return arguments; } diff --git a/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/orchestration/DefaultKernelFunction.java b/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/orchestration/DefaultKernelFunction.java index e1f33b23ea..a6b2cb2eda 100644 --- a/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/orchestration/DefaultKernelFunction.java +++ b/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/orchestration/DefaultKernelFunction.java @@ -1,5 +1,6 @@ package com.microsoft.semantickernel.orchestration; +import com.microsoft.semantickernel.Todo; import java.util.Collections; import java.util.Map; import javax.annotation.Nullable; @@ -43,23 +44,19 @@ public abstract class DefaultKernelFunction implements KernelFunction { } @Override - public String toFullyQualifiedName() { - return null; - } - - @Override + @Nullable public String getDescription() { - return null; + return metadata.getDescription(); } @Override public String toEmbeddingString() { - return null; + throw new Todo(); } @Override public String toManualString(boolean includeOutputs) { - return null; + throw new Todo(); } @Override diff --git a/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/orchestration/FunctionResult.java b/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/orchestration/FunctionResult.java index d5a270d958..00cf5a0ddd 100644 --- a/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/orchestration/FunctionResult.java +++ b/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/orchestration/FunctionResult.java @@ -1,22 +1,25 @@ package com.microsoft.semantickernel.orchestration; import com.microsoft.semantickernel.orchestration.contextvariables.ContextVariable; +import javax.annotation.Nullable; public class FunctionResult { private final ContextVariable result; private final FunctionResultMetadata metadata; - public FunctionResult(ContextVariable result, FunctionResultMetadata metadata) { + public FunctionResult( + ContextVariable result, + @Nullable FunctionResultMetadata metadata) { this.result = result; - this.metadata = metadata; + this.metadata = metadata == null ? FunctionResultMetadata.empty() : metadata; } public FunctionResult(ContextVariable of) { this(of, FunctionResultMetadata.empty()); } - + @Nullable public T getResult() { return result.getValue(); } diff --git a/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/orchestration/FunctionResultMetadata.java b/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/orchestration/FunctionResultMetadata.java index 185bf6ea29..d04d5bb781 100644 --- a/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/orchestration/FunctionResultMetadata.java +++ b/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/orchestration/FunctionResultMetadata.java @@ -4,6 +4,7 @@ import com.azure.ai.openai.models.CompletionsUsage; import com.microsoft.semantickernel.orchestration.contextvariables.CaseInsensitiveMap; import com.microsoft.semantickernel.orchestration.contextvariables.ContextVariable; import java.time.OffsetDateTime; +import javax.annotation.Nullable; public class FunctionResultMetadata { @@ -14,6 +15,10 @@ public class FunctionResultMetadata { private final CaseInsensitiveMap> metadata; + public FunctionResultMetadata() { + this.metadata = new CaseInsensitiveMap<>(); + } + public FunctionResultMetadata(CaseInsensitiveMap> metadata) { this.metadata = new CaseInsensitiveMap<>(metadata); } @@ -39,15 +44,30 @@ public class FunctionResultMetadata { return new CaseInsensitiveMap<>(metadata); } + @Nullable public String getId() { - return metadata.get(ID).getValue(String.class); + ContextVariable id = metadata.get(ID); + if (id == null) { + return null; + } + return id.getValue(String.class); } + @Nullable public CompletionsUsage getUsage() { - return metadata.get(USAGE).getValue(CompletionsUsage.class); + ContextVariable usage = metadata.get(USAGE); + if (usage == null) { + return null; + } + return usage.getValue(CompletionsUsage.class); } + @Nullable public OffsetDateTime getCreatedAt() { - return metadata.get(CREATED_AT).getValue(OffsetDateTime.class); + ContextVariable createdAt = metadata.get(CREATED_AT); + if (createdAt == null) { + return null; + } + return createdAt.getValue(OffsetDateTime.class); } } diff --git a/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/orchestration/KernelFunction.java b/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/orchestration/KernelFunction.java index 8aff64caa2..c74714998e 100644 --- a/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/orchestration/KernelFunction.java +++ b/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/orchestration/KernelFunction.java @@ -33,16 +33,11 @@ public interface KernelFunction extends Buildable { */ String getName(); - /** - * The function to create a fully qualified name for - * - * @return A fully qualified name for a function - */ - String toFullyQualifiedName(); - /** * @return A description of the function */ + + @Nullable String getDescription(); /** @@ -80,41 +75,46 @@ public interface KernelFunction extends Buildable { Mono> invokeAsync( Kernel kernel, @Nullable KernelArguments arguments, - ContextVariableType variableType); + @Nullable ContextVariableType variableType); Mono> invokeAsync( Kernel kernel, @Nullable KernelArguments arguments, KernelHooks kernelHooks, - ContextVariableType variableType); + @Nullable ContextVariableType variableType); @Nullable Map getExecutionSettings(); interface FromPromptBuilder { - FromPromptBuilder withName(String name); + FromPromptBuilder withName(@Nullable String name); - FromPromptBuilder withInputParameters(List inputVariables); + FromPromptBuilder withInputParameters( + @Nullable List inputVariables); - FromPromptBuilder withPromptTemplate(PromptTemplate promptTemplate); + FromPromptBuilder withPromptTemplate( + @Nullable PromptTemplate promptTemplate); FromPromptBuilder withExecutionSettings( + @Nullable Map executionSettings); FromPromptBuilder withDefaultExecutionSettings( + @Nullable PromptExecutionSettings executionSettings); - FromPromptBuilder withDescription(String description); + FromPromptBuilder withDescription(@Nullable String description); - FromPromptBuilder withTemplate(String template); + FromPromptBuilder withTemplate(@Nullable String template); KernelFunction build(); FromPromptBuilder withTemplateFormat(String templateFormat); - FromPromptBuilder withOutputVariable(OutputVariable outputVariable); + FromPromptBuilder withOutputVariable(@Nullable OutputVariable outputVariable); - FromPromptBuilder withPromptTemplateFactory(PromptTemplateFactory promptTemplateFactory); + FromPromptBuilder withPromptTemplateFactory( + @Nullable PromptTemplateFactory promptTemplateFactory); } } diff --git a/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/orchestration/KernelFunctionMetadata.java b/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/orchestration/KernelFunctionMetadata.java index f48166069a..faacea20d6 100644 --- a/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/orchestration/KernelFunctionMetadata.java +++ b/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/orchestration/KernelFunctionMetadata.java @@ -5,15 +5,17 @@ import com.microsoft.semantickernel.plugin.KernelReturnParameterMetadata; import java.util.ArrayList; import java.util.Collections; import java.util.List; +import javax.annotation.Nullable; public class KernelFunctionMetadata { /// The name of the function. private final String name; /// The description of the function. + @Nullable private final String description; /// The function's parameters. - private final List parameters; + private final List> parameters; /// The function's return parameter. @@ -22,12 +24,19 @@ public class KernelFunctionMetadata { public KernelFunctionMetadata( String name, + @Nullable String description, - List parameters, + @Nullable + List> parameters, KernelReturnParameterMetadata returnParameter) { this.name = name; this.description = description; - this.parameters = new ArrayList<>(parameters); + if (parameters == null) { + this.parameters = new ArrayList<>(); + } else { + this.parameters = new ArrayList<>(parameters); + } + this.returnParameter = returnParameter; } @@ -35,10 +44,11 @@ public class KernelFunctionMetadata { return name; } - public List getParameters() { + public List> getParameters() { return Collections.unmodifiableList(parameters); } + @Nullable public String getDescription() { return description; } diff --git a/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/orchestration/MethodDetails.java b/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/orchestration/MethodDetails.java index 07e7baf837..918fafcb0c 100644 --- a/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/orchestration/MethodDetails.java +++ b/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/orchestration/MethodDetails.java @@ -6,21 +6,24 @@ import com.microsoft.semantickernel.semanticfunctions.KernelFunctionFromMethod.I import java.util.ArrayList; import java.util.Collections; import java.util.List; +import javax.annotation.Nullable; public class MethodDetails { private final String name; + @Nullable private final String description; private final ImplementationFunc function; - private final List parameters; - private final KernelReturnParameterMetadata returnParameter; + private final List> parameters; + private final KernelReturnParameterMetadata returnParameter; public MethodDetails( String name, + @Nullable String description, ImplementationFunc function, - List parameters, - KernelReturnParameterMetadata returnParameter) { + List> parameters, + KernelReturnParameterMetadata returnParameter) { this.name = name; this.description = description; this.function = function; @@ -32,6 +35,7 @@ public class MethodDetails { return name; } + @Nullable public String getDescription() { return description; } @@ -40,11 +44,11 @@ public class MethodDetails { return function; } - public List getParameters() { + public List> getParameters() { return Collections.unmodifiableList(parameters); } - public KernelReturnParameterMetadata getReturnParameter() { + public KernelReturnParameterMetadata getReturnParameter() { return returnParameter; } } diff --git a/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/orchestration/contextvariables/ContextVariable.java b/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/orchestration/contextvariables/ContextVariable.java index 910f7e6829..97d4f0177e 100644 --- a/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/orchestration/contextvariables/ContextVariable.java +++ b/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/orchestration/contextvariables/ContextVariable.java @@ -1,23 +1,148 @@ // Copyright (c) Microsoft. All rights reserved. package com.microsoft.semantickernel.orchestration.contextvariables; +import com.microsoft.semantickernel.exceptions.SKException; +import com.microsoft.semantickernel.orchestration.contextvariables.ContextVariableTypeConverter.NoopConverter; +import javax.annotation.Nullable; + public class ContextVariable { private final ContextVariableType type; + + @Nullable private final T value; - public ContextVariable(ContextVariableType type, T value) { + public ContextVariable(ContextVariableType type, + @Nullable T value) { this.type = type; this.value = value; } + public static ContextVariable of(Class type, @Nullable T t) { + if (t == null) { + return new ContextVariable<>( + ContextVariableTypes.getDefaultVariableTypeForClass(type), + null); + } + + return of(t); + } + + public static ContextVariable untypedOf( + @Nullable Object value, + Class clazz) { + ContextVariableType type = ContextVariableTypes.getDefaultVariableTypeForClass(clazz); + return new ContextVariable(type, value); + } + + public static ContextVariable untypedOf( + @Nullable Object value, + ContextVariableTypeConverter converter) { + ContextVariableType type = new ContextVariableType(converter, converter.getType()); + return new ContextVariable(type, value); + } + + public static ContextVariable convert( + @Nullable + U it, + Class requestedResultType) { + return convert( + it, + requestedResultType, + ContextVariableTypes.DEFAULT_TYPES + ); + } + + public static ContextVariable convert( + @Nullable + U it, + Class requestedResultType, + ContextVariableTypes contextVariableTypes) { + if (it == null) { + return new ContextVariable<>( + contextVariableTypes.getVariableTypeForClass(requestedResultType), + null); + } + + if (requestedResultType.isAssignableFrom(it.getClass())) { + try { + return ContextVariable.of(requestedResultType, requestedResultType.cast(it)); + } catch (Exception e) { + return new ContextVariable<>( + new ContextVariableType<>( + new NoopConverter<>(requestedResultType), + requestedResultType + ), + requestedResultType.cast(it) + ); + } + } + + ContextVariableType requestedResultTypeVariable; + + try { + requestedResultTypeVariable = contextVariableTypes.getVariableTypeForClass( + requestedResultType); + } catch (Exception e) { + throw new SKException("Unable to find variable type for " + requestedResultType, e); + } + + ContextVariableType typeOfActualReturnedType; + + try { + // First try to convert from type ? to T using the converter of ? and see if it can convert it to T. + typeOfActualReturnedType = contextVariableTypes.getVariableTypeForClass( + (Class) it.getClass()); + } catch (Exception e) { + typeOfActualReturnedType = contextVariableTypes.getVariableTypeForSuperClass( + (Class) it.getClass()); + } + + if (typeOfActualReturnedType != null) { + // Try the to object + T converted = typeOfActualReturnedType.getConverter().toObject(it, requestedResultType); + + if (converted != null) { + return contextVariableTypes.getVariableTypeForClass(requestedResultType) + .of(converted); + } + + if (requestedResultType.isAssignableFrom(String.class)) { + // Try using toPromptString + String str = typeOfActualReturnedType.getConverter() + .toPromptString(typeOfActualReturnedType.getClazz().cast(it)); + + return requestedResultTypeVariable.of(str); + } + } + + // Try getting a converter of type T and see if it can convert ? to T. + if (requestedResultTypeVariable != null) { + // Try using from object + T result = requestedResultTypeVariable.getConverter().fromObject(it); + if (result != null) { + return requestedResultTypeVariable.of(result); + } + + if (requestedResultType.equals(String.class)) { + // Try using from prompt string + return requestedResultTypeVariable.of( + requestedResultTypeVariable.getConverter().fromPromptString((String) it)); + } + } + + throw new SKException("Unable to convert " + it.getClass() + " to " + requestedResultType); + } + + @Nullable public T getValue() { return value; } + @Nullable public U getValue(Class clazz) { - if (clazz.isAssignableFrom(value.getClass())) { - return (U) value; + if (value == null || clazz.isAssignableFrom(value.getClass())) { + return clazz.cast(value); } else { throw new RuntimeException("Cannot cast " + value.getClass() + " to " + clazz); } diff --git a/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/orchestration/contextvariables/ContextVariableType.java b/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/orchestration/contextvariables/ContextVariableType.java index efc4b7e98e..72f49db7e3 100644 --- a/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/orchestration/contextvariables/ContextVariableType.java +++ b/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/orchestration/contextvariables/ContextVariableType.java @@ -1,5 +1,7 @@ package com.microsoft.semantickernel.orchestration.contextvariables; +import javax.annotation.Nullable; + public class ContextVariableType { private final ContextVariableTypeConverter contextVariableTypeConverter; @@ -26,10 +28,15 @@ public class ContextVariableType { * @param it The object to convert. * @return A context variable of this type. */ - public ContextVariable of(Object it) { - if (getClazz().isAssignableFrom(it.getClass())) { - return ContextVariable.of((T) it, getConverter()); + public ContextVariable of(@Nullable Object it) { + if (it == null) { + return ContextVariable.of(clazz, clazz.cast(null)); } - return ContextVariable.of(getConverter().fromObject(it)); + + if (clazz.isAssignableFrom(it.getClass())) { + return ContextVariable.of(clazz.cast(it), getConverter()); + } + ContextVariableTypeConverter converter = getConverter(); + return ContextVariable.of(converter.getType(), converter.fromObject(it)); } } diff --git a/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/orchestration/contextvariables/ContextVariableTypeConverter.java b/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/orchestration/contextvariables/ContextVariableTypeConverter.java index 22160b15b5..41722fe19b 100644 --- a/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/orchestration/contextvariables/ContextVariableTypeConverter.java +++ b/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/orchestration/contextvariables/ContextVariableTypeConverter.java @@ -79,10 +79,14 @@ public class ContextVariableTypeConverter { this.toObjects = new ArrayList<>(toObjects); } - + @Nullable @SuppressWarnings("unchecked") - public U toObject(Object t, Class clazz) { + public U toObject(@Nullable Object t, Class clazz) { + if (t == null) { + return null; + } + Optional> converter = toObjects .stream() .filter(c -> c.getTargetType().equals(clazz)) @@ -106,14 +110,14 @@ public class ContextVariableTypeConverter { } @Nullable - public T fromObject(Object s) { + public T fromObject(@Nullable Object s) { if (s == null) { return null; } return fromObject.apply(s); } - public String toPromptString(T t) { + public String toPromptString(@Nullable T t) { if (t == null) { return ""; } @@ -121,7 +125,7 @@ public class ContextVariableTypeConverter { } @Nullable - public T fromPromptString(String t) { + public T fromPromptString(@Nullable String t) { if (t == null) { return null; } diff --git a/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/orchestration/contextvariables/ContextVariableTypes.java b/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/orchestration/contextvariables/ContextVariableTypes.java index 9463d45a25..f917f7943e 100644 --- a/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/orchestration/contextvariables/ContextVariableTypes.java +++ b/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/orchestration/contextvariables/ContextVariableTypes.java @@ -5,6 +5,7 @@ import com.microsoft.semantickernel.orchestration.contextvariables.converters.Ch import com.microsoft.semantickernel.orchestration.contextvariables.converters.ChatHistoryVariableContextVariableTypeConverter; import com.microsoft.semantickernel.orchestration.contextvariables.converters.CompletionUsageContextVariableTypeConverter; import com.microsoft.semantickernel.orchestration.contextvariables.converters.DateTimeContextVariableTypeConverter; +import com.microsoft.semantickernel.orchestration.contextvariables.converters.InstantContextVariableTypeConverter; import com.microsoft.semantickernel.orchestration.contextvariables.converters.NumberVariableContextVariableTypeConverter; import com.microsoft.semantickernel.orchestration.contextvariables.converters.StringVariableContextVariableTypeConverter; import com.microsoft.semantickernel.orchestration.contextvariables.converters.VoidVariableContextVariableTypeConverter; @@ -12,6 +13,7 @@ import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; +import javax.annotation.Nullable; public class ContextVariableTypes { @@ -24,7 +26,9 @@ public class ContextVariableTypes { new ChatHistoryVariableContextVariableTypeConverter(), new StringVariableContextVariableTypeConverter(), new VoidVariableContextVariableTypeConverter(), + new ContextVariableTypeConverter<>(void.class, s -> null, s -> null, s -> null), new DateTimeContextVariableTypeConverter(), + new InstantContextVariableTypeConverter(), new CompletionUsageContextVariableTypeConverter(), new NumberVariableContextVariableTypeConverter<>(Byte.class, Byte::parseByte), @@ -64,8 +68,9 @@ public class ContextVariableTypes { contextVariableTypeConverter.getType())); } + @Nullable @SuppressWarnings("unchecked") - public static T convert(Object s, Class clazz) { + public static T convert(@Nullable Object s, Class clazz) { if (s instanceof ContextVariable && ((ContextVariable) s).getType().getClazz() .isAssignableFrom(clazz)) { return ((ContextVariable) s).getValue(); @@ -92,10 +97,29 @@ public class ContextVariableTypes { return (ContextVariableType) variableTypes .values() .stream() - .filter(c -> c.getClazz().isAssignableFrom(aClass)) + .filter(c -> { + return c.getClazz().isAssignableFrom(aClass); + }) .findFirst() .orElseThrow( () -> new RuntimeException("Unknown context variable type: " + aClass.getName())); + } + public ContextVariableType getVariableTypeForSuperClass(Class aClass) { + ContextVariableType contextVariableType = variableTypes.get(aClass); + + if (contextVariableType != null) { + return (ContextVariableType) contextVariableType; + } + + return (ContextVariableType) variableTypes + .values() + .stream() + .filter(c -> { + return aClass.isAssignableFrom(c.getClazz()); + }) + .findFirst() + .orElseThrow( + () -> new RuntimeException("Unknown context variable type: " + aClass.getName())); } } \ No newline at end of file diff --git a/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/orchestration/contextvariables/DefaultKernelArguments.java b/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/orchestration/contextvariables/DefaultKernelArguments.java index 3fa69cd2b1..e372347cb2 100644 --- a/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/orchestration/contextvariables/DefaultKernelArguments.java +++ b/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/orchestration/contextvariables/DefaultKernelArguments.java @@ -90,11 +90,6 @@ public class DefaultKernelArguments implements KernelArguments, WritableKernelAr return setVariable(MAIN_KEY, content); } - @Override - public KernelArguments update(String content) { - return null; - } - public DefaultKernelArguments update(@NonNull DefaultKernelArguments newData) { return update(newData, true); } @@ -198,6 +193,7 @@ public class DefaultKernelArguments implements KernelArguments, WritableKernelAr } @Override + @Nullable public ContextVariable get(Object key) { return variables.get(key); } diff --git a/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/orchestration/contextvariables/NullContextVariable.java b/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/orchestration/contextvariables/NullContextVariable.java new file mode 100644 index 0000000000..c41a908718 --- /dev/null +++ b/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/orchestration/contextvariables/NullContextVariable.java @@ -0,0 +1,13 @@ +package com.microsoft.semantickernel.orchestration.contextvariables; + +import com.microsoft.semantickernel.orchestration.contextvariables.ContextVariableTypeConverter.NoopConverter; + +public class NullContextVariable extends ContextVariable { + + public NullContextVariable(Class type) { + super(new ContextVariableType<>( + new NoopConverter<>(type), + type + ), null); + } +} diff --git a/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/orchestration/contextvariables/WritableKernelArguments.java b/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/orchestration/contextvariables/WritableKernelArguments.java index f8562c28a9..44a548bc4e 100644 --- a/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/orchestration/contextvariables/WritableKernelArguments.java +++ b/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/orchestration/contextvariables/WritableKernelArguments.java @@ -29,8 +29,6 @@ public interface WritableKernelArguments extends KernelArguments { */ KernelArguments update(ContextVariable content); - KernelArguments update(String content); - /** * Updates the variables merging or overwriting in the new values. * diff --git a/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/orchestration/contextvariables/converters/DateTimeContextVariableTypeConverter.java b/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/orchestration/contextvariables/converters/DateTimeContextVariableTypeConverter.java index f5c0f1e260..bb97fb4f65 100644 --- a/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/orchestration/contextvariables/converters/DateTimeContextVariableTypeConverter.java +++ b/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/orchestration/contextvariables/converters/DateTimeContextVariableTypeConverter.java @@ -1,8 +1,10 @@ package com.microsoft.semantickernel.orchestration.contextvariables.converters; import com.microsoft.semantickernel.orchestration.contextvariables.ContextVariableTypeConverter; +import java.time.Instant; import java.time.OffsetDateTime; import java.time.ZonedDateTime; +import java.util.Arrays; public class DateTimeContextVariableTypeConverter extends ContextVariableTypeConverter { @@ -21,6 +23,14 @@ public class DateTimeContextVariableTypeConverter extends Object::toString, o -> { return ZonedDateTime.parse(o).toOffsetDateTime(); - }); + }, + Arrays.asList( + new DefaultConverter(OffsetDateTime.class, Instant.class) { + @Override + public Instant toObject(OffsetDateTime offsetDateTime) { + return offsetDateTime.toInstant(); + } + } + )); } } diff --git a/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/orchestration/contextvariables/converters/InstantContextVariableTypeConverter.java b/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/orchestration/contextvariables/converters/InstantContextVariableTypeConverter.java new file mode 100644 index 0000000000..9aae5c0c1f --- /dev/null +++ b/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/orchestration/contextvariables/converters/InstantContextVariableTypeConverter.java @@ -0,0 +1,26 @@ +package com.microsoft.semantickernel.orchestration.contextvariables.converters; + +import com.microsoft.semantickernel.orchestration.contextvariables.ContextVariableTypeConverter; +import java.time.Instant; +import java.time.OffsetDateTime; + +public class InstantContextVariableTypeConverter extends + ContextVariableTypeConverter { + + public InstantContextVariableTypeConverter() { + super( + Instant.class, + s -> { + if (s instanceof String) { + return Instant.parse((String) s); + } else if (s instanceof OffsetDateTime) { + return ((OffsetDateTime) s).toInstant(); + } + return null; + }, + Object::toString, + o -> { + return Instant.parse(o); + }); + } +} diff --git a/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/orchestration/contextvariables/converters/NumberVariableContextVariableTypeConverter.java b/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/orchestration/contextvariables/converters/NumberVariableContextVariableTypeConverter.java index c0c38b951b..cd970a46e2 100644 --- a/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/orchestration/contextvariables/converters/NumberVariableContextVariableTypeConverter.java +++ b/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/orchestration/contextvariables/converters/NumberVariableContextVariableTypeConverter.java @@ -1,11 +1,10 @@ package com.microsoft.semantickernel.orchestration.contextvariables.converters; -import java.util.function.Function; - -import javax.annotation.Nullable; +import static com.microsoft.semantickernel.orchestration.contextvariables.ContextVariableTypes.convert; import com.microsoft.semantickernel.orchestration.contextvariables.ContextVariableTypeConverter; -import static com.microsoft.semantickernel.orchestration.contextvariables.ContextVariableTypes.convert; +import java.util.function.Function; +import javax.annotation.Nullable; public class NumberVariableContextVariableTypeConverter extends ContextVariableTypeConverter { @@ -21,7 +20,11 @@ public class NumberVariableContextVariableTypeConverter extend @Override @Nullable - public U toObject(Object t, Class clazz) { + public U toObject(@Nullable Object t, Class clazz) { + if (t == null) { + return null; + } + // Let the parent class have a crack at it first // since someone may have installed a special converter. U obj = super.toObject(t, clazz); @@ -35,12 +38,13 @@ public class NumberVariableContextVariableTypeConverter extend } return null; } - + @Override - public T fromObject(Object s) { + @Nullable + public T fromObject(@Nullable Object s) { T obj = super.fromObject(s); if (obj != null) { - return obj; + return obj; } if (s instanceof String) { return fromPromptString((String) s); @@ -49,11 +53,12 @@ public class NumberVariableContextVariableTypeConverter extend } @Override - public T fromPromptString(String s) { + @Nullable + public T fromPromptString(@Nullable String s) { if (s != null && !s.isEmpty()) { return super.fromPromptString(s); } - return null; + return null; } } diff --git a/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/plugin/KernelFunctionFactory.java b/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/plugin/KernelFunctionFactory.java index 834b09c3ab..586b967906 100644 --- a/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/plugin/KernelFunctionFactory.java +++ b/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/plugin/KernelFunctionFactory.java @@ -1,8 +1,8 @@ package com.microsoft.semantickernel.plugin; import com.microsoft.semantickernel.orchestration.KernelFunction; -import com.microsoft.semantickernel.semanticfunctions.KernelFunctionFromMethod; import com.microsoft.semantickernel.orchestration.PromptExecutionSettings; +import com.microsoft.semantickernel.semanticfunctions.KernelFunctionFromMethod; import com.microsoft.semantickernel.semanticfunctions.KernelFunctionFromPrompt; import com.microsoft.semantickernel.semanticfunctions.KernelPromptTemplateFactory; import com.microsoft.semantickernel.semanticfunctions.PromptTemplate; @@ -16,30 +16,6 @@ import javax.annotation.Nullable; public class KernelFunctionFactory { - /// - /// Creates a instance for a method, specified via a delegate. - /// - /// The method to be represented via the created . - /// Optional function name. If null, it will default to one derived from the method represented by . - /// Optional description of the method. If null, it will default to one derived from the method represented by , if possible (e.g. via a on the method). - /// Optional parameter descriptions. If null, it will default to one derived from the method represented by . - /// Optional return parameter description. If null, it will default to one derived from the method represented by . - /// The to use for logging. If null, no logging will be performed. - /// The created wrapper for . - public static KernelFunction createFromMethod( - Method method, - @Nullable String functionName, - @Nullable String description, - List parameters, - KernelReturnParameterMetadata returnParameter) { - return createFromMethod( - method, - null, - functionName, - description, - parameters, - returnParameter); - } /// /// Creates a instance for a method, specified via an instance @@ -55,10 +31,10 @@ public class KernelFunctionFactory { /// The created wrapper for . public static KernelFunction createFromMethod( Method method, - @Nullable Object target, + Object target, @Nullable String functionName, @Nullable String description, - @Nullable List parameters, + @Nullable List> parameters, @Nullable KernelReturnParameterMetadata returnParameter) { return KernelFunctionFromMethod.create(method, target, functionName, description, parameters, returnParameter); @@ -87,8 +63,8 @@ public class KernelFunctionFactory { * @param templateFormat The template format of {@code promptTemplate}. This must be * provided if {@code promptTemplateFactory} is not null. * @param promptTemplateFactory The {@link PromptTemplateFactory} to use when interpreting the - * {@code promptTemplate} into a {@link IPromptTemplate}. If null, - * a default factory will be used. + * {@code promptTemplate} into a {@link PromptTemplate}. If null, a + * default factory will be used. * @return The created {@link KernelFunction} for invoking the prompt. */ public static KernelFunction createFromPrompt( @@ -129,9 +105,12 @@ public class KernelFunctionFactory { } private static Map createSettingsDictionary( + @Nullable PromptExecutionSettings executionSettings) { HashMap map = new HashMap<>(); - map.put("default", executionSettings); + if (executionSettings != null) { + map.put("default", executionSettings); + } return map; } } diff --git a/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/plugin/KernelParameterMetadata.java b/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/plugin/KernelParameterMetadata.java index a3f88c9dd9..83ed62c927 100644 --- a/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/plugin/KernelParameterMetadata.java +++ b/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/plugin/KernelParameterMetadata.java @@ -1,20 +1,27 @@ package com.microsoft.semantickernel.plugin; +import javax.annotation.Nullable; + public class KernelParameterMetadata { /// The name of the parameter. private final String name; /// The description of the parameter. + @Nullable private final String description; + @Nullable private final String defaultValue; private final boolean isRequired; private final Class parameterType; public KernelParameterMetadata( String name, + @Nullable String description, Class parameterType, - String defaultValue, boolean isRequired) { + @Nullable + String defaultValue, + boolean isRequired) { this.name = name; this.description = description; this.defaultValue = defaultValue; @@ -26,10 +33,12 @@ public class KernelParameterMetadata { return name; } + @Nullable public String getDescription() { return description; } + @Nullable public String getDefaultValue() { return defaultValue; } diff --git a/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/plugin/KernelPlugin.java b/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/plugin/KernelPlugin.java index 6e22e632e2..675c0e5105 100644 --- a/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/plugin/KernelPlugin.java +++ b/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/plugin/KernelPlugin.java @@ -5,15 +5,20 @@ import com.microsoft.semantickernel.orchestration.contextvariables.CaseInsensiti import java.util.Collections; import java.util.Iterator; import java.util.Map; +import javax.annotation.Nullable; public class KernelPlugin implements Iterable { private final String name; + @Nullable private final String description; private final CaseInsensitiveMap functions; - public KernelPlugin(String name, String description, + public KernelPlugin( + String name, + @Nullable + String description, Map plugins) { this.name = name; this.description = description; @@ -23,10 +28,15 @@ public class KernelPlugin implements Iterable { } } + public void addFunction(KernelFunction function) { + functions.put(function.getName(), function); + } + public Map getFunctions() { return Collections.unmodifiableMap(functions); } + @Nullable public KernelFunction get(String functionName) { return functions.get(functionName); } @@ -40,6 +50,7 @@ public class KernelPlugin implements Iterable { return name; } + @Nullable public String getDescription() { return description; } diff --git a/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/plugin/KernelPluginFactory.java b/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/plugin/KernelPluginFactory.java index 3d3da008dc..743a6f1050 100644 --- a/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/plugin/KernelPluginFactory.java +++ b/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/plugin/KernelPluginFactory.java @@ -6,7 +6,7 @@ import com.microsoft.semantickernel.orchestration.KernelFunction; import com.microsoft.semantickernel.orchestration.contextvariables.CaseInsensitiveMap; import com.microsoft.semantickernel.plugin.annotations.DefineKernelFunction; import com.microsoft.semantickernel.plugin.annotations.KernelFunctionParameter; -import com.microsoft.semantickernel.semanticfunctions.KernelFunctionFromPrompt.Builder; +import com.microsoft.semantickernel.semanticfunctions.KernelFunctionFromPrompt; import com.microsoft.semantickernel.semanticfunctions.KernelPromptTemplateFactory; import com.microsoft.semantickernel.semanticfunctions.PromptTemplate; import com.microsoft.semantickernel.semanticfunctions.PromptTemplateConfig; @@ -46,13 +46,13 @@ public class KernelPluginFactory { /// Public methods decorated with will be included in the plugin. /// Attributed methods must all have different names; overloads are not supported. /// - public static KernelPlugin createFromObject(Object target, @Nullable String pluginName) { + public static KernelPlugin createFromObject(Object target, String pluginName) { List methods = Arrays.stream(target.getClass().getMethods()) .filter(method -> method.isAnnotationPresent(DefineKernelFunction.class)) .map(method -> { DefineKernelFunction annotation = method.getAnnotation(DefineKernelFunction.class); Class returnType = getReturnType(annotation, method); - KernelReturnParameterMetadata kernelReturnParameterMetadata = new KernelReturnParameterMetadata( + KernelReturnParameterMetadata kernelReturnParameterMetadata = new KernelReturnParameterMetadata<>( annotation.returnDescription(), returnType); @@ -75,10 +75,9 @@ public class KernelPluginFactory { returnType = method.getReturnType(); if (Publisher.class.isAssignableFrom(returnType)) { - LOGGER.warn( - "For method: " + method.getDeclaringClass().getName() + "." + method.getName() - + ", this is an async method, if a return type is required, please specify it in the annotation. Defaulting to Void return type"); - returnType = Void.class; + throw new SKException( + "Method: " + method.getDeclaringClass().getName() + "." + method.getName() + + ", this is an async method, It is required to add an annotation to specify the return type"); } } else { try { @@ -109,7 +108,8 @@ public class KernelPluginFactory { /// is an invalid plugin name. /// contains a null function. /// contains two functions with the same name. - public static KernelPlugin createFromFunctions(String pluginName, + public static KernelPlugin createFromFunctions( + String pluginName, @Nullable List functions) { return createFromFunctions(pluginName, null, functions); } @@ -132,15 +132,19 @@ public class KernelPluginFactory { } - private static List getParameters(Method method) { + private static List> getParameters(Method method) { return Arrays.stream(method.getParameters()) .filter(parameter -> parameter.isAnnotationPresent(KernelFunctionParameter.class)) .map(parameter -> { KernelFunctionParameter annotation = parameter.getAnnotation( KernelFunctionParameter.class); - return new KernelParameterMetadata(annotation.name(), annotation.description(), - null, annotation.defaultValue(), annotation.required()); + return new KernelParameterMetadata<>( + annotation.name(), + annotation.description(), + annotation.type(), + annotation.defaultValue(), + annotation.required()); }).collect(Collectors.toList()); } @@ -176,8 +180,8 @@ public class KernelPluginFactory { // configuration, unable to parse {configPath}"); } - KernelFunction plugin = getKernelFunction(pluginDirectoryName, - promptTemplateFactory, configPath, promptPath); + KernelFunction plugin = getKernelFunction(promptTemplateFactory, configPath, + promptPath); plugins.put(dir.getName(), plugin); } catch (IOException e) { @@ -192,8 +196,10 @@ public class KernelPluginFactory { ); } - private static KernelFunction getKernelFunction(String pluginDirectoryName, - PromptTemplateFactory promptTemplateFactory, File configPath, File promptPath) + private static KernelFunction getKernelFunction( + PromptTemplateFactory promptTemplateFactory, + File configPath, + File promptPath) throws IOException { PromptTemplateConfig config = new ObjectMapper().readValue(configPath, PromptTemplateConfig.class); @@ -202,11 +208,13 @@ public class KernelPluginFactory { String template = new String(Files.readAllBytes(promptPath.toPath()), Charset.defaultCharset()); - return getKernelFunction(pluginDirectoryName, promptTemplateFactory, config, template); + return getKernelFunction(promptTemplateFactory, config, template); } - private static KernelFunction getKernelFunction(String pluginDirectoryName, - PromptTemplateFactory promptTemplateFactory, PromptTemplateConfig config, String template) { + private static KernelFunction getKernelFunction( + PromptTemplateFactory promptTemplateFactory, + PromptTemplateConfig config, + String template) { PromptTemplate promptTemplate; if (promptTemplateFactory != null) { @@ -215,10 +223,12 @@ public class KernelPluginFactory { promptTemplate = new KernelPromptTemplateFactory().tryCreate(config); } - return new Builder().withName(config.getName()).withDescription(config.getDescription()) + return new KernelFunctionFromPrompt.Builder() + .withName(config.getName()) + .withDescription(config.getDescription()) .withExecutionSettings(config.getExecutionSettings()) - .withInputParameters(config.getInputVariables() - ).withPromptTemplate(promptTemplate) + .withInputParameters(config.getInputVariables()) + .withPromptTemplate(promptTemplate) .withTemplate(template) .withTemplateFormat(config.getTemplateFormat()) .withOutputVariable(config.getOutputVariable()) @@ -226,9 +236,13 @@ public class KernelPluginFactory { .build(); } - public static KernelPlugin importPluginFromResourcesDirectory(String parentDirectory, - String pluginDirectoryName, String functionName, - PromptTemplateFactory promptTemplateFactory, @Nullable Class clazz) { + @Nullable + public static KernelPlugin importPluginFromResourcesDirectory( + String parentDirectory, + String pluginDirectoryName, + String functionName, + PromptTemplateFactory promptTemplateFactory, + Class clazz) { String template = getTemplatePrompt(parentDirectory, pluginDirectoryName, functionName, clazz); @@ -236,7 +250,12 @@ public class KernelPluginFactory { PromptTemplateConfig promptTemplateConfig = getPromptTemplateConfig(parentDirectory, pluginDirectoryName, functionName, clazz); - KernelFunction function = getKernelFunction(pluginDirectoryName, promptTemplateFactory, + if (promptTemplateConfig == null) { + LOGGER.warn("Unable to load prompt template config for " + functionName + " in " + + pluginDirectoryName); + return null; + } + KernelFunction function = getKernelFunction(promptTemplateFactory, promptTemplateConfig, template); HashMap plugins = new HashMap<>(); @@ -250,8 +269,11 @@ public class KernelPluginFactory { ); } - private static String getTemplatePrompt(String pluginDirectory, String pluginName, - String functionName, @Nullable Class clazz) { + private static String getTemplatePrompt( + String pluginDirectory, + String pluginName, + String functionName, + Class clazz) { String promptFileName = pluginDirectory + File.separator + pluginName + File.separator + functionName + File.separator + PROMPT_FILE; @@ -265,14 +287,17 @@ public class KernelPluginFactory { } } - private static String getFileContents(String file, @Nullable Class clazz) + private static String getFileContents(String file, Class clazz) throws FileNotFoundException { return EmbeddedResourceLoader.readFile(file, clazz, ResourceLocation.CLASSPATH_ROOT, ResourceLocation.CLASSPATH, ResourceLocation.FILESYSTEM); } - private static PromptTemplateConfig getPromptTemplateConfig(String pluginDirectory, - String pluginName, String functionName, @Nullable Class clazz) { + @Nullable + private static PromptTemplateConfig getPromptTemplateConfig( + String pluginDirectory, + String pluginName, String functionName, + Class clazz) { String configFileName = pluginDirectory + File.separator + pluginName + File.separator + functionName + File.separator + CONFIG_FILE; diff --git a/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/plugin/KernelReturnParameterMetadata.java b/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/plugin/KernelReturnParameterMetadata.java index 3d3f5d119a..6098c3cfa6 100644 --- a/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/plugin/KernelReturnParameterMetadata.java +++ b/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/plugin/KernelReturnParameterMetadata.java @@ -1,17 +1,22 @@ package com.microsoft.semantickernel.plugin; +import javax.annotation.Nullable; + public class KernelReturnParameterMetadata { + @Nullable private final String description; private final Class parameterType; public KernelReturnParameterMetadata( + @Nullable String description, Class parameterType) { this.description = description; this.parameterType = parameterType; } + @Nullable public String getDescription() { return description; } diff --git a/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/semanticfunctions/AggregatorPromptTemplateFactory.java b/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/semanticfunctions/AggregatorPromptTemplateFactory.java index 7b056b754a..668f846687 100644 --- a/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/semanticfunctions/AggregatorPromptTemplateFactory.java +++ b/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/semanticfunctions/AggregatorPromptTemplateFactory.java @@ -1,21 +1,25 @@ package com.microsoft.semantickernel.semanticfunctions; -import reactor.util.annotation.NonNull; - import java.util.ArrayList; import java.util.List; +import reactor.util.annotation.NonNull; public class AggregatorPromptTemplateFactory implements PromptTemplateFactory { + private final List templateFactories; + public AggregatorPromptTemplateFactory(List templateFactories) { this.templateFactories = new ArrayList<>(templateFactories); } + @Override public PromptTemplate tryCreate(@NonNull PromptTemplateConfig templateConfig) { for (PromptTemplateFactory factory : templateFactories) { try { return factory.tryCreate(templateConfig); - } catch (UnknownTemplateFormatException ignored) { } + } catch (UnknownTemplateFormatException ignored) { + // No-op + } } throw new UnknownTemplateFormatException(templateConfig.getTemplateFormat()); diff --git a/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/semanticfunctions/HandlebarsPromptTemplateFactory.java b/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/semanticfunctions/HandlebarsPromptTemplateFactory.java index 80ce4d710d..f2574dbfd5 100644 --- a/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/semanticfunctions/HandlebarsPromptTemplateFactory.java +++ b/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/semanticfunctions/HandlebarsPromptTemplateFactory.java @@ -1,17 +1,18 @@ package com.microsoft.semantickernel.semanticfunctions; import com.microsoft.semantickernel.templateengine.handlebars.HandlebarsPromptTemplate; -import reactor.util.annotation.NonNull; - import java.util.Locale; +import reactor.util.annotation.NonNull; public class HandlebarsPromptTemplateFactory implements PromptTemplateFactory { public static final String HANDLEBARS_TEMPLATE_FORMAT = "handlebars"; + @Override public PromptTemplate tryCreate(@NonNull PromptTemplateConfig templateConfig) { if (templateConfig.getTemplateFormat() != null && - HANDLEBARS_TEMPLATE_FORMAT.equals(templateConfig.getTemplateFormat().toLowerCase(Locale.ROOT))) { + HANDLEBARS_TEMPLATE_FORMAT.equals( + templateConfig.getTemplateFormat().toLowerCase(Locale.ROOT))) { return new HandlebarsPromptTemplate(templateConfig); } diff --git a/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/semanticfunctions/InputVariable.java b/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/semanticfunctions/InputVariable.java index 63795bbad6..9ee6c5f2cc 100644 --- a/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/semanticfunctions/InputVariable.java +++ b/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/semanticfunctions/InputVariable.java @@ -1,6 +1,8 @@ package com.microsoft.semantickernel.semanticfunctions; import com.fasterxml.jackson.annotation.JsonProperty; +import com.microsoft.semantickernel.exceptions.SKException; +import javax.annotation.Nullable; public class InputVariable { @@ -10,27 +12,31 @@ public class InputVariable { @JsonProperty("type") private String type; + @Nullable @JsonProperty("description") private String description; @JsonProperty("default") + @Nullable private String defaultValue; @JsonProperty("is_required") private boolean isRequired; - public InputVariable() { - } - - public InputVariable( - String name) { + public InputVariable(String name) { this.name = name; + this.type = String.class.getName(); + this.description = null; + this.defaultValue = null; + this.isRequired = true; } public InputVariable( String name, String type, + @Nullable String description, + @Nullable String defaultValue, boolean isRequired) { this.name = name; @@ -56,6 +62,7 @@ public class InputVariable { this.type = type; } + @Nullable public String getDescription() { return description; } @@ -64,6 +71,7 @@ public class InputVariable { this.description = description; } + @Nullable public String getDefaultValue() { return defaultValue; } @@ -79,4 +87,14 @@ public class InputVariable { public void setRequired(boolean required) { isRequired = required; } + + public Class getTypeClass() { + try { + return Thread.currentThread().getContextClassLoader().loadClass(type); + } catch (ClassNotFoundException e) { + throw new SKException( + "Could not load class for type: " + type + " when for input variable " + name + + ", note this needs to be a fully qualified class name, i.e 'java.lang.String'."); + } + } } diff --git a/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/semanticfunctions/KernelFunctionFromMethod.java b/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/semanticfunctions/KernelFunctionFromMethod.java index 8856b254c0..028f6826a8 100644 --- a/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/semanticfunctions/KernelFunctionFromMethod.java +++ b/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/semanticfunctions/KernelFunctionFromMethod.java @@ -16,20 +16,18 @@ import com.microsoft.semantickernel.orchestration.MethodDetails; import com.microsoft.semantickernel.orchestration.contextvariables.ContextVariable; import com.microsoft.semantickernel.orchestration.contextvariables.ContextVariableType; import com.microsoft.semantickernel.orchestration.contextvariables.ContextVariableTypeConverter; +import com.microsoft.semantickernel.orchestration.contextvariables.ContextVariableTypeConverter.NoopConverter; import com.microsoft.semantickernel.orchestration.contextvariables.ContextVariableTypes; import com.microsoft.semantickernel.orchestration.contextvariables.KernelArguments; import com.microsoft.semantickernel.plugin.KernelParameterMetadata; import com.microsoft.semantickernel.plugin.KernelReturnParameterMetadata; import com.microsoft.semantickernel.plugin.annotations.DefineKernelFunction; import com.microsoft.semantickernel.plugin.annotations.KernelFunctionParameter; - import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.lang.reflect.Parameter; import java.util.Arrays; -import java.util.HashSet; import java.util.List; -import java.util.Set; import java.util.function.Function; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -49,8 +47,10 @@ public class KernelFunctionFromMethod extends DefaultKernelFunction { private KernelFunctionFromMethod( ImplementationFunc implementationFunc, String functionName, + @Nullable String description, - List parameters, + @Nullable + List> parameters, KernelReturnParameterMetadata returnParameter) { super( new KernelFunctionMetadata( @@ -68,8 +68,12 @@ public class KernelFunctionFromMethod extends DefaultKernelFunction { public Mono> invokeAsync( Kernel kernel, @Nullable KernelArguments arguments, - ContextVariableType variableType) { - return function.invoke(kernel, this, arguments, kernel.getHookService()); + @Nullable ContextVariableType variableType) { + return invokeAsync( + kernel, + arguments, + kernel.getHookService(), + variableType); } @Override @@ -77,13 +81,41 @@ public class KernelFunctionFromMethod extends DefaultKernelFunction { Kernel kernel, @Nullable KernelArguments arguments, @Nullable KernelHooks kernelHooks, - ContextVariableType variableType) { + @Nullable ContextVariableType variableType) { if (kernelHooks == null) { kernelHooks = kernel.getHookService(); } - return function.invoke(kernel, this, arguments, kernelHooks); + return function + .invoke(kernel, this, arguments, kernelHooks) + .map(it -> convertVariableToReturnType(variableType, it)); + } + + private FunctionResult convertVariableToReturnType( + @Nullable ContextVariableType variableType, + FunctionResult it) { + if (variableType != null) { + return new FunctionResult<>( + variableType.of(it.getResult()), + it.getMetadata()); + } else { + Object result = it.getResult(); + if (result != null && getMetadata().getReturnParameter().getParameterType() + .isAssignableFrom(result.getClass())) { + return new FunctionResult<>( + new ContextVariable<>( + new ContextVariableType<>( + new NoopConverter( + getMetadata().getReturnParameter().getParameterType()), + getMetadata().getReturnParameter().getParameterType() + ), + it.getResult()) + ); + } + throw new RuntimeException("Unable to convert result to type: " + + getMetadata().getReturnParameter().getParameterType().getName()); + } } public interface ImplementationFunc { @@ -100,9 +132,13 @@ public class KernelFunctionFromMethod extends DefaultKernelFunction { public static KernelFunction create( Method method, Object target, + @Nullable String functionName, + @Nullable String description, - List parameters, + @Nullable + List> parameters, + @Nullable KernelReturnParameterMetadata returnParameter) { MethodDetails methodDetails = getMethodDetails(functionName, method, target); @@ -129,6 +165,7 @@ public class KernelFunctionFromMethod extends DefaultKernelFunction { private static MethodDetails getMethodDetails( + @Nullable String functionName, Method method, Object target) { @@ -142,12 +179,19 @@ public class KernelFunctionFromMethod extends DefaultKernelFunction { returnDescription = annotation.returnDescription(); } + if (functionName == null) { + functionName = method.getName(); + } + return new MethodDetails( functionName, description, getFunction(method, target), getParameters(method), - new KernelReturnParameterMetadata<>(returnDescription, method.getReturnType()) + new KernelReturnParameterMetadata<>( + returnDescription, + method.getReturnType() + ) ); } @@ -193,11 +237,30 @@ public class KernelFunctionFromMethod extends DefaultKernelFunction { }); return r - .map(it -> new FunctionResult<>(ContextVariable.of(it))) + .map(it -> { + return new FunctionResult( + new ContextVariable( + new ContextVariableType( + new NoopConverter( + function + .getMetadata() + .getReturnParameter() + .getParameterType() + ), + function + .getMetadata() + .getReturnParameter() + .getParameterType() + ), + it)); + }) .map(it -> { FunctionInvokedEvent updatedResult = kernelHooks .executeHooks( - new FunctionInvokedEvent<>(function, updatedArguments, it)); + new FunctionInvokedEvent<>( + function, + updatedArguments, + it)); return updatedResult.getResult(); }); } catch (Exception e) { @@ -239,6 +302,7 @@ public class KernelFunctionFromMethod extends DefaultKernelFunction { .subscribeOn(Schedulers.boundedElastic())); } + @Nullable private static Function getParameters( Method method, @Nullable @@ -253,6 +317,7 @@ public class KernelFunctionFromMethod extends DefaultKernelFunction { }; } + @Nullable private static Object getArgumentValue( Method method, @Nullable KernelArguments context, @@ -265,15 +330,16 @@ public class KernelFunctionFromMethod extends DefaultKernelFunction { KernelFunctionParameter annotation = parameter.getAnnotation(KernelFunctionParameter.class); if (annotation != null) { - // Convert from the defaultValue, which is a String to the argument type - // Expectation here is that the fromPromptString method will be able to handle a null or empty string - Class type = annotation.type(); - ContextVariableType cvType = ContextVariableTypes.getDefaultVariableTypeForClass(type); - if (cvType != null) { - String defaultValue = annotation.defaultValue(); - Object value = cvType.getConverter().fromPromptString(defaultValue); - arg = ContextVariable.of(value); - } + // Convert from the defaultValue, which is a String to the argument type + // Expectation here is that the fromPromptString method will be able to handle a null or empty string + Class type = annotation.type(); + ContextVariableType cvType = ContextVariableTypes.getDefaultVariableTypeForClass( + type); + if (cvType != null) { + String defaultValue = annotation.defaultValue(); + Object value = cvType.getConverter().fromPromptString(defaultValue); + arg = ContextVariable.untypedOf(value, cvType.getConverter()); + } if (arg != null && NO_DEFAULT_VALUE.equals(arg.getValue())) { if (!annotation.required()) { @@ -331,19 +397,22 @@ public class KernelFunctionFromMethod extends DefaultKernelFunction { Object value = arg; - if (parameter.getType().isAssignableFrom(arg.getValue().getClass())) { - return arg.getValue(); - } + if (arg != null) { - if (isPrimative(arg.getValue().getClass(), parameter.getType())) { - return arg.getValue(); - } + if (parameter.getType().isAssignableFrom(arg.getType().getClazz())) { + return arg.getValue(); + } - ContextVariableTypeConverter c = arg.getType().getConverter(); + if (isPrimative(arg.getType().getClazz(), parameter.getType())) { + return arg.getValue(); + } - Object converted = c.toObject(arg.getValue(), parameter.getType()); - if (converted != null) { - return converted; + ContextVariableTypeConverter c = arg.getType().getConverter(); + + Object converted = c.toObject(arg.getValue(), parameter.getType()); + if (converted != null) { + return converted; + } } // Well-known types only @@ -366,6 +435,7 @@ public class KernelFunctionFromMethod extends DefaultKernelFunction { return value; } + @SuppressWarnings("OperatorPrecedence") private static boolean isPrimative(Class argType, Class param) { return (argType == Byte.class || argType == byte.class) && (param == Byte.class || param == byte.class) || @@ -415,81 +485,7 @@ public class KernelFunctionFromMethod extends DefaultKernelFunction { + " was invoked with a required context variable missing and no default value."; } - private static Set determineInputArgs(Method method) { - // Something is bound to the input if either: - // - it is annotated with @SKFunctionInputAttribute - // - SKFunctionParameters annotation has a name of "input" - // - the arg name is "input" - // - there is only 1 string argument to the function - - // Get all parameters annotated with SKFunctionInputAttribute - List annotated = - Arrays.stream(method.getParameters()) - .filter(it -> it.isAnnotationPresent(KernelFunctionParameter.class)) - .collect(Collectors.toList()); - - if (annotated.size() > 1) { - LOGGER.warn( - "Multiple arguments of " - + method.getName() - + " have the @SKFunctionInputAttribute annotation. This is likely an" - + " error."); - } - - // Get all parameters annotated with SKFunctionParameters with a name of "input" - List annotatedWithName = - Arrays.stream(method.getParameters()) - .filter(it -> it.isAnnotationPresent(KernelFunctionParameter.class)) - .filter(it -> it.getName().equals("input")) - .collect(Collectors.toList()); - - if (annotatedWithName.size() > 1) { - LOGGER.warn( - "Multiple arguments of " - + method.getName() - + " have the name input. This is likely an error."); - } - - // Get all parameters named "input", this will frequently fail as compilers strip out - // argument names - List calledInput = - Arrays.stream(method.getParameters()) - .filter(it -> getGetVariableName(it).equals("input")) - .collect(Collectors.toList()); - - // Get parameter if there is only 1 string, and it has not been annotated with - // SKFunctionParameters - List soloString = - Arrays.stream(method.getParameters()) - .filter(it -> it.getType().equals(String.class)) - .filter( - it -> - !(it.isAnnotationPresent(KernelFunctionParameter.class) - && !it.getAnnotation(KernelFunctionParameter.class) - .name() - .isEmpty())) - .collect(Collectors.toList()); - if (soloString.size() > 1) { - soloString.clear(); - } - - Set params = new HashSet<>(); - params.addAll(annotated); - params.addAll(annotatedWithName); - params.addAll(calledInput); - params.addAll(soloString); - - if (params.size() > 1) { - LOGGER.warn( - "Multiple arguments of " - + method.getName() - + " are bound to the input variable. This is likely an error."); - } - - return params; - } - - private static List getParameters(Method method) { + private static List> getParameters(Method method) { return Arrays.stream(method .getParameters()) @@ -497,7 +493,7 @@ public class KernelFunctionFromMethod extends DefaultKernelFunction { .collect(Collectors.toList()); } - private static KernelParameterMetadata toKernelParameterMetadata(Parameter parameter) { + private static KernelParameterMetadata toKernelParameterMetadata(Parameter parameter) { KernelFunctionParameter annotation = parameter.getAnnotation( KernelFunctionParameter.class); @@ -505,19 +501,22 @@ public class KernelFunctionFromMethod extends DefaultKernelFunction { String description = null; String defaultValue = null; boolean isRequired = true; + Class type = parameter.getType(); if (annotation != null) { name = annotation.name(); description = annotation.description(); defaultValue = annotation.defaultValue(); isRequired = annotation.required(); + type = annotation.type(); } - return new KernelParameterMetadata( + return new KernelParameterMetadata<>( name, description, - null, - defaultValue, isRequired + type, + defaultValue, + isRequired ); } } diff --git a/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/semanticfunctions/KernelFunctionFromPrompt.java b/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/semanticfunctions/KernelFunctionFromPrompt.java index 6ba0d479f1..7af9279d11 100644 --- a/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/semanticfunctions/KernelFunctionFromPrompt.java +++ b/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/semanticfunctions/KernelFunctionFromPrompt.java @@ -8,6 +8,7 @@ import com.microsoft.semantickernel.Kernel; import com.microsoft.semantickernel.TextAIService; import com.microsoft.semantickernel.chatcompletion.AuthorRole; import com.microsoft.semantickernel.chatcompletion.ChatCompletionService; +import com.microsoft.semantickernel.exceptions.SKException; import com.microsoft.semantickernel.hooks.FunctionInvokedEvent; import com.microsoft.semantickernel.hooks.FunctionInvokingEvent; import com.microsoft.semantickernel.hooks.KernelHooks; @@ -21,6 +22,7 @@ import com.microsoft.semantickernel.orchestration.PromptExecutionSettings; import com.microsoft.semantickernel.orchestration.contextvariables.ContextVariable; import com.microsoft.semantickernel.orchestration.contextvariables.ContextVariableType; import com.microsoft.semantickernel.orchestration.contextvariables.ContextVariableTypes; +import com.microsoft.semantickernel.orchestration.contextvariables.DefaultKernelArguments; import com.microsoft.semantickernel.orchestration.contextvariables.KernelArguments; import com.microsoft.semantickernel.services.AIServiceSelection; import com.microsoft.semantickernel.textcompletion.TextGenerationService; @@ -28,6 +30,7 @@ import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.UUID; import javax.annotation.Nullable; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -47,7 +50,7 @@ public class KernelFunctionFromPrompt extends DefaultKernelFunction { Map executionSettings) { super( new KernelFunctionMetadata( - promptConfig.getName(), + getName(promptConfig), promptConfig.getDescription(), promptConfig.getKernelParametersMetadata(), promptConfig.getKernelReturnParameterMetadata() @@ -57,6 +60,14 @@ public class KernelFunctionFromPrompt extends DefaultKernelFunction { this.template = template; } + private static String getName(PromptTemplateConfig promptConfig) { + if (promptConfig.getName() == null) { + return UUID.randomUUID().toString(); + } else { + return promptConfig.getName(); + } + } + public static KernelFunction create( PromptTemplateConfig promptConfig ) { @@ -91,10 +102,17 @@ public class KernelFunctionFromPrompt extends DefaultKernelFunction { private Flux> invokeInternalAsync( Kernel kernel, - @Nullable KernelArguments arguments, + @Nullable KernelArguments argumentsIn, KernelHooks kernelHooks, ContextVariableType variableType) { + KernelArguments arguments; + if (argumentsIn == null) { + arguments = new DefaultKernelArguments(); + } else { + arguments = argumentsIn; + } + PromptRenderingEvent preRenderingHookResult = kernelHooks .executeHooks(new PromptRenderingEvent(this, arguments)); @@ -121,6 +139,11 @@ public class KernelFunctionFromPrompt extends DefaultKernelFunction { args ); + if (aiServiceSelection == null) { + throw new IllegalStateException( + "Failed to initialise aiService, could not find any TextAIService implementations"); + } + AIService client = aiServiceSelection.getService(); if (client == null) { throw new IllegalStateException( @@ -162,8 +185,13 @@ public class KernelFunctionFromPrompt extends DefaultKernelFunction { ); } else if (chatMessageContent.getAuthorRole() == AuthorRole.TOOL) { + String content = chatMessageContent.getContent(); + if (content == null || content.isEmpty()) { + return Flux.error(new IllegalStateException( + "Tool message content is empty")); + } Mono> toolResult = invokeTool(kernel, - chatMessageContent.getContent()); + content); return toolResult.flux(); } return Flux.empty(); @@ -238,8 +266,8 @@ public class KernelFunctionFromPrompt extends DefaultKernelFunction { if (ex instanceof HttpResponseException && ((HttpResponseException) ex).getResponse().getStatusCode() == 400 - && ex.getMessage() - .contains("parameters are not available" + " on")) { + && ex.getMessage() != null + && ex.getMessage().contains("parameters are not available" + " on")) { LOGGER.warn( "This error indicates that you have attempted" + " to use a chat completion model in a" @@ -258,7 +286,13 @@ public class KernelFunctionFromPrompt extends DefaultKernelFunction { Kernel kernel, @Nullable KernelArguments arguments, @Nullable KernelHooks kernelHooks, + @Nullable ContextVariableType variableType) { + if (variableType == null) { + return Mono.error(new SKException( + "Cannot invoke a prompt function without a variable return type. Ensure that there is" + + " a valid type converter for the return type of the prompt function.")); + } if (kernelHooks == null) { kernelHooks = kernel.getHookService(); } @@ -270,7 +304,9 @@ public class KernelFunctionFromPrompt extends DefaultKernelFunction { @Override public Mono> invokeAsync( Kernel kernel, - @Nullable KernelArguments arguments, + @Nullable + KernelArguments arguments, + @Nullable ContextVariableType variableType) { return invokeAsync(kernel, arguments, null, variableType); } @@ -300,6 +336,7 @@ public class KernelFunctionFromPrompt extends DefaultKernelFunction { /* * The jsonNode should represent: {"name":"search-search", "parameters": {"query":"Banksy"}}} */ + @SuppressWarnings("StringSplitter") private Mono> invokeFunction(Kernel kernel, JsonNode jsonNode) { String name = jsonNode.get("name").asText(); String[] parts = name.split("-"); @@ -307,22 +344,25 @@ public class KernelFunctionFromPrompt extends DefaultKernelFunction { String fnName = parts.length > 1 ? parts[1] : ""; JsonNode parameters = jsonNode.get("parameters"); if (parameters != null) { - KernelFunction kernelFunction = kernel.getPlugins().getFunction(pluginName, fnName); - if (kernelFunction == null) { - return Mono.empty(); + try { + KernelFunction kernelFunction = kernel.getPlugins().getFunction(pluginName, fnName); + + ContextVariableType variableType = ContextVariableTypes.getDefaultVariableTypeForClass( + String.class); + Map> variables = new HashMap<>(); + parameters.fields().forEachRemaining(entry -> { + String paramName = entry.getKey(); + String paramValue = entry.getValue().asText(); + ContextVariable contextVariable = new ContextVariable<>(variableType, + paramValue); + variables.put(paramName, contextVariable); + }); + KernelArguments arguments = KernelArguments.builder().withVariables(variables) + .build(); + return kernelFunction.invokeAsync(kernel, arguments, variableType); + } catch (Exception e) { + return Mono.error(e); } - ContextVariableType variableType = ContextVariableTypes.getDefaultVariableTypeForClass( - String.class); - Map> variables = new HashMap<>(); - parameters.fields().forEachRemaining(entry -> { - String paramName = entry.getKey(); - String paramValue = entry.getValue().asText(); - ContextVariable contextVariable = new ContextVariable<>(variableType, - paramValue); - variables.put(paramName, contextVariable); - }); - KernelArguments arguments = KernelArguments.builder().withVariables(variables).build(); - return kernelFunction.invokeAsync(kernel, arguments, variableType); } return Mono.empty(); } @@ -351,6 +391,14 @@ public class KernelFunctionFromPrompt extends DefaultKernelFunction { @Nullable String templateFormat, @Nullable PromptTemplateFactory promptTemplateFactory) { + if (templateFormat == null) { + templateFormat = PromptTemplateConfig.SEMANTIC_KERNEL_TEMPLATE_FORMAT; + } + + if (functionName == null) { + functionName = UUID.randomUUID().toString(); + } + return new KernelFunctionFromPrompt.Builder() .withName(functionName) .withDescription(description) @@ -367,37 +415,50 @@ public class KernelFunctionFromPrompt extends DefaultKernelFunction { public static final class Builder implements FromPromptBuilder { + @Nullable private PromptTemplate promptTemplate; + @Nullable private String name; - private Map executionSettings; + @Nullable + private Map executionSettings = null; + @Nullable private String description; + @Nullable private List inputVariables; + @Nullable private String template; private String templateFormat = PromptTemplateConfig.SEMANTIC_KERNEL_TEMPLATE_FORMAT; + @Nullable private OutputVariable outputVariable; + @Nullable private PromptTemplateFactory promptTemplateFactory; @Override - public FromPromptBuilder withName(String name) { + public FromPromptBuilder withName(@Nullable String name) { this.name = name; return this; } @Override - public FromPromptBuilder withInputParameters(List inputVariables) { - this.inputVariables = new ArrayList<>(inputVariables); + public FromPromptBuilder withInputParameters(@Nullable List inputVariables) { + if (inputVariables != null) { + this.inputVariables = new ArrayList<>(inputVariables); + } else { + this.inputVariables = null; + } return this; } @Override - public FromPromptBuilder withPromptTemplate(PromptTemplate promptTemplate) { + public FromPromptBuilder withPromptTemplate(@Nullable PromptTemplate promptTemplate) { this.promptTemplate = promptTemplate; return this; } @Override public FromPromptBuilder withExecutionSettings( + @Nullable Map executionSettings) { if (this.executionSettings == null) { this.executionSettings = new HashMap<>(); @@ -411,7 +472,11 @@ public class KernelFunctionFromPrompt extends DefaultKernelFunction { @Override public FromPromptBuilder withDefaultExecutionSettings( - PromptExecutionSettings executionSettings) { + @Nullable PromptExecutionSettings executionSettings) { + if (executionSettings == null) { + return this; + } + if (this.executionSettings == null) { this.executionSettings = new HashMap<>(); } @@ -426,13 +491,13 @@ public class KernelFunctionFromPrompt extends DefaultKernelFunction { } @Override - public FromPromptBuilder withDescription(String description) { + public FromPromptBuilder withDescription(@Nullable String description) { this.description = description; return this; } @Override - public FromPromptBuilder withTemplate(String template) { + public FromPromptBuilder withTemplate(@Nullable String template) { this.template = template; return this; } @@ -445,13 +510,14 @@ public class KernelFunctionFromPrompt extends DefaultKernelFunction { } @Override - public FromPromptBuilder withOutputVariable(OutputVariable outputVariable) { + public FromPromptBuilder withOutputVariable(@Nullable OutputVariable outputVariable) { this.outputVariable = outputVariable; return this; } @Override public FromPromptBuilder withPromptTemplateFactory( + @Nullable PromptTemplateFactory promptTemplateFactory) { this.promptTemplateFactory = promptTemplateFactory; return this; @@ -459,6 +525,11 @@ public class KernelFunctionFromPrompt extends DefaultKernelFunction { @Override public KernelFunction build() { + + if (template == null) { + throw new IllegalStateException("Template must be provided"); + } + PromptTemplateConfig config = new PromptTemplateConfig( name, template, @@ -483,6 +554,11 @@ public class KernelFunctionFromPrompt extends DefaultKernelFunction { } public KernelFunction build(PromptTemplateConfig functionModel) { + + if (promptTemplate == null) { + throw new IllegalStateException("Template must be provided"); + } + return new KernelFunctionFromPrompt( promptTemplate, functionModel, diff --git a/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/semanticfunctions/KernelPromptTemplateFactory.java b/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/semanticfunctions/KernelPromptTemplateFactory.java index e1ab221e10..dda485e3d5 100644 --- a/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/semanticfunctions/KernelPromptTemplateFactory.java +++ b/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/semanticfunctions/KernelPromptTemplateFactory.java @@ -5,11 +5,12 @@ import static com.microsoft.semantickernel.semanticfunctions.PromptTemplateConfi import com.microsoft.semantickernel.templateengine.handlebars.HandlebarsPromptTemplate; import com.microsoft.semantickernel.templateengine.semantickernel.DefaultPromptTemplate; -import javax.annotation.Nonnull; import java.util.Locale; +import javax.annotation.Nonnull; public class KernelPromptTemplateFactory implements PromptTemplateFactory { + @Override public PromptTemplate tryCreate(@Nonnull PromptTemplateConfig templateConfig) { switch (templateConfig.getTemplateFormat().toLowerCase(Locale.ROOT)) { case SEMANTIC_KERNEL_TEMPLATE_FORMAT: diff --git a/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/semanticfunctions/PromptTemplateConfig.java b/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/semanticfunctions/PromptTemplateConfig.java index 08052e0164..ba1d15b4b8 100644 --- a/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/semanticfunctions/PromptTemplateConfig.java +++ b/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/semanticfunctions/PromptTemplateConfig.java @@ -23,18 +23,15 @@ public class PromptTemplateConfig { public static final int CURRENT_SCHEMA = 1; public static final String DEFAULT_CONFIG_NAME = "default"; + @Nullable private String name; - private String template; - private String templateFormat; - + @Nullable private String description; - private List inputVariables; - + @Nullable private OutputVariable outputVariable; - private Map executionSettings; public static final String SEMANTIC_KERNEL_TEMPLATE_FORMAT = "semantic-kernel"; @@ -56,6 +53,7 @@ public class PromptTemplateConfig { public PromptTemplateConfig( @JsonProperty("schema") int schema, + @Nullable @JsonProperty("name") String name, @JsonProperty("template") @@ -65,12 +63,16 @@ public class PromptTemplateConfig { value = "template_format", defaultValue = SEMANTIC_KERNEL_TEMPLATE_FORMAT) String templateFormat, + @Nullable @JsonProperty("description") String description, + @Nullable @JsonProperty("input_variables") List inputVariables, + @Nullable @JsonProperty("output_variable") OutputVariable outputVariable, + @Nullable @JsonProperty("execution_settings") Map executionSettings) { this.name = name; @@ -104,8 +106,19 @@ public class PromptTemplateConfig { * @param outputVariable Output variable * @param executionSettings Execution settings */ - public PromptTemplateConfig(String name, String template, String templateFormat, - String description, List inputVariables, OutputVariable outputVariable, + public PromptTemplateConfig( + @Nullable + String name, + String template, + @Nullable + String templateFormat, + @Nullable + String description, + @Nullable + List inputVariables, + @Nullable + OutputVariable outputVariable, + @Nullable Map executionSettings) { this( CURRENT_SCHEMA, @@ -131,16 +144,16 @@ public class PromptTemplateConfig { ); } - public List getKernelParametersMetadata() { + public List> getKernelParametersMetadata() { if (inputVariables == null) { return Collections.emptyList(); } return inputVariables .stream() - .map(inputVariable -> new KernelParameterMetadata( + .map(inputVariable -> new KernelParameterMetadata<>( inputVariable.getName(), inputVariable.getDescription(), - null, + inputVariable.getTypeClass(), inputVariable.getDefaultValue(), inputVariable.isRequired() )) .collect(Collectors.toList()); @@ -161,6 +174,7 @@ public class PromptTemplateConfig { inputVariables.add(inputVariable); } + @Nullable public String getName() { return name; } @@ -186,6 +200,7 @@ public class PromptTemplateConfig { this.templateFormat = templateFormat; } + @Nullable public String getDescription() { return description; } @@ -202,6 +217,7 @@ public class PromptTemplateConfig { this.inputVariables = new ArrayList<>(inputVariables); } + @Nullable public OutputVariable getOutputVariable() { return outputVariable; } @@ -210,8 +226,12 @@ public class PromptTemplateConfig { this.outputVariable = outputVariable; } + @Nullable public Map getExecutionSettings() { - return Collections.unmodifiableMap(executionSettings); + if (executionSettings != null) { + return Collections.unmodifiableMap(executionSettings); + } + return null; } diff --git a/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/semanticfunctions/PromptTemplateFactory.java b/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/semanticfunctions/PromptTemplateFactory.java index c44b799dfb..8e190d5f89 100644 --- a/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/semanticfunctions/PromptTemplateFactory.java +++ b/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/semanticfunctions/PromptTemplateFactory.java @@ -1,5 +1,7 @@ package com.microsoft.semantickernel.semanticfunctions; +import javax.annotation.Nullable; + public interface PromptTemplateFactory { PromptTemplate tryCreate(PromptTemplateConfig templateConfig); @@ -10,7 +12,7 @@ public interface PromptTemplateFactory { class UnknownTemplateFormatException extends IllegalArgumentException { - public UnknownTemplateFormatException(String templateFormat) { + public UnknownTemplateFormatException(@Nullable String templateFormat) { super("Unknown template format: " + templateFormat); } } diff --git a/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/services/AIServiceSelection.java b/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/services/AIServiceSelection.java index 715d1c8be3..bb44473c76 100644 --- a/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/services/AIServiceSelection.java +++ b/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/services/AIServiceSelection.java @@ -3,6 +3,7 @@ package com.microsoft.semantickernel.services; import com.microsoft.semantickernel.AIService; import com.microsoft.semantickernel.orchestration.PromptExecutionSettings; +import javax.annotation.Nullable; /** * The result of an AI service selection. @@ -10,6 +11,7 @@ import com.microsoft.semantickernel.orchestration.PromptExecutionSettings; public class AIServiceSelection { private final T service; + @Nullable private final PromptExecutionSettings settings; /** @@ -19,7 +21,7 @@ public class AIServiceSelection { * @param settings The settings associated with the selected service. This may be null even if a * service is selected.. */ - public AIServiceSelection(T service, PromptExecutionSettings settings) { + public AIServiceSelection(T service, @Nullable PromptExecutionSettings settings) { this.service = service; this.settings = settings; } @@ -39,6 +41,7 @@ public class AIServiceSelection { * @return The settings associated with the selected service. This may be null even if a service * is selected. */ + @Nullable public PromptExecutionSettings getSettings() { return settings; } diff --git a/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/services/OrderedAIServiceSelector.java b/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/services/OrderedAIServiceSelector.java index 2ed0391015..ac0c2aa422 100644 --- a/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/services/OrderedAIServiceSelector.java +++ b/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/services/OrderedAIServiceSelector.java @@ -29,6 +29,7 @@ public class OrderedAIServiceSelector extends BaseAIServiceSelector { } @Nullable + @Override public AIServiceSelection trySelectAIService( Class serviceType, @Nullable @@ -38,9 +39,7 @@ public class OrderedAIServiceSelector extends BaseAIServiceSelector { Map, AIService> services) { // Allow the execution settings from the kernel arguments to take precedence - Map executionSettings = null; - - executionSettings = settingsFromArguments(arguments, executionSettings); + Map executionSettings = settingsFromArguments(arguments); executionSettings = settingsFromFunctionSettings(function, executionSettings); if (executionSettings == null || executionSettings.isEmpty()) { @@ -105,12 +104,14 @@ public class OrderedAIServiceSelector extends BaseAIServiceSelector { } AIService service = getAnyService(serviceType); - PromptExecutionSettings settings; + PromptExecutionSettings settings = null; - if (executionSettings.containsKey(PromptExecutionSettings.DEFAULT_SERVICE_ID)) { - settings = executionSettings.get(PromptExecutionSettings.DEFAULT_SERVICE_ID); - } else { - settings = executionSettings.values().stream().findFirst().orElseGet(() -> null); + if (executionSettings != null && !executionSettings.isEmpty()) { + if (executionSettings.containsKey(PromptExecutionSettings.DEFAULT_SERVICE_ID)) { + settings = executionSettings.get(PromptExecutionSettings.DEFAULT_SERVICE_ID); + } else { + settings = executionSettings.values().stream().findFirst().orElseGet(() -> null); + } } if (service != null) { @@ -125,6 +126,7 @@ public class OrderedAIServiceSelector extends BaseAIServiceSelector { private static Map settingsFromFunctionSettings( @Nullable KernelFunction function, + @Nullable Map executionSettings) { if (executionSettings == null || executionSettings.isEmpty()) { if (function != null) { @@ -136,12 +138,12 @@ public class OrderedAIServiceSelector extends BaseAIServiceSelector { @Nullable private static Map settingsFromArguments( - @Nullable KernelArguments arguments, - Map executionSettings) { + @Nullable + KernelArguments arguments) { if (arguments != null) { - executionSettings = arguments.getExecutionSettings(); + return arguments.getExecutionSettings(); } - return executionSettings; + return null; } private AIService getServiceByModelId(String modelId) { @@ -189,6 +191,7 @@ public class OrderedAIServiceSelector extends BaseAIServiceSelector { return service; } + @Nullable AIService getAnyService(Class serviceType) { List services = getServices(serviceType); if (services.isEmpty()) { diff --git a/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/templateengine/handlebars/HandlebarsPromptTemplate.java b/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/templateengine/handlebars/HandlebarsPromptTemplate.java index 9f28ac6b5c..6b53470028 100644 --- a/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/templateengine/handlebars/HandlebarsPromptTemplate.java +++ b/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/templateengine/handlebars/HandlebarsPromptTemplate.java @@ -10,6 +10,7 @@ import com.microsoft.semantickernel.chatcompletion.ChatHistory; import com.microsoft.semantickernel.chatcompletion.ChatMessageContent; import com.microsoft.semantickernel.orchestration.KernelFunction; import com.microsoft.semantickernel.orchestration.contextvariables.ContextVariable; +import com.microsoft.semantickernel.orchestration.contextvariables.DefaultKernelArguments; import com.microsoft.semantickernel.orchestration.contextvariables.KernelArguments; import com.microsoft.semantickernel.plugin.KernelParameterMetadata; import com.microsoft.semantickernel.plugin.KernelPlugin; @@ -42,22 +43,27 @@ public class HandlebarsPromptTemplate implements PromptTemplate { @Override - public Mono renderAsync(Kernel kernel, + public Mono renderAsync( + Kernel kernel, @Nullable KernelArguments arguments) { HandleBarsPromptTemplateHandler handler = new HandleBarsPromptTemplateHandler(kernel, promptTemplate.getTemplate()); + if (arguments == null) { + arguments = new DefaultKernelArguments(); + } return handler.render(arguments); } private static class MessageResolver implements ValueResolver { @Override + @SuppressWarnings("NullAway") public Object resolve(Object context, String name) { if (context instanceof ChatMessageContent) { - if ("role".equals(name.toLowerCase())) { + if ("role".equalsIgnoreCase(name)) { return ((ChatMessageContent) context).getAuthorRole().name(); - } else if ("content".equals(name.toLowerCase())) { + } else if ("content".equalsIgnoreCase(name)) { return ((ChatMessageContent) context).getContent(); } } @@ -67,7 +73,8 @@ public class HandlebarsPromptTemplate implements PromptTemplate { @Override public Object resolve(Object context) { if (context instanceof ChatMessageContent) { - return ((ChatMessageContent) context).getContent(); + String result = ((ChatMessageContent) context).getContent(); + return result != null ? result : UNRESOLVED; } return UNRESOLVED; } @@ -93,9 +100,9 @@ public class HandlebarsPromptTemplate implements PromptTemplate { } if (context instanceof KernelFunction) { KernelFunction function = (KernelFunction) context; - if ("pluginname".equals(name.toLowerCase())) { + if ("pluginname".equalsIgnoreCase(name)) { return function.getSkillName(); - } else if ("name".equals(name.toLowerCase())) { + } else if ("name".equalsIgnoreCase(name)) { return function.getName(); } } @@ -120,19 +127,26 @@ public class HandlebarsPromptTemplate implements PromptTemplate { @Override public Object resolve(Object context, String name) { + Object value = null; if (context instanceof KernelArguments) { - return ((KernelArguments) context).get(name).getValue(); + ContextVariable variable = ((KernelArguments) context).get(name); + value = variable != null ? variable.getValue() : null; } if (context instanceof ContextVariable) { - return ((ContextVariable) context).getValue(); + value = ((ContextVariable) context).getValue(); + } + if (value == null) { + return UNRESOLVED; + } else { + return value; } - return UNRESOLVED; } @Override public Object resolve(Object context) { if (context instanceof ContextVariable) { - return ((ContextVariable) context).getValue(); + Object result = ((ContextVariable) context).getValue(); + return result != null ? result : UNRESOLVED; } return UNRESOLVED; } @@ -182,7 +196,7 @@ public class HandlebarsPromptTemplate implements PromptTemplate { String.format( "", pluginName, functionName, description)); - List parameters = function.getMetadata().getParameters(); + List> parameters = function.getMetadata().getParameters(); parameters.forEach(p -> { sb.append(String.format( "", diff --git a/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/templateengine/semantickernel/CodeTokenizer.java b/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/templateengine/semantickernel/CodeTokenizer.java index 8c593e14ab..efcfccfb56 100644 --- a/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/templateengine/semantickernel/CodeTokenizer.java +++ b/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/templateengine/semantickernel/CodeTokenizer.java @@ -116,7 +116,7 @@ public class CodeTokenizer { for (int nextCharCursor = 1; nextCharCursor < text.length(); nextCharCursor++) { char currentChar = nextChar; nextChar = text.charAt(nextCharCursor); - + if (skipNextChar) { skipNextChar = false; continue; @@ -161,7 +161,7 @@ public class CodeTokenizer { spaceSeparatorFound = false; } else if (currentChar == namedArgValuePrefix && currentTokenType == TokenTypes.NamedArg) { - blocks.add(new NamedArgBlock(currentTokenContent.toString())); + blocks.add(NamedArgBlock.from(currentTokenContent.toString())); currentTokenContent = new StringBuilder(); currentTokenType = TokenTypes.None; spaceSeparatorFound = false; @@ -195,7 +195,7 @@ public class CodeTokenizer { } else if (currentTokenType == TokenTypes.NamedArg && namedArgSeparatorFound && namedArgValuePrefix != 0) { blocks.add( - new NamedArgBlock(currentTokenContent.toString())); + NamedArgBlock.from(currentTokenContent.toString())); currentTokenContent = new StringBuilder(); namedArgSeparatorFound = false; namedArgValuePrefix = '\0'; @@ -277,7 +277,7 @@ public class CodeTokenizer { break; case NamedArg: - blocks.add(new NamedArgBlock(currentTokenContent.toString())); + blocks.add(NamedArgBlock.from(currentTokenContent.toString())); break; case None: @@ -305,6 +305,7 @@ public class CodeTokenizer { return c == Symbols.DblQuote || c == Symbols.SglQuote || c == Symbols.EscapeChar; } + @SuppressWarnings("NullAway") @Nullable private static NamedArgBlock getNamedArg(String tokenContent) { @@ -314,7 +315,6 @@ public class CodeTokenizer { if (Verify.isNullOrEmpty(name) || Verify.isNullOrEmpty(value)) { return null; } - NamedArgBlock block = new NamedArgBlock(tokenContent, name, value); if (block.isValid()) { return block; diff --git a/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/templateengine/semantickernel/DefaultPromptTemplate.java b/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/templateengine/semantickernel/DefaultPromptTemplate.java index 85787259c4..8fe0b7abef 100644 --- a/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/templateengine/semantickernel/DefaultPromptTemplate.java +++ b/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/templateengine/semantickernel/DefaultPromptTemplate.java @@ -63,10 +63,8 @@ public class DefaultPromptTemplate implements PromptTemplate { /// Augments 's with any variables /// not already contained there but that are referenced in the prompt template. /// + @SuppressWarnings("NullAway") private void addMissingInputVariables(List blocks) { - if (promptTemplate == null) { - return; - } // Add all of the existing input variables to our known set. We'll avoid adding any // dynamically discovered input variables with the same name. Set seen = new HashSet<>(); @@ -84,7 +82,8 @@ public class DefaultPromptTemplate implements PromptTemplate { if (block.getType() == BlockTypes.Variable) { name = ((VarBlock) block).getName(); } else if (block.getType() == BlockTypes.NamedArg) { - name = ((NamedArgBlock) block).getVarBlock().getName(); + VarBlock blockName = ((NamedArgBlock) block).getVarBlock(); + name = blockName == null ? null : blockName.getName(); } if (!Verify.isNullOrEmpty(name) && !seen.contains(name)) { diff --git a/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/templateengine/semantickernel/TemplateException.java b/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/templateengine/semantickernel/TemplateException.java index 32220fd82e..33a29440c5 100644 --- a/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/templateengine/semantickernel/TemplateException.java +++ b/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/templateengine/semantickernel/TemplateException.java @@ -7,10 +7,12 @@ import javax.annotation.Nullable; public class TemplateException extends SKException { - @Nonnull private final ErrorCodes errorCode; + @Nonnull + private final ErrorCodes errorCode; /** - * Initializes a new instance of the {@code TemplateException} class with a provided error code. + * Initializes a new instance of the {@code TemplateException} class with a provided error + * code. * * @param error The error code. */ @@ -19,26 +21,28 @@ public class TemplateException extends SKException { } /** - * Initializes a new instance of the {@code TemplateException} class with a provided error code. + * Initializes a new instance of the {@code TemplateException} class with a provided error + * code. * * @param errorCode The error code. - * @param message The exception message. + * @param message The exception message. */ public TemplateException(@Nonnull ErrorCodes errorCode, @Nullable String message) { this(errorCode, message, null); } /** - * Initializes a new instance of the {@code TemplateException} class with a provided error code. + * Initializes a new instance of the {@code TemplateException} class with a provided error + * code. * - * @param errorCode The error code. - * @param message The exception message. + * @param errorCode The error code. + * @param message The exception message. * @param innerException The exception that is the cause of the current exception. */ public TemplateException( - @Nonnull ErrorCodes errorCode, - @Nullable String message, - @Nullable Throwable innerException) { + @Nonnull ErrorCodes errorCode, + @Nullable String message, + @Nullable Throwable innerException) { super(formatDefaultMessage(errorCode.getMessage(), message), innerException); this.errorCode = errorCode; } @@ -57,19 +61,6 @@ public class TemplateException extends SKException { // spotless:off - /** - *

- * Translate the error code into a default message. - *

- */ - //spotless:on - private static String getDefaultMessage( - @Nonnull ErrorCodes errorCode, @Nullable String message) { - return String.format("%s: %s", errorCode.getMessage(), message); - } - - // spotless:off - /** *

* Error codes for {@code TemplateException}. diff --git a/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/templateengine/semantickernel/blocks/Block.java b/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/templateengine/semantickernel/blocks/Block.java index 02dedc77a4..d4edfc29cc 100644 --- a/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/templateengine/semantickernel/blocks/Block.java +++ b/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/templateengine/semantickernel/blocks/Block.java @@ -1,8 +1,13 @@ // Copyright (c) Microsoft. All rights reserved. package com.microsoft.semantickernel.templateengine.semantickernel.blocks; -/** Base class for blocks parsed from a prompt template */ +import javax.annotation.Nullable; + +/** + * Base class for blocks parsed from a prompt template + */ public abstract class Block { + private final String content; private final BlockTypes type; @@ -10,9 +15,11 @@ public abstract class Block { * Base constructor * * @param content Block content - * @param type Block type + * @param type Block type */ - public Block(String content, BlockTypes type) { + public Block( + @Nullable String content, + BlockTypes type) { if (content == null) { content = ""; } diff --git a/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/templateengine/semantickernel/blocks/CodeBlock.java b/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/templateengine/semantickernel/blocks/CodeBlock.java index 148a2c714b..6fd5a5fd3f 100644 --- a/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/templateengine/semantickernel/blocks/CodeBlock.java +++ b/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/templateengine/semantickernel/blocks/CodeBlock.java @@ -3,19 +3,18 @@ package com.microsoft.semantickernel.templateengine.semantickernel.blocks; import com.microsoft.semantickernel.Kernel; import com.microsoft.semantickernel.exceptions.SKException; +import com.microsoft.semantickernel.orchestration.FunctionResult; import com.microsoft.semantickernel.orchestration.KernelFunctionMetadata; import com.microsoft.semantickernel.orchestration.contextvariables.ContextVariable; import com.microsoft.semantickernel.orchestration.contextvariables.ContextVariableType; import com.microsoft.semantickernel.orchestration.contextvariables.ContextVariableTypes; import com.microsoft.semantickernel.orchestration.contextvariables.DefaultKernelArguments; -import com.microsoft.semantickernel.orchestration.FunctionResult; import com.microsoft.semantickernel.orchestration.contextvariables.KernelArguments; import com.microsoft.semantickernel.templateengine.semantickernel.TemplateException; -import java.util.ArrayList; +import com.microsoft.semantickernel.templateengine.semantickernel.TemplateException.ErrorCodes; import java.util.Collections; import java.util.HashMap; import java.util.List; -import java.util.Locale; import java.util.Optional; import javax.annotation.Nullable; import org.slf4j.Logger; @@ -81,7 +80,7 @@ public final class CodeBlock extends Block implements CodeRendering { @Override public Mono renderCodeAsync(Kernel kernel, @Nullable KernelArguments arguments) { if (!this.isValid()) { - throw new TemplateException(TemplateException.ErrorCodes.SYNTAX_ERROR); + throw new TemplateException(ErrorCodes.SYNTAX_ERROR); } // this.Log.LogTrace("Rendering code: `{0}`", this.Content); @@ -149,7 +148,9 @@ public final class CodeBlock extends Block implements CodeRendering { /// The prompt rendering arguments. /// The function arguments. /// Occurs when any argument other than the first is not a named argument. - private KernelArguments enrichFunctionArguments(Kernel kernel, FunctionIdBlock fBlock, + private KernelArguments enrichFunctionArguments( + Kernel kernel, + FunctionIdBlock fBlock, KernelArguments arguments) { Block firstArg = this.tokens.get(1); @@ -158,14 +159,14 @@ public final class CodeBlock extends Block implements CodeRendering { .getFunction(fBlock.getPluginName(), fBlock.getFunctionName()).getMetadata(); // Check if the function has parameters to be set - if (functionMetadata.getParameters().size() == 0) { + if (functionMetadata.getParameters().isEmpty()) { throw new IllegalArgumentException( "Function " + fBlock.getPluginName() + "." + fBlock.getFunctionName() + " does not take any arguments but it is being called in the template with {this._tokens.Count - 1} arguments."); } String firstPositionalParameterName = null; - Object firstPositionalInputValue = null; + Object firstPositionalInputValue; int namedArgsStartIndex = 1; if (firstArg.getType() != BlockTypes.NamedArg) { @@ -175,6 +176,12 @@ public final class CodeBlock extends Block implements CodeRendering { firstPositionalInputValue = ((TextRendering) tokens.get(1)).render(arguments); // Type check is avoided and marshalling is done by the function itself + if (firstPositionalInputValue == null) { + throw new SKException( + "Unexpected null value for first positional argument: " + tokens.get(1) + .getContent()); + } + // Keep previous trust information when updating the input arguments.put(firstPositionalParameterName, ContextVariable.of(firstPositionalInputValue)); @@ -191,8 +198,7 @@ public final class CodeBlock extends Block implements CodeRendering { // Check if the positional parameter clashes with a named parameter if (firstPositionalParameterName != null && - firstPositionalParameterName.toLowerCase(Locale.ROOT) - .equals(arg.getName().toLowerCase(Locale.ROOT))) { + firstPositionalParameterName.equalsIgnoreCase(arg.getName())) { throw new SKException( "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}"); } diff --git a/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/templateengine/semantickernel/blocks/FunctionIdBlock.java b/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/templateengine/semantickernel/blocks/FunctionIdBlock.java index ad9723d63f..572f887c8d 100644 --- a/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/templateengine/semantickernel/blocks/FunctionIdBlock.java +++ b/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/templateengine/semantickernel/blocks/FunctionIdBlock.java @@ -31,7 +31,7 @@ public final class FunctionIdBlock extends Block implements TextRendering { @Override @Nullable - public String render(KernelArguments variables) { + public String render(@Nullable KernelArguments variables) { return this.getContent(); } diff --git a/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/templateengine/semantickernel/blocks/NamedArgBlock.java b/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/templateengine/semantickernel/blocks/NamedArgBlock.java index 4b914b9e2c..029b43f7a6 100644 --- a/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/templateengine/semantickernel/blocks/NamedArgBlock.java +++ b/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/templateengine/semantickernel/blocks/NamedArgBlock.java @@ -12,10 +12,14 @@ public class NamedArgBlock extends Block implements TextRendering { private static final Logger LOGGER = LoggerFactory.getLogger(NamedArgBlock.class); + @Nullable private final String name; + @Nullable private final String value; private final VarBlock argNameAsVarBlock; + @Nullable private final VarBlock varBlock; + @Nullable private final ValBlock valBlock; public NamedArgBlock(String content, String name, String value) { @@ -34,20 +38,47 @@ public class NamedArgBlock extends Block implements TextRendering { } } - public NamedArgBlock(String content) { + protected NamedArgBlock( + String content, + @Nullable + String name, + @Nullable + String value, + VarBlock argNameAsVarBlock, + @Nullable + VarBlock varBlock, + @Nullable + ValBlock valBlock) { super(content, NamedArg); + this.name = name; + this.value = value; + this.argNameAsVarBlock = argNameAsVarBlock; + this.varBlock = varBlock; + this.valBlock = valBlock; + } - this.name = tryGetName(content); - this.value = tryGetValue(content); - this.argNameAsVarBlock = new VarBlock(Symbols.VarPrefix + name); + public static NamedArgBlock from(String content) { + + String name = tryGetName(content); + String value = tryGetValue(content); + VarBlock argNameAsVarBlock = new VarBlock(Symbols.VarPrefix + name); + + if (value == null) { + throw new IllegalArgumentException("Unable to extract value from: " + content); + } + + VarBlock varBlock; + ValBlock valBlock; if (value.startsWith(String.valueOf(Symbols.VarPrefix))) { - this.varBlock = new VarBlock(value); + varBlock = new VarBlock(value); valBlock = null; } else { - this.valBlock = new ValBlock(value); + valBlock = new ValBlock(value); varBlock = null; } + + return new NamedArgBlock(content, name, value, argNameAsVarBlock, varBlock, valBlock); } @Override @@ -72,9 +103,8 @@ public class NamedArgBlock extends Block implements TextRendering { return this.argNameAsVarBlock.isValid(); } - @Nullable @Override - public String render(KernelArguments variables) { + public String render(@Nullable KernelArguments variables) { return getContent(); } @@ -84,10 +114,12 @@ public class NamedArgBlock extends Block implements TextRendering { } + @Nullable public String getName() { return name; } + @SuppressWarnings("NullAway") public String getValue(KernelArguments arguments) { boolean valueIsValidValBlock = this.valBlock != null && this.valBlock.isValid(); if (valueIsValidValBlock) { @@ -120,6 +152,7 @@ public class NamedArgBlock extends Block implements TextRendering { return splitAndGetPart(text, 1); } + @SuppressWarnings("StringSplitter") @Nullable private static String splitAndGetPart(String text, int x) { if (Verify.isNullOrEmpty(text)) { diff --git a/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/templateengine/semantickernel/blocks/TextBlock.java b/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/templateengine/semantickernel/blocks/TextBlock.java index b490f5b9e8..65017a6dc2 100644 --- a/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/templateengine/semantickernel/blocks/TextBlock.java +++ b/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/templateengine/semantickernel/blocks/TextBlock.java @@ -2,6 +2,7 @@ package com.microsoft.semantickernel.templateengine.semantickernel.blocks; import com.microsoft.semantickernel.orchestration.contextvariables.KernelArguments; +import javax.annotation.Nullable; public final class TextBlock extends Block implements TextRendering { @@ -15,7 +16,7 @@ public final class TextBlock extends Block implements TextRendering { } @Override - public String render(KernelArguments variables) { + public String render(@Nullable KernelArguments variables) { return super.getContent(); } diff --git a/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/templateengine/semantickernel/blocks/ValBlock.java b/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/templateengine/semantickernel/blocks/ValBlock.java index 663f257ac2..da51473a8a 100644 --- a/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/templateengine/semantickernel/blocks/ValBlock.java +++ b/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/templateengine/semantickernel/blocks/ValBlock.java @@ -31,8 +31,7 @@ public final class ValBlock extends Block implements TextRendering { } @Override - @Nullable - public String render(KernelArguments variables) { + public String render(@Nullable KernelArguments variables) { return value; } diff --git a/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/templateengine/semantickernel/blocks/VarBlock.java b/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/templateengine/semantickernel/blocks/VarBlock.java index b73db89962..d3a570bcb9 100644 --- a/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/templateengine/semantickernel/blocks/VarBlock.java +++ b/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/templateengine/semantickernel/blocks/VarBlock.java @@ -6,6 +6,7 @@ import com.microsoft.semantickernel.orchestration.contextvariables.KernelArgumen import com.microsoft.semantickernel.templateengine.semantickernel.TemplateException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import javax.annotation.Nullable; public final class VarBlock extends Block implements TextRendering { @@ -23,7 +24,7 @@ public final class VarBlock extends Block implements TextRendering { } @Override - public String render(KernelArguments variables) { + public String render(@Nullable KernelArguments variables) { if (variables == null) { return ""; } diff --git a/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/textcompletion/StreamingTextContent.java b/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/textcompletion/StreamingTextContent.java index 9c7fb7c14c..cd67402c6c 100644 --- a/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/textcompletion/StreamingTextContent.java +++ b/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/textcompletion/StreamingTextContent.java @@ -8,8 +8,4 @@ public class StreamingTextContent extends StreamingKernelContent { super(content, 0, null, null); } - @Override - public byte[] toByteArray() { - return new byte[0]; - } } diff --git a/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/textcompletion/TextContent.java b/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/textcompletion/TextContent.java index 7ce52b031e..6867094bb8 100644 --- a/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/textcompletion/TextContent.java +++ b/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/textcompletion/TextContent.java @@ -2,8 +2,6 @@ package com.microsoft.semantickernel.textcompletion; import com.microsoft.semantickernel.KernelContent; import com.microsoft.semantickernel.orchestration.FunctionResultMetadata; -import com.microsoft.semantickernel.orchestration.contextvariables.ContextVariable; -import java.util.Map; public class TextContent extends KernelContent { @@ -21,6 +19,7 @@ public class TextContent extends KernelContent { return content; } + @Override public String getContent() { return content; } diff --git a/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/textcompletion/TextGenerationService.java b/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/textcompletion/TextGenerationService.java index 53874448e8..5adb97ba6d 100644 --- a/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/textcompletion/TextGenerationService.java +++ b/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/textcompletion/TextGenerationService.java @@ -58,8 +58,11 @@ public interface TextGenerationService extends Buildable, TextAIService { */ abstract class Builder implements SemanticKernelBuilder { + @Nullable protected String modelId; + @Nullable protected OpenAIAsyncClient client; + @Nullable protected String serviceId; public Builder withModelId(String modelId) { @@ -77,6 +80,7 @@ public interface TextGenerationService extends Buildable, TextAIService { return this; } + @Override public abstract TextGenerationService build(); }