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 b13c6a6a00..c14755c7ff 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 @@ -19,23 +19,28 @@ import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import com.microsoft.semantickernel.Kernel; +import com.microsoft.semantickernel.aiservices.openai.textcompletion.OpenAITextGenerationService; import com.microsoft.semantickernel.chatcompletion.AuthorRole; import com.microsoft.semantickernel.chatcompletion.ChatCompletionService; import com.microsoft.semantickernel.chatcompletion.ChatHistory; import com.microsoft.semantickernel.chatcompletion.ChatMessageContent; import com.microsoft.semantickernel.chatcompletion.StreamingChatMessageContent; +import com.microsoft.semantickernel.orchestration.FunctionResultMetadata; import com.microsoft.semantickernel.orchestration.KernelFunction; 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.FunctionResult; import com.microsoft.semantickernel.orchestration.contextvariables.KernelArguments; +import java.nio.charset.Charset; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Objects; +import java.util.function.Function; import java.util.stream.Collectors; import javax.annotation.Nullable; import org.slf4j.Logger; @@ -123,8 +128,7 @@ public class OpenAIChatCompletion implements ChatCompletionService { return client .getChatCompletions(getModelId(), options) - .mapNotNull(ChatCompletions::getChoices) - .filter(choices -> choices != null && !choices.isEmpty()) + .filter(choices -> choices.getChoices() != null && !choices.getChoices().isEmpty()) .map(this::accumulateResponses) .map(responses -> responses.stream() @@ -136,33 +140,50 @@ public class OpenAIChatCompletion implements ChatCompletionService { // non-streaming case - private List accumulateResponses(List choices) { + private List accumulateResponses(ChatCompletions chatCompletions) { List collectors = new ArrayList<>(); - choices.stream() + chatCompletions + .getChoices() + .stream() .map(ChatChoice::getMessage) .filter(Objects::nonNull) - .map(this::accumulateResponse) + .map(accumulateResponse(chatCompletions)) .forEach(collectors::add); return collectors; } - private ChatResponseCollector accumulateResponse(ChatResponseMessage response) { - ChatResponseCollector collector = new ChatResponseCollector(); + private Function accumulateResponse( + ChatCompletions completions) { - // collector is null for the non-streaming case and not null for the streaming case - if (response.getContent() != null) { - collector.append(AuthorRole.ASSISTANT, response.getContent()); - } else if (response.getToolCalls() != null) { - List toolCalls = response.getToolCalls(); - // TODO: This assumes one tool call per response, which is _definitely_ a bad assumption. - for (ChatCompletionsToolCall toolCall : toolCalls) { - if (toolCall instanceof ChatCompletionsFunctionToolCall) { - collector.append(AuthorRole.TOOL, (ChatCompletionsFunctionToolCall) toolCall); + FunctionResultMetadata metadata = FunctionResultMetadata.build( + completions.getId(), + completions.getUsage(), + completions.getCreatedAt()); + + return response -> { + ChatResponseCollector collector = new ChatResponseCollector( + getModelId(), + null, + null, + metadata + ); + + // collector is null for the non-streaming case and not null for the streaming case + if (response.getContent() != null) { + collector.append(AuthorRole.ASSISTANT, response.getContent()); + } else if (response.getToolCalls() != null) { + List toolCalls = response.getToolCalls(); + // TODO: This assumes one tool call per response, which is _definitely_ a bad assumption. + for (ChatCompletionsToolCall toolCall : toolCalls) { + if (toolCall instanceof ChatCompletionsFunctionToolCall) { + collector.append(AuthorRole.TOOL, + (ChatCompletionsFunctionToolCall) toolCall); + } } } - } - return collector; + return collector; + }; } /* @@ -180,10 +201,10 @@ public class OpenAIChatCompletion implements ChatCompletionService { jsonNode = jsonNode.get("function"); if (jsonNode != null) { // function is the only tool we have right now. - Mono> result = invokeFunction(kernel, jsonNode); + Mono> result = invokeFunction(kernel, jsonNode); if (result != null) { return result.map(contextVariable -> { - String content = contextVariable.getValue(); + String content = contextVariable.getResult(); return new StreamingChatMessageContent(AuthorRole.TOOL, content).setModelId( id); }); @@ -198,7 +219,7 @@ public class OpenAIChatCompletion implements ChatCompletionService { /* * The jsonNode should represent: {"name":"search-search", "parameters": {"query":"Banksy"}}} */ - private Mono> invokeFunction(Kernel kernel, JsonNode jsonNode) { + private Mono> invokeFunction(Kernel kernel, JsonNode jsonNode) { String name = jsonNode.get("name").asText(); String[] parts = name.split("-"); String pluginName = parts.length > 0 ? parts[0] : ""; @@ -321,6 +342,23 @@ public class OpenAIChatCompletion implements ChatCompletionService { private static class AssistantContentBuffer implements ContentBuffer { private final StringBuilder sb = new StringBuilder(); + @Nullable + private final String modelId; + @Nullable + private final String innerContent; + @Nullable + private final Charset encoding; + @Nullable + private final FunctionResultMetadata metadata; + + private AssistantContentBuffer(@Nullable String modelId, @Nullable String innerContent, + @Nullable Charset encoding, @Nullable FunctionResultMetadata metadata) { + this.modelId = modelId; + this.innerContent = innerContent; + this.encoding = encoding; + this.metadata = metadata; + } + @Override public void append(String content) { @@ -329,7 +367,14 @@ public class OpenAIChatCompletion implements ChatCompletionService { @Override public ChatMessageContent toChatMessageContent() { - return new ChatMessageContent(AuthorRole.ASSISTANT, sb.toString()); + return new ChatMessageContent( + AuthorRole.ASSISTANT, + sb.toString(), + modelId, + innerContent, + encoding, + metadata + ); } } @@ -398,14 +443,39 @@ public class OpenAIChatCompletion implements ChatCompletionService { // For streaming, private static class ChatResponseCollector { + @Nullable + private final String modelId; + @Nullable + private final String innerContent; + @Nullable + private final Charset encoding; + @Nullable + private final FunctionResultMetadata metadata; + private final Map> roleToContent = new HashMap<>(); + private ChatResponseCollector( + @Nullable String modelId, + @Nullable String innerContent, + @Nullable Charset encoding, + @Nullable FunctionResultMetadata metadata) { + this.modelId = modelId; + this.innerContent = innerContent; + this.encoding = encoding; + this.metadata = metadata; + } + private ContentBuffer collectorFor(AuthorRole role) { return roleToContent.computeIfAbsent(role, k -> { if (k == AuthorRole.TOOL) { return new ToolContentBuffer(); } - return new AssistantContentBuffer(); + return new AssistantContentBuffer( + modelId, + innerContent, + encoding, + metadata + ); }); } 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 c400a0d995..afb2379681 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 @@ -1,16 +1,17 @@ package com.microsoft.semantickernel.aiservices.openai.textcompletion; import com.azure.ai.openai.OpenAIAsyncClient; -import com.azure.ai.openai.models.Choice; -import com.azure.ai.openai.models.Completions; import com.azure.ai.openai.models.CompletionsOptions; +import com.azure.ai.openai.models.CompletionsUsage; import com.microsoft.semantickernel.Kernel; import com.microsoft.semantickernel.exceptions.AIException; +import com.microsoft.semantickernel.orchestration.FunctionResultMetadata; import com.microsoft.semantickernel.orchestration.PromptExecutionSettings; import com.microsoft.semantickernel.orchestration.contextvariables.ContextVariable; import com.microsoft.semantickernel.textcompletion.StreamingTextContent; import com.microsoft.semantickernel.textcompletion.TextContent; import com.microsoft.semantickernel.textcompletion.TextGenerationService; +import java.time.OffsetDateTime; import java.util.Collections; import java.util.HashMap; import java.util.List; @@ -61,14 +62,7 @@ public class OpenAITextGenerationService implements TextGenerationService { @Override public Mono> getTextContentsAsync(String prompt, @Nullable PromptExecutionSettings executionSettings, @Nullable Kernel kernel) { - return this - .internalCompleteTextAsync(prompt, executionSettings) - .map(it -> { - return it.stream().map( - TextContent::new - ) - .collect(Collectors.toList()); - }); + return this.internalCompleteTextAsync(prompt, executionSettings); } @Override @@ -78,14 +72,11 @@ public class OpenAITextGenerationService implements TextGenerationService { @Nullable Kernel kernel) { return this .internalCompleteTextAsync(prompt, executionSettings) - .flatMapMany(it -> { - return Flux.fromStream(it.stream()) - .map(TextContent::new) - .map(StreamingTextContent::new); - }); + .flatMapMany(it -> Flux.fromStream(it.stream()) + .map(StreamingTextContent::new)); } - protected Mono> internalCompleteTextAsync( + protected Mono> internalCompleteTextAsync( String text, PromptExecutionSettings requestSettings) { @@ -93,9 +84,37 @@ public class OpenAITextGenerationService implements TextGenerationService { return client .getCompletions(getModelId(), completionsOptions) - .flatMapIterable(Completions::getChoices) - .mapNotNull(Choice::getText) - .collectList(); + .map(completions -> { + + FunctionResultMetadata metadata = FunctionResultMetadata.build( + completions.getId(), + completions.getUsage(), + completions.getCreatedAt()); + + return completions + .getChoices() + .stream() + .map(choice -> { + return new TextContent( + choice.getText(), + completionsOptions.getModel(), + metadata); + }) + .collect(Collectors.toList()); + }); + } + + public static Map> buildMetadata( + String id, + CompletionsUsage usage, + OffsetDateTime createdAt) { + + Map> metadata = new HashMap<>(); + metadata.put("id", ContextVariable.of(id)); + metadata.put("usage", ContextVariable.of(usage)); + metadata.put("created_at", ContextVariable.of(createdAt)); + + return metadata; } private CompletionsOptions getCompletionsOptions( 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 b1ef760734..2367d35524 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 @@ -1,9 +1,8 @@ // Copyright (c) Microsoft. All rights reserved. package com.microsoft.semantickernel.syntaxexamples; -import com.microsoft.semantickernel.DefaultKernel; import com.microsoft.semantickernel.Kernel; -import com.microsoft.semantickernel.orchestration.contextvariables.ContextVariable; +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; @@ -19,7 +18,7 @@ public class Example03_ArgumentsTest { @Test public void main() { - Kernel kernel = new DefaultKernel.Builder().build(); + Kernel kernel = Kernel.builder().build(); // Load native skill KernelPlugin functionCollection = @@ -31,12 +30,12 @@ public class Example03_ArgumentsTest { .writableClone() .setVariable("day", "Monday"); - ContextVariable resultValue = kernel.invokeAsync( + FunctionResult resultValue = kernel.invokeAsync( functionCollection.get("AppendDay"), arguments, String.class) .block(); - Assertions.assertEquals("Today is: Monday", resultValue.getValue()); + Assertions.assertEquals("Today is: Monday", resultValue.getResult()); } } 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 26917d45a7..1ac1d29664 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 @@ -6,11 +6,10 @@ import com.azure.ai.openai.OpenAIAsyncClient; import com.azure.ai.openai.OpenAIClientBuilder; import com.github.tomakehurst.wiremock.junit5.WireMockRuntimeInfo; import com.github.tomakehurst.wiremock.junit5.WireMockTest; -import com.microsoft.semantickernel.DefaultKernel; import com.microsoft.semantickernel.Kernel; import com.microsoft.semantickernel.aiservices.openai.textcompletion.OpenAITextGenerationService; +import com.microsoft.semantickernel.orchestration.FunctionResult; import com.microsoft.semantickernel.orchestration.PromptExecutionSettings; -import com.microsoft.semantickernel.orchestration.contextvariables.ContextVariable; import com.microsoft.semantickernel.orchestration.contextvariables.KernelArguments; import com.microsoft.semantickernel.plugin.KernelFunctionFactory; import com.microsoft.semantickernel.semanticfunctions.KernelFunctionFromPrompt; @@ -35,7 +34,7 @@ public class Example05_InlineFunctionDefinitionTest { .withModelId("text-davinci-003") .build(); - Kernel kernel = new DefaultKernel.Builder() + Kernel kernel = Kernel.builder() .withAIService(TextGenerationService.class, textGenerationService) .build(); @@ -75,7 +74,7 @@ public class Example05_InlineFunctionDefinitionTest { String.class) .block(); - Assertions.assertEquals("a-response", result.getValue()); + Assertions.assertEquals("a-response", result.getResult()); WireMockUtil.mockCompletionResponse("sorry I forgot your birthday", "a-response-2"); @@ -86,7 +85,7 @@ public class Example05_InlineFunctionDefinitionTest { String.class) .block(); - Assertions.assertEquals("a-response-2", result.getValue()); + Assertions.assertEquals("a-response-2", result.getResult()); WireMockUtil.mockCompletionResponse("Translate this date ", "a-response-3"); @@ -104,11 +103,11 @@ public class Example05_InlineFunctionDefinitionTest { null, null); - ContextVariable fixedFunctionResult = kernel + FunctionResult fixedFunctionResult = kernel .invokeAsync(fixedFunction, null, String.class) .block(); - Assertions.assertEquals("a-response-3", fixedFunctionResult.getValue()); + Assertions.assertEquals("a-response-3", fixedFunctionResult.getResult()); } } 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 ceacf1503b..d0093a1cd9 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 @@ -134,7 +134,7 @@ public class ConversationSummaryPlugin { ContextVariableTypes.getDefaultVariableTypeForClass(String.class)); }) .reduce("", (acc, next) -> { - return acc + "\n" + next.getValue(); + return acc + "\n" + next.getResultVariable(); }); } } diff --git a/java/samples/sample-code/src/main/java/com/microsoft/semantickernel/samples/plugins/TimePlugin.java b/java/samples/sample-code/src/main/java/com/microsoft/semantickernel/samples/plugins/TimePlugin.java new file mode 100644 index 0000000000..28155fa248 --- /dev/null +++ b/java/samples/sample-code/src/main/java/com/microsoft/semantickernel/samples/plugins/TimePlugin.java @@ -0,0 +1,427 @@ +// Copyright (c) Microsoft. All rights reserved. +package com.microsoft.semantickernel.samples.plugins; + +import com.microsoft.semantickernel.plugin.annotations.DefineKernelFunction; +import com.microsoft.semantickernel.plugin.annotations.KernelFunctionParameter; +import com.microsoft.semantickernel.util.LocaleParser; +import java.time.ZoneId; +import java.time.ZoneOffset; +import java.time.ZonedDateTime; +import java.time.format.DateTimeFormatter; +import java.time.format.TextStyle; +import java.util.Locale; + +/** + * Description: TimeSkill provides a set of functions to get the current time and date. + * + *

Usage: kernel.importSkill(new TimeSkill(), "time"); + * + *

Examples: + * + *

{{time.date}} => Sunday, 12 January, 2031 + * + *

{{time.today}} => Sunday, 12 January, 2031 + * + *

{{time.now}} => Sunday, January 12, 2031 9:15 PM + * + *

{{time.utcNow}} => Sunday, January 13, 2031 5:15 AM + * + *

{{time.time}} => 09:15:07 PM + * + *

{{time.year}} => 2031 + * + *

{{time.month}} => January + * + *

{{time.monthNumber}} => 01 + * + *

{{time.day}} => 12 + * + *

{{time.dayOfWeek}} => Sunday + * + *

{{time.hour}} => 9 PM + * + *

{{time.hourNumber}} => 21 + * + *

{{time.days_ago $days}} => Sunday, 7 May, 2023 + * + *

{{time.last_matching_day $dayName}} => Sunday, 7 May, 2023 + * + *

{{time.minute}} => 15 + * + *

{{time.second}} => 7 + * + *

{{time.timeZoneOffset}} => -08:00 + * + *

{{time.timeZoneName}} => Pacific Time + */ +public class TimePlugin { + + public static final String DAY_MONTH_DAY_YEAR = "EEEE, MMMM d, yyyy"; + + /** + * Get the current date and time for the system default timezone. + * + * @return a ZonedDateTime object with the current date and time. + */ + public ZonedDateTime now() { + return ZonedDateTime.now(ZoneId.systemDefault()); + } + + /** + * Get the current date. + * + *

Example: {{time.date}} => Sunday, January 12, 2025 + * + * @return The current date. + */ + @DefineKernelFunction(name = "date", description = "Get the current date") + public String date( + @KernelFunctionParameter( + name = "locale", + description = "Locale to use when formatting the date", + required = false) + String locale) { + // Example: Sunday, 12 January, 2025 + return DateTimeFormatter.ofPattern(DAY_MONTH_DAY_YEAR) + .withLocale(parseLocale(locale)) + .format(now()); + } + + /** + * Get the current time. + * + *

Example: {{time.time}} => 9:15:00 AM + * + * @return The current time. + */ + @DefineKernelFunction(name = "time", description = "Get the current time") + public String time( + @KernelFunctionParameter( + name = "locale", + description = "Locale to use when formatting the date", + required = false) + String locale) { + // Example: 09:15:07 PM + return DateTimeFormatter.ofPattern("hh:mm:ss a") + .withLocale(parseLocale(locale)) + .format(now()); + } + + /** + * Get the current UTC date and time. + * + *

Example: {{time.utcNow}} => Sunday, January 13, 2025 5:15 AM + * + * @return The current UTC date and time. + */ + @DefineKernelFunction(name = "utcNow", description = "Get the current UTC date and time") + public String utcNow( + @KernelFunctionParameter( + name = "locale", + description = "Locale to use when formatting the date", + required = false) + String locale) { + return DateTimeFormatter.ofPattern(DAY_MONTH_DAY_YEAR + " h:mm a") + .withLocale(parseLocale(locale)) + .format(now().withZoneSameInstant(ZoneOffset.UTC)); + } + + /** + * Get the current date (alias for date() method). + * + *

Example: {{time.today}} => Sunday, January 12, 2025 + * + * @return The current date. + */ + @DefineKernelFunction(name = "today", description = "Get the current date") + public String today( + @KernelFunctionParameter( + name = "locale", + description = "Locale to use when formatting the date", + required = false) + String locale) { + return date(locale); + } + + /** + * Get the current date and time in the local time zone. + * + *

Example: {{time.now}} => Sunday, January 12, 2025 9:15 AM + * + * @return The current date and time in the local time zone. + */ + @DefineKernelFunction( + name = "now", + description = "Get the current date and time in the local time zone") + public String now( + @KernelFunctionParameter( + name = "locale", + description = "Locale to use when formatting the date", + required = false) + String locale) { + return DateTimeFormatter.ofPattern(DAY_MONTH_DAY_YEAR + " h:mm a") + .withLocale(parseLocale(locale)) + .format(now()); + } + + /** + * Get the current year. + * + *

Example: {{time.year}} => 2025 + * + * @return The current year. + */ + @DefineKernelFunction(name = "year", description = "Get the current year") + public String year( + @KernelFunctionParameter( + name = "locale", + description = "Locale to use when formatting the date", + required = false) + String locale) { + return DateTimeFormatter.ofPattern("yyyy").withLocale(parseLocale(locale)).format(now()); + } + + /** + * Get the current month name. + * + *

Example: {{time.month}} => January + * + * @return The current month name. + */ + @DefineKernelFunction(name = "month", description = "Get the current month name") + public String month( + @KernelFunctionParameter( + name = "locale", + description = "Locale to use when formatting the date", + required = false) + String locale) { + return DateTimeFormatter.ofPattern("MMMM").withLocale(parseLocale(locale)).format(now()); + } + + /** + * Get the current month number. + * + *

Example: {{time.monthNumber}} => 01 + * + * @return The current month number. + */ + @DefineKernelFunction(name = "monthNumber", description = "Get the current month number") + public String monthNumber( + @KernelFunctionParameter( + name = "locale", + description = "Locale to use when formatting the date", + required = false) + String locale) { + return DateTimeFormatter.ofPattern("MM").withLocale(parseLocale(locale)).format(now()); + } + + /** + * Get the current day of the month. + * + *

Example: {{time.day}} => 12 + * + * @return The current day of the month. + */ + @DefineKernelFunction(name = "day", description = "Get the current day of the month") + public String day( + @KernelFunctionParameter( + name = "locale", + description = "Locale to use when formatting the date", + required = false) + String locale) { + return DateTimeFormatter.ofPattern("d").withLocale(parseLocale(locale)).format(now()); + } + + /** + * Get the current day of the week. + * + *

Example: {{time.dayOfWeek}} => Sunday + * + * @return The current day of the week. + */ + @DefineKernelFunction(name = "dayOfWeek", description = "Get the current day of the week") + public String dayOfWeek( + @KernelFunctionParameter( + name = "locale", + description = "Locale to use when formatting the date", + required = false) + String locale) { + return DateTimeFormatter.ofPattern("EEEE").withLocale(parseLocale(locale)).format(now()); + } + + /** + * Get the current clock hour. + * + *

Example: {{time.hour}} => 9 AM + * + * @return The current clock hour. + */ + @DefineKernelFunction(name = "hour", description = "Get the current clock hour") + public String hour( + @KernelFunctionParameter( + name = "locale", + description = "Locale to use when formatting the date", + required = false) + String locale) { + return DateTimeFormatter.ofPattern("h a").withLocale(parseLocale(locale)).format(now()); + } + + /** + * Get the current clock 24-hour number. + * + *

Example: {{time.hourNumber}} => 09 + * + * @return The current clock 24-hour number. + */ + @DefineKernelFunction(name = "hourNumber", description = "Get the current clock 24-hour number") + public String hourNumber( + @KernelFunctionParameter( + name = "locale", + description = "Locale to use when formatting the date", + required = false) + String locale) { + return DateTimeFormatter.ofPattern("HH").withLocale(parseLocale(locale)).format(now()); + } + + /** + * Get the date of offset from today by a provided number of days + * + *

Example: SKContext context = SKBuilders.context().build(); context.setVariable("input", + * "3"); {{time.daysAgo $input}} => Saturday, January 11, 2031 + * + * @param days Number of days to subtract from the current day + * @return The date of offset from today by a provided number of days + */ + @DefineKernelFunction( + name = "daysAgo", + description = "Get the date of offset from today by a provided number of days") + public String daysAgo( + @KernelFunctionParameter(name = "days", description = "Number of days to offset from today.") + String days, + @KernelFunctionParameter( + name = "locale", + description = "Locale to use when formatting the date", + required = false) + String locale) { + int offsetDays = Integer.parseInt(days); + return DateTimeFormatter.ofPattern(DAY_MONTH_DAY_YEAR) + .withLocale(parseLocale(locale)) + .format(now().minusDays(offsetDays)); + } + + /** + * Get the date of the last day matching the supplied week day name + * + *

Example: {{time.dateMatchingLastDayName "Monday"}} => Monday, January 6, 2031 + * + * @param dayName Name of the day to match + * @return The date of the last day matching the supplied week day name + */ + @DefineKernelFunction( + name = "dateMatchingLastDayName", + description = "Get the date of the last day matching the supplied week day name") + public String dateMatchingLastDayName( + @KernelFunctionParameter(name = "dayName", description = "Week name day.") String dayName, + @KernelFunctionParameter( + name = "locale", + description = "Locale to use when formatting the date", + required = false) + String locale) { + ZonedDateTime currentDate = now(); + for (int i = 1; i <= 7; i++) { + currentDate = currentDate.minusDays(1); + String currentDayName = + currentDate.getDayOfWeek().getDisplayName(TextStyle.FULL, parseLocale(locale)); + + if (currentDayName.equalsIgnoreCase(dayName)) { + return DateTimeFormatter.ofPattern(DAY_MONTH_DAY_YEAR) + .withLocale(parseLocale(locale)) + .format(currentDate); + } + } + throw new IllegalArgumentException("dayName is not recognized"); + } + + /** + * Get the minutes on the current hour. + * + *

Example: {{time.minute}} => 15 + * + * @return The minutes on the current hour. + */ + @DefineKernelFunction(name = "minute", description = "Get the minutes on the current hour") + public String minute( + @KernelFunctionParameter( + name = "locale", + description = "Locale to use when formatting the date", + required = false) + String locale) { + return DateTimeFormatter.ofPattern("mm").withLocale(parseLocale(locale)).format(now()); + } + + /** + * Get the seconds on the current minute. + * + *

Example: {{time.second}} => 00 + * + * @return The seconds on the current minute. + */ + @DefineKernelFunction(name = "second", description = "Get the seconds on the current minute") + public String second( + @KernelFunctionParameter( + name = "locale", + description = "Locale to use when formatting the date", + required = false) + String locale) { + return DateTimeFormatter.ofPattern("ss").withLocale(parseLocale(locale)).format(now()); + } + + /** + * Get the local time zone offset from UTC. + * + *

Example: {{time.timeZoneOffset}} => +03:00 + * + * @return The local time zone offset from UTC. + */ + @DefineKernelFunction( + name = "timeZoneOffset", + description = "Get the local time zone offset from UTC") + public String timeZoneOffset( + @KernelFunctionParameter( + name = "locale", + description = "Locale to use when formatting the date", + required = false) + String locale) { + return DateTimeFormatter.ofPattern("XXX").withLocale(parseLocale(locale)).format(now()); + } + + /** + * Get the local time zone name. + * + *

Example: {{time.timeZoneName}} => Pacific Time + * + * @return The local time zone name. + */ + @DefineKernelFunction(name = "timeZoneName", description = "Get the local time zone name") + public String timeZoneName( + @KernelFunctionParameter( + name = "locale", + description = "Locale to use when formatting the date", + required = false) + String locale) { + ZoneId zoneId = ZoneId.systemDefault(); + return zoneId.getDisplayName(TextStyle.FULL, parseLocale(locale)); + } + + /** + * Parse the locale string into a Locale object. + * + *

By default, it parses using the LocaleParser utility class. + * + * @param locale string + * @return a locale object + */ + protected Locale parseLocale(String locale) { + return LocaleParser.parseLocale(locale); + } +} 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 2732b41b22..051c3e415c 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 @@ -1,9 +1,8 @@ // Copyright (c) Microsoft. All rights reserved. package com.microsoft.semantickernel.samples.syntaxexamples; -import com.microsoft.semantickernel.DefaultKernel; import com.microsoft.semantickernel.Kernel; -import com.microsoft.semantickernel.orchestration.contextvariables.ContextVariable; +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; @@ -52,12 +51,12 @@ public class Example03_Arguments { .writableClone() .setVariable("day", "Monday"); - ContextVariable resultValue = kernel.invokeAsync( + FunctionResult resultValue = kernel.invokeAsync( functionCollection.get("AppendDay"), arguments, String.class) .block(); - System.out.println(resultValue.getValue()); + System.out.println(resultValue.getResultVariable()); } } 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 9aabd14a0e..6bf7f45a6d 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 @@ -9,7 +9,7 @@ import com.azure.core.credential.KeyCredential; import com.microsoft.semantickernel.Kernel; import com.microsoft.semantickernel.exceptions.ConfigurationException; import com.microsoft.semantickernel.orchestration.PromptExecutionSettings; -import com.microsoft.semantickernel.orchestration.contextvariables.ContextVariable; +import com.microsoft.semantickernel.orchestration.FunctionResult; import com.microsoft.semantickernel.orchestration.contextvariables.KernelArguments; import com.microsoft.semantickernel.plugin.KernelFunctionFactory; import com.microsoft.semantickernel.semanticfunctions.KernelFunctionFromPrompt; @@ -85,7 +85,7 @@ public class Example05_InlineFunctionDefinition { .build(), String.class) .block(); - System.out.println(result.getValue()); + System.out.println(result.getResultVariable()); result = kernel.invokeAsync(excuseFunction, KernelArguments.builder() @@ -93,7 +93,7 @@ public class Example05_InlineFunctionDefinition { .build(), String.class) .block(); - System.out.println(result.getValue()); + System.out.println(result.getResultVariable()); var fixedFunction = KernelFunctionFactory.createFromPrompt( "Translate this date " + DateTimeFormatter @@ -109,10 +109,10 @@ public class Example05_InlineFunctionDefinition { null, null); - ContextVariable fixedFunctionResult = kernel + FunctionResult fixedFunctionResult = kernel .invokeAsync(fixedFunction, null, String.class) .block(); - System.out.println(fixedFunctionResult.getValue()); + System.out.println(fixedFunctionResult.getResultVariable()); } } 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 3dd7cad9c3..a4baef2c33 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.getValue()); + System.out.println(result.getResultVariable()); } } diff --git a/java/samples/sample-code/src/main/java/com/microsoft/semantickernel/samples/syntaxexamples/Example08_RetryHandler.java b/java/samples/sample-code/src/main/java/com/microsoft/semantickernel/samples/syntaxexamples/Example08_RetryHandler.java index 8a81abd0e6..db90ec442d 100644 --- a/java/samples/sample-code/src/main/java/com/microsoft/semantickernel/samples/syntaxexamples/Example08_RetryHandler.java +++ b/java/samples/sample-code/src/main/java/com/microsoft/semantickernel/samples/syntaxexamples/Example08_RetryHandler.java @@ -5,7 +5,6 @@ import com.azure.ai.openai.OpenAIClientBuilder; import com.azure.core.credential.AzureKeyCredential; import com.azure.core.http.policy.ExponentialBackoffOptions; import com.azure.core.http.policy.RetryOptions; -import com.microsoft.semantickernel.DefaultKernel; import com.microsoft.semantickernel.Kernel; import com.microsoft.semantickernel.exceptions.ConfigurationException; import com.microsoft.semantickernel.orchestration.KernelFunction; 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 9747a877e4..fbd8ee80df 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 @@ -195,6 +195,12 @@ public class Example09_FunctionTypes { Temporal.class) .block(); + kernel + .invokeAsync(plugin.get("withDefaultValue"), + null, + String.class) + .block(); + /* TODO: support FunctionResult kernel @@ -377,7 +383,6 @@ public class Example09_FunctionTypes { ); } - @DefineKernelFunction( name = "noInputComplexReturnType", returnType = "java.time.temporal.Temporal" @@ -386,6 +391,21 @@ public class Example09_FunctionTypes { return LocalDateTime.now(); } + @DefineKernelFunction( + name = "withDefaultValue", + returnType = "java.lang.String" + ) + public String withDefaultValue( + @KernelFunctionParameter( + name = "x", + defaultValue = "1", + type = int.class + ) + int x + ) { + return Integer.toString(x); + } + /* ///

/// 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 e7fc0307e3..46564cf8d2 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 @@ -8,8 +8,8 @@ import com.azure.core.credential.KeyCredential; import com.microsoft.semantickernel.Kernel; import com.microsoft.semantickernel.aiservices.openai.textcompletion.OpenAITextGenerationService; import com.microsoft.semantickernel.exceptions.ConfigurationException; -import com.microsoft.semantickernel.orchestration.contextvariables.ContextVariable; import com.microsoft.semantickernel.orchestration.contextvariables.DefaultKernelArguments.Builder; +import com.microsoft.semantickernel.orchestration.FunctionResult; import com.microsoft.semantickernel.plugin.KernelPlugin; import com.microsoft.semantickernel.plugin.KernelPluginFactory; import com.microsoft.semantickernel.samples.plugins.ConversationSummaryPlugin; @@ -158,7 +158,7 @@ public class Example13_ConversationSummaryPlugin { KernelPlugin conversationSummaryPlugin = KernelPluginFactory.createFromObject( new ConversationSummaryPlugin(), null); - Mono> summary = kernel + Mono> summary = kernel .invokeAsync( conversationSummaryPlugin .getFunctions() @@ -170,7 +170,7 @@ public class Example13_ConversationSummaryPlugin { ); System.out.println("Generated Action Items:"); - System.out.println(summary.block().getValue()); + System.out.println(summary.block().getResultVariable()); } private static void getConversationTopicsAsync() { @@ -179,7 +179,7 @@ public class Example13_ConversationSummaryPlugin { KernelPlugin conversationSummaryPlugin = KernelPluginFactory.createFromObject( new ConversationSummaryPlugin(), null); - Mono> summary = kernel + Mono> summary = kernel .invokeAsync( conversationSummaryPlugin .getFunctions() @@ -191,7 +191,7 @@ public class Example13_ConversationSummaryPlugin { ); System.out.println("Generated Topics:"); - System.out.println(summary.block().getValue()); + System.out.println(summary.block().getResultVariable()); } @@ -203,7 +203,7 @@ public class Example13_ConversationSummaryPlugin { KernelPlugin conversationSummaryPlugin = KernelPluginFactory.createFromObject( new ConversationSummaryPlugin(), null); - Mono> summary = kernel + Mono> summary = kernel .invokeAsync( conversationSummaryPlugin .getFunctions() @@ -215,7 +215,7 @@ public class Example13_ConversationSummaryPlugin { ); System.out.println("Generated Summary (This may take some time):"); - System.out.println(summary.block().getValue()); + System.out.println(summary.block().getResultVariable()); } 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 56f0df406e..d6c24a9483 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.getValue()); + System.out.println(result.getResultVariable()); } } diff --git a/java/samples/sample-code/src/main/java/com/microsoft/semantickernel/samples/syntaxexamples/Example30_ChatWithPrompts.java b/java/samples/sample-code/src/main/java/com/microsoft/semantickernel/samples/syntaxexamples/Example30_ChatWithPrompts.java new file mode 100644 index 0000000000..2b45cf3759 --- /dev/null +++ b/java/samples/sample-code/src/main/java/com/microsoft/semantickernel/samples/syntaxexamples/Example30_ChatWithPrompts.java @@ -0,0 +1,126 @@ +package com.microsoft.semantickernel.samples.syntaxexamples; + +import com.azure.ai.openai.OpenAIAsyncClient; +import com.azure.ai.openai.OpenAIClientBuilder; +import com.azure.core.credential.AzureKeyCredential; +import com.azure.core.credential.KeyCredential; +import com.microsoft.semantickernel.Kernel; +import com.microsoft.semantickernel.chatcompletion.ChatCompletionService; +import com.microsoft.semantickernel.chatcompletion.ChatHistory; +import com.microsoft.semantickernel.orchestration.contextvariables.KernelArguments; +import com.microsoft.semantickernel.plugin.KernelPlugin; +import com.microsoft.semantickernel.plugin.KernelPluginFactory; +import com.microsoft.semantickernel.samples.plugins.TimePlugin; +import com.microsoft.semantickernel.semanticfunctions.PromptTemplateConfig; +import com.microsoft.semantickernel.semanticfunctions.PromptTemplateFactory; +import com.microsoft.semantickernel.util.EmbeddedResourceLoader; +import java.io.FileNotFoundException; +import java.time.ZoneId; +import java.time.ZonedDateTime; +import java.time.format.DateTimeFormatter; + +public class Example30_ChatWithPrompts { + + + private static final String CLIENT_KEY = System.getenv("CLIENT_KEY"); + private static final String AZURE_CLIENT_KEY = System.getenv("AZURE_CLIENT_KEY"); + + // Only required if AZURE_CLIENT_KEY is set + private static final String CLIENT_ENDPOINT = System.getenv("CLIENT_ENDPOINT"); + private static final String MODEL_ID = System.getenv() + .getOrDefault("MODEL_ID", "gpt-35-turbo"); + + public static void main(String[] args) throws FileNotFoundException { + OpenAIAsyncClient client; + + if (AZURE_CLIENT_KEY != null) { + client = new OpenAIClientBuilder() + .credential(new AzureKeyCredential(AZURE_CLIENT_KEY)) + .endpoint(CLIENT_ENDPOINT) + .buildAsyncClient(); + + } else { + client = new OpenAIClientBuilder() + .credential(new KeyCredential(CLIENT_KEY)) + .buildAsyncClient(); + } + + ChatCompletionService openAIChatCompletion = ChatCompletionService.builder() + .withOpenAIAsyncClient(client) + .withModelId(MODEL_ID) + .build(); + + Kernel kernel = Kernel.builder() + .withAIService(ChatCompletionService.class, openAIChatCompletion) + .build(); + + System.out.println("======== Chat with prompts ========"); + + /* Load 3 files: + * - 30-system-prompt.txt: the system prompt, used to initialize the chat session. + * - 30-user-context.txt: the user context, e.g. a piece of a document the user selected and is asking to process. + * - 30-user-prompt.txt: the user prompt, just for demo purpose showing that one can leverage the same approach also to augment user messages. + */ + + var systemPromptTemplate = EmbeddedResourceLoader.readFile("30-system-prompt.txt", + Example30_ChatWithPrompts.class); + var selectedText = EmbeddedResourceLoader.readFile("30-user-context.txt", + Example30_ChatWithPrompts.class); + var userPromptTemplate = EmbeddedResourceLoader.readFile("30-user-prompt.txt", + Example30_ChatWithPrompts.class); + + // As an example, we import the time plugin, which is used in system prompt to read the current date. + // We could also use a variable, this is just to show that the prompt can invoke functions. + KernelPlugin timePlugin = KernelPluginFactory.createFromObject( + new TimePlugin(), "time"); + kernel.getPlugins().add(timePlugin); + + // Adding required arguments referenced by the prompt templates. + + var arguments = KernelArguments + .builder() + .withVariable("selectedText", selectedText) + .withVariable("startTime", DateTimeFormatter.ofPattern("hh:mm:ss a zz").format( + ZonedDateTime.now(ZoneId.systemDefault()))) + .withVariable("userMessage", "extract locations as a bullet point list") + .build(); + + // Render the system prompt. This string is used to configure the chat. + // This contains the context, ie a piece of a wikipedia page selected by the user. + String systemMessage = PromptTemplateFactory + .build(new PromptTemplateConfig(systemPromptTemplate)) + .renderAsync(kernel, arguments) + .block(); + + System.out.println("------------------------------------\n" + systemMessage); + + // Render the user prompt. This string is the query sent by the user + // This contains the user request, ie "extract locations as a bullet point list" + String userMessage = PromptTemplateFactory + .build(new PromptTemplateConfig(userPromptTemplate)) + .renderAsync(kernel, arguments) + .block(); + + System.out.println("------------------------------------\n" + userMessage); + + // Client used to request answers + var chatCompletion = kernel.getService(ChatCompletionService.class); + + // The full chat history. Depending on your scenario, you can pass the full chat if useful, + // or create a new one every time, assuming that the "system message" contains all the + // information needed. + var chatHistory = new ChatHistory(systemMessage); + + // Add the user query to the chat history + chatHistory.addUserMessage(userMessage); + + // Finally, get the response from AI + var answer = chatCompletion + .getChatMessageContentsAsync(chatHistory, null, kernel) + .block() + .stream() + .findFirst() + .get(); + System.out.println("------------------------------------\n" + answer); + } +} diff --git a/java/samples/sample-code/src/main/java/com/microsoft/semantickernel/samples/syntaxexamples/Example42_KernelBuilder.java b/java/samples/sample-code/src/main/java/com/microsoft/semantickernel/samples/syntaxexamples/Example42_KernelBuilder.java index 775b5fdc55..f985c05d69 100644 --- a/java/samples/sample-code/src/main/java/com/microsoft/semantickernel/samples/syntaxexamples/Example42_KernelBuilder.java +++ b/java/samples/sample-code/src/main/java/com/microsoft/semantickernel/samples/syntaxexamples/Example42_KernelBuilder.java @@ -5,7 +5,6 @@ import com.azure.ai.openai.OpenAIClientBuilder; import com.azure.core.credential.AzureKeyCredential; import com.azure.core.credential.KeyCredential; import com.azure.core.http.HttpClient; -import com.microsoft.semantickernel.DefaultKernel; import com.microsoft.semantickernel.Kernel; import com.microsoft.semantickernel.chatcompletion.ChatCompletionService; import com.microsoft.semantickernel.plugin.KernelPluginCollection; @@ -66,7 +65,7 @@ public class Example42_KernelBuilder { // KernelBuilder provides a convenient API for creating Kernel instances. However, it is just a // wrapper, ultimately constructing a Kernel // using the public constructor that's available for anyone to use directly if desired. - Kernel kernel = new DefaultKernel( + Kernel kernel = new Kernel( new OrderedAIServiceSelector(), new KernelPluginCollection() ); diff --git a/java/samples/sample-code/src/main/java/com/microsoft/semantickernel/samples/syntaxexamples/Example43_GetModelResult.java b/java/samples/sample-code/src/main/java/com/microsoft/semantickernel/samples/syntaxexamples/Example43_GetModelResult.java new file mode 100644 index 0000000000..c286adb4e7 --- /dev/null +++ b/java/samples/sample-code/src/main/java/com/microsoft/semantickernel/samples/syntaxexamples/Example43_GetModelResult.java @@ -0,0 +1,76 @@ +package com.microsoft.semantickernel.samples.syntaxexamples; + +import com.azure.ai.openai.OpenAIAsyncClient; +import com.azure.ai.openai.OpenAIClientBuilder; +import com.azure.core.credential.AzureKeyCredential; +import com.azure.core.credential.KeyCredential; +import com.microsoft.semantickernel.Kernel; +import com.microsoft.semantickernel.aiservices.openai.chatcompletion.OpenAIChatCompletion; +import com.microsoft.semantickernel.chatcompletion.ChatCompletionService; +import com.microsoft.semantickernel.orchestration.FunctionResult; +import com.microsoft.semantickernel.orchestration.KernelFunction; +import com.microsoft.semantickernel.orchestration.contextvariables.KernelArguments; +import com.microsoft.semantickernel.semanticfunctions.KernelFunctionFromPrompt; + +public class Example43_GetModelResult { + + private static final String CLIENT_KEY = System.getenv("CLIENT_KEY"); + private static final String AZURE_CLIENT_KEY = System.getenv("AZURE_CLIENT_KEY"); + + // Only required if AZURE_CLIENT_KEY is set + private static final String CLIENT_ENDPOINT = System.getenv("CLIENT_ENDPOINT"); + private static final String MODEL_ID = System.getenv() + .getOrDefault("MODEL_ID", "gpt-35-turbo"); + + + public static void main(String[] args) { + System.out.println("======== Open AI - ChatGPT Streaming ========"); + + OpenAIAsyncClient client; + + if (AZURE_CLIENT_KEY != null) { + client = new OpenAIClientBuilder() + .credential(new AzureKeyCredential(AZURE_CLIENT_KEY)) + .endpoint(CLIENT_ENDPOINT) + .buildAsyncClient(); + + } else { + client = new OpenAIClientBuilder() + .credential(new KeyCredential(CLIENT_KEY)) + .buildAsyncClient(); + } + + ChatCompletionService chatCompletionService = OpenAIChatCompletion.builder() + .withModelId(MODEL_ID) + .withOpenAIAsyncClient(client) + .build(); + + Kernel kernel = Kernel.builder() + .withAIService(ChatCompletionService.class, chatCompletionService) + .build(); + + System.out.println("======== Inline Function Definition + Invocation ========"); + + // Create function + String FunctionDefinition = "Hi, give me 5 book suggestions about: {{$input}}"; + KernelFunction myFunction = KernelFunctionFromPrompt.create( + FunctionDefinition); + + // Invoke function through kernel + FunctionResult result = kernel.invokeAsync( + myFunction, + KernelArguments.builder() + .withVariable("input", "travel") + .build(), + String.class) + .block(); + + // Display results + System.out.println(result.getResult()); + System.out.println( + "Usage: " + result + .getMetadata() + .getUsage().getTotalTokens()); + System.out.println(); + } +} 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 c12d74b506..2f2bace055 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.getValue()); + System.out.println(result.getResultVariable()); } } 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 f1f58699dc..da4ce682e6 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.getValue()); + System.out.println(result.getResultVariable()); // 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.getValue()); + System.out.println(result.getResultVariable()); } } 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 f6f8d38ca6..bcec3f8bec 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.getValue()); + System.out.println(result.getResultVariable()); } @@ -108,7 +108,7 @@ public class Example61_MultipleLLMs { String.class ).block(); - System.out.println(result.getValue()); + System.out.println(result.getResultVariable()); } @@ -141,7 +141,7 @@ public class Example61_MultipleLLMs { String.class) .block(); - System.out.println(result.getValue()); + System.out.println(result.getResultVariable()); } } 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 f6c78664d4..913cd910df 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.getValue()); + System.out.println(result.getResultVariable()); } // A dumb AIServiceSelector that just returns the first service and execution settings it finds @@ -92,7 +92,11 @@ public class Example62_CustomAIServiceSelector { @Override public AIServiceSelection trySelectAIService( Class serviceType, + + @Nullable KernelFunction function, + + @Nullable KernelArguments arguments, Map, AIService> services) { 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 new file mode 100644 index 0000000000..db2b4d527a --- /dev/null +++ b/java/samples/sample-code/src/main/java/com/microsoft/semantickernel/samples/syntaxexamples/Example63_ChatCompletionPrompts.java @@ -0,0 +1,69 @@ +package com.microsoft.semantickernel.samples.syntaxexamples; + +import com.azure.ai.openai.OpenAIAsyncClient; +import com.azure.ai.openai.OpenAIClientBuilder; +import com.azure.core.credential.AzureKeyCredential; +import com.azure.core.credential.KeyCredential; +import com.microsoft.semantickernel.Kernel; +import com.microsoft.semantickernel.chatcompletion.ChatCompletionService; +import com.microsoft.semantickernel.semanticfunctions.KernelFunctionFromPrompt; +import java.util.concurrent.CountDownLatch; + +public class Example63_ChatCompletionPrompts { + + private static final String CLIENT_KEY = System.getenv("CLIENT_KEY"); + private static final String AZURE_CLIENT_KEY = System.getenv("AZURE_CLIENT_KEY"); + + // Only required if AZURE_CLIENT_KEY is set + private static final String CLIENT_ENDPOINT = System.getenv("CLIENT_ENDPOINT"); + private static final String MODEL_ID = System.getenv() + .getOrDefault("MODEL_ID", "gpt-35-turbo"); + + public static void main(String[] args) throws InterruptedException { + + OpenAIAsyncClient client; + + if (AZURE_CLIENT_KEY != null) { + client = new OpenAIClientBuilder() + .credential(new AzureKeyCredential(AZURE_CLIENT_KEY)) + .endpoint(CLIENT_ENDPOINT) + .buildAsyncClient(); + + } else { + client = new OpenAIClientBuilder() + .credential(new KeyCredential(CLIENT_KEY)) + .buildAsyncClient(); + } + + ChatCompletionService openAIChatCompletion = ChatCompletionService.builder() + .withOpenAIAsyncClient(client) + .withModelId(MODEL_ID) + .build(); + + Kernel kernel = Kernel.builder() + .withAIService(ChatCompletionService.class, openAIChatCompletion) + .build(); + + String chatPrompt = """ + What is Seattle? + Respond with JSON. + """.stripIndent(); + + var chatSemanticFunction = KernelFunctionFromPrompt.create(chatPrompt); + var chatPromptResult = kernel.invokeAsync(chatSemanticFunction, null, String.class).block(); + + System.out.println("Chat Prompt:"); + System.out.println(chatPrompt); + System.out.println("Chat Prompt Result:"); + System.out.println(chatPromptResult.getResultVariable()); + + 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())); + + 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 22de9ec092..79782a1918 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 @@ -4,7 +4,6 @@ import com.azure.ai.openai.OpenAIAsyncClient; import com.azure.ai.openai.OpenAIClientBuilder; import com.azure.core.credential.AzureKeyCredential; import com.azure.core.credential.KeyCredential; -import com.microsoft.semantickernel.DefaultKernel; import com.microsoft.semantickernel.Kernel; import com.microsoft.semantickernel.aiservices.openai.chatcompletion.OpenAIChatCompletion; import com.microsoft.semantickernel.chatcompletion.ChatCompletionService; @@ -49,7 +48,7 @@ public class Example64_MultiplePromptTemplates { .withOpenAIAsyncClient(client) .build(); - var kernel = new DefaultKernel.Builder() + var kernel = Kernel.builder() .withAIService(ChatCompletionService.class, openAIChatCompletion) .build(); @@ -83,6 +82,6 @@ public class Example64_MultiplePromptTemplates { .build(); var result = kernel.invokeAsync(function, arguments, String.class).block(); - System.out.println(result.getValue()); + System.out.println(result.getResultVariable()); } } 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 fff8194278..f8e8b69c2e 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 @@ -8,6 +8,7 @@ import com.microsoft.semantickernel.plugin.annotations.DefineKernelFunction; public class Example69_MutableKernelPlugin { public static class Time { + @DefineKernelFunction(name = "date") public String date() { return "2021-09-01"; @@ -17,21 +18,22 @@ public class Example69_MutableKernelPlugin { /** * Mutable KernelPlugin example *

- * KernelFunction can be added directly to the function collection of the KernelPlugin - * by using KernelPlugin.getFunctions and putting the corresponding KernelFunction + * KernelFunction can be added directly to the function collection of the KernelPlugin by using + * KernelPlugin.getFunctions and putting the corresponding KernelFunction */ public static void main(String[] args) throws NoSuchMethodException { 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)); + Time.class.getMethod("date"), new Time(), "date", null, null, null)); Kernel kernel = Kernel.builder().build(); kernel.getPlugins().add(plugin); - var result = kernel.invokeAsync(kernel.getPlugins().getFunction("Plugin", "dateFunction"), null, String.class).block(); + var result = kernel.invokeAsync(kernel.getPlugins().getFunction("Plugin", "dateFunction"), + null, String.class).block(); - System.out.println("Result: " + result.getValue()); + System.out.println("Result: " + result.getResult()); } } diff --git a/java/samples/sample-code/src/main/resources/com/microsoft/semantickernel/samples/syntaxexamples/30-system-prompt.txt b/java/samples/sample-code/src/main/resources/com/microsoft/semantickernel/samples/syntaxexamples/30-system-prompt.txt new file mode 100644 index 0000000000..e358d3a91e --- /dev/null +++ b/java/samples/sample-code/src/main/resources/com/microsoft/semantickernel/samples/syntaxexamples/30-system-prompt.txt @@ -0,0 +1,5 @@ +You are an AI assistant that helps people find information. +The chat started at: {{ $startTime }} +The current time is: {{ time.now }} +Text selected: +{{ $selectedText }} \ No newline at end of file diff --git a/java/samples/sample-code/src/main/resources/com/microsoft/semantickernel/samples/syntaxexamples/30-user-context.txt b/java/samples/sample-code/src/main/resources/com/microsoft/semantickernel/samples/syntaxexamples/30-user-context.txt new file mode 100644 index 0000000000..dec71acb4b --- /dev/null +++ b/java/samples/sample-code/src/main/resources/com/microsoft/semantickernel/samples/syntaxexamples/30-user-context.txt @@ -0,0 +1 @@ +The central Sahara is hyperarid, with sparse vegetation. The northern and southern reaches of the desert, along with the highlands, have areas of sparse grassland and desert shrub, with trees and taller shrubs in wadis, where moisture collects. In the central, hyperarid region, there are many subdivisions of the great desert: Tanezrouft, the Ténéré, the Libyan Desert, the Eastern Desert, the Nubian Desert and others. These extremely arid areas often receive no rain for years. \ No newline at end of file diff --git a/java/samples/sample-code/src/main/resources/com/microsoft/semantickernel/samples/syntaxexamples/30-user-prompt.txt b/java/samples/sample-code/src/main/resources/com/microsoft/semantickernel/samples/syntaxexamples/30-user-prompt.txt new file mode 100644 index 0000000000..6b13c80041 --- /dev/null +++ b/java/samples/sample-code/src/main/resources/com/microsoft/semantickernel/samples/syntaxexamples/30-user-prompt.txt @@ -0,0 +1 @@ +{{ time.now }}: {{ $userMessage }} \ No newline at end of file diff --git a/java/samples/sk-v1/01-SimpleChat/src/main/java/Main.java b/java/samples/sk-v1/01-SimpleChat/src/main/java/Main.java index aa59cc7bb7..54ee48fdcd 100644 --- a/java/samples/sk-v1/01-SimpleChat/src/main/java/Main.java +++ b/java/samples/sk-v1/01-SimpleChat/src/main/java/Main.java @@ -6,7 +6,7 @@ import com.microsoft.semantickernel.chatcompletion.ChatCompletionService; import com.microsoft.semantickernel.chatcompletion.ChatHistory; import com.microsoft.semantickernel.orchestration.KernelFunction; import com.microsoft.semantickernel.orchestration.KernelFunctionYaml; -import com.microsoft.semantickernel.orchestration.contextvariables.ContextVariable; +import com.microsoft.semantickernel.orchestration.FunctionResult; import com.microsoft.semantickernel.orchestration.contextvariables.KernelArguments; import com.microsoft.semantickernel.templateengine.handlebars.HandlebarsPromptTemplate; import java.io.BufferedReader; @@ -58,7 +58,7 @@ public class Main { String input = bf.readLine(); chatHistory.addUserMessage(input); - ContextVariable message = kernel + FunctionResult message = kernel .invokeAsync( chatFunction, KernelArguments @@ -70,8 +70,8 @@ public class Main { .block(); System.out.print("Assistant > "); - System.out.println(message.getValue()); - chatHistory.addAssistantMessage(message.getValue()); + System.out.println(message.getResult()); + chatHistory.addAssistantMessage(message.getResult()); } } } \ No newline at end of file diff --git a/java/samples/sk-v1/01-SimpleChat/src/test/java/SimpleChatTest.java b/java/samples/sk-v1/01-SimpleChat/src/test/java/SimpleChatTest.java index 07a6eb2b5e..aff3b2ead9 100644 --- a/java/samples/sk-v1/01-SimpleChat/src/test/java/SimpleChatTest.java +++ b/java/samples/sk-v1/01-SimpleChat/src/test/java/SimpleChatTest.java @@ -5,7 +5,7 @@ import com.microsoft.semantickernel.chatcompletion.ChatHistory; import com.microsoft.semantickernel.chatcompletion.ChatMessageContent; import com.microsoft.semantickernel.orchestration.KernelFunction; import com.microsoft.semantickernel.orchestration.KernelFunctionYaml; -import com.microsoft.semantickernel.orchestration.contextvariables.ContextVariable; +import com.microsoft.semantickernel.orchestration.FunctionResult; import com.microsoft.semantickernel.orchestration.contextvariables.KernelArguments; import com.microsoft.semantickernel.templateengine.handlebars.HandlebarsPromptTemplate; import java.io.IOException; @@ -77,7 +77,7 @@ public class SimpleChatTest { messages.forEach(message -> { chatHistory.addUserMessage(message.matcher); - ContextVariable result = kernel + FunctionResult result = kernel .invokeAsync( chatFunction, KernelArguments @@ -88,8 +88,8 @@ public class SimpleChatTest { ) .block(); - Assertions.assertEquals(message.content(), result.getValue()); - chatHistory.addAssistantMessage(result.getValue()); + Assertions.assertEquals(message.content(), result.getResult()); + chatHistory.addAssistantMessage(result.getResult()); }); } } diff --git a/java/samples/sk-v1/02-PersonaChat/src/main/java/Main.java b/java/samples/sk-v1/02-PersonaChat/src/main/java/Main.java index 8cabf6b7f5..a60bd7323f 100644 --- a/java/samples/sk-v1/02-PersonaChat/src/main/java/Main.java +++ b/java/samples/sk-v1/02-PersonaChat/src/main/java/Main.java @@ -6,14 +6,13 @@ import com.microsoft.semantickernel.chatcompletion.ChatCompletionService; import com.microsoft.semantickernel.chatcompletion.ChatHistory; import com.microsoft.semantickernel.orchestration.KernelFunction; import com.microsoft.semantickernel.orchestration.KernelFunctionYaml; -import com.microsoft.semantickernel.orchestration.contextvariables.ContextVariable; +import com.microsoft.semantickernel.orchestration.FunctionResult; import com.microsoft.semantickernel.orchestration.contextvariables.KernelArguments; import com.microsoft.semantickernel.templateengine.handlebars.HandlebarsPromptTemplate; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.nio.file.Path; -import java.util.List; public class Main { @@ -56,7 +55,7 @@ public class Main { // Run the chat function // The persona chat function uses the persona variable to set the persona of the chat using a system message // See Plugins/ChatPlugin/PersonaChat.prompt.yaml for the full prompt - ContextVariable message = kernel + FunctionResult message = kernel .invokeAsync( chatFunction, KernelArguments @@ -70,8 +69,8 @@ public class Main { .block(); System.out.print("Assistant > "); - System.out.print(message.getValue()); - chatHistory.addAssistantMessage(message.getValue()); + System.out.print(message.getResult()); + chatHistory.addAssistantMessage(message.getResult()); } } } diff --git a/java/samples/sk-v1/02-PersonaChat/src/test/java/PersonaChatTest.java b/java/samples/sk-v1/02-PersonaChat/src/test/java/PersonaChatTest.java index d394179161..1de8095ced 100644 --- a/java/samples/sk-v1/02-PersonaChat/src/test/java/PersonaChatTest.java +++ b/java/samples/sk-v1/02-PersonaChat/src/test/java/PersonaChatTest.java @@ -5,7 +5,7 @@ import com.microsoft.semantickernel.chatcompletion.ChatHistory; import com.microsoft.semantickernel.chatcompletion.ChatMessageContent; import com.microsoft.semantickernel.orchestration.KernelFunction; import com.microsoft.semantickernel.orchestration.KernelFunctionYaml; -import com.microsoft.semantickernel.orchestration.contextvariables.ContextVariable; +import com.microsoft.semantickernel.orchestration.FunctionResult; import com.microsoft.semantickernel.orchestration.contextvariables.KernelArguments; import com.microsoft.semantickernel.templateengine.handlebars.HandlebarsPromptTemplate; import java.io.IOException; @@ -71,7 +71,7 @@ public class PersonaChatTest { messages.forEach(message -> { chatHistory.addUserMessage(message.matcher); - ContextVariable result = kernel + FunctionResult result = kernel .invokeAsync( chatFunction, KernelArguments @@ -84,8 +84,8 @@ public class PersonaChatTest { ) .block(); - Assertions.assertEquals(message.content(), result.getValue()); - chatHistory.addAssistantMessage(result.getValue()); + Assertions.assertEquals(message.content(), result.getResult()); + chatHistory.addAssistantMessage(result.getResult()); }); } } diff --git a/java/samples/sk-v1/03-SimpleRag/src/main/java/Main.java b/java/samples/sk-v1/03-SimpleRag/src/main/java/Main.java index 71857e14c5..88a3eb48b6 100644 --- a/java/samples/sk-v1/03-SimpleRag/src/main/java/Main.java +++ b/java/samples/sk-v1/03-SimpleRag/src/main/java/Main.java @@ -6,7 +6,7 @@ import com.microsoft.semantickernel.chatcompletion.ChatCompletionService; import com.microsoft.semantickernel.chatcompletion.ChatHistory; import com.microsoft.semantickernel.orchestration.KernelFunction; import com.microsoft.semantickernel.orchestration.KernelFunctionYaml; -import com.microsoft.semantickernel.orchestration.contextvariables.ContextVariable; +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; @@ -61,7 +61,7 @@ public class Main { // Run the chat function // The grounded chat function uses the search plugin to perform a Bing search to ground the response // See Plugins/ChatPlugin/GroundedChat.prompt.yaml for the full prompt - ContextVariable message = kernel + FunctionResult message = kernel .invokeAsync( chatFunction, KernelArguments.builder() @@ -74,8 +74,8 @@ public class Main { .block(); System.console().printf("Assistant > "); - System.out.println(message.getValue()); - chatHistory.addAssistantMessage(message.getValue()); + System.out.println(message.getResult()); + chatHistory.addAssistantMessage(message.getResult()); } } } diff --git a/java/samples/sk-v1/03-SimpleRag/src/test/java/SimpleRagTest.java b/java/samples/sk-v1/03-SimpleRag/src/test/java/SimpleRagTest.java index 6d0b3fb161..4a441bc3dc 100644 --- a/java/samples/sk-v1/03-SimpleRag/src/test/java/SimpleRagTest.java +++ b/java/samples/sk-v1/03-SimpleRag/src/test/java/SimpleRagTest.java @@ -5,7 +5,7 @@ import com.microsoft.semantickernel.chatcompletion.ChatHistory; import com.microsoft.semantickernel.chatcompletion.ChatMessageContent; import com.microsoft.semantickernel.orchestration.KernelFunction; import com.microsoft.semantickernel.orchestration.KernelFunctionYaml; -import com.microsoft.semantickernel.orchestration.contextvariables.ContextVariable; +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; @@ -84,7 +84,7 @@ public class SimpleRagTest { // Run the chat function // The grounded chat function uses the search plugin to perform a Bing search to ground the response // See Plugins/ChatPlugin/GroundedChat.prompt.yaml for the full prompt - ContextVariable result = kernel + FunctionResult result = kernel .invokeAsync( chatFunction, KernelArguments.builder() @@ -96,8 +96,8 @@ public class SimpleRagTest { ) .block(); - Assertions.assertEquals(message.content(), result.getValue()); - chatHistory.addAssistantMessage(result.getValue()); + Assertions.assertEquals(message.content(), result.getResult()); + chatHistory.addAssistantMessage(result.getResult()); }); } } \ No newline at end of file diff --git a/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/DefaultKernel.java b/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/DefaultKernel.java deleted file mode 100644 index f45040379f..0000000000 --- a/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/DefaultKernel.java +++ /dev/null @@ -1,143 +0,0 @@ -package com.microsoft.semantickernel; - -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.plugin.KernelPlugin; -import com.microsoft.semantickernel.plugin.KernelPluginCollection; -import com.microsoft.semantickernel.semanticfunctions.PromptTemplate; -import com.microsoft.semantickernel.services.AIServiceSelector; -import com.microsoft.semantickernel.services.OrderedAIServiceSelector; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.function.Function; -import javax.annotation.Nullable; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import reactor.core.publisher.Mono; - -public class DefaultKernel implements Kernel { - - private static final Logger LOGGER = LoggerFactory.getLogger(DefaultKernel.class); - - private final AIServiceSelector serviceSelector; - public KernelPluginCollection plugins; - - public DefaultKernel( - AIServiceSelector serviceSelector, - @Nullable KernelPluginCollection plugins) { - this.serviceSelector = serviceSelector; - if (plugins != null) { - this.plugins = plugins; - } else { - this.plugins = new KernelPluginCollection(); - } - } - - @Override - public Mono> invokeAsync( - KernelFunction function, - @Nullable KernelArguments arguments, - ContextVariableType resultType) { - return function.invokeAsync(this, arguments, resultType); - } - - @Override - public Mono> invokeAsync( - String pluginName, - String functionName, - @Nullable KernelArguments arguments, - ContextVariableType resultType) { - return plugins.getFunction(pluginName, functionName) - .invokeAsync(this, arguments, resultType); - } - - @Override - public Mono> invokeAsync( - KernelFunction function, - @Nullable KernelArguments arguments, - Class resultType) { - - ContextVariableType contextVariable; - - try { - contextVariable = ContextVariableTypes.getDefaultVariableTypeForClass(resultType); - } catch (Exception e) { - if (resultType.isAssignableFrom( - function.getMetadata().getReturnParameter().getParameterType())) { - contextVariable = new ContextVariableType<>(new NoopConverter<>(resultType), - resultType); - } else { - throw e; - } - } - return function.invokeAsync(this, arguments, contextVariable); - } - - @Override - public List getFunctions() { - return null; - } - - @Override - public AIServiceSelector getServiceSelector() { - return serviceSelector; - } - - @Override - public KernelPluginCollection getPlugins() { - return plugins; - } - - - public static class Builder implements Kernel.Builder { - - private final Map, AIService> services = new HashMap<>(); - private final List plugins = new ArrayList<>(); - private Function, AIService>, AIServiceSelector> serviceSelectorProvider; - - @Override - public Builder withAIService(Class clazz, T aiService) { - services.put(clazz, aiService); - return this; - } - - @Override - public Builder withPromptTemplate(PromptTemplate promptTemplate) { - return this; - } - - @Override - public Kernel.Builder withPlugin(KernelPlugin plugin) { - plugins.add(plugin); - return this; - } - - @Override - public Kernel.Builder withServiceSelector( - Function, AIService>, AIServiceSelector> serviceSelector) { - this.serviceSelectorProvider = serviceSelector; - return this; - } - - @Override - public Kernel build() { - - AIServiceSelector serviceSelector; - if (serviceSelectorProvider == null) { - serviceSelector = new OrderedAIServiceSelector(services); - } else { - serviceSelector = serviceSelectorProvider.apply(services); - } - - return new DefaultKernel( - serviceSelector, - new KernelPluginCollection(plugins)); - } - } -} 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 146e1f6cbe..3b038f81f6 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 @@ -3,70 +3,152 @@ package com.microsoft.semantickernel; import com.microsoft.semantickernel.builders.Buildable; import com.microsoft.semantickernel.builders.SemanticKernelBuilder; +import com.microsoft.semantickernel.orchestration.FunctionResult; 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.plugin.KernelPlugin; import com.microsoft.semantickernel.plugin.KernelPluginCollection; import com.microsoft.semantickernel.semanticfunctions.PromptTemplate; import com.microsoft.semantickernel.services.AIServiceSelector; +import com.microsoft.semantickernel.services.OrderedAIServiceSelector; +import java.util.ArrayList; +import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.function.Function; import javax.annotation.Nullable; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import reactor.core.publisher.Mono; /** * Interface for the semantic kernel. */ -public interface Kernel extends Buildable { +public class Kernel implements Buildable { - /** - * Invokes the {@link KernelFunction}. - * - * @param the type of the result value of the function. - * @param function the {@link KernelFunction} to invoke. - * @param arguments the arguments to pass to the function's invocation, including any - * {@link com.microsoft.semantickernel.orchestration.PromptExecutionSettings}. - * @return the result of the function's execution, cast to {@link T}. - */ - Mono> invokeAsync( + private static final Logger LOGGER = LoggerFactory.getLogger(Kernel.class); + + private final AIServiceSelector serviceSelector; + private final KernelPluginCollection plugins; + + public Kernel( + AIServiceSelector serviceSelector, + @Nullable KernelPluginCollection plugins) { + this.serviceSelector = serviceSelector; + if (plugins != null) { + this.plugins = plugins; + } else { + this.plugins = new KernelPluginCollection(); + } + } + + public Mono> invokeAsync( KernelFunction function, @Nullable KernelArguments arguments, - ContextVariableType resultType); + ContextVariableType resultType) { + return function.invokeAsync(this, arguments, resultType); + } - Mono> invokeAsync( + + public Mono> invokeAsync( String pluginName, String functionName, @Nullable KernelArguments arguments, - ContextVariableType resultType); + ContextVariableType resultType) { + return plugins.getFunction(pluginName, functionName) + .invokeAsync(this, arguments, resultType); + } - Mono> invokeAsync( + public Mono> invokeAsync( KernelFunction function, @Nullable KernelArguments arguments, - Class resultType); + Class resultType) { - List getFunctions(); + ContextVariableType contextVariable; - AIServiceSelector getServiceSelector(); - - KernelPluginCollection getPlugins(); - - static Builder builder() { - return new DefaultKernel.Builder(); + try { + contextVariable = ContextVariableTypes.getDefaultVariableTypeForClass(resultType); + } catch (Exception e) { + if (resultType.isAssignableFrom( + function.getMetadata().getReturnParameter().getParameterType())) { + contextVariable = new ContextVariableType<>(new NoopConverter<>(resultType), + resultType); + } else { + throw e; + } + } + return function.invokeAsync(this, arguments, contextVariable); } - - interface Builder extends SemanticKernelBuilder { - - Builder withAIService(Class clazz, T aiService); - - Builder withPromptTemplate(PromptTemplate promptTemplate); - - Builder withPlugin(KernelPlugin searchPlugin); - - Builder withServiceSelector( - Function, AIService>, AIServiceSelector> serviceSelector); + public List getFunctions() { + return null; } + + public AIServiceSelector getServiceSelector() { + return serviceSelector; + } + + public KernelPluginCollection getPlugins() { + return plugins; + } + + public T getService(Class clazz) { + return (T) serviceSelector + .trySelectAIService( + clazz, + null, + null + ) + .getService(); + } + + public static Kernel.Builder builder() { + return new Kernel.Builder(); + } + + public static class Builder implements SemanticKernelBuilder { + + private final Map, AIService> services = new HashMap<>(); + private final List plugins = new ArrayList<>(); + private Function, AIService>, AIServiceSelector> serviceSelectorProvider; + + public Builder withAIService(Class clazz, T aiService) { + services.put(clazz, aiService); + return this; + } + + public Builder withPromptTemplate(PromptTemplate promptTemplate) { + return this; + } + + public Kernel.Builder withPlugin(KernelPlugin plugin) { + plugins.add(plugin); + return this; + } + + public Kernel.Builder withServiceSelector( + Function, AIService>, AIServiceSelector> serviceSelector) { + this.serviceSelectorProvider = serviceSelector; + return this; + } + + @Override + public Kernel build() { + + AIServiceSelector serviceSelector; + if (serviceSelectorProvider == null) { + serviceSelector = new OrderedAIServiceSelector(services); + } else { + serviceSelector = serviceSelectorProvider.apply(services); + } + + return new Kernel( + serviceSelector, + new KernelPluginCollection(plugins)); + } + } + } 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 f6a0f80338..2f5b046cb3 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,19 +1,19 @@ 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 class KernelContent> { +public abstract class KernelContent> { @Nullable private Object innerContent; @Nullable private String modelId; @Nullable - private Map> metadata; + private FunctionResultMetadata metadata; public KernelContent( @Nullable @@ -21,11 +21,13 @@ public class KernelContent> { @Nullable String modelId, @Nullable - Map> metadata + FunctionResultMetadata metadata ) { this.innerContent = innerContent; this.modelId = modelId; - this.metadata = metadata; + if (metadata != null) { + this.metadata = metadata; + } } T setInnerContent(@Nullable Object innerContent) { @@ -44,8 +46,9 @@ public class KernelContent> { } @CheckForNull - public Map> getMetadata() { + public FunctionResultMetadata getMetadata() { return metadata; } + public abstract String getContent(); } diff --git a/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/chatcompletion/ChatHistory.java b/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/chatcompletion/ChatHistory.java index de00b658ab..591341e1b3 100644 --- a/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/chatcompletion/ChatHistory.java +++ b/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/chatcompletion/ChatHistory.java @@ -11,6 +11,7 @@ import java.util.Optional; import java.util.Spliterator; import java.util.function.Consumer; +import com.microsoft.semantickernel.orchestration.FunctionResultMetadata; import com.microsoft.semantickernel.orchestration.contextvariables.ContextVariable; /** @@ -81,7 +82,7 @@ public class ChatHistory implements Iterable { } public void addMessage(AuthorRole authorRole, String content, Charset encoding, - Map> metadata) { + FunctionResultMetadata metadata) { chatMessageContents.add( new ChatMessageContent(authorRole, content, null, null, encoding, metadata)); } 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 eda87b53e0..d91bb3fe94 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 @@ -2,6 +2,7 @@ package com.microsoft.semantickernel.chatcompletion; import com.microsoft.semantickernel.KernelContent; +import com.microsoft.semantickernel.orchestration.FunctionResultMetadata; import com.microsoft.semantickernel.orchestration.contextvariables.ContextVariable; import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; @@ -41,7 +42,7 @@ public class ChatMessageContent extends KernelContent { @Nullable Charset encoding, @Nullable - Map> metadata + FunctionResultMetadata metadata ) { super(innerContent, modelId, metadata); this.authorRole = authorRole; @@ -55,7 +56,7 @@ public class ChatMessageContent extends KernelContent { String modelId, String innerContent, Charset encoding, - Map> metadata + FunctionResultMetadata metadata ) { super(innerContent, modelId, metadata); this.authorRole = authorRole; 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 new file mode 100644 index 0000000000..d5a270d958 --- /dev/null +++ b/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/orchestration/FunctionResult.java @@ -0,0 +1,31 @@ +package com.microsoft.semantickernel.orchestration; + +import com.microsoft.semantickernel.orchestration.contextvariables.ContextVariable; + +public class FunctionResult { + + private final ContextVariable result; + private final FunctionResultMetadata metadata; + + public FunctionResult(ContextVariable result, FunctionResultMetadata metadata) { + this.result = result; + this.metadata = metadata; + } + + public FunctionResult(ContextVariable of) { + this(of, FunctionResultMetadata.empty()); + } + + + public T getResult() { + return result.getValue(); + } + + public ContextVariable getResultVariable() { + return result; + } + + public FunctionResultMetadata getMetadata() { + return metadata; + } +} 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 new file mode 100644 index 0000000000..afec88a79e --- /dev/null +++ b/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/orchestration/FunctionResultMetadata.java @@ -0,0 +1,53 @@ +package com.microsoft.semantickernel.orchestration; + +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; + +public class FunctionResultMetadata { + + public static final String ID = "id"; + public static final String USAGE = "usage"; + public static final String CREATED_AT = "createdAt"; + + + private final CaseInsensitiveMap> metadata; + + public FunctionResultMetadata(CaseInsensitiveMap> metadata) { + this.metadata = metadata; + } + + public static FunctionResultMetadata build( + String id, + CompletionsUsage usage, + OffsetDateTime createdAt) { + + CaseInsensitiveMap> metadata = new CaseInsensitiveMap<>(); + metadata.put(ID, ContextVariable.of(id)); + metadata.put(USAGE, ContextVariable.of(usage)); + metadata.put(CREATED_AT, ContextVariable.of(createdAt)); + + return new FunctionResultMetadata(metadata); + } + + public static FunctionResultMetadata empty() { + return new FunctionResultMetadata(new CaseInsensitiveMap<>()); + } + + public CaseInsensitiveMap> getMetadata() { + return new CaseInsensitiveMap<>(metadata); + } + + public String getId() { + return metadata.get(ID).getValue(String.class); + } + + public CompletionsUsage getUsage() { + return metadata.get(USAGE).getValue(CompletionsUsage.class); + } + + public OffsetDateTime getCreatedAt() { + return metadata.get(CREATED_AT).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 fc3b05bd55..e005f0faa6 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 @@ -3,7 +3,6 @@ package com.microsoft.semantickernel.orchestration; import com.microsoft.semantickernel.Kernel; import com.microsoft.semantickernel.builders.Buildable; -import com.microsoft.semantickernel.orchestration.contextvariables.ContextVariable; import com.microsoft.semantickernel.orchestration.contextvariables.ContextVariableType; import com.microsoft.semantickernel.orchestration.contextvariables.KernelArguments; import com.microsoft.semantickernel.semanticfunctions.InputVariable; @@ -77,7 +76,7 @@ public interface KernelFunction extends Buildable { * @param The type of the context variable * @return The result of the function's execution. */ - Mono> invokeAsync( + Mono> invokeAsync( Kernel kernel, @Nullable KernelArguments arguments, ContextVariableType variableType); 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 8ab005a85b..183edf0b98 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 @@ -1,6 +1,6 @@ package com.microsoft.semantickernel.orchestration; -import com.microsoft.semantickernel.orchestration.KernelFunctionFromMethod.ImplementationFunc; +import com.microsoft.semantickernel.semanticfunctions.KernelFunctionFromMethod.ImplementationFunc; import com.microsoft.semantickernel.plugin.KernelParameterMetadata; import com.microsoft.semantickernel.plugin.KernelReturnParameterMetadata; import java.util.List; 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 f4fd7ef250..34b6693b04 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 @@ -15,6 +15,15 @@ public class ContextVariable { return value; } + public U getValue(Class clazz) { + if (clazz.isAssignableFrom(value.getClass())) { + return (U) value; + } else { + throw new RuntimeException("Cannot cast " + value.getClass() + " to " + clazz); + } + } + + public ContextVariableType getType() { return type; } @@ -41,7 +50,6 @@ public class ContextVariable { return new ContextVariable(type, value); } - public static ContextVariable of(T value, ContextVariableTypeConverter converter) { ContextVariableType type = new ContextVariableType<>(converter, converter.getType()); return new ContextVariable<>(type, value); 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 68502c0be5..720e1a1128 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 @@ -3,6 +3,8 @@ package com.microsoft.semantickernel.orchestration.contextvariables; import com.microsoft.semantickernel.orchestration.contextvariables.converters.BooleanVariableContextVariableTypeConverter; import com.microsoft.semantickernel.orchestration.contextvariables.converters.CharacterVariableContextVariableTypeConverter; 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.NumberVariableContextVariableTypeConverter; import com.microsoft.semantickernel.orchestration.contextvariables.converters.StringVariableContextVariableTypeConverter; import com.microsoft.semantickernel.orchestration.contextvariables.converters.VoidVariableContextVariableTypeConverter; @@ -22,6 +24,8 @@ public class ContextVariableTypes { new ChatHistoryVariableContextVariableTypeConverter(), new StringVariableContextVariableTypeConverter(), new VoidVariableContextVariableTypeConverter(), + new DateTimeContextVariableTypeConverter(), + new CompletionUsageContextVariableTypeConverter(), new NumberVariableContextVariableTypeConverter<>(Byte.class, Byte::parseByte), new NumberVariableContextVariableTypeConverter<>(byte.class, Byte::parseByte), diff --git a/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/orchestration/contextvariables/converters/CompletionUsageContextVariableTypeConverter.java b/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/orchestration/contextvariables/converters/CompletionUsageContextVariableTypeConverter.java new file mode 100644 index 0000000000..c17f6a9525 --- /dev/null +++ b/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/orchestration/contextvariables/converters/CompletionUsageContextVariableTypeConverter.java @@ -0,0 +1,22 @@ +package com.microsoft.semantickernel.orchestration.contextvariables.converters; + +import com.azure.ai.openai.models.CompletionsUsage; +import com.microsoft.semantickernel.orchestration.contextvariables.ContextVariableTypeConverter; + +public class CompletionUsageContextVariableTypeConverter extends + ContextVariableTypeConverter { + + public CompletionUsageContextVariableTypeConverter() { + super( + CompletionsUsage.class, + s -> { + if (s instanceof CompletionsUsage) { + return (CompletionsUsage) s; + } + return null; + }, + Object::toString, + o -> null // Do not support parsing from string + ); + } +} 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 new file mode 100644 index 0000000000..f5c0f1e260 --- /dev/null +++ b/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/orchestration/contextvariables/converters/DateTimeContextVariableTypeConverter.java @@ -0,0 +1,26 @@ +package com.microsoft.semantickernel.orchestration.contextvariables.converters; + +import com.microsoft.semantickernel.orchestration.contextvariables.ContextVariableTypeConverter; +import java.time.OffsetDateTime; +import java.time.ZonedDateTime; + +public class DateTimeContextVariableTypeConverter extends + ContextVariableTypeConverter { + + public DateTimeContextVariableTypeConverter() { + super( + OffsetDateTime.class, + s -> { + if (s instanceof String) { + return ZonedDateTime.parse((String) s).toOffsetDateTime(); + } else if (s instanceof OffsetDateTime) { + return (OffsetDateTime) s; + } + return null; + }, + Object::toString, + o -> { + return ZonedDateTime.parse(o).toOffsetDateTime(); + }); + } +} 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 54b57ea086..da5de33a56 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,14 +1,15 @@ package com.microsoft.semantickernel.orchestration.contextvariables.converters; +import static com.microsoft.semantickernel.orchestration.contextvariables.ContextVariableTypes.convert; + import com.microsoft.semantickernel.orchestration.contextvariables.ContextVariableTypeConverter; import java.util.function.Function; -import static com.microsoft.semantickernel.orchestration.contextvariables.ContextVariableTypes.convert; - public class NumberVariableContextVariableTypeConverter extends ContextVariableTypeConverter { - public NumberVariableContextVariableTypeConverter(Class clazz, Function fromPromptString) { + public NumberVariableContextVariableTypeConverter(Class clazz, + Function fromPromptString) { super( clazz, s -> convert(s, clazz), 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 17d19dc706..8bda2e6a4b 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,7 +1,7 @@ package com.microsoft.semantickernel.plugin; import com.microsoft.semantickernel.orchestration.KernelFunction; -import com.microsoft.semantickernel.orchestration.KernelFunctionFromMethod; +import com.microsoft.semantickernel.semanticfunctions.KernelFunctionFromMethod; import com.microsoft.semantickernel.orchestration.PromptExecutionSettings; import com.microsoft.semantickernel.semanticfunctions.KernelFunctionFromPrompt; import com.microsoft.semantickernel.semanticfunctions.KernelPromptTemplateFactory; diff --git a/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/orchestration/KernelFunctionFromMethod.java b/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/semanticfunctions/KernelFunctionFromMethod.java similarity index 95% rename from java/semantickernel-api/src/main/java/com/microsoft/semantickernel/orchestration/KernelFunctionFromMethod.java rename to java/semantickernel-api/src/main/java/com/microsoft/semantickernel/semanticfunctions/KernelFunctionFromMethod.java index f3a85ac2eb..e40f3a69b2 100644 --- a/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/orchestration/KernelFunctionFromMethod.java +++ b/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/semanticfunctions/KernelFunctionFromMethod.java @@ -1,10 +1,15 @@ -package com.microsoft.semantickernel.orchestration; +package com.microsoft.semantickernel.semanticfunctions; import static com.microsoft.semantickernel.plugin.annotations.KernelFunctionParameter.NO_DEFAULT_VALUE; import com.microsoft.semantickernel.Kernel; import com.microsoft.semantickernel.exceptions.AIException; import com.microsoft.semantickernel.exceptions.AIException.ErrorCodes; +import com.microsoft.semantickernel.orchestration.DefaultKernelFunction; +import com.microsoft.semantickernel.orchestration.FunctionResult; +import com.microsoft.semantickernel.orchestration.KernelFunction; +import com.microsoft.semantickernel.orchestration.KernelFunctionMetadata; +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; @@ -56,17 +61,16 @@ public class KernelFunctionFromMethod extends DefaultKernelFunction { } @Override - public Mono> invokeAsync( + public Mono> invokeAsync( Kernel kernel, @Nullable KernelArguments arguments, ContextVariableType variableType) { - return function.invoke(kernel, this, arguments) - .map(variableType::of); + return function.invoke(kernel, this, arguments); } public interface ImplementationFunc { - Mono invoke( + Mono> invoke( Kernel kernel, KernelFunction function, @Nullable @@ -133,7 +137,7 @@ public class KernelFunctionFromMethod extends DefaultKernelFunction { return new ImplementationFunc() { @Override - public Mono invoke(Kernel kernel, KernelFunction function, + public Mono> invoke(Kernel kernel, KernelFunction function, @Nullable KernelArguments arguments) { @@ -161,21 +165,12 @@ public class KernelFunctionFromMethod extends DefaultKernelFunction { } else { return (T) it; } - /* - if (it instanceof KernelFunctionParameter) { - return it; - } else { - return context.update((String) it); - } - - */ }); - return r; + return r + .map(it -> new FunctionResult<>(ContextVariable.of(it))); } catch (Exception e) { - - throw new RuntimeException(e); - //return Mono.error(e); + return Mono.error(e); } } }; @@ -232,12 +227,9 @@ public class KernelFunctionFromMethod extends DefaultKernelFunction { @Nullable KernelArguments context, Parameter parameter, Kernel kernel) { - if (context == null) { - return context; - } String variableName = getGetVariableName(parameter); - ContextVariable arg = context.get(variableName); + ContextVariable arg = context == null ? null : context.get(variableName); if (arg == null) { /* // If this is bound to input get the input value 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 46a9e27d2e..cb041612f8 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 @@ -12,10 +12,10 @@ import com.microsoft.semantickernel.orchestration.DefaultKernelFunction; import com.microsoft.semantickernel.orchestration.KernelFunction; import com.microsoft.semantickernel.orchestration.KernelFunctionMetadata; import com.microsoft.semantickernel.orchestration.PromptExecutionSettings; -import com.microsoft.semantickernel.orchestration.TextRequestContent; 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.FunctionResult; import com.microsoft.semantickernel.orchestration.contextvariables.KernelArguments; import com.microsoft.semantickernel.services.AIServiceSelection; import com.microsoft.semantickernel.textcompletion.TextGenerationService; @@ -84,7 +84,7 @@ public class KernelFunctionFromPrompt extends DefaultKernelFunction { } - private Flux> invokeInternalAsync( + private Flux> invokeInternalAsync( Kernel kernel, @Nullable KernelArguments arguments, ContextVariableType variableType) { @@ -108,7 +108,7 @@ public class KernelFunctionFromPrompt extends DefaultKernelFunction { "Failed to initialise aiService, could not find any TextAIService implementations"); } - Flux> result; + Flux> result; PromptExecutionSettings executionSettings = aiServiceSelection.getSettings(); @@ -122,25 +122,40 @@ public class KernelFunctionFromPrompt extends DefaultKernelFunction { .concatMap(chatMessageContent -> { if (chatMessageContent.getAuthorRole() == AuthorRole.ASSISTANT) { + T value = variableType .getConverter() - .fromPromptString( - chatMessageContent.getContent()); - return Flux.just(new TextRequestContent<>(value)); + .fromObject(chatMessageContent); - } else if (chatMessageContent.getAuthorRole() - == AuthorRole.TOOL) { - Mono> toolResult = invokeTool(kernel, - chatMessageContent.getContent()); - return toolResult.flatMapMany(contextVariable -> { - T value = variableType + if (value == null) { + value = variableType .getConverter() .fromPromptString( - contextVariable.getValue()); - return Flux.just(new TextRequestContent<>(value)); - }); + chatMessageContent.getContent()); + } + + return Flux.just( + new FunctionResult<>( + new ContextVariable<>(variableType, value), + chatMessageContent.getMetadata() + ) + ); + } else if (chatMessageContent.getAuthorRole() + == AuthorRole.TOOL) { + Mono> toolResult = invokeTool(kernel, + chatMessageContent.getContent()); + return toolResult.flux(); } return Flux.empty(); + }) + .map(it -> { + return new FunctionResult<>( + new ContextVariable<>( + variableType, + variableType.of(it.getResult()).getValue() + ), + it.getMetadata() + ); }); return result; @@ -152,9 +167,25 @@ public class KernelFunctionFromPrompt extends DefaultKernelFunction { kernel) .flatMapMany(Flux::fromIterable) .concatMap(textContent -> { - T value = variableType.getConverter().fromPromptString( - textContent.getContent()); - return Flux.just(new TextRequestContent<>(value)); + T value = variableType + .getConverter() + .fromObject(textContent); + + if (value == null) { + value = variableType + .getConverter() + .fromPromptString(textContent.getContent()); + } + + return Flux.just( + new FunctionResult<>( + new ContextVariable<>( + variableType, + value + ), + textContent.getMetadata() + ) + ); }); } else { return Flux.error(new IllegalStateException("Unknown service type")); @@ -194,22 +225,13 @@ public class KernelFunctionFromPrompt extends DefaultKernelFunction { } @Override - public Mono> invokeAsync( + public Mono> invokeAsync( Kernel kernel, @Nullable KernelArguments arguments, ContextVariableType variableType) { - return invokeInternalAsync(kernel, arguments, variableType). - collectList() - .map(contents -> { - StringBuilder result = contents - .stream() - .reduce( - new StringBuilder(), - (sb, textRequestContent) -> sb.append(textRequestContent.innerContent), - StringBuilder::append); - T x = variableType.getConverter().fromPromptString(result.toString()); - return new ContextVariable<>(variableType, x); - }); + return invokeInternalAsync(kernel, arguments, variableType) + .take(1) + .single(); } /* @@ -219,7 +241,7 @@ public class KernelFunctionFromPrompt extends DefaultKernelFunction { * {"type":"function", "function": {"name":"search-search", "parameters": {"query":"Banksy"}}} * where 'name' is . */ - private Mono> invokeTool(Kernel kernel, String json) { + private Mono> invokeTool(Kernel kernel, String json) { try { ObjectMapper mapper = new ObjectMapper(); JsonNode jsonNode = mapper.readTree(json); @@ -236,7 +258,7 @@ public class KernelFunctionFromPrompt extends DefaultKernelFunction { /* * The jsonNode should represent: {"name":"search-search", "parameters": {"query":"Banksy"}}} */ - private Mono> invokeFunction(Kernel kernel, JsonNode jsonNode) { + private Mono> invokeFunction(Kernel kernel, JsonNode jsonNode) { String name = jsonNode.get("name").asText(); String[] parts = name.split("-"); String pluginName = parts.length > 0 ? parts[0] : ""; @@ -345,7 +367,10 @@ public class KernelFunctionFromPrompt extends DefaultKernelFunction { if (this.executionSettings == null) { this.executionSettings = new HashMap<>(); } - this.executionSettings.putAll(executionSettings); + + if (executionSettings != null) { + this.executionSettings.putAll(executionSettings); + } return this; } diff --git a/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/semanticfunctions/PromptConfig.java.ignore b/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/semanticfunctions/PromptConfig.java.ignore deleted file mode 100644 index 32320ad360..0000000000 --- a/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/semanticfunctions/PromptConfig.java.ignore +++ /dev/null @@ -1,442 +0,0 @@ -// Copyright (c) Microsoft. All rights reserved. -package com.microsoft.semantickernel.semanticfunctions; - -import com.fasterxml.jackson.annotation.JsonCreator; -import com.fasterxml.jackson.annotation.JsonIgnoreProperties; -import com.fasterxml.jackson.annotation.JsonProperty; -import com.microsoft.semantickernel.builders.Buildable; -import com.microsoft.semantickernel.builders.BuildersSingleton; -import com.microsoft.semantickernel.builders.SemanticKernelBuilder; -import com.microsoft.semantickernel.orchestration.PromptExecutionSettings; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; -import reactor.util.annotation.Nullable; - -/** - * Prompt template configuration - */ -@JsonIgnoreProperties(ignoreUnknown = true) -public class PromptConfig { - - private final CompletionConfig completionConfig; - private final InputConfig input; - - /** - * A returns the configuration for the text completion - * - * @return CompletionConfig - */ - public CompletionConfig getCompletionConfig() { - return completionConfig; - } - - public int getSchema() { - return schema; - } - - public String getType() { - return type; - } - - public String getName() { - return null; - } - - public List getKernelParametersMetadata() { - return null; - } - - public KernelReturnParameterMetadata getKernelReturnParameterMetadata() { - return null; - } - - public List getExecutionSettings() { - return null; - } - - - /** - * Builder for CompletionConfig - */ - public static class CompletionConfigBuilder implements SemanticKernelBuilder { - - private CompletionConfig completionConfig; - - public CompletionConfigBuilder() { - completionConfig = new CompletionConfig(); - } - - public CompletionConfigBuilder(CompletionConfig completionConfig) { - this.completionConfig = completionConfig; - } - - public CompletionConfigBuilder temperature(double temperature) { - return new CompletionConfigBuilder( - new CompletionConfig( - temperature, - completionConfig.topP, - completionConfig.presencePenalty, - completionConfig.frequencyPenalty, - completionConfig.maxTokens, - completionConfig.bestOf, - completionConfig.user, - completionConfig.stopSequences)); - } - - public CompletionConfigBuilder topP(double topP) { - return new CompletionConfigBuilder( - new CompletionConfig( - completionConfig.temperature, - topP, - completionConfig.presencePenalty, - completionConfig.frequencyPenalty, - completionConfig.maxTokens, - completionConfig.bestOf, - completionConfig.user, - completionConfig.stopSequences)); - } - - public CompletionConfigBuilder presencePenalty(double presencePenalty) { - return new CompletionConfigBuilder( - new CompletionConfig( - completionConfig.temperature, - completionConfig.topP, - presencePenalty, - completionConfig.frequencyPenalty, - completionConfig.maxTokens, - completionConfig.bestOf, - completionConfig.user, - completionConfig.stopSequences)); - } - - public CompletionConfigBuilder frequencyPenalty(double frequencyPenalty) { - return new CompletionConfigBuilder( - new CompletionConfig( - completionConfig.temperature, - completionConfig.topP, - completionConfig.presencePenalty, - frequencyPenalty, - completionConfig.maxTokens, - completionConfig.bestOf, - completionConfig.user, - completionConfig.stopSequences)); - } - - public CompletionConfigBuilder maxTokens(int maxTokens) { - return new CompletionConfigBuilder( - new CompletionConfig( - completionConfig.temperature, - completionConfig.topP, - completionConfig.presencePenalty, - completionConfig.frequencyPenalty, - maxTokens, - completionConfig.bestOf, - completionConfig.user, - completionConfig.stopSequences)); - } - - public CompletionConfigBuilder stopSequences(List stopSequences) { - return new CompletionConfigBuilder( - new CompletionConfig( - completionConfig.temperature, - completionConfig.topP, - completionConfig.presencePenalty, - completionConfig.frequencyPenalty, - completionConfig.maxTokens, - completionConfig.bestOf, - completionConfig.user, - stopSequences)); - } - - public CompletionConfig build() { - return completionConfig; - } - } - - public InputConfig getInput() { - return input; - } - - /** - * Completion configuration parameters - */ - public static class CompletionConfig implements Buildable { - - /* - ///

- /// Sampling temperature to use, between 0 and 2. Higher values will make the output more random. - /// Lower values will make it more focused and deterministic. - /// - [JsonPropertyName("temperature")] - [JsonPropertyOrder(1)] - */ - private final double temperature; - /* - /// - /// Cut-off of top_p probability mass of tokens to consider. - /// For example, 0.1 means only the tokens comprising the top 10% probability mass are considered. - /// - [JsonPropertyName("top_p")] - [JsonPropertyOrder(2)] - */ - private final double topP; - - /* - /// - /// Lowers the probability of a word appearing if it already appeared in the predicted text. - /// Unlike the frequency penalty, the presence penalty does not depend on the frequency at which words - /// appear in past predictions. - /// - [JsonPropertyName("presence_penalty")] - [JsonPropertyOrder(3)] - */ - private final double presencePenalty; - - /* - /// - /// Controls the model’s tendency to repeat predictions. The frequency penalty reduces the probability - /// of words that have already been generated. The penalty depends on how many times a word has already - /// occurred in the prediction. - /// - [JsonPropertyName("frequency_penalty")] - [JsonPropertyOrder(4)] - */ - private final double frequencyPenalty; - /* - /// - /// Maximum number of tokens that can be generated. - /// - [JsonPropertyName("max_tokens")] - [JsonPropertyOrder(5)]*/ - private final int maxTokens; // { get; set; } = 256; - /* - /// - /// Stop sequences are optional sequences that tells the AI model when to stop generating tokens. - /// - [JsonPropertyName("stop_sequences")] - [JsonPropertyOrder(6)] - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - */ - public final List stopSequences; // { get; set; } = new(); - - /** - * The maximum number of completions to generate for each prompt. This is used by the - * CompletionService to generate multiple completions for a single prompt. - */ - private final Integer bestOf; - - /** - * A unique identifier representing your end-user, which can help OpenAI to monitor and - * detect abuse - */ - private final String user; - - public CompletionConfig() { - this(0.0, 0.0, 0.0, 0.0, 256, 1, "", new ArrayList<>()); - } - - public CompletionConfig( - double temperature, - double topP, - double presencePenalty, - double frequencyPenalty, - int maxTokens) { - this( - temperature, - topP, - presencePenalty, - frequencyPenalty, - maxTokens, - 1, - "", - new ArrayList<>()); - } - - @JsonCreator - public CompletionConfig( - @JsonProperty("temperature") double temperature, - @JsonProperty("top_p") double topP, - @JsonProperty("presence_penalty") double presencePenalty, - @JsonProperty("frequency_penalty") double frequencyPenalty, - @JsonProperty("max_tokens") int maxTokens, - @JsonProperty("best_of") int bestOf, - @JsonProperty("user") String user, - @JsonProperty(value = "stop_sequences") List stopSequences) { - this.temperature = temperature; - this.topP = topP; - this.presencePenalty = presencePenalty; - this.frequencyPenalty = frequencyPenalty; - this.maxTokens = maxTokens; - - // bestOf must be at least 1 - this.bestOf = Math.max(1, bestOf); - - if (user == null) { - user = ""; - } - this.user = user; - if (stopSequences == null) { - stopSequences = new ArrayList<>(); - } - this.stopSequences = stopSequences; - } - - public double getTemperature() { - return temperature; - } - - public double getTopP() { - return topP; - } - - public double getPresencePenalty() { - return presencePenalty; - } - - public double getFrequencyPenalty() { - return frequencyPenalty; - } - - public int getMaxTokens() { - return maxTokens; - } - - /** - * The maximum number of completions to generate for each prompt. This is used by the - * CompletionService to generate multiple completions for a single prompt. - */ - public int getBestOf() { - return bestOf; - } - - /** - * A unique identifier representing your end-user, which can help OpenAI to monitor and - * detect abuse - */ - public String getUser() { - return user; - } - - public SemanticKernelBuilder builder() { - return BuildersSingleton.INST.getInstance(CompletionConfigBuilder.class); - } - - public List getStopSequences() { - return Collections.unmodifiableList(stopSequences); - } - } - - /** - * Input parameter for semantic functions - */ - public static class InputParameter { - - private final String name; - private final String description; - - private final String defaultValue; - - @JsonCreator - public InputParameter( - @JsonProperty("name") String name, - @JsonProperty("description") String description, - @JsonProperty("defaultValue") String defaultValue) { - this.name = name; - this.description = description; - this.defaultValue = defaultValue; - } - - /** - * Name of the parameter to pass to the function. e.g. when using "{{$input}}" the name is - * "input", when using "{{$style}}" the name is "style", etc. - * - * @return name - */ - public String getName() { - return name; - } - - /** - * Parameter description for UI apps and planner. Localization is not supported here. - * - * @return description - */ - public String getDescription() { - return description; - } - - /** - * Default value when nothing is provided - * - * @return the default value - */ - public String getDefaultValue() { - return defaultValue; - } - } - - /** - * Input configuration (list of all input parameters for a semantic function). - */ - public static class InputConfig { - - public final List parameters; - - @JsonCreator - public InputConfig(@JsonProperty("parameters") List parameters) { - this.parameters = Collections.unmodifiableList(parameters); - } - - public List getParameters() { - return parameters; - } - } - - private final int schema; - - private final String type; // { get; set; } = "completion"; - private final String description; - - public PromptConfig() { - this("", "", null); - } - - public PromptConfig(CompletionConfig completionConfig) { - this(1, "", "", completionConfig, new InputConfig(new ArrayList<>())); - } - - public PromptConfig( - String description, String type, @Nullable CompletionConfig completionConfig) { - this(1, description, type, completionConfig, new InputConfig(new ArrayList<>())); - } - - @JsonCreator - public PromptConfig( - @JsonProperty("schema") int schema, - @JsonProperty("description") String description, - @JsonProperty("type") String type, - @Nullable @JsonProperty("completion") CompletionConfig completionConfig, - @Nullable @JsonProperty("input") InputConfig input) { - if (completionConfig == null) { - completionConfig = new CompletionConfig(); - } - this.schema = schema; - this.description = description; - this.type = type; - this.completionConfig = completionConfig; - if (input == null) { - input = new InputConfig(new ArrayList<>()); - } - this.input = input; - } - - /** - * Description - * - * @return Description - */ - public String getDescription() { - return description; - } -} 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 38be4687c8..c44b799dfb 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,9 +1,15 @@ package com.microsoft.semantickernel.semanticfunctions; public interface PromptTemplateFactory { + PromptTemplate tryCreate(PromptTemplateConfig templateConfig); + static PromptTemplate build(PromptTemplateConfig templateConfig) { + return new KernelPromptTemplateFactory().tryCreate(templateConfig); + } + class UnknownTemplateFormatException extends IllegalArgumentException { + public UnknownTemplateFormatException(String templateFormat) { super("Unknown template format: " + templateFormat); } diff --git a/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/services/AIServiceSelector.java b/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/services/AIServiceSelector.java index 18e7d56e3b..b74b906f9f 100644 --- a/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/services/AIServiceSelector.java +++ b/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/services/AIServiceSelector.java @@ -28,7 +28,10 @@ public interface AIServiceSelector { @Nullable AIServiceSelection trySelectAIService( Class serviceType, + @Nullable KernelFunction function, + + @Nullable KernelArguments arguments ); } diff --git a/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/services/BaseAIServiceSelector.java b/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/services/BaseAIServiceSelector.java index 4e1472ff84..540d4f3985 100644 --- a/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/services/BaseAIServiceSelector.java +++ b/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/services/BaseAIServiceSelector.java @@ -22,7 +22,11 @@ public abstract class BaseAIServiceSelector implements AIServiceSelector { @Nullable public AIServiceSelection trySelectAIService( Class serviceType, + + @Nullable KernelFunction function, + + @Nullable KernelArguments arguments) { return trySelectAIService(serviceType, function, arguments, services); } @@ -30,7 +34,11 @@ public abstract class BaseAIServiceSelector implements AIServiceSelector { @Nullable public abstract AIServiceSelection trySelectAIService( Class serviceType, + + @Nullable KernelFunction function, + + @Nullable KernelArguments arguments, Map, AIService> services); } 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 8731516bfc..93e9f62ba9 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 @@ -31,7 +31,9 @@ public class OrderedAIServiceSelector extends BaseAIServiceSelector { @Nullable public AIServiceSelection trySelectAIService( Class serviceType, + @Nullable KernelFunction function, + @Nullable KernelArguments arguments, Map, AIService> services) { @@ -122,16 +124,21 @@ public class OrderedAIServiceSelector extends BaseAIServiceSelector { @Nullable private static Map settingsFromFunctionSettings( - KernelFunction function, Map executionSettings) { + @Nullable + KernelFunction function, + Map executionSettings) { if (executionSettings == null || executionSettings.isEmpty()) { - executionSettings = function.getExecutionSettings(); + if (function != null) { + executionSettings = function.getExecutionSettings(); + } } return executionSettings; } @Nullable private static Map settingsFromArguments( - KernelArguments arguments, Map executionSettings) { + @Nullable KernelArguments arguments, + Map executionSettings) { if (arguments != null) { executionSettings = arguments.getExecutionSettings(); } diff --git a/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/services/ParentAIServiceSelector.java b/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/services/ParentAIServiceSelector.java deleted file mode 100644 index 03df16aa5e..0000000000 --- a/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/services/ParentAIServiceSelector.java +++ /dev/null @@ -1,41 +0,0 @@ -package com.microsoft.semantickernel.services; - -import com.microsoft.semantickernel.AIService; -import com.microsoft.semantickernel.orchestration.KernelFunction; -import com.microsoft.semantickernel.orchestration.contextvariables.KernelArguments; -import java.util.HashMap; -import java.util.Map; -import javax.annotation.Nullable; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -public abstract class ParentAIServiceSelector implements AIServiceSelector { - - private static final Logger LOGGER = LoggerFactory.getLogger(ParentAIServiceSelector.class); - - protected final Map, AIService> services; - - public ParentAIServiceSelector() { - this.services = new HashMap<>(); - } - - public ParentAIServiceSelector(Map, AIService> services) { - this.services = services; - } - - @Override - @Nullable - public AIServiceSelection trySelectAIService( - Class serviceType, - KernelFunction function, - KernelArguments arguments) { - return trySelectAIService(serviceType, function, arguments, services); - } - - @Nullable - public abstract AIServiceSelection trySelectAIService( - Class serviceType, - KernelFunction function, - KernelArguments arguments, - Map, AIService> services); -} 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 665f870ce9..e3fcb48142 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 @@ -8,6 +8,7 @@ import com.microsoft.semantickernel.orchestration.contextvariables.ContextVariab 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.Collections; @@ -121,7 +122,8 @@ public final class CodeBlock extends Block implements CodeRendering { //Cloning the original arguments to avoid side effects - arguments added to the original arguments collection as a result of rendering template variables. arguments = this.enrichFunctionArguments(kernel, fBlock, arguments == - null ? new DefaultKernelArguments() : new DefaultKernelArguments(arguments, new HashMap<>())); + null ? new DefaultKernelArguments() + : new DefaultKernelArguments(arguments, new HashMap<>())); } return kernel @@ -129,7 +131,8 @@ public final class CodeBlock extends Block implements CodeRendering { fBlock.getPluginName(), fBlock.getFunctionName(), arguments, - resultType); + resultType) + .map(FunctionResult::getResultVariable); } 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 185162c4f5..7ce52b031e 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 @@ -1,13 +1,19 @@ 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 { private final String content; - public TextContent(String content) { - super(content, null, null); + public TextContent( + String content, + String modelId, + FunctionResultMetadata metadata) { + super(content, modelId, metadata); this.content = content; }