mirror of
https://github.com/microsoft/semantic-kernel.git
synced 2026-10-12 02:19:06 +00:00
+58
-14
@@ -26,6 +26,7 @@ import com.microsoft.semantickernel.chatcompletion.ChatHistory;
|
||||
import com.microsoft.semantickernel.chatcompletion.ChatMessageContent;
|
||||
import com.microsoft.semantickernel.chatcompletion.StreamingChatMessageContent;
|
||||
import com.microsoft.semantickernel.exceptions.AIException;
|
||||
import com.microsoft.semantickernel.exceptions.SKException;
|
||||
import com.microsoft.semantickernel.hooks.KernelHooks;
|
||||
import com.microsoft.semantickernel.hooks.PreChatCompletionEvent;
|
||||
import com.microsoft.semantickernel.orchestration.FunctionResult;
|
||||
@@ -56,9 +57,15 @@ public class OpenAIChatCompletion implements ChatCompletionService {
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(OpenAIChatCompletion.class);
|
||||
private final OpenAIAsyncClient client;
|
||||
private final Map<String, ContextVariable<?>> attributes;
|
||||
|
||||
@Nullable
|
||||
private final String serviceId;
|
||||
|
||||
public OpenAIChatCompletion(OpenAIAsyncClient client, String modelId, String serviceId) {
|
||||
public OpenAIChatCompletion(
|
||||
OpenAIAsyncClient client,
|
||||
String modelId,
|
||||
@Nullable
|
||||
String serviceId) {
|
||||
this.serviceId = serviceId;
|
||||
this.client = client;
|
||||
this.attributes = new HashMap<>();
|
||||
@@ -75,6 +82,7 @@ public class OpenAIChatCompletion implements ChatCompletionService {
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public String getServiceId() {
|
||||
return serviceId;
|
||||
}
|
||||
@@ -82,8 +90,11 @@ public class OpenAIChatCompletion implements ChatCompletionService {
|
||||
@Override
|
||||
public Mono<List<ChatMessageContent>> getChatMessageContentsAsync(
|
||||
ChatHistory chatHistory,
|
||||
@Nullable
|
||||
PromptExecutionSettings promptExecutionSettings,
|
||||
@Nullable
|
||||
Kernel kernel,
|
||||
@Nullable
|
||||
KernelHooks kernelHooks) {
|
||||
|
||||
List<ChatRequestMessage> chatRequestMessages = getChatRequestMessages(chatHistory);
|
||||
@@ -112,6 +123,7 @@ public class OpenAIChatCompletion implements ChatCompletionService {
|
||||
@Override
|
||||
public Mono<List<ChatMessageContent>> getChatMessageContentsAsync(
|
||||
String prompt,
|
||||
@Nullable
|
||||
PromptExecutionSettings promptExecutionSettings,
|
||||
Kernel kernel,
|
||||
@Nullable
|
||||
@@ -138,7 +150,9 @@ public class OpenAIChatCompletion implements ChatCompletionService {
|
||||
|
||||
private Mono<List<ChatMessageContent>> internalChatMessageContentsAsync(
|
||||
List<ChatRequestMessage> chatRequestMessages,
|
||||
@Nullable
|
||||
List<FunctionDefinition> functions,
|
||||
@Nullable
|
||||
PromptExecutionSettings promptExecutionSettings,
|
||||
KernelHooks kernelHooks) {
|
||||
|
||||
@@ -242,7 +256,8 @@ public class OpenAIChatCompletion implements ChatCompletionService {
|
||||
* {"type":"function", "function": {"name":"search-search", "parameters": {"query":"Banksy"}}}
|
||||
* where 'name' is <plugin name '-' function name>.
|
||||
*/
|
||||
private Mono<StreamingChatMessageContent> invokeTool(Kernel kernel, String json) {
|
||||
@SuppressWarnings("UnusedMethod")
|
||||
private Mono<StreamingChatMessageContent<?>> invokeTool(Kernel kernel, String json) {
|
||||
try {
|
||||
ObjectMapper mapper = new ObjectMapper();
|
||||
JsonNode jsonNode = mapper.readTree(json);
|
||||
@@ -254,8 +269,10 @@ public class OpenAIChatCompletion implements ChatCompletionService {
|
||||
if (result != null) {
|
||||
return result.map(contextVariable -> {
|
||||
String content = contextVariable.getResult();
|
||||
return new StreamingChatMessageContent(AuthorRole.TOOL, content).setModelId(
|
||||
id);
|
||||
if (content == null) {
|
||||
throw new SKException("Function result must not be null");
|
||||
}
|
||||
return new StreamingChatMessageContent<>(AuthorRole.TOOL, content, id);
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -268,6 +285,7 @@ public class OpenAIChatCompletion implements ChatCompletionService {
|
||||
/*
|
||||
* The jsonNode should represent: {"name":"search-search", "parameters": {"query":"Banksy"}}}
|
||||
*/
|
||||
@SuppressWarnings("StringSplitter")
|
||||
private Mono<FunctionResult<String>> invokeFunction(Kernel kernel, JsonNode jsonNode) {
|
||||
String name = jsonNode.get("name").asText();
|
||||
String[] parts = name.split("-");
|
||||
@@ -298,23 +316,28 @@ public class OpenAIChatCompletion implements ChatCompletionService {
|
||||
private static ChatCompletionsOptions getCompletionsOptions(
|
||||
ChatCompletionService chatCompletionService,
|
||||
List<ChatRequestMessage> chatRequestMessages,
|
||||
@Nullable
|
||||
List<FunctionDefinition> functions,
|
||||
@Nullable
|
||||
PromptExecutionSettings promptExecutionSettings) {
|
||||
|
||||
|
||||
ChatCompletionsOptions options = new ChatCompletionsOptions(chatRequestMessages)
|
||||
.setModel(chatCompletionService.getModelId());
|
||||
|
||||
if (promptExecutionSettings == null) {
|
||||
return options;
|
||||
}
|
||||
|
||||
|
||||
if (promptExecutionSettings.getResultsPerPrompt() < 1
|
||||
|| promptExecutionSettings.getResultsPerPrompt() > MAX_RESULTS_PER_PROMPT) {
|
||||
throw new AIException(AIException.ErrorCodes.INVALID_REQUEST, String.format("Results per prompt must be in range between 1 and %d, inclusive.", MAX_RESULTS_PER_PROMPT));
|
||||
|| promptExecutionSettings.getResultsPerPrompt() > MAX_RESULTS_PER_PROMPT) {
|
||||
throw new AIException(AIException.ErrorCodes.INVALID_REQUEST,
|
||||
String.format("Results per prompt must be in range between 1 and %d, inclusive.",
|
||||
MAX_RESULTS_PER_PROMPT));
|
||||
}
|
||||
|
||||
List<ChatCompletionsToolDefinition> toolDefinitions =
|
||||
chatCompletionsToolDefinitions(promptExecutionSettings.getToolCallBehavior(), functions);
|
||||
List<ChatCompletionsToolDefinition> toolDefinitions =
|
||||
chatCompletionsToolDefinitions(promptExecutionSettings.getToolCallBehavior(),
|
||||
functions);
|
||||
if (toolDefinitions != null && !toolDefinitions.isEmpty()) {
|
||||
options.setTools(toolDefinitions);
|
||||
// TODO: options.setToolChoices(toolChoices);
|
||||
@@ -351,18 +374,22 @@ public class OpenAIChatCompletion implements ChatCompletionService {
|
||||
return options;
|
||||
}
|
||||
|
||||
@SuppressWarnings("StringSplitter")
|
||||
private static List<ChatCompletionsToolDefinition> chatCompletionsToolDefinitions(
|
||||
@Nullable
|
||||
ToolCallBehavior toolCallBehavior,
|
||||
@Nullable
|
||||
List<FunctionDefinition> functions) {
|
||||
|
||||
if (functions == null || functions.isEmpty()) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
if (toolCallBehavior == null || !(toolCallBehavior.kernelFunctionsEnabled() || toolCallBehavior.autoInvokeEnabled())) {
|
||||
|
||||
if (toolCallBehavior == null || !(toolCallBehavior.kernelFunctionsEnabled()
|
||||
|| toolCallBehavior.autoInvokeEnabled())) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
|
||||
return functions.stream()
|
||||
.filter(function -> {
|
||||
String[] parts = function.getName().split("-");
|
||||
@@ -384,6 +411,9 @@ public class OpenAIChatCompletion implements ChatCompletionService {
|
||||
.map(message -> {
|
||||
AuthorRole authorRole = message.getAuthorRole();
|
||||
String content = message.getContent();
|
||||
if (content == null) {
|
||||
throw new SKException("ChatMessageContent content must not be null");
|
||||
}
|
||||
return getChatRequestMessage(authorRole, content);
|
||||
})
|
||||
.collect(Collectors.toList());
|
||||
@@ -405,7 +435,7 @@ public class OpenAIChatCompletion implements ChatCompletionService {
|
||||
return new ChatRequestToolMessage(content, null);
|
||||
default:
|
||||
LOGGER.debug("Unexpected author role: " + authorRole);
|
||||
return null;
|
||||
throw new SKException("Unexpected author role: " + authorRole);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -459,7 +489,10 @@ public class OpenAIChatCompletion implements ChatCompletionService {
|
||||
private static class ToolContentBuffer implements
|
||||
ContentBuffer<ChatCompletionsFunctionToolCall> {
|
||||
|
||||
@Nullable
|
||||
private String id = null;
|
||||
|
||||
@Nullable
|
||||
private String name = null;
|
||||
private List<String> arguments = new ArrayList<>();
|
||||
|
||||
@@ -575,6 +608,17 @@ public class OpenAIChatCompletion implements ChatCompletionService {
|
||||
|
||||
@Override
|
||||
public OpenAIChatCompletion build() {
|
||||
|
||||
if (this.client == null) {
|
||||
throw new AIException(AIException.ErrorCodes.INVALID_REQUEST,
|
||||
"OpenAI client must be provided");
|
||||
}
|
||||
|
||||
if (this.modelId == null || modelId.isEmpty()) {
|
||||
throw new AIException(AIException.ErrorCodes.INVALID_REQUEST,
|
||||
"OpenAI model id must be provided");
|
||||
}
|
||||
|
||||
return new OpenAIChatCompletion(client, modelId, serviceId);
|
||||
}
|
||||
}
|
||||
|
||||
+6
@@ -2,7 +2,9 @@ package com.microsoft.semantickernel.aiservices.openai.chatcompletion;
|
||||
|
||||
import com.azure.ai.openai.models.ChatRequestMessage;
|
||||
import com.azure.ai.openai.models.FunctionDefinition;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
class ParsedPrompt {
|
||||
|
||||
@@ -10,8 +12,12 @@ class ParsedPrompt {
|
||||
private final List<FunctionDefinition> functions;
|
||||
|
||||
protected ParsedPrompt(List<ChatRequestMessage> parsedMessages,
|
||||
@Nullable
|
||||
List<FunctionDefinition> parsedFunctions) {
|
||||
this.chatRequestMessages = parsedMessages;
|
||||
if (parsedFunctions == null) {
|
||||
parsedFunctions = new ArrayList<>();
|
||||
}
|
||||
this.functions = parsedFunctions;
|
||||
}
|
||||
|
||||
|
||||
+5
-3
@@ -144,7 +144,9 @@ class XMLPromptParser {
|
||||
// },
|
||||
// },
|
||||
//}
|
||||
assert functionDefinition != null;
|
||||
if (functionDefinition == null) {
|
||||
throw new SKException("Failed to parse function definition");
|
||||
}
|
||||
if (!parameters.isEmpty()) {
|
||||
StringBuilder sb = new StringBuilder(
|
||||
"{\"type\": \"object\", \"properties\": {");
|
||||
@@ -207,11 +209,11 @@ class XMLPromptParser {
|
||||
String role,
|
||||
String content) {
|
||||
try {
|
||||
AuthorRole authorRole = AuthorRole.valueOf(role.toUpperCase());
|
||||
AuthorRole authorRole = AuthorRole.valueOf(role.toUpperCase(Locale.ROOT));
|
||||
return OpenAIChatCompletion.getChatRequestMessage(authorRole, content);
|
||||
} catch (IllegalArgumentException e) {
|
||||
LOGGER.debug("Unknown author role: " + role);
|
||||
return null;
|
||||
throw new SKException("Unknown author role: " + role);
|
||||
}
|
||||
}
|
||||
}
|
||||
+28
-12
@@ -25,6 +25,7 @@ public class OpenAITextGenerationService implements TextGenerationService {
|
||||
|
||||
private final OpenAIAsyncClient client;
|
||||
private final Map<String, ContextVariable<?>> attributes;
|
||||
@Nullable
|
||||
private final String serviceId;
|
||||
|
||||
/// <summary>
|
||||
@@ -38,7 +39,9 @@ public class OpenAITextGenerationService implements TextGenerationService {
|
||||
/// <param name="loggerFactory">The <see cref="ILoggerFactory"/> to use for logging. If null, no logging will be performed.</param>
|
||||
public OpenAITextGenerationService(
|
||||
OpenAIAsyncClient client,
|
||||
String modelId, String serviceId) {
|
||||
String modelId,
|
||||
@Nullable
|
||||
String serviceId) {
|
||||
this.serviceId = serviceId;
|
||||
this.client = client;
|
||||
attributes = new HashMap<>();
|
||||
@@ -55,13 +58,16 @@ public class OpenAITextGenerationService implements TextGenerationService {
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public String getServiceId() {
|
||||
return serviceId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<List<TextContent>> getTextContentsAsync(String prompt,
|
||||
@Nullable PromptExecutionSettings executionSettings, @Nullable Kernel kernel) {
|
||||
public Mono<List<TextContent>> getTextContentsAsync(
|
||||
String prompt,
|
||||
@Nullable PromptExecutionSettings executionSettings,
|
||||
@Nullable Kernel kernel) {
|
||||
return this.internalCompleteTextAsync(prompt, executionSettings);
|
||||
}
|
||||
|
||||
@@ -78,6 +84,7 @@ public class OpenAITextGenerationService implements TextGenerationService {
|
||||
|
||||
protected Mono<List<TextContent>> internalCompleteTextAsync(
|
||||
String text,
|
||||
@Nullable
|
||||
PromptExecutionSettings requestSettings) {
|
||||
|
||||
CompletionsOptions completionsOptions = getCompletionsOptions(text, requestSettings);
|
||||
@@ -118,7 +125,9 @@ public class OpenAITextGenerationService implements TextGenerationService {
|
||||
}
|
||||
|
||||
private CompletionsOptions getCompletionsOptions(
|
||||
String text, PromptExecutionSettings requestSettings) {
|
||||
String text,
|
||||
@Nullable
|
||||
PromptExecutionSettings requestSettings) {
|
||||
if (requestSettings == null) {
|
||||
return new CompletionsOptions(Collections.singletonList(text))
|
||||
.setMaxTokens(PromptExecutionSettings.DEFAULT_MAX_TOKENS);
|
||||
@@ -127,8 +136,10 @@ public class OpenAITextGenerationService implements TextGenerationService {
|
||||
throw new AIException(AIException.ErrorCodes.INVALID_REQUEST, "Max tokens must be >0");
|
||||
}
|
||||
if (requestSettings.getResultsPerPrompt() < 1
|
||||
|| requestSettings.getResultsPerPrompt() > MAX_RESULTS_PER_PROMPT) {
|
||||
throw new AIException(AIException.ErrorCodes.INVALID_REQUEST, String.format("Results per prompt must be in range between 1 and %d, inclusive.", MAX_RESULTS_PER_PROMPT));
|
||||
|| requestSettings.getResultsPerPrompt() > MAX_RESULTS_PER_PROMPT) {
|
||||
throw new AIException(AIException.ErrorCodes.INVALID_REQUEST,
|
||||
String.format("Results per prompt must be in range between 1 and %d, inclusive.",
|
||||
MAX_RESULTS_PER_PROMPT));
|
||||
}
|
||||
|
||||
CompletionsOptions options =
|
||||
@@ -143,12 +154,6 @@ public class OpenAITextGenerationService implements TextGenerationService {
|
||||
.setUser(requestSettings.getUser())
|
||||
.setBestOf(requestSettings.getBestOf())
|
||||
.setLogitBias(new HashMap<>());
|
||||
/*
|
||||
if (requestSettings instanceof ChatRequestSettings) {
|
||||
options = options.setStop(requestSettings.getStopSequences());
|
||||
}
|
||||
|
||||
*/
|
||||
return options;
|
||||
}
|
||||
|
||||
@@ -157,7 +162,18 @@ public class OpenAITextGenerationService implements TextGenerationService {
|
||||
*/
|
||||
public static class Builder extends TextGenerationService.Builder {
|
||||
|
||||
@Override
|
||||
public TextGenerationService build() {
|
||||
|
||||
if (this.client == null) {
|
||||
throw new AIException(AIException.ErrorCodes.INVALID_REQUEST,
|
||||
"OpenAI client must be provided");
|
||||
}
|
||||
if (this.modelId == null) {
|
||||
throw new AIException(AIException.ErrorCodes.INVALID_REQUEST,
|
||||
"OpenAI model id must be provided");
|
||||
}
|
||||
|
||||
return new OpenAITextGenerationService(
|
||||
this.client,
|
||||
this.modelId,
|
||||
|
||||
+2
-2
@@ -6,7 +6,7 @@ import com.microsoft.semantickernel.orchestration.FunctionResult;
|
||||
import com.microsoft.semantickernel.orchestration.contextvariables.KernelArguments;
|
||||
import com.microsoft.semantickernel.plugin.KernelPlugin;
|
||||
import com.microsoft.semantickernel.plugin.KernelPluginFactory;
|
||||
import com.microsoft.semantickernel.samples.syntaxexamples.Example03_Arguments.StaticTextSkill;
|
||||
import com.microsoft.semantickernel.samples.syntaxexamples.Example03_Arguments.StaticTextPlugin;
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
@@ -22,7 +22,7 @@ public class Example03_ArgumentsTest {
|
||||
|
||||
// Load native skill
|
||||
KernelPlugin functionCollection =
|
||||
KernelPluginFactory.createFromObject(new StaticTextSkill(), "text");
|
||||
KernelPluginFactory.createFromObject(new StaticTextPlugin(), "text");
|
||||
|
||||
KernelArguments arguments = KernelArguments.builder()
|
||||
.withInput("Today is: ")
|
||||
|
||||
+2
-1
@@ -29,7 +29,7 @@ public class Example05_InlineFunctionDefinitionTest {
|
||||
.endpoint("http://localhost:" + wmRuntimeInfo.getHttpPort())
|
||||
.buildAsyncClient();
|
||||
|
||||
TextGenerationService textGenerationService = OpenAITextGenerationService.builder()
|
||||
TextGenerationService textGenerationService = TextGenerationService.builder()
|
||||
.withOpenAIAsyncClient(client)
|
||||
.withModelId("text-davinci-003")
|
||||
.build();
|
||||
@@ -55,6 +55,7 @@ public class Example05_InlineFunctionDefinitionTest {
|
||||
""".stripIndent();
|
||||
|
||||
var excuseFunction = new KernelFunctionFromPrompt.Builder()
|
||||
.withName("Excuse")
|
||||
.withTemplate(promptTemplate)
|
||||
.withDefaultExecutionSettings(
|
||||
new PromptExecutionSettings.Builder()
|
||||
|
||||
@@ -126,6 +126,10 @@
|
||||
<target>${maven.compiler.release}</target>
|
||||
<encoding>${project.build.sourceEncoding}</encoding>
|
||||
<showWarnings>true</showWarnings>
|
||||
<compilerArgs>
|
||||
<arg></arg>
|
||||
<compilerArg></compilerArg>
|
||||
</compilerArgs>
|
||||
</configuration>
|
||||
</plugin>
|
||||
|
||||
|
||||
@@ -75,6 +75,30 @@
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<profiles>
|
||||
<profile>
|
||||
<id>bug-check</id>
|
||||
|
||||
<activation>
|
||||
<activeByDefault>false</activeByDefault>
|
||||
</activation>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<!-- Ignore linting on samples -->
|
||||
<plugin>
|
||||
<groupId>com.github.spotbugs</groupId>
|
||||
<artifactId>spotbugs-maven-plugin</artifactId>
|
||||
<version>${maven.spotbugs-plugin.version}</version>
|
||||
<configuration>
|
||||
<skip>true</skip>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</profile>
|
||||
</profiles>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
|
||||
+13
-9
@@ -69,7 +69,8 @@ public class ConversationSummaryPlugin {
|
||||
/// <param name="kernel">The <see cref="Kernel"/> containing services, plugins, and other state for use throughout the operation.</param>
|
||||
@DefineKernelFunction(
|
||||
description = "Given a long conversation transcript, summarize the conversation.",
|
||||
name = "SummarizeConversation"
|
||||
name = "SummarizeConversation",
|
||||
returnType = "java.lang.String"
|
||||
)
|
||||
public Mono<String> SummarizeConversationAsync(
|
||||
@KernelFunctionParameter(
|
||||
@@ -88,7 +89,8 @@ public class ConversationSummaryPlugin {
|
||||
/// <param name="kernel">The <see cref="Kernel"/> containing services, plugins, and other state for use throughout the operation.</param>
|
||||
@DefineKernelFunction(
|
||||
description = "Given a long conversation transcript, identify action items.",
|
||||
name = "GetConversationActionItems"
|
||||
name = "GetConversationActionItems",
|
||||
returnType = "java.lang.String"
|
||||
)
|
||||
public Mono<String> GetConversationActionItemsAsync(
|
||||
@KernelFunctionParameter(
|
||||
@@ -107,7 +109,8 @@ public class ConversationSummaryPlugin {
|
||||
/// <param name="kernel">The <see cref="Kernel"/> containing services, plugins, and other state for use throughout the operation.</param>
|
||||
@DefineKernelFunction(
|
||||
description = "Given a long conversation transcript, identify topics worth remembering.",
|
||||
name = "GetConversationTopics"
|
||||
name = "GetConversationTopics",
|
||||
returnType = "java.lang.String"
|
||||
)
|
||||
|
||||
public Mono<String> GetConversationTopicsAsync(
|
||||
@@ -127,14 +130,15 @@ public class ConversationSummaryPlugin {
|
||||
return Flux.fromIterable(paragraphs)
|
||||
.concatMap(paragraph -> {
|
||||
// The first parameter is the input text.
|
||||
return func.invokeAsync(kernel,
|
||||
new DefaultKernelArguments.Builder()
|
||||
.withInput(paragraph)
|
||||
.build(),
|
||||
ContextVariableTypes.getDefaultVariableTypeForClass(String.class));
|
||||
return func
|
||||
.invokeAsync(kernel,
|
||||
new DefaultKernelArguments.Builder()
|
||||
.withInput(paragraph)
|
||||
.build(),
|
||||
ContextVariableTypes.getDefaultVariableTypeForClass(String.class));
|
||||
})
|
||||
.reduce("", (acc, next) -> {
|
||||
return acc + "\n" + next.getResultVariable();
|
||||
return acc + "\n" + next.getResult();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
+10
-5
@@ -17,10 +17,12 @@ import reactor.core.publisher.Mono;
|
||||
*/
|
||||
public class Example03_Arguments {
|
||||
|
||||
public static class StaticTextSkill {
|
||||
public static class StaticTextPlugin {
|
||||
|
||||
@DefineKernelFunction(
|
||||
description = "Change all string chars to uppercase.",
|
||||
name = "Uppercase")
|
||||
name = "Uppercase",
|
||||
returnType = "java.lang.String")
|
||||
public static Mono<String> uppercase(
|
||||
@KernelFunctionParameter(
|
||||
description = "Text to uppercase",
|
||||
@@ -29,7 +31,10 @@ public class Example03_Arguments {
|
||||
return Mono.just(text.toUpperCase(Locale.ROOT));
|
||||
}
|
||||
|
||||
@DefineKernelFunction(description = "Append the day variable", name = "appendDay")
|
||||
@DefineKernelFunction(
|
||||
description = "Append the day variable",
|
||||
name = "appendDay",
|
||||
returnType = "java.lang.String")
|
||||
public Mono<String> appendDay(
|
||||
@KernelFunctionParameter(description = "Text to append to", name = "input")
|
||||
String input,
|
||||
@@ -43,7 +48,7 @@ public class Example03_Arguments {
|
||||
|
||||
// Load native skill
|
||||
KernelPlugin functionCollection =
|
||||
KernelPluginFactory.createFromObject(new StaticTextSkill(), "text");
|
||||
KernelPluginFactory.createFromObject(new StaticTextPlugin(), "text");
|
||||
|
||||
KernelArguments arguments = KernelArguments.builder()
|
||||
.withInput("Today is: ")
|
||||
@@ -57,6 +62,6 @@ public class Example03_Arguments {
|
||||
String.class)
|
||||
.block();
|
||||
|
||||
System.out.println(resultValue.getResultVariable());
|
||||
System.out.println(resultValue.getResult());
|
||||
}
|
||||
}
|
||||
|
||||
+3
-3
@@ -85,7 +85,7 @@ public class Example05_InlineFunctionDefinition {
|
||||
.build(),
|
||||
String.class)
|
||||
.block();
|
||||
System.out.println(result.getResultVariable());
|
||||
System.out.println(result.getResult());
|
||||
|
||||
result = kernel.invokeAsync(excuseFunction,
|
||||
KernelArguments.builder()
|
||||
@@ -93,7 +93,7 @@ public class Example05_InlineFunctionDefinition {
|
||||
.build(),
|
||||
String.class)
|
||||
.block();
|
||||
System.out.println(result.getResultVariable());
|
||||
System.out.println(result.getResult());
|
||||
|
||||
var fixedFunction = KernelFunctionFactory.createFromPrompt(
|
||||
"Translate this date " + DateTimeFormatter
|
||||
@@ -112,7 +112,7 @@ public class Example05_InlineFunctionDefinition {
|
||||
FunctionResult<String> fixedFunctionResult = kernel
|
||||
.invokeAsync(fixedFunction, null, String.class)
|
||||
.block();
|
||||
System.out.println(fixedFunctionResult.getResultVariable());
|
||||
System.out.println(fixedFunctionResult.getResult());
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -105,6 +105,6 @@ public class Example06_TemplateLanguage {
|
||||
// Show the result
|
||||
System.out.println("--- Prompt Function result");
|
||||
var result = kernel.invokeAsync(kindOfDay, null, String.class).block();
|
||||
System.out.println(result.getResultVariable());
|
||||
System.out.println(result.getResult());
|
||||
}
|
||||
}
|
||||
|
||||
+123
-21
@@ -8,6 +8,7 @@ import com.azure.core.credential.AzureKeyCredential;
|
||||
import com.azure.core.credential.KeyCredential;
|
||||
import com.microsoft.semantickernel.Kernel;
|
||||
import com.microsoft.semantickernel.aiservices.openai.textcompletion.OpenAITextGenerationService;
|
||||
import com.microsoft.semantickernel.orchestration.FunctionResult;
|
||||
import com.microsoft.semantickernel.orchestration.contextvariables.ContextVariable;
|
||||
import com.microsoft.semantickernel.orchestration.contextvariables.ContextVariableTypeConverter;
|
||||
import com.microsoft.semantickernel.orchestration.contextvariables.ContextVariableTypeConverter.NoopConverter;
|
||||
@@ -18,7 +19,8 @@ import com.microsoft.semantickernel.plugin.annotations.DefineKernelFunction;
|
||||
import com.microsoft.semantickernel.plugin.annotations.KernelFunctionParameter;
|
||||
import com.microsoft.semantickernel.textcompletion.TextGenerationService;
|
||||
import java.nio.file.Path;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.Instant;
|
||||
import java.time.OffsetDateTime;
|
||||
import java.time.ZonedDateTime;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.time.temporal.Temporal;
|
||||
@@ -115,10 +117,14 @@ public class Example09_FunctionTypes {
|
||||
.build();
|
||||
|
||||
// Different ways to invoke a function (not limited to these examples)
|
||||
kernel.invokeAsync(plugin.get("NoInputWithVoidResult"), null, String.class).block();
|
||||
kernel.invokeAsync(plugin.get("NoInputTaskWithVoidResult"), null, String.class).block();
|
||||
FunctionResult<?> result = kernel.invokeAsync(plugin.get("NoInputWithVoidResult"), null,
|
||||
String.class).block();
|
||||
System.out.println(result.getResult());
|
||||
result = kernel.invokeAsync(plugin.get("NoInputTaskWithVoidResult"), null, String.class)
|
||||
.block();
|
||||
System.out.println(result.getResult());
|
||||
|
||||
kernel
|
||||
result = kernel
|
||||
.invokeAsync(
|
||||
plugin.get("InputDateTimeWithStringResult"),
|
||||
KernelArguments
|
||||
@@ -132,10 +138,13 @@ public class Example09_FunctionTypes {
|
||||
.build(),
|
||||
String.class
|
||||
).block();
|
||||
System.out.println(result.getResult());
|
||||
|
||||
kernel.invokeAsync(plugin.get("NoInputTaskWithStringResult"), null, String.class).block();
|
||||
result = kernel.invokeAsync(plugin.get("NoInputTaskWithStringResult"), null, String.class)
|
||||
.block();
|
||||
System.out.println(result.getResult());
|
||||
|
||||
kernel.invokeAsync(plugin.get("MultipleInputsWithVoidResult"),
|
||||
result = kernel.invokeAsync(plugin.get("MultipleInputsWithVoidResult"),
|
||||
KernelArguments
|
||||
.builder()
|
||||
.withVariable("x", "x string")
|
||||
@@ -144,8 +153,9 @@ public class Example09_FunctionTypes {
|
||||
.build(),
|
||||
String.class)
|
||||
.block();
|
||||
System.out.println(result.getResult());
|
||||
|
||||
kernel
|
||||
result = kernel
|
||||
.invokeAsync(plugin.get("ComplexInputWithStringResult"),
|
||||
KernelArguments
|
||||
.builder()
|
||||
@@ -164,8 +174,9 @@ public class Example09_FunctionTypes {
|
||||
.build(),
|
||||
String.class)
|
||||
.block();
|
||||
System.out.println(result.getResult());
|
||||
|
||||
kernel
|
||||
result = kernel
|
||||
.invokeAsync(plugin.get("InputStringTaskWithStringResult"),
|
||||
KernelArguments
|
||||
.builder()
|
||||
@@ -173,8 +184,9 @@ public class Example09_FunctionTypes {
|
||||
.build(),
|
||||
String.class)
|
||||
.block();
|
||||
System.out.println(result.getResult());
|
||||
|
||||
kernel
|
||||
result = kernel
|
||||
.invokeAsync(plugin.get("InputStringTaskWithVoidResult"),
|
||||
KernelArguments
|
||||
.builder()
|
||||
@@ -182,24 +194,80 @@ public class Example09_FunctionTypes {
|
||||
.build(),
|
||||
Void.class)
|
||||
.block();
|
||||
System.out.println(result.getResult());
|
||||
|
||||
kernel
|
||||
result = kernel
|
||||
.invokeAsync(plugin.get("noInputComplexReturnTypeAsync"),
|
||||
null,
|
||||
Temporal.class)
|
||||
.block();
|
||||
System.out.println(result.getResult());
|
||||
|
||||
kernel
|
||||
result = kernel
|
||||
.invokeAsync(plugin.get("noInputComplexReturnType"),
|
||||
null,
|
||||
Temporal.class)
|
||||
.block();
|
||||
System.out.println(result.getResult());
|
||||
|
||||
kernel
|
||||
// Possibilities for return type combinations:
|
||||
// | Method return type | Declared Function Return Type | Invocation Return type |
|
||||
// |--------------------|-------------------------------|------------------------|
|
||||
// | T | T | T |
|
||||
// | T | T | V converted from T |
|
||||
|
||||
// | T | U extends T | T |
|
||||
// | T | U extends T | U |
|
||||
// | T | U extends T | V converted from U |
|
||||
|
||||
result = kernel
|
||||
.invokeAsync(plugin.get("conversionScenarioA"),
|
||||
null,
|
||||
OffsetDateTime.class)
|
||||
.block();
|
||||
System.out.println(result.getResult());
|
||||
|
||||
result = kernel
|
||||
.invokeAsync(plugin.get("conversionScenarioA"),
|
||||
null,
|
||||
Instant.class)
|
||||
.block();
|
||||
System.out.println(result.getResult());
|
||||
|
||||
result = kernel
|
||||
.invokeAsync(plugin.get("conversionScenarioB"),
|
||||
null,
|
||||
OffsetDateTime.class)
|
||||
.block();
|
||||
System.out.println(result.getResult());
|
||||
|
||||
result = kernel
|
||||
.invokeAsync(plugin.get("conversionScenarioB"),
|
||||
null,
|
||||
Temporal.class)
|
||||
.block();
|
||||
System.out.println(result.getResult());
|
||||
|
||||
result = kernel
|
||||
.invokeAsync(plugin.get("conversionScenarioB"),
|
||||
null,
|
||||
Instant.class)
|
||||
.block();
|
||||
System.out.println(result.getResult());
|
||||
|
||||
result = kernel
|
||||
.invokeAsync(plugin.get("noInputComplexReturnType"),
|
||||
null,
|
||||
String.class)
|
||||
.block();
|
||||
System.out.println(result.getResult());
|
||||
|
||||
result = kernel
|
||||
.invokeAsync(plugin.get("withDefaultValue"),
|
||||
null,
|
||||
String.class)
|
||||
.block();
|
||||
System.out.println(result.getResult());
|
||||
|
||||
/*
|
||||
TODO: support FunctionResult
|
||||
@@ -254,7 +322,10 @@ public class Example09_FunctionTypes {
|
||||
/// <summary>
|
||||
/// Example of using a void task function with no input
|
||||
/// </summary>
|
||||
@DefineKernelFunction(name = "NoInputTaskWithVoidResult")
|
||||
@DefineKernelFunction(
|
||||
name = "NoInputTaskWithVoidResult",
|
||||
returnType = "java.lang.Void"
|
||||
)
|
||||
public Mono<Void> NoInputTaskWithVoidResult() {
|
||||
return Mono.fromRunnable(
|
||||
() -> System.out.println("Running this.NoInputTaskWithVoidResult) -> No input"));
|
||||
@@ -263,7 +334,9 @@ public class Example09_FunctionTypes {
|
||||
/// <summary>
|
||||
/// Example of using a function with a DateTime input and a string result
|
||||
/// </summary>
|
||||
@DefineKernelFunction(name = "InputDateTimeWithStringResult")
|
||||
@DefineKernelFunction(
|
||||
name = "InputDateTimeWithStringResult",
|
||||
returnType = "java.lang.String")
|
||||
public String InputDateTimeWithStringResult(
|
||||
@KernelFunctionParameter(
|
||||
name = "currentDate",
|
||||
@@ -281,7 +354,9 @@ public class Example09_FunctionTypes {
|
||||
/// <summary>
|
||||
/// Example of using a Task function with no input and a string result
|
||||
/// </summary>
|
||||
@DefineKernelFunction(name = "NoInputTaskWithStringResult")
|
||||
@DefineKernelFunction(
|
||||
name = "NoInputTaskWithStringResult",
|
||||
returnType = "java.lang.String")
|
||||
public Mono<String> NoInputTaskWithStringResult() {
|
||||
return Mono.fromCallable(() -> {
|
||||
var result = "string result";
|
||||
@@ -341,7 +416,10 @@ public class Example09_FunctionTypes {
|
||||
/// <summary>
|
||||
/// Example using an async task function echoing the input
|
||||
/// </summary>
|
||||
@DefineKernelFunction(name = "InputStringTaskWithStringResult")
|
||||
@DefineKernelFunction(
|
||||
name = "InputStringTaskWithStringResult",
|
||||
returnType = "java.lang.String"
|
||||
)
|
||||
public Mono<String> InputStringTaskWithStringResult(
|
||||
|
||||
@KernelFunctionParameter(
|
||||
@@ -360,7 +438,10 @@ public class Example09_FunctionTypes {
|
||||
/// <summary>
|
||||
/// Example using an async void task with string input
|
||||
/// </summary>
|
||||
@DefineKernelFunction(name = "InputStringTaskWithVoidResult")
|
||||
@DefineKernelFunction(
|
||||
name = "InputStringTaskWithVoidResult",
|
||||
returnType = "java.lang.Void"
|
||||
)
|
||||
public Mono<Void> InputStringTaskWithVoidResult(
|
||||
@KernelFunctionParameter(
|
||||
name = "x"
|
||||
@@ -377,9 +458,9 @@ public class Example09_FunctionTypes {
|
||||
name = "noInputComplexReturnTypeAsync",
|
||||
returnType = "java.time.temporal.Temporal"
|
||||
)
|
||||
public Mono<LocalDateTime> noInputComplexReturnTypeAsync() {
|
||||
public Mono<OffsetDateTime> noInputComplexReturnTypeAsync() {
|
||||
return Mono.just(
|
||||
LocalDateTime.now()
|
||||
OffsetDateTime.now()
|
||||
);
|
||||
}
|
||||
|
||||
@@ -387,8 +468,8 @@ public class Example09_FunctionTypes {
|
||||
name = "noInputComplexReturnType",
|
||||
returnType = "java.time.temporal.Temporal"
|
||||
)
|
||||
public LocalDateTime noInputComplexReturnType() {
|
||||
return LocalDateTime.now();
|
||||
public OffsetDateTime noInputComplexReturnType() {
|
||||
return OffsetDateTime.now();
|
||||
}
|
||||
|
||||
@DefineKernelFunction(
|
||||
@@ -406,6 +487,27 @@ public class Example09_FunctionTypes {
|
||||
return Integer.toString(x);
|
||||
}
|
||||
|
||||
// Possibilities for return type combinations:
|
||||
// | Method return type | Declared Function Return Type |
|
||||
// |--------------------|-------------------------------|
|
||||
// | T | T |
|
||||
// | T | U extends T |
|
||||
@DefineKernelFunction(
|
||||
name = "conversionScenarioA",
|
||||
returnType = "java.time.OffsetDateTime"
|
||||
)
|
||||
public OffsetDateTime conversionScenarioA() {
|
||||
return OffsetDateTime.now();
|
||||
}
|
||||
|
||||
@DefineKernelFunction(
|
||||
name = "conversionScenarioB",
|
||||
returnType = "java.time.temporal.Temporal"
|
||||
)
|
||||
public OffsetDateTime conversionScenarioB() {
|
||||
return OffsetDateTime.now();
|
||||
}
|
||||
|
||||
/*
|
||||
/// <summary>
|
||||
/// Example using a function to return the result of another inner function
|
||||
|
||||
+5
-5
@@ -170,7 +170,7 @@ public class Example13_ConversationSummaryPlugin {
|
||||
);
|
||||
|
||||
System.out.println("Generated Action Items:");
|
||||
System.out.println(summary.block().getResultVariable());
|
||||
System.out.println(summary.block().getResult());
|
||||
}
|
||||
|
||||
private static void getConversationTopicsAsync() {
|
||||
@@ -191,7 +191,7 @@ public class Example13_ConversationSummaryPlugin {
|
||||
);
|
||||
|
||||
System.out.println("Generated Topics:");
|
||||
System.out.println(summary.block().getResultVariable());
|
||||
System.out.println(summary.block().getResult());
|
||||
}
|
||||
|
||||
|
||||
@@ -203,7 +203,7 @@ public class Example13_ConversationSummaryPlugin {
|
||||
KernelPlugin conversationSummaryPlugin = KernelPluginFactory.createFromObject(
|
||||
new ConversationSummaryPlugin(), null);
|
||||
|
||||
Mono<FunctionResult<String>> summary = kernel
|
||||
FunctionResult<String> summary = kernel
|
||||
.invokeAsync(
|
||||
conversationSummaryPlugin
|
||||
.getFunctions()
|
||||
@@ -212,10 +212,10 @@ public class Example13_ConversationSummaryPlugin {
|
||||
.withInput(ChatTranscript)
|
||||
.build(),
|
||||
String.class
|
||||
);
|
||||
).block();
|
||||
|
||||
System.out.println("Generated Summary (This may take some time):");
|
||||
System.out.println(summary.block().getResultVariable());
|
||||
System.out.println(summary.getResult());
|
||||
}
|
||||
|
||||
private static Kernel initializeKernel() {
|
||||
|
||||
+1
-1
@@ -58,7 +58,7 @@ public class Example27_PromptFunctionsUsingChatGPT {
|
||||
ContextVariableTypes.getDefaultVariableTypeForClass(String.class)
|
||||
)
|
||||
.block();
|
||||
System.out.println(result.getResultVariable());
|
||||
System.out.println(result.getResult());
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -85,7 +85,7 @@ public class Example56_TemplateMethodFunctionsWithMultipleArguments {
|
||||
// Show the result
|
||||
System.out.println("--- Prompt Function result");
|
||||
var result = kernel.invokeAsync(haiku, arguments, String.class).block();
|
||||
System.out.println(result.getResultVariable());
|
||||
System.out.println(result.getResult());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+2
-2
@@ -65,7 +65,7 @@ public class Example58_ConfigureExecutionSettings {
|
||||
String.class
|
||||
).block();
|
||||
|
||||
System.out.println(result.getResultVariable());
|
||||
System.out.println(result.getResult());
|
||||
|
||||
// Option 2:
|
||||
// Load prompt template configuration including the execution settings from a JSON payload
|
||||
@@ -92,6 +92,6 @@ public class Example58_ConfigureExecutionSettings {
|
||||
var func = KernelFunctionFactory.createFromPrompt(promptConfig, null);
|
||||
|
||||
result = kernel.invokeAsync(func, null, String.class).block();
|
||||
System.out.println(result.getResultVariable());
|
||||
System.out.println(result.getResult());
|
||||
}
|
||||
}
|
||||
|
||||
+3
-3
@@ -85,7 +85,7 @@ public class Example61_MultipleLLMs {
|
||||
.build();
|
||||
|
||||
var result = kernel.invokeAsync(func, arguments, String.class).block();
|
||||
System.out.println(result.getResultVariable());
|
||||
System.out.println(result.getResult());
|
||||
}
|
||||
|
||||
|
||||
@@ -108,7 +108,7 @@ public class Example61_MultipleLLMs {
|
||||
String.class
|
||||
).block();
|
||||
|
||||
System.out.println(result.getResultVariable());
|
||||
System.out.println(result.getResult());
|
||||
}
|
||||
|
||||
|
||||
@@ -141,7 +141,7 @@ public class Example61_MultipleLLMs {
|
||||
String.class)
|
||||
.block();
|
||||
|
||||
System.out.println(result.getResultVariable());
|
||||
System.out.println(result.getResult());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+1
-1
@@ -78,7 +78,7 @@ public class Example62_CustomAIServiceSelector {
|
||||
.build();
|
||||
|
||||
var result = kernel.invokeAsync(func, arguments, String.class).block();
|
||||
System.out.println(result.getResultVariable());
|
||||
System.out.println(result.getResult());
|
||||
}
|
||||
|
||||
// A dumb AIServiceSelector that just returns the first service and execution settings it finds
|
||||
|
||||
+2
-2
@@ -55,14 +55,14 @@ public class Example63_ChatCompletionPrompts {
|
||||
System.out.println("Chat Prompt:");
|
||||
System.out.println(chatPrompt);
|
||||
System.out.println("Chat Prompt Result:");
|
||||
System.out.println(chatPromptResult.getResultVariable());
|
||||
System.out.println(chatPromptResult.getResult());
|
||||
|
||||
CountDownLatch cdl = new CountDownLatch(1);
|
||||
System.out.println("Chat Prompt Result:");
|
||||
kernel
|
||||
.invokeAsync(chatSemanticFunction, null, String.class)
|
||||
.doFinally(x -> cdl.countDown())
|
||||
.subscribe(result -> System.out.println(result.getResultVariable()));
|
||||
.subscribe(result -> System.out.println(result.getResult()));
|
||||
|
||||
cdl.await();
|
||||
}
|
||||
|
||||
+1
-1
@@ -82,6 +82,6 @@ public class Example64_MultiplePromptTemplates {
|
||||
.build();
|
||||
|
||||
var result = kernel.invokeAsync(function, arguments, String.class).block();
|
||||
System.out.println(result.getResultVariable());
|
||||
System.out.println(result.getResult());
|
||||
}
|
||||
}
|
||||
|
||||
+2
-2
@@ -25,8 +25,8 @@ public class Example69_MutableKernelPlugin {
|
||||
System.out.println("======== Example69_MutableKernelPlugin ========");
|
||||
|
||||
KernelPlugin plugin = new KernelPlugin("Plugin", "Mutable plugin", null);
|
||||
plugin.getFunctions().put("dateFunction", KernelFunctionFactory.createFromMethod(
|
||||
Time.class.getMethod("date"), new Time(), "date", null, null, null));
|
||||
plugin.addFunction(KernelFunctionFactory.createFromMethod(
|
||||
Time.class.getMethod("date"), new Time(), "dateFunction", null, null, null));
|
||||
|
||||
Kernel kernel = Kernel.builder().build();
|
||||
kernel.getPlugins().add(plugin);
|
||||
|
||||
+67
@@ -0,0 +1,67 @@
|
||||
package com.microsoft.semantickernel.samples.syntaxexamples;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Scanner;
|
||||
|
||||
/**
|
||||
* Run all the syntax examples.
|
||||
* <p>
|
||||
* Refer to the <a href=
|
||||
* "https://github.com/microsoft/semantic-kernel/blob/experimental-java/java/samples/sample-code/README.md">
|
||||
* README</a> for configuring your environment to run the examples.
|
||||
*/
|
||||
public class RunAll {
|
||||
|
||||
public interface MainMethod {
|
||||
|
||||
void run(String[] args) throws Exception;
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
List<MainMethod> mains = Arrays.asList(
|
||||
Example01_NativeFunctions::main,
|
||||
Example03_Arguments::main,
|
||||
Example05_InlineFunctionDefinition::main,
|
||||
Example06_TemplateLanguage::main,
|
||||
Example08_RetryHandler::main,
|
||||
Example09_FunctionTypes::main,
|
||||
Example10_DescribeAllPluginsAndFunctions::main,
|
||||
//Example11_WebSearchQueries::main,
|
||||
Example13_ConversationSummaryPlugin::main,
|
||||
Example17_ChatGPT::main,
|
||||
//Example26_AADAuth::main,
|
||||
|
||||
Example27_PromptFunctionsUsingChatGPT::main,
|
||||
Example30_ChatWithPrompts::main,
|
||||
Example33_Chat::main,
|
||||
Example41_HttpClientUsage::main,
|
||||
Example43_GetModelResult::main,
|
||||
Example44_MultiChatCompletion::main,
|
||||
Example49_LogitBias::main,
|
||||
Example55_TextChunker::main,
|
||||
Example56_TemplateMethodFunctionsWithMultipleArguments::main,
|
||||
Example57_KernelHooks::main,
|
||||
Example58_ConfigureExecutionSettings::main,
|
||||
Example61_MultipleLLMs::main,
|
||||
Example62_CustomAIServiceSelector::main,
|
||||
Example63_ChatCompletionPrompts::main,
|
||||
Example64_MultiplePromptTemplates::main,
|
||||
Example69_MutableKernelPlugin::main
|
||||
);
|
||||
|
||||
Scanner scanner = new Scanner(System.in);
|
||||
mains.forEach(mainMethod -> {
|
||||
try {
|
||||
|
||||
System.out.println("========================================");
|
||||
mainMethod.run(args);
|
||||
|
||||
System.out.println("Press any key to continue...");
|
||||
scanner.nextLine();
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -2,62 +2,75 @@
|
||||
package com.microsoft.semantickernel;
|
||||
|
||||
import com.microsoft.semantickernel.orchestration.contextvariables.ContextVariable;
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.Map;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
/**
|
||||
* Marker interface for AI services
|
||||
* Marker interface for AI services
|
||||
*/
|
||||
public interface AIService {
|
||||
|
||||
Map<String, ContextVariable<?>> getAttributes();
|
||||
|
||||
|
||||
/**
|
||||
* Key used to store the model identifier in the {@code attributes} map.
|
||||
*/
|
||||
static final String MODEL_ID_KEY = "ModelId";
|
||||
*/
|
||||
String MODEL_ID_KEY = "ModelId";
|
||||
|
||||
/**
|
||||
* Key used to store the endpoint in the {@code attributes} map.
|
||||
*/
|
||||
static final String ENDPOINT_KEY = "Endpoint";
|
||||
*/
|
||||
String ENDPOINT_KEY = "Endpoint";
|
||||
|
||||
/**
|
||||
* Key used to store the API version in the {@code attributes} map.
|
||||
*/
|
||||
|
||||
static final String API_VERSION_KEY = "ApiVersion";
|
||||
|
||||
/**
|
||||
* Gets the model identifier from the {@code attributes} map.
|
||||
* @return The model identifier if it was specified in the service's attributes; otherwise, null.
|
||||
*/
|
||||
default String getModelId() { return getAttribute(this, MODEL_ID_KEY); }
|
||||
|
||||
String API_VERSION_KEY = "ApiVersion";
|
||||
|
||||
/**
|
||||
* Gets the model identifier from the {@code attributes} map.
|
||||
*
|
||||
* @return The model identifier if it was specified in the service's attributes; otherwise,
|
||||
* null.
|
||||
*/
|
||||
@Nullable
|
||||
default String getModelId() {
|
||||
return getAttribute(this, MODEL_ID_KEY);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the endpoint from the {@code attributes} map.
|
||||
*
|
||||
* @return The endpoint if it was specified in the service's attributes; otherwise, null.
|
||||
*/
|
||||
default String getEndpoint() { return getAttribute(this, ENDPOINT_KEY); }
|
||||
@Nullable
|
||||
default String getEndpoint() {
|
||||
return getAttribute(this, ENDPOINT_KEY);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the API version from the {@code attributes} map.
|
||||
*
|
||||
* @return The API version if it was specified in the service's attributes; otherwise, null.
|
||||
*/
|
||||
default String getApiVersion() { return getAttribute(this, API_VERSION_KEY); }
|
||||
@Nullable
|
||||
default String getApiVersion() {
|
||||
return getAttribute(this, API_VERSION_KEY);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
String getServiceId();
|
||||
|
||||
/// <summary>
|
||||
/// Gets the specified attribute.
|
||||
/// </summary>
|
||||
@Nullable
|
||||
static String getAttribute(AIService service, String key)
|
||||
{
|
||||
static String getAttribute(AIService service, String key) {
|
||||
if (service.getAttributes() != null) {
|
||||
ContextVariable<?> contextVariable = service.getAttributes().get(key);
|
||||
if (contextVariable != null) {
|
||||
return contextVariable.getValue().toString();
|
||||
return contextVariable.getValue(String.class);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
|
||||
@@ -5,11 +5,14 @@ import com.microsoft.semantickernel.builders.Buildable;
|
||||
import com.microsoft.semantickernel.builders.SemanticKernelBuilder;
|
||||
import com.microsoft.semantickernel.hooks.KernelHooks;
|
||||
import com.microsoft.semantickernel.orchestration.FunctionResult;
|
||||
import com.microsoft.semantickernel.orchestration.FunctionResultMetadata;
|
||||
import com.microsoft.semantickernel.orchestration.KernelFunction;
|
||||
import com.microsoft.semantickernel.orchestration.contextvariables.ContextVariable;
|
||||
import com.microsoft.semantickernel.orchestration.contextvariables.ContextVariableType;
|
||||
import com.microsoft.semantickernel.orchestration.contextvariables.ContextVariableTypeConverter.NoopConverter;
|
||||
import com.microsoft.semantickernel.orchestration.contextvariables.ContextVariableTypes;
|
||||
import com.microsoft.semantickernel.orchestration.contextvariables.KernelArguments;
|
||||
import com.microsoft.semantickernel.orchestration.contextvariables.NullContextVariable;
|
||||
import com.microsoft.semantickernel.plugin.KernelPlugin;
|
||||
import com.microsoft.semantickernel.plugin.KernelPluginCollection;
|
||||
import com.microsoft.semantickernel.semanticfunctions.PromptTemplate;
|
||||
@@ -98,20 +101,41 @@ public class Kernel implements Buildable {
|
||||
|
||||
kernelHooks = mergeInGlobalHooks(kernelHooks);
|
||||
|
||||
ContextVariableType<T> contextVariable;
|
||||
ContextVariableType<?> contextVariable = getContextVariableType(function, resultType);
|
||||
|
||||
return function
|
||||
.invokeAsync(this, arguments, kernelHooks, contextVariable)
|
||||
.map(it -> {
|
||||
return new FunctionResult<>(
|
||||
ContextVariable.convert(it.getResult(), resultType),
|
||||
it.getMetadata()
|
||||
);
|
||||
})
|
||||
.defaultIfEmpty(
|
||||
new FunctionResult<>(
|
||||
new NullContextVariable<>(resultType),
|
||||
new FunctionResultMetadata()));
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static <T> ContextVariableType<?> getContextVariableType(
|
||||
KernelFunction function,
|
||||
Class<T> resultType) {
|
||||
|
||||
Class functionReturnType = function.getMetadata().getReturnParameter().getParameterType();
|
||||
|
||||
try {
|
||||
contextVariable = ContextVariableTypes.getDefaultVariableTypeForClass(resultType);
|
||||
return ContextVariableTypes.getDefaultVariableTypeForClass(
|
||||
functionReturnType);
|
||||
} catch (Exception e) {
|
||||
if (resultType.isAssignableFrom(
|
||||
function.getMetadata().getReturnParameter().getParameterType())) {
|
||||
contextVariable = new ContextVariableType<>(new NoopConverter<>(resultType),
|
||||
resultType);
|
||||
if (functionReturnType.isAssignableFrom(resultType)) {
|
||||
return new ContextVariableType<>(
|
||||
new NoopConverter<>(functionReturnType),
|
||||
functionReturnType);
|
||||
} else {
|
||||
throw e;
|
||||
return null;
|
||||
}
|
||||
}
|
||||
return function.invokeAsync(this, arguments, kernelHooks, contextVariable);
|
||||
}
|
||||
|
||||
public <T> Mono<FunctionResult<T>> invokeAsync(
|
||||
|
||||
+7
-9
@@ -1,9 +1,6 @@
|
||||
package com.microsoft.semantickernel;
|
||||
|
||||
import com.microsoft.semantickernel.orchestration.FunctionResultMetadata;
|
||||
import com.microsoft.semantickernel.orchestration.contextvariables.ContextVariable;
|
||||
import java.util.Map;
|
||||
import javax.annotation.CheckForNull;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
public abstract class KernelContent<T extends KernelContent<T>> {
|
||||
@@ -11,7 +8,7 @@ public abstract class KernelContent<T extends KernelContent<T>> {
|
||||
@Nullable
|
||||
private Object innerContent;
|
||||
@Nullable
|
||||
private String modelId;
|
||||
private final String modelId;
|
||||
@Nullable
|
||||
private FunctionResultMetadata metadata;
|
||||
|
||||
@@ -30,25 +27,26 @@ public abstract class KernelContent<T extends KernelContent<T>> {
|
||||
}
|
||||
}
|
||||
|
||||
T setInnerContent(@Nullable Object innerContent) {
|
||||
KernelContent<T> setInnerContent(@Nullable Object innerContent) {
|
||||
this.innerContent = innerContent;
|
||||
return (T) this;
|
||||
return this;
|
||||
}
|
||||
|
||||
@CheckForNull
|
||||
@Nullable
|
||||
public Object getInnerContent() {
|
||||
return innerContent;
|
||||
}
|
||||
|
||||
@CheckForNull
|
||||
@Nullable
|
||||
public String getModelId() {
|
||||
return modelId;
|
||||
}
|
||||
|
||||
@CheckForNull
|
||||
@Nullable
|
||||
public FunctionResultMetadata getMetadata() {
|
||||
return metadata;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public abstract String getContent();
|
||||
}
|
||||
|
||||
+6
-10
@@ -24,7 +24,7 @@ public abstract class StreamingKernelContent<T> {
|
||||
/// The model ID used to generate the content.
|
||||
/// </summary>
|
||||
@Nullable
|
||||
private final String modelId;
|
||||
private String modelId;
|
||||
|
||||
/// <summary>
|
||||
/// The metadata associated with the content.
|
||||
@@ -32,18 +32,14 @@ public abstract class StreamingKernelContent<T> {
|
||||
@Nullable
|
||||
private final Map<String, ContextVariable<?>> metadata;
|
||||
|
||||
protected StreamingKernelContent(@Nullable T innerContent, int choiceIndex,
|
||||
@Nullable String modelId, @Nullable Map<String, ContextVariable<?>> metadata) {
|
||||
protected StreamingKernelContent(
|
||||
@Nullable T innerContent,
|
||||
int choiceIndex,
|
||||
@Nullable String modelId,
|
||||
@Nullable Map<String, ContextVariable<?>> metadata) {
|
||||
this.innerContent = innerContent;
|
||||
this.choiceIndex = choiceIndex;
|
||||
this.modelId = modelId;
|
||||
this.metadata = metadata;
|
||||
}
|
||||
|
||||
|
||||
public abstract byte[] toByteArray();
|
||||
|
||||
public Object getInnerContent() {
|
||||
return innerContent;
|
||||
}
|
||||
}
|
||||
|
||||
+1
-10
@@ -16,18 +16,9 @@ public enum AuthorRole {
|
||||
return role;
|
||||
}
|
||||
|
||||
public AuthorRole fromString(String role) {
|
||||
for (AuthorRole authorRole : AuthorRole.values()) {
|
||||
if (authorRole.role.equalsIgnoreCase(role)) {
|
||||
return authorRole;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private final String role;
|
||||
|
||||
private AuthorRole(String role) {
|
||||
AuthorRole(String role) {
|
||||
this.role = role;
|
||||
}
|
||||
}
|
||||
|
||||
+8
@@ -11,6 +11,7 @@ import com.microsoft.semantickernel.builders.ServiceLoadUtil;
|
||||
import com.microsoft.semantickernel.hooks.KernelHooks;
|
||||
import com.microsoft.semantickernel.orchestration.PromptExecutionSettings;
|
||||
import java.util.List;
|
||||
import javax.annotation.Nullable;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
public interface ChatCompletionService extends Buildable, TextAIService {
|
||||
@@ -36,8 +37,10 @@ public interface ChatCompletionService extends Buildable, TextAIService {
|
||||
|
||||
Mono<List<ChatMessageContent>> getChatMessageContentsAsync(
|
||||
String prompt,
|
||||
@Nullable
|
||||
PromptExecutionSettings promptExecutionSettings,
|
||||
Kernel kernel,
|
||||
@Nullable
|
||||
KernelHooks kernelHooks
|
||||
);
|
||||
|
||||
@@ -49,8 +52,13 @@ public interface ChatCompletionService extends Buildable, TextAIService {
|
||||
|
||||
abstract class Builder implements SemanticKernelBuilder<ChatCompletionService> {
|
||||
|
||||
@Nullable
|
||||
protected OpenAIAsyncClient client;
|
||||
|
||||
@Nullable
|
||||
protected String modelId;
|
||||
|
||||
@Nullable
|
||||
protected String serviceId;
|
||||
|
||||
|
||||
|
||||
+6
-2
@@ -13,6 +13,7 @@ import javax.annotation.Nullable;
|
||||
public class ChatMessageContent extends KernelContent<ChatMessageContent> {
|
||||
|
||||
private AuthorRole authorRole;
|
||||
@Nullable
|
||||
private String content;
|
||||
@Nullable
|
||||
private List<KernelContent> items;
|
||||
@@ -52,13 +53,14 @@ public class ChatMessageContent extends KernelContent<ChatMessageContent> {
|
||||
|
||||
public ChatMessageContent(
|
||||
AuthorRole authorRole,
|
||||
List<KernelContent> items,
|
||||
List<KernelContent<?>> items,
|
||||
String modelId,
|
||||
String innerContent,
|
||||
Charset encoding,
|
||||
FunctionResultMetadata metadata
|
||||
) {
|
||||
super(innerContent, modelId, metadata);
|
||||
this.content = null;
|
||||
this.authorRole = authorRole;
|
||||
this.encoding = encoding != null ? encoding : StandardCharsets.UTF_8;
|
||||
this.items = new ArrayList<>(items);
|
||||
@@ -72,11 +74,13 @@ public class ChatMessageContent extends KernelContent<ChatMessageContent> {
|
||||
this.authorRole = authorRole;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public String getContent() {
|
||||
return content;
|
||||
}
|
||||
|
||||
public void setContent(String content) {
|
||||
public void setContent(@Nullable String content) {
|
||||
this.content = content;
|
||||
}
|
||||
|
||||
|
||||
+14
-23
@@ -1,33 +1,38 @@
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
package com.microsoft.semantickernel.chatcompletion;
|
||||
|
||||
import com.microsoft.semantickernel.StreamingKernelContent;
|
||||
import com.microsoft.semantickernel.orchestration.contextvariables.ContextVariable;
|
||||
import java.nio.charset.Charset;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Map;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
import com.microsoft.semantickernel.StreamingKernelContent;
|
||||
import com.microsoft.semantickernel.orchestration.contextvariables.ContextVariable;
|
||||
|
||||
public class StreamingChatMessageContent extends StreamingKernelContent {
|
||||
public class StreamingChatMessageContent<T> extends StreamingKernelContent<T> {
|
||||
|
||||
private final String content;
|
||||
private final AuthorRole role;
|
||||
private Charset encoding;
|
||||
private String modelId;
|
||||
|
||||
public StreamingChatMessageContent(
|
||||
AuthorRole role,
|
||||
String content) {
|
||||
this(role, content, null, 0, null, null, null);
|
||||
String content,
|
||||
@Nullable
|
||||
String modelId) {
|
||||
this(role, content, null, 0, modelId, null, null);
|
||||
}
|
||||
|
||||
public StreamingChatMessageContent(
|
||||
AuthorRole role,
|
||||
String content,
|
||||
Object innerContent,
|
||||
@Nullable
|
||||
T innerContent,
|
||||
int choiceIndex,
|
||||
@Nullable
|
||||
String modelId,
|
||||
@Nullable
|
||||
Charset encoding,
|
||||
@Nullable
|
||||
Map<String, ContextVariable<?>> metadata) {
|
||||
super(innerContent, choiceIndex, modelId, metadata);
|
||||
this.role = role;
|
||||
@@ -40,11 +45,6 @@ public class StreamingChatMessageContent extends StreamingKernelContent {
|
||||
return content != null ? content : "";
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] toByteArray() {
|
||||
return toString().getBytes(encoding);
|
||||
}
|
||||
|
||||
public String getContent() {
|
||||
return content;
|
||||
}
|
||||
@@ -57,18 +57,9 @@ public class StreamingChatMessageContent extends StreamingKernelContent {
|
||||
return encoding;
|
||||
}
|
||||
|
||||
public StreamingChatMessageContent setEncoding(Charset encoding) {
|
||||
public StreamingChatMessageContent<T> setEncoding(Charset encoding) {
|
||||
this.encoding = encoding;
|
||||
return this;
|
||||
}
|
||||
|
||||
public StreamingChatMessageContent setModelId(String modelId) {
|
||||
this.modelId = modelId;
|
||||
return this;
|
||||
}
|
||||
|
||||
public String getModelId() {
|
||||
return modelId;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
-42
@@ -1,42 +0,0 @@
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
package com.microsoft.semantickernel.exceptions;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
public class SkillsNotFoundException extends SKException {
|
||||
|
||||
private final ErrorCodes errorCode;
|
||||
|
||||
public SkillsNotFoundException(@Nonnull ErrorCodes errorCodes) {
|
||||
this(errorCodes, null, null);
|
||||
}
|
||||
|
||||
public SkillsNotFoundException(@Nonnull ErrorCodes errorCodes, @Nullable String message) {
|
||||
this(errorCodes, message, null);
|
||||
}
|
||||
|
||||
public SkillsNotFoundException(
|
||||
@Nonnull ErrorCodes errorCodes, @Nullable String message, @Nullable Throwable cause) {
|
||||
super(formatDefaultMessage(errorCodes.getMessage(), message), cause);
|
||||
this.errorCode = errorCodes;
|
||||
}
|
||||
|
||||
public ErrorCodes getErrorCode() {
|
||||
return errorCode;
|
||||
}
|
||||
|
||||
public enum ErrorCodes {
|
||||
SKILLS_NOT_FOUND("Skills not found");
|
||||
|
||||
final String message;
|
||||
|
||||
ErrorCodes(String message) {
|
||||
this.message = message;
|
||||
}
|
||||
|
||||
String getMessage() {
|
||||
return message;
|
||||
}
|
||||
}
|
||||
}
|
||||
+6
-1
@@ -3,14 +3,18 @@ package com.microsoft.semantickernel.hooks;
|
||||
import com.microsoft.semantickernel.orchestration.FunctionResult;
|
||||
import com.microsoft.semantickernel.orchestration.KernelFunction;
|
||||
import com.microsoft.semantickernel.orchestration.contextvariables.KernelArguments;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
public class FunctionInvokedEvent<T> implements KernelHookEvent {
|
||||
|
||||
private final KernelFunction function;
|
||||
@Nullable
|
||||
private final KernelArguments arguments;
|
||||
private final FunctionResult<T> result;
|
||||
|
||||
public FunctionInvokedEvent(KernelFunction function, KernelArguments arguments,
|
||||
public FunctionInvokedEvent(
|
||||
KernelFunction function,
|
||||
@Nullable KernelArguments arguments,
|
||||
FunctionResult<T> result) {
|
||||
this.function = function;
|
||||
this.arguments = arguments;
|
||||
@@ -21,6 +25,7 @@ public class FunctionInvokedEvent<T> implements KernelHookEvent {
|
||||
return function;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public KernelArguments getArguments() {
|
||||
return arguments;
|
||||
}
|
||||
|
||||
+4
-1
@@ -2,13 +2,15 @@ package com.microsoft.semantickernel.hooks;
|
||||
|
||||
import com.microsoft.semantickernel.orchestration.KernelFunction;
|
||||
import com.microsoft.semantickernel.orchestration.contextvariables.KernelArguments;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
public class FunctionInvokingEvent implements KernelHookEvent {
|
||||
|
||||
private final KernelFunction function;
|
||||
@Nullable
|
||||
private final KernelArguments arguments;
|
||||
|
||||
public FunctionInvokingEvent(KernelFunction function, KernelArguments arguments) {
|
||||
public FunctionInvokingEvent(KernelFunction function, @Nullable KernelArguments arguments) {
|
||||
this.function = function;
|
||||
this.arguments = arguments;
|
||||
}
|
||||
@@ -17,6 +19,7 @@ public class FunctionInvokingEvent implements KernelHookEvent {
|
||||
return function;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public KernelArguments getArguments() {
|
||||
return arguments;
|
||||
}
|
||||
|
||||
+5
-8
@@ -1,5 +1,6 @@
|
||||
package com.microsoft.semantickernel.orchestration;
|
||||
|
||||
import com.microsoft.semantickernel.Todo;
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
import javax.annotation.Nullable;
|
||||
@@ -43,23 +44,19 @@ public abstract class DefaultKernelFunction implements KernelFunction {
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toFullyQualifiedName() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public String getDescription() {
|
||||
return null;
|
||||
return metadata.getDescription();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toEmbeddingString() {
|
||||
return null;
|
||||
throw new Todo();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toManualString(boolean includeOutputs) {
|
||||
return null;
|
||||
throw new Todo();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+6
-3
@@ -1,22 +1,25 @@
|
||||
package com.microsoft.semantickernel.orchestration;
|
||||
|
||||
import com.microsoft.semantickernel.orchestration.contextvariables.ContextVariable;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
public class FunctionResult<T> {
|
||||
|
||||
private final ContextVariable<T> result;
|
||||
private final FunctionResultMetadata metadata;
|
||||
|
||||
public FunctionResult(ContextVariable<T> result, FunctionResultMetadata metadata) {
|
||||
public FunctionResult(
|
||||
ContextVariable<T> result,
|
||||
@Nullable FunctionResultMetadata metadata) {
|
||||
this.result = result;
|
||||
this.metadata = metadata;
|
||||
this.metadata = metadata == null ? FunctionResultMetadata.empty() : metadata;
|
||||
}
|
||||
|
||||
public FunctionResult(ContextVariable<T> of) {
|
||||
this(of, FunctionResultMetadata.empty());
|
||||
}
|
||||
|
||||
|
||||
@Nullable
|
||||
public T getResult() {
|
||||
return result.getValue();
|
||||
}
|
||||
|
||||
+23
-3
@@ -4,6 +4,7 @@ import com.azure.ai.openai.models.CompletionsUsage;
|
||||
import com.microsoft.semantickernel.orchestration.contextvariables.CaseInsensitiveMap;
|
||||
import com.microsoft.semantickernel.orchestration.contextvariables.ContextVariable;
|
||||
import java.time.OffsetDateTime;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
public class FunctionResultMetadata {
|
||||
|
||||
@@ -14,6 +15,10 @@ public class FunctionResultMetadata {
|
||||
|
||||
private final CaseInsensitiveMap<ContextVariable<?>> metadata;
|
||||
|
||||
public FunctionResultMetadata() {
|
||||
this.metadata = new CaseInsensitiveMap<>();
|
||||
}
|
||||
|
||||
public FunctionResultMetadata(CaseInsensitiveMap<ContextVariable<?>> metadata) {
|
||||
this.metadata = new CaseInsensitiveMap<>(metadata);
|
||||
}
|
||||
@@ -39,15 +44,30 @@ public class FunctionResultMetadata {
|
||||
return new CaseInsensitiveMap<>(metadata);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public String getId() {
|
||||
return metadata.get(ID).getValue(String.class);
|
||||
ContextVariable<?> id = metadata.get(ID);
|
||||
if (id == null) {
|
||||
return null;
|
||||
}
|
||||
return id.getValue(String.class);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public CompletionsUsage getUsage() {
|
||||
return metadata.get(USAGE).getValue(CompletionsUsage.class);
|
||||
ContextVariable<?> usage = metadata.get(USAGE);
|
||||
if (usage == null) {
|
||||
return null;
|
||||
}
|
||||
return usage.getValue(CompletionsUsage.class);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public OffsetDateTime getCreatedAt() {
|
||||
return metadata.get(CREATED_AT).getValue(OffsetDateTime.class);
|
||||
ContextVariable<?> createdAt = metadata.get(CREATED_AT);
|
||||
if (createdAt == null) {
|
||||
return null;
|
||||
}
|
||||
return createdAt.getValue(OffsetDateTime.class);
|
||||
}
|
||||
}
|
||||
|
||||
+16
-16
@@ -33,16 +33,11 @@ public interface KernelFunction extends Buildable {
|
||||
*/
|
||||
String getName();
|
||||
|
||||
/**
|
||||
* The function to create a fully qualified name for
|
||||
*
|
||||
* @return A fully qualified name for a function
|
||||
*/
|
||||
String toFullyQualifiedName();
|
||||
|
||||
/**
|
||||
* @return A description of the function
|
||||
*/
|
||||
|
||||
@Nullable
|
||||
String getDescription();
|
||||
|
||||
/**
|
||||
@@ -80,41 +75,46 @@ public interface KernelFunction extends Buildable {
|
||||
<T> Mono<FunctionResult<T>> invokeAsync(
|
||||
Kernel kernel,
|
||||
@Nullable KernelArguments arguments,
|
||||
ContextVariableType<T> variableType);
|
||||
@Nullable ContextVariableType<T> variableType);
|
||||
|
||||
<T> Mono<FunctionResult<T>> invokeAsync(
|
||||
Kernel kernel,
|
||||
@Nullable KernelArguments arguments,
|
||||
KernelHooks kernelHooks,
|
||||
ContextVariableType<T> variableType);
|
||||
@Nullable ContextVariableType<T> variableType);
|
||||
|
||||
@Nullable
|
||||
Map<String, PromptExecutionSettings> getExecutionSettings();
|
||||
|
||||
interface FromPromptBuilder {
|
||||
|
||||
FromPromptBuilder withName(String name);
|
||||
FromPromptBuilder withName(@Nullable String name);
|
||||
|
||||
FromPromptBuilder withInputParameters(List<InputVariable> inputVariables);
|
||||
FromPromptBuilder withInputParameters(
|
||||
@Nullable List<InputVariable> inputVariables);
|
||||
|
||||
FromPromptBuilder withPromptTemplate(PromptTemplate promptTemplate);
|
||||
FromPromptBuilder withPromptTemplate(
|
||||
@Nullable PromptTemplate promptTemplate);
|
||||
|
||||
FromPromptBuilder withExecutionSettings(
|
||||
@Nullable
|
||||
Map<String, PromptExecutionSettings> executionSettings);
|
||||
|
||||
FromPromptBuilder withDefaultExecutionSettings(
|
||||
@Nullable
|
||||
PromptExecutionSettings executionSettings);
|
||||
|
||||
FromPromptBuilder withDescription(String description);
|
||||
FromPromptBuilder withDescription(@Nullable String description);
|
||||
|
||||
FromPromptBuilder withTemplate(String template);
|
||||
FromPromptBuilder withTemplate(@Nullable String template);
|
||||
|
||||
KernelFunction build();
|
||||
|
||||
FromPromptBuilder withTemplateFormat(String templateFormat);
|
||||
|
||||
FromPromptBuilder withOutputVariable(OutputVariable outputVariable);
|
||||
FromPromptBuilder withOutputVariable(@Nullable OutputVariable outputVariable);
|
||||
|
||||
FromPromptBuilder withPromptTemplateFactory(PromptTemplateFactory promptTemplateFactory);
|
||||
FromPromptBuilder withPromptTemplateFactory(
|
||||
@Nullable PromptTemplateFactory promptTemplateFactory);
|
||||
}
|
||||
}
|
||||
|
||||
+14
-4
@@ -5,15 +5,17 @@ import com.microsoft.semantickernel.plugin.KernelReturnParameterMetadata;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
public class KernelFunctionMetadata {
|
||||
|
||||
/// <summary>The name of the function.</summary>
|
||||
private final String name;
|
||||
/// <summary>The description of the function.</summary>
|
||||
@Nullable
|
||||
private final String description;
|
||||
/// <summary>The function's parameters.</summary>
|
||||
private final List<KernelParameterMetadata> parameters;
|
||||
private final List<KernelParameterMetadata<?>> parameters;
|
||||
|
||||
|
||||
/// <summary>The function's return parameter.</summary>
|
||||
@@ -22,12 +24,19 @@ public class KernelFunctionMetadata {
|
||||
|
||||
public KernelFunctionMetadata(
|
||||
String name,
|
||||
@Nullable
|
||||
String description,
|
||||
List<KernelParameterMetadata> parameters,
|
||||
@Nullable
|
||||
List<KernelParameterMetadata<?>> parameters,
|
||||
KernelReturnParameterMetadata<?> returnParameter) {
|
||||
this.name = name;
|
||||
this.description = description;
|
||||
this.parameters = new ArrayList<>(parameters);
|
||||
if (parameters == null) {
|
||||
this.parameters = new ArrayList<>();
|
||||
} else {
|
||||
this.parameters = new ArrayList<>(parameters);
|
||||
}
|
||||
|
||||
this.returnParameter = returnParameter;
|
||||
}
|
||||
|
||||
@@ -35,10 +44,11 @@ public class KernelFunctionMetadata {
|
||||
return name;
|
||||
}
|
||||
|
||||
public List<KernelParameterMetadata> getParameters() {
|
||||
public List<KernelParameterMetadata<?>> getParameters() {
|
||||
return Collections.unmodifiableList(parameters);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public String getDescription() {
|
||||
return description;
|
||||
}
|
||||
|
||||
+10
-6
@@ -6,21 +6,24 @@ import com.microsoft.semantickernel.semanticfunctions.KernelFunctionFromMethod.I
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
public class MethodDetails {
|
||||
|
||||
private final String name;
|
||||
@Nullable
|
||||
private final String description;
|
||||
private final ImplementationFunc function;
|
||||
private final List<KernelParameterMetadata> parameters;
|
||||
private final KernelReturnParameterMetadata returnParameter;
|
||||
private final List<KernelParameterMetadata<?>> parameters;
|
||||
private final KernelReturnParameterMetadata<?> returnParameter;
|
||||
|
||||
public MethodDetails(
|
||||
String name,
|
||||
@Nullable
|
||||
String description,
|
||||
ImplementationFunc function,
|
||||
List<KernelParameterMetadata> parameters,
|
||||
KernelReturnParameterMetadata returnParameter) {
|
||||
List<KernelParameterMetadata<?>> parameters,
|
||||
KernelReturnParameterMetadata<?> returnParameter) {
|
||||
this.name = name;
|
||||
this.description = description;
|
||||
this.function = function;
|
||||
@@ -32,6 +35,7 @@ public class MethodDetails {
|
||||
return name;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public String getDescription() {
|
||||
return description;
|
||||
}
|
||||
@@ -40,11 +44,11 @@ public class MethodDetails {
|
||||
return function;
|
||||
}
|
||||
|
||||
public List<KernelParameterMetadata> getParameters() {
|
||||
public List<KernelParameterMetadata<?>> getParameters() {
|
||||
return Collections.unmodifiableList(parameters);
|
||||
}
|
||||
|
||||
public KernelReturnParameterMetadata getReturnParameter() {
|
||||
public KernelReturnParameterMetadata<?> getReturnParameter() {
|
||||
return returnParameter;
|
||||
}
|
||||
}
|
||||
|
||||
+128
-3
@@ -1,23 +1,148 @@
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
package com.microsoft.semantickernel.orchestration.contextvariables;
|
||||
|
||||
import com.microsoft.semantickernel.exceptions.SKException;
|
||||
import com.microsoft.semantickernel.orchestration.contextvariables.ContextVariableTypeConverter.NoopConverter;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
public class ContextVariable<T> {
|
||||
|
||||
private final ContextVariableType<T> type;
|
||||
|
||||
@Nullable
|
||||
private final T value;
|
||||
|
||||
public ContextVariable(ContextVariableType<T> type, T value) {
|
||||
public ContextVariable(ContextVariableType<T> type,
|
||||
@Nullable T value) {
|
||||
this.type = type;
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public static <T> ContextVariable<T> of(Class<T> type, @Nullable T t) {
|
||||
if (t == null) {
|
||||
return new ContextVariable<>(
|
||||
ContextVariableTypes.getDefaultVariableTypeForClass(type),
|
||||
null);
|
||||
}
|
||||
|
||||
return of(t);
|
||||
}
|
||||
|
||||
public static ContextVariable<?> untypedOf(
|
||||
@Nullable Object value,
|
||||
Class<?> clazz) {
|
||||
ContextVariableType<?> type = ContextVariableTypes.getDefaultVariableTypeForClass(clazz);
|
||||
return new ContextVariable(type, value);
|
||||
}
|
||||
|
||||
public static ContextVariable<?> untypedOf(
|
||||
@Nullable Object value,
|
||||
ContextVariableTypeConverter<?> converter) {
|
||||
ContextVariableType<?> type = new ContextVariableType(converter, converter.getType());
|
||||
return new ContextVariable(type, value);
|
||||
}
|
||||
|
||||
public static <T, U> ContextVariable<T> convert(
|
||||
@Nullable
|
||||
U it,
|
||||
Class<T> requestedResultType) {
|
||||
return convert(
|
||||
it,
|
||||
requestedResultType,
|
||||
ContextVariableTypes.DEFAULT_TYPES
|
||||
);
|
||||
}
|
||||
|
||||
public static <T, U> ContextVariable<T> convert(
|
||||
@Nullable
|
||||
U it,
|
||||
Class<T> requestedResultType,
|
||||
ContextVariableTypes contextVariableTypes) {
|
||||
if (it == null) {
|
||||
return new ContextVariable<>(
|
||||
contextVariableTypes.getVariableTypeForClass(requestedResultType),
|
||||
null);
|
||||
}
|
||||
|
||||
if (requestedResultType.isAssignableFrom(it.getClass())) {
|
||||
try {
|
||||
return ContextVariable.of(requestedResultType, requestedResultType.cast(it));
|
||||
} catch (Exception e) {
|
||||
return new ContextVariable<>(
|
||||
new ContextVariableType<>(
|
||||
new NoopConverter<>(requestedResultType),
|
||||
requestedResultType
|
||||
),
|
||||
requestedResultType.cast(it)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
ContextVariableType<T> requestedResultTypeVariable;
|
||||
|
||||
try {
|
||||
requestedResultTypeVariable = contextVariableTypes.getVariableTypeForClass(
|
||||
requestedResultType);
|
||||
} catch (Exception e) {
|
||||
throw new SKException("Unable to find variable type for " + requestedResultType, e);
|
||||
}
|
||||
|
||||
ContextVariableType<U> typeOfActualReturnedType;
|
||||
|
||||
try {
|
||||
// First try to convert from type ? to T using the converter of ? and see if it can convert it to T.
|
||||
typeOfActualReturnedType = contextVariableTypes.getVariableTypeForClass(
|
||||
(Class<U>) it.getClass());
|
||||
} catch (Exception e) {
|
||||
typeOfActualReturnedType = contextVariableTypes.getVariableTypeForSuperClass(
|
||||
(Class<U>) it.getClass());
|
||||
}
|
||||
|
||||
if (typeOfActualReturnedType != null) {
|
||||
// Try the to object
|
||||
T converted = typeOfActualReturnedType.getConverter().toObject(it, requestedResultType);
|
||||
|
||||
if (converted != null) {
|
||||
return contextVariableTypes.getVariableTypeForClass(requestedResultType)
|
||||
.of(converted);
|
||||
}
|
||||
|
||||
if (requestedResultType.isAssignableFrom(String.class)) {
|
||||
// Try using toPromptString
|
||||
String str = typeOfActualReturnedType.getConverter()
|
||||
.toPromptString(typeOfActualReturnedType.getClazz().cast(it));
|
||||
|
||||
return requestedResultTypeVariable.of(str);
|
||||
}
|
||||
}
|
||||
|
||||
// Try getting a converter of type T and see if it can convert ? to T.
|
||||
if (requestedResultTypeVariable != null) {
|
||||
// Try using from object
|
||||
T result = requestedResultTypeVariable.getConverter().fromObject(it);
|
||||
if (result != null) {
|
||||
return requestedResultTypeVariable.of(result);
|
||||
}
|
||||
|
||||
if (requestedResultType.equals(String.class)) {
|
||||
// Try using from prompt string
|
||||
return requestedResultTypeVariable.of(
|
||||
requestedResultTypeVariable.getConverter().fromPromptString((String) it));
|
||||
}
|
||||
}
|
||||
|
||||
throw new SKException("Unable to convert " + it.getClass() + " to " + requestedResultType);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public T getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public <U> U getValue(Class<U> clazz) {
|
||||
if (clazz.isAssignableFrom(value.getClass())) {
|
||||
return (U) value;
|
||||
if (value == null || clazz.isAssignableFrom(value.getClass())) {
|
||||
return clazz.cast(value);
|
||||
} else {
|
||||
throw new RuntimeException("Cannot cast " + value.getClass() + " to " + clazz);
|
||||
}
|
||||
|
||||
+11
-4
@@ -1,5 +1,7 @@
|
||||
package com.microsoft.semantickernel.orchestration.contextvariables;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
public class ContextVariableType<T> {
|
||||
|
||||
private final ContextVariableTypeConverter<T> contextVariableTypeConverter;
|
||||
@@ -26,10 +28,15 @@ public class ContextVariableType<T> {
|
||||
* @param it The object to convert.
|
||||
* @return A context variable of this type.
|
||||
*/
|
||||
public ContextVariable<T> of(Object it) {
|
||||
if (getClazz().isAssignableFrom(it.getClass())) {
|
||||
return ContextVariable.of((T) it, getConverter());
|
||||
public ContextVariable<T> of(@Nullable Object it) {
|
||||
if (it == null) {
|
||||
return ContextVariable.of(clazz, clazz.cast(null));
|
||||
}
|
||||
return ContextVariable.of(getConverter().fromObject(it));
|
||||
|
||||
if (clazz.isAssignableFrom(it.getClass())) {
|
||||
return ContextVariable.of(clazz.cast(it), getConverter());
|
||||
}
|
||||
ContextVariableTypeConverter<T> converter = getConverter();
|
||||
return ContextVariable.of(converter.getType(), converter.fromObject(it));
|
||||
}
|
||||
}
|
||||
|
||||
+9
-5
@@ -79,10 +79,14 @@ public class ContextVariableTypeConverter<T> {
|
||||
this.toObjects = new ArrayList<>(toObjects);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Nullable
|
||||
@SuppressWarnings("unchecked")
|
||||
public <U> U toObject(Object t, Class<U> clazz) {
|
||||
public <U> U toObject(@Nullable Object t, Class<U> clazz) {
|
||||
if (t == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
Optional<Converter<T, ?>> converter = toObjects
|
||||
.stream()
|
||||
.filter(c -> c.getTargetType().equals(clazz))
|
||||
@@ -106,14 +110,14 @@ public class ContextVariableTypeConverter<T> {
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public T fromObject(Object s) {
|
||||
public T fromObject(@Nullable Object s) {
|
||||
if (s == null) {
|
||||
return null;
|
||||
}
|
||||
return fromObject.apply(s);
|
||||
}
|
||||
|
||||
public String toPromptString(T t) {
|
||||
public String toPromptString(@Nullable T t) {
|
||||
if (t == null) {
|
||||
return "";
|
||||
}
|
||||
@@ -121,7 +125,7 @@ public class ContextVariableTypeConverter<T> {
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public T fromPromptString(String t) {
|
||||
public T fromPromptString(@Nullable String t) {
|
||||
if (t == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
+26
-2
@@ -5,6 +5,7 @@ import com.microsoft.semantickernel.orchestration.contextvariables.converters.Ch
|
||||
import com.microsoft.semantickernel.orchestration.contextvariables.converters.ChatHistoryVariableContextVariableTypeConverter;
|
||||
import com.microsoft.semantickernel.orchestration.contextvariables.converters.CompletionUsageContextVariableTypeConverter;
|
||||
import com.microsoft.semantickernel.orchestration.contextvariables.converters.DateTimeContextVariableTypeConverter;
|
||||
import com.microsoft.semantickernel.orchestration.contextvariables.converters.InstantContextVariableTypeConverter;
|
||||
import com.microsoft.semantickernel.orchestration.contextvariables.converters.NumberVariableContextVariableTypeConverter;
|
||||
import com.microsoft.semantickernel.orchestration.contextvariables.converters.StringVariableContextVariableTypeConverter;
|
||||
import com.microsoft.semantickernel.orchestration.contextvariables.converters.VoidVariableContextVariableTypeConverter;
|
||||
@@ -12,6 +13,7 @@ import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
public class ContextVariableTypes {
|
||||
|
||||
@@ -24,7 +26,9 @@ public class ContextVariableTypes {
|
||||
new ChatHistoryVariableContextVariableTypeConverter(),
|
||||
new StringVariableContextVariableTypeConverter(),
|
||||
new VoidVariableContextVariableTypeConverter(),
|
||||
new ContextVariableTypeConverter<>(void.class, s -> null, s -> null, s -> null),
|
||||
new DateTimeContextVariableTypeConverter(),
|
||||
new InstantContextVariableTypeConverter(),
|
||||
new CompletionUsageContextVariableTypeConverter(),
|
||||
|
||||
new NumberVariableContextVariableTypeConverter<>(Byte.class, Byte::parseByte),
|
||||
@@ -64,8 +68,9 @@ public class ContextVariableTypes {
|
||||
contextVariableTypeConverter.getType()));
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@SuppressWarnings("unchecked")
|
||||
public static <T> T convert(Object s, Class<T> clazz) {
|
||||
public static <T> T convert(@Nullable Object s, Class<T> clazz) {
|
||||
if (s instanceof ContextVariable && ((ContextVariable<?>) s).getType().getClazz()
|
||||
.isAssignableFrom(clazz)) {
|
||||
return ((ContextVariable<T>) s).getValue();
|
||||
@@ -92,10 +97,29 @@ public class ContextVariableTypes {
|
||||
return (ContextVariableType<T>) variableTypes
|
||||
.values()
|
||||
.stream()
|
||||
.filter(c -> c.getClazz().isAssignableFrom(aClass))
|
||||
.filter(c -> {
|
||||
return c.getClazz().isAssignableFrom(aClass);
|
||||
})
|
||||
.findFirst()
|
||||
.orElseThrow(
|
||||
() -> new RuntimeException("Unknown context variable type: " + aClass.getName()));
|
||||
}
|
||||
|
||||
public <T> ContextVariableType<T> getVariableTypeForSuperClass(Class<T> aClass) {
|
||||
ContextVariableType<?> contextVariableType = variableTypes.get(aClass);
|
||||
|
||||
if (contextVariableType != null) {
|
||||
return (ContextVariableType<T>) contextVariableType;
|
||||
}
|
||||
|
||||
return (ContextVariableType<T>) variableTypes
|
||||
.values()
|
||||
.stream()
|
||||
.filter(c -> {
|
||||
return aClass.isAssignableFrom(c.getClazz());
|
||||
})
|
||||
.findFirst()
|
||||
.orElseThrow(
|
||||
() -> new RuntimeException("Unknown context variable type: " + aClass.getName()));
|
||||
}
|
||||
}
|
||||
+1
-5
@@ -90,11 +90,6 @@ public class DefaultKernelArguments implements KernelArguments, WritableKernelAr
|
||||
return setVariable(MAIN_KEY, content);
|
||||
}
|
||||
|
||||
@Override
|
||||
public KernelArguments update(String content) {
|
||||
return null;
|
||||
}
|
||||
|
||||
public DefaultKernelArguments update(@NonNull DefaultKernelArguments newData) {
|
||||
return update(newData, true);
|
||||
}
|
||||
@@ -198,6 +193,7 @@ public class DefaultKernelArguments implements KernelArguments, WritableKernelAr
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public ContextVariable<?> get(Object key) {
|
||||
return variables.get(key);
|
||||
}
|
||||
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
package com.microsoft.semantickernel.orchestration.contextvariables;
|
||||
|
||||
import com.microsoft.semantickernel.orchestration.contextvariables.ContextVariableTypeConverter.NoopConverter;
|
||||
|
||||
public class NullContextVariable<T> extends ContextVariable<T> {
|
||||
|
||||
public NullContextVariable(Class<T> type) {
|
||||
super(new ContextVariableType<>(
|
||||
new NoopConverter<>(type),
|
||||
type
|
||||
), null);
|
||||
}
|
||||
}
|
||||
-2
@@ -29,8 +29,6 @@ public interface WritableKernelArguments extends KernelArguments {
|
||||
*/
|
||||
KernelArguments update(ContextVariable<?> content);
|
||||
|
||||
KernelArguments update(String content);
|
||||
|
||||
/**
|
||||
* Updates the variables merging or overwriting in the new values.
|
||||
*
|
||||
|
||||
+11
-1
@@ -1,8 +1,10 @@
|
||||
package com.microsoft.semantickernel.orchestration.contextvariables.converters;
|
||||
|
||||
import com.microsoft.semantickernel.orchestration.contextvariables.ContextVariableTypeConverter;
|
||||
import java.time.Instant;
|
||||
import java.time.OffsetDateTime;
|
||||
import java.time.ZonedDateTime;
|
||||
import java.util.Arrays;
|
||||
|
||||
public class DateTimeContextVariableTypeConverter extends
|
||||
ContextVariableTypeConverter<OffsetDateTime> {
|
||||
@@ -21,6 +23,14 @@ public class DateTimeContextVariableTypeConverter extends
|
||||
Object::toString,
|
||||
o -> {
|
||||
return ZonedDateTime.parse(o).toOffsetDateTime();
|
||||
});
|
||||
},
|
||||
Arrays.asList(
|
||||
new DefaultConverter<OffsetDateTime, Instant>(OffsetDateTime.class, Instant.class) {
|
||||
@Override
|
||||
public Instant toObject(OffsetDateTime offsetDateTime) {
|
||||
return offsetDateTime.toInstant();
|
||||
}
|
||||
}
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
+26
@@ -0,0 +1,26 @@
|
||||
package com.microsoft.semantickernel.orchestration.contextvariables.converters;
|
||||
|
||||
import com.microsoft.semantickernel.orchestration.contextvariables.ContextVariableTypeConverter;
|
||||
import java.time.Instant;
|
||||
import java.time.OffsetDateTime;
|
||||
|
||||
public class InstantContextVariableTypeConverter extends
|
||||
ContextVariableTypeConverter<Instant> {
|
||||
|
||||
public InstantContextVariableTypeConverter() {
|
||||
super(
|
||||
Instant.class,
|
||||
s -> {
|
||||
if (s instanceof String) {
|
||||
return Instant.parse((String) s);
|
||||
} else if (s instanceof OffsetDateTime) {
|
||||
return ((OffsetDateTime) s).toInstant();
|
||||
}
|
||||
return null;
|
||||
},
|
||||
Object::toString,
|
||||
o -> {
|
||||
return Instant.parse(o);
|
||||
});
|
||||
}
|
||||
}
|
||||
+15
-10
@@ -1,11 +1,10 @@
|
||||
package com.microsoft.semantickernel.orchestration.contextvariables.converters;
|
||||
|
||||
import java.util.function.Function;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import static com.microsoft.semantickernel.orchestration.contextvariables.ContextVariableTypes.convert;
|
||||
|
||||
import com.microsoft.semantickernel.orchestration.contextvariables.ContextVariableTypeConverter;
|
||||
import static com.microsoft.semantickernel.orchestration.contextvariables.ContextVariableTypes.convert;
|
||||
import java.util.function.Function;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
public class NumberVariableContextVariableTypeConverter<T extends Number> extends
|
||||
ContextVariableTypeConverter<T> {
|
||||
@@ -21,7 +20,11 @@ public class NumberVariableContextVariableTypeConverter<T extends Number> extend
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public <U> U toObject(Object t, Class<U> clazz) {
|
||||
public <U> U toObject(@Nullable Object t, Class<U> clazz) {
|
||||
if (t == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Let the parent class have a crack at it first
|
||||
// since someone may have installed a special converter.
|
||||
U obj = super.toObject(t, clazz);
|
||||
@@ -35,12 +38,13 @@ public class NumberVariableContextVariableTypeConverter<T extends Number> extend
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public T fromObject(Object s) {
|
||||
@Nullable
|
||||
public T fromObject(@Nullable Object s) {
|
||||
T obj = super.fromObject(s);
|
||||
if (obj != null) {
|
||||
return obj;
|
||||
return obj;
|
||||
}
|
||||
if (s instanceof String) {
|
||||
return fromPromptString((String) s);
|
||||
@@ -49,11 +53,12 @@ public class NumberVariableContextVariableTypeConverter<T extends Number> extend
|
||||
}
|
||||
|
||||
@Override
|
||||
public T fromPromptString(String s) {
|
||||
@Nullable
|
||||
public T fromPromptString(@Nullable String s) {
|
||||
if (s != null && !s.isEmpty()) {
|
||||
return super.fromPromptString(s);
|
||||
}
|
||||
return null;
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+9
-30
@@ -1,8 +1,8 @@
|
||||
package com.microsoft.semantickernel.plugin;
|
||||
|
||||
import com.microsoft.semantickernel.orchestration.KernelFunction;
|
||||
import com.microsoft.semantickernel.semanticfunctions.KernelFunctionFromMethod;
|
||||
import com.microsoft.semantickernel.orchestration.PromptExecutionSettings;
|
||||
import com.microsoft.semantickernel.semanticfunctions.KernelFunctionFromMethod;
|
||||
import com.microsoft.semantickernel.semanticfunctions.KernelFunctionFromPrompt;
|
||||
import com.microsoft.semantickernel.semanticfunctions.KernelPromptTemplateFactory;
|
||||
import com.microsoft.semantickernel.semanticfunctions.PromptTemplate;
|
||||
@@ -16,30 +16,6 @@ import javax.annotation.Nullable;
|
||||
|
||||
public class KernelFunctionFactory {
|
||||
|
||||
/// <summary>
|
||||
/// Creates a <see cref="KernelFunction"/> instance for a method, specified via a delegate.
|
||||
/// </summary>
|
||||
/// <param name="method">The method to be represented via the created <see cref="KernelFunction"/>.</param>
|
||||
/// <param name="functionName">Optional function name. If null, it will default to one derived from the method represented by <paramref name="method"/>.</param>
|
||||
/// <param name="description">Optional description of the method. If null, it will default to one derived from the method represented by <paramref name="method"/>, if possible (e.g. via a <see cref="DescriptionAttribute"/> on the method).</param>
|
||||
/// <param name="parameters">Optional parameter descriptions. If null, it will default to one derived from the method represented by <paramref name="method"/>.</param>
|
||||
/// <param name="returnParameter">Optional return parameter description. If null, it will default to one derived from the method represented by <paramref name="method"/>.</param>
|
||||
/// <param name="loggerFactory">The <see cref="ILoggerFactory"/> to use for logging. If null, no logging will be performed.</param>
|
||||
/// <returns>The created <see cref="KernelFunction"/> wrapper for <paramref name="method"/>.</returns>
|
||||
public static KernelFunction createFromMethod(
|
||||
Method method,
|
||||
@Nullable String functionName,
|
||||
@Nullable String description,
|
||||
List<KernelParameterMetadata> parameters,
|
||||
KernelReturnParameterMetadata<?> returnParameter) {
|
||||
return createFromMethod(
|
||||
method,
|
||||
null,
|
||||
functionName,
|
||||
description,
|
||||
parameters,
|
||||
returnParameter);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a <see cref="KernelFunction"/> instance for a method, specified via an <see cref="MethodInfo"/> instance
|
||||
@@ -55,10 +31,10 @@ public class KernelFunctionFactory {
|
||||
/// <returns>The created <see cref="KernelFunction"/> wrapper for <paramref name="method"/>.</returns>
|
||||
public static KernelFunction createFromMethod(
|
||||
Method method,
|
||||
@Nullable Object target,
|
||||
Object target,
|
||||
@Nullable String functionName,
|
||||
@Nullable String description,
|
||||
@Nullable List<KernelParameterMetadata> parameters,
|
||||
@Nullable List<KernelParameterMetadata<?>> parameters,
|
||||
@Nullable KernelReturnParameterMetadata<?> returnParameter) {
|
||||
return KernelFunctionFromMethod.create(method, target, functionName, description,
|
||||
parameters, returnParameter);
|
||||
@@ -87,8 +63,8 @@ public class KernelFunctionFactory {
|
||||
* @param templateFormat The template format of {@code promptTemplate}. This must be
|
||||
* provided if {@code promptTemplateFactory} is not null.
|
||||
* @param promptTemplateFactory The {@link PromptTemplateFactory} to use when interpreting the
|
||||
* {@code promptTemplate} into a {@link IPromptTemplate}. If null,
|
||||
* a default factory will be used.
|
||||
* {@code promptTemplate} into a {@link PromptTemplate}. If null, a
|
||||
* default factory will be used.
|
||||
* @return The created {@link KernelFunction} for invoking the prompt.
|
||||
*/
|
||||
public static KernelFunction createFromPrompt(
|
||||
@@ -129,9 +105,12 @@ public class KernelFunctionFactory {
|
||||
}
|
||||
|
||||
private static Map<String, PromptExecutionSettings> createSettingsDictionary(
|
||||
@Nullable
|
||||
PromptExecutionSettings executionSettings) {
|
||||
HashMap<String, PromptExecutionSettings> map = new HashMap<>();
|
||||
map.put("default", executionSettings);
|
||||
if (executionSettings != null) {
|
||||
map.put("default", executionSettings);
|
||||
}
|
||||
return map;
|
||||
}
|
||||
}
|
||||
|
||||
+10
-1
@@ -1,20 +1,27 @@
|
||||
package com.microsoft.semantickernel.plugin;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
public class KernelParameterMetadata<T> {
|
||||
|
||||
/// <summary>The name of the parameter.</summary>
|
||||
private final String name;
|
||||
/// <summary>The description of the parameter.</summary>
|
||||
@Nullable
|
||||
private final String description;
|
||||
@Nullable
|
||||
private final String defaultValue;
|
||||
private final boolean isRequired;
|
||||
private final Class<T> parameterType;
|
||||
|
||||
public KernelParameterMetadata(
|
||||
String name,
|
||||
@Nullable
|
||||
String description,
|
||||
Class<T> parameterType,
|
||||
String defaultValue, boolean isRequired) {
|
||||
@Nullable
|
||||
String defaultValue,
|
||||
boolean isRequired) {
|
||||
this.name = name;
|
||||
this.description = description;
|
||||
this.defaultValue = defaultValue;
|
||||
@@ -26,10 +33,12 @@ public class KernelParameterMetadata<T> {
|
||||
return name;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public String getDescription() {
|
||||
return description;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public String getDefaultValue() {
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
+12
-1
@@ -5,15 +5,20 @@ import com.microsoft.semantickernel.orchestration.contextvariables.CaseInsensiti
|
||||
import java.util.Collections;
|
||||
import java.util.Iterator;
|
||||
import java.util.Map;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
public class KernelPlugin implements Iterable<KernelFunction> {
|
||||
|
||||
private final String name;
|
||||
@Nullable
|
||||
private final String description;
|
||||
|
||||
private final CaseInsensitiveMap<KernelFunction> functions;
|
||||
|
||||
public KernelPlugin(String name, String description,
|
||||
public KernelPlugin(
|
||||
String name,
|
||||
@Nullable
|
||||
String description,
|
||||
Map<String, KernelFunction> plugins) {
|
||||
this.name = name;
|
||||
this.description = description;
|
||||
@@ -23,10 +28,15 @@ public class KernelPlugin implements Iterable<KernelFunction> {
|
||||
}
|
||||
}
|
||||
|
||||
public void addFunction(KernelFunction function) {
|
||||
functions.put(function.getName(), function);
|
||||
}
|
||||
|
||||
public Map<String, KernelFunction> getFunctions() {
|
||||
return Collections.unmodifiableMap(functions);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public KernelFunction get(String functionName) {
|
||||
return functions.get(functionName);
|
||||
}
|
||||
@@ -40,6 +50,7 @@ public class KernelPlugin implements Iterable<KernelFunction> {
|
||||
return name;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public String getDescription() {
|
||||
return description;
|
||||
}
|
||||
|
||||
+55
-30
@@ -6,7 +6,7 @@ import com.microsoft.semantickernel.orchestration.KernelFunction;
|
||||
import com.microsoft.semantickernel.orchestration.contextvariables.CaseInsensitiveMap;
|
||||
import com.microsoft.semantickernel.plugin.annotations.DefineKernelFunction;
|
||||
import com.microsoft.semantickernel.plugin.annotations.KernelFunctionParameter;
|
||||
import com.microsoft.semantickernel.semanticfunctions.KernelFunctionFromPrompt.Builder;
|
||||
import com.microsoft.semantickernel.semanticfunctions.KernelFunctionFromPrompt;
|
||||
import com.microsoft.semantickernel.semanticfunctions.KernelPromptTemplateFactory;
|
||||
import com.microsoft.semantickernel.semanticfunctions.PromptTemplate;
|
||||
import com.microsoft.semantickernel.semanticfunctions.PromptTemplateConfig;
|
||||
@@ -46,13 +46,13 @@ public class KernelPluginFactory {
|
||||
/// Public methods decorated with <see cref="KernelFunctionAttribute"/> will be included in the plugin.
|
||||
/// Attributed methods must all have different names; overloads are not supported.
|
||||
/// </remarks>
|
||||
public static KernelPlugin createFromObject(Object target, @Nullable String pluginName) {
|
||||
public static KernelPlugin createFromObject(Object target, String pluginName) {
|
||||
List<KernelFunction> methods = Arrays.stream(target.getClass().getMethods())
|
||||
.filter(method -> method.isAnnotationPresent(DefineKernelFunction.class))
|
||||
.map(method -> {
|
||||
DefineKernelFunction annotation = method.getAnnotation(DefineKernelFunction.class);
|
||||
Class<?> returnType = getReturnType(annotation, method);
|
||||
KernelReturnParameterMetadata kernelReturnParameterMetadata = new KernelReturnParameterMetadata(
|
||||
KernelReturnParameterMetadata<?> kernelReturnParameterMetadata = new KernelReturnParameterMetadata<>(
|
||||
annotation.returnDescription(),
|
||||
returnType);
|
||||
|
||||
@@ -75,10 +75,9 @@ public class KernelPluginFactory {
|
||||
returnType = method.getReturnType();
|
||||
|
||||
if (Publisher.class.isAssignableFrom(returnType)) {
|
||||
LOGGER.warn(
|
||||
"For method: " + method.getDeclaringClass().getName() + "." + method.getName()
|
||||
+ ", this is an async method, if a return type is required, please specify it in the annotation. Defaulting to Void return type");
|
||||
returnType = Void.class;
|
||||
throw new SKException(
|
||||
"Method: " + method.getDeclaringClass().getName() + "." + method.getName()
|
||||
+ ", this is an async method, It is required to add an annotation to specify the return type");
|
||||
}
|
||||
} else {
|
||||
try {
|
||||
@@ -109,7 +108,8 @@ public class KernelPluginFactory {
|
||||
/// <exception cref="ArgumentException"><paramref name="pluginName"/> is an invalid plugin name.</exception>
|
||||
/// <exception cref="ArgumentNullException"><paramref name="functions"/> contains a null function.</exception>
|
||||
/// <exception cref="ArgumentException"><paramref name="functions"/> contains two functions with the same name.</exception>
|
||||
public static KernelPlugin createFromFunctions(String pluginName,
|
||||
public static KernelPlugin createFromFunctions(
|
||||
String pluginName,
|
||||
@Nullable List<KernelFunction> functions) {
|
||||
return createFromFunctions(pluginName, null, functions);
|
||||
}
|
||||
@@ -132,15 +132,19 @@ public class KernelPluginFactory {
|
||||
}
|
||||
|
||||
|
||||
private static List<KernelParameterMetadata> getParameters(Method method) {
|
||||
private static List<KernelParameterMetadata<?>> getParameters(Method method) {
|
||||
return Arrays.stream(method.getParameters())
|
||||
.filter(parameter -> parameter.isAnnotationPresent(KernelFunctionParameter.class))
|
||||
.map(parameter -> {
|
||||
KernelFunctionParameter annotation = parameter.getAnnotation(
|
||||
KernelFunctionParameter.class);
|
||||
|
||||
return new KernelParameterMetadata(annotation.name(), annotation.description(),
|
||||
null, annotation.defaultValue(), annotation.required());
|
||||
return new KernelParameterMetadata<>(
|
||||
annotation.name(),
|
||||
annotation.description(),
|
||||
annotation.type(),
|
||||
annotation.defaultValue(),
|
||||
annotation.required());
|
||||
}).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@@ -176,8 +180,8 @@ public class KernelPluginFactory {
|
||||
// configuration, unable to parse {configPath}");
|
||||
}
|
||||
|
||||
KernelFunction plugin = getKernelFunction(pluginDirectoryName,
|
||||
promptTemplateFactory, configPath, promptPath);
|
||||
KernelFunction plugin = getKernelFunction(promptTemplateFactory, configPath,
|
||||
promptPath);
|
||||
|
||||
plugins.put(dir.getName(), plugin);
|
||||
} catch (IOException e) {
|
||||
@@ -192,8 +196,10 @@ public class KernelPluginFactory {
|
||||
);
|
||||
}
|
||||
|
||||
private static KernelFunction getKernelFunction(String pluginDirectoryName,
|
||||
PromptTemplateFactory promptTemplateFactory, File configPath, File promptPath)
|
||||
private static KernelFunction getKernelFunction(
|
||||
PromptTemplateFactory promptTemplateFactory,
|
||||
File configPath,
|
||||
File promptPath)
|
||||
throws IOException {
|
||||
PromptTemplateConfig config = new ObjectMapper().readValue(configPath,
|
||||
PromptTemplateConfig.class);
|
||||
@@ -202,11 +208,13 @@ public class KernelPluginFactory {
|
||||
String template = new String(Files.readAllBytes(promptPath.toPath()),
|
||||
Charset.defaultCharset());
|
||||
|
||||
return getKernelFunction(pluginDirectoryName, promptTemplateFactory, config, template);
|
||||
return getKernelFunction(promptTemplateFactory, config, template);
|
||||
}
|
||||
|
||||
private static KernelFunction getKernelFunction(String pluginDirectoryName,
|
||||
PromptTemplateFactory promptTemplateFactory, PromptTemplateConfig config, String template) {
|
||||
private static KernelFunction getKernelFunction(
|
||||
PromptTemplateFactory promptTemplateFactory,
|
||||
PromptTemplateConfig config,
|
||||
String template) {
|
||||
PromptTemplate promptTemplate;
|
||||
|
||||
if (promptTemplateFactory != null) {
|
||||
@@ -215,10 +223,12 @@ public class KernelPluginFactory {
|
||||
promptTemplate = new KernelPromptTemplateFactory().tryCreate(config);
|
||||
}
|
||||
|
||||
return new Builder().withName(config.getName()).withDescription(config.getDescription())
|
||||
return new KernelFunctionFromPrompt.Builder()
|
||||
.withName(config.getName())
|
||||
.withDescription(config.getDescription())
|
||||
.withExecutionSettings(config.getExecutionSettings())
|
||||
.withInputParameters(config.getInputVariables()
|
||||
).withPromptTemplate(promptTemplate)
|
||||
.withInputParameters(config.getInputVariables())
|
||||
.withPromptTemplate(promptTemplate)
|
||||
.withTemplate(template)
|
||||
.withTemplateFormat(config.getTemplateFormat())
|
||||
.withOutputVariable(config.getOutputVariable())
|
||||
@@ -226,9 +236,13 @@ public class KernelPluginFactory {
|
||||
.build();
|
||||
}
|
||||
|
||||
public static KernelPlugin importPluginFromResourcesDirectory(String parentDirectory,
|
||||
String pluginDirectoryName, String functionName,
|
||||
PromptTemplateFactory promptTemplateFactory, @Nullable Class<?> clazz) {
|
||||
@Nullable
|
||||
public static KernelPlugin importPluginFromResourcesDirectory(
|
||||
String parentDirectory,
|
||||
String pluginDirectoryName,
|
||||
String functionName,
|
||||
PromptTemplateFactory promptTemplateFactory,
|
||||
Class<?> clazz) {
|
||||
|
||||
String template = getTemplatePrompt(parentDirectory, pluginDirectoryName, functionName,
|
||||
clazz);
|
||||
@@ -236,7 +250,12 @@ public class KernelPluginFactory {
|
||||
PromptTemplateConfig promptTemplateConfig = getPromptTemplateConfig(parentDirectory,
|
||||
pluginDirectoryName, functionName, clazz);
|
||||
|
||||
KernelFunction function = getKernelFunction(pluginDirectoryName, promptTemplateFactory,
|
||||
if (promptTemplateConfig == null) {
|
||||
LOGGER.warn("Unable to load prompt template config for " + functionName + " in "
|
||||
+ pluginDirectoryName);
|
||||
return null;
|
||||
}
|
||||
KernelFunction function = getKernelFunction(promptTemplateFactory,
|
||||
promptTemplateConfig, template);
|
||||
|
||||
HashMap<String, KernelFunction> plugins = new HashMap<>();
|
||||
@@ -250,8 +269,11 @@ public class KernelPluginFactory {
|
||||
);
|
||||
}
|
||||
|
||||
private static String getTemplatePrompt(String pluginDirectory, String pluginName,
|
||||
String functionName, @Nullable Class clazz) {
|
||||
private static String getTemplatePrompt(
|
||||
String pluginDirectory,
|
||||
String pluginName,
|
||||
String functionName,
|
||||
Class<?> clazz) {
|
||||
String promptFileName =
|
||||
pluginDirectory + File.separator + pluginName + File.separator + functionName
|
||||
+ File.separator + PROMPT_FILE;
|
||||
@@ -265,14 +287,17 @@ public class KernelPluginFactory {
|
||||
}
|
||||
}
|
||||
|
||||
private static String getFileContents(String file, @Nullable Class clazz)
|
||||
private static String getFileContents(String file, Class<?> clazz)
|
||||
throws FileNotFoundException {
|
||||
return EmbeddedResourceLoader.readFile(file, clazz, ResourceLocation.CLASSPATH_ROOT,
|
||||
ResourceLocation.CLASSPATH, ResourceLocation.FILESYSTEM);
|
||||
}
|
||||
|
||||
private static PromptTemplateConfig getPromptTemplateConfig(String pluginDirectory,
|
||||
String pluginName, String functionName, @Nullable Class clazz) {
|
||||
@Nullable
|
||||
private static PromptTemplateConfig getPromptTemplateConfig(
|
||||
String pluginDirectory,
|
||||
String pluginName, String functionName,
|
||||
Class<?> clazz) {
|
||||
String configFileName =
|
||||
pluginDirectory + File.separator + pluginName + File.separator + functionName
|
||||
+ File.separator + CONFIG_FILE;
|
||||
|
||||
+5
@@ -1,17 +1,22 @@
|
||||
package com.microsoft.semantickernel.plugin;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
public class KernelReturnParameterMetadata<T> {
|
||||
|
||||
@Nullable
|
||||
private final String description;
|
||||
private final Class<T> parameterType;
|
||||
|
||||
public KernelReturnParameterMetadata(
|
||||
@Nullable
|
||||
String description,
|
||||
Class<T> parameterType) {
|
||||
this.description = description;
|
||||
this.parameterType = parameterType;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public String getDescription() {
|
||||
return description;
|
||||
}
|
||||
|
||||
+7
-3
@@ -1,21 +1,25 @@
|
||||
package com.microsoft.semantickernel.semanticfunctions;
|
||||
|
||||
import reactor.util.annotation.NonNull;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import reactor.util.annotation.NonNull;
|
||||
|
||||
public class AggregatorPromptTemplateFactory implements PromptTemplateFactory {
|
||||
|
||||
private final List<PromptTemplateFactory> templateFactories;
|
||||
|
||||
public AggregatorPromptTemplateFactory(List<PromptTemplateFactory> templateFactories) {
|
||||
this.templateFactories = new ArrayList<>(templateFactories);
|
||||
}
|
||||
|
||||
@Override
|
||||
public PromptTemplate tryCreate(@NonNull PromptTemplateConfig templateConfig) {
|
||||
for (PromptTemplateFactory factory : templateFactories) {
|
||||
try {
|
||||
return factory.tryCreate(templateConfig);
|
||||
} catch (UnknownTemplateFormatException ignored) { }
|
||||
} catch (UnknownTemplateFormatException ignored) {
|
||||
// No-op
|
||||
}
|
||||
}
|
||||
|
||||
throw new UnknownTemplateFormatException(templateConfig.getTemplateFormat());
|
||||
|
||||
+4
-3
@@ -1,17 +1,18 @@
|
||||
package com.microsoft.semantickernel.semanticfunctions;
|
||||
|
||||
import com.microsoft.semantickernel.templateengine.handlebars.HandlebarsPromptTemplate;
|
||||
import reactor.util.annotation.NonNull;
|
||||
|
||||
import java.util.Locale;
|
||||
import reactor.util.annotation.NonNull;
|
||||
|
||||
public class HandlebarsPromptTemplateFactory implements PromptTemplateFactory {
|
||||
|
||||
public static final String HANDLEBARS_TEMPLATE_FORMAT = "handlebars";
|
||||
|
||||
@Override
|
||||
public PromptTemplate tryCreate(@NonNull PromptTemplateConfig templateConfig) {
|
||||
if (templateConfig.getTemplateFormat() != null &&
|
||||
HANDLEBARS_TEMPLATE_FORMAT.equals(templateConfig.getTemplateFormat().toLowerCase(Locale.ROOT))) {
|
||||
HANDLEBARS_TEMPLATE_FORMAT.equals(
|
||||
templateConfig.getTemplateFormat().toLowerCase(Locale.ROOT))) {
|
||||
return new HandlebarsPromptTemplate(templateConfig);
|
||||
}
|
||||
|
||||
|
||||
+23
-5
@@ -1,6 +1,8 @@
|
||||
package com.microsoft.semantickernel.semanticfunctions;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import com.microsoft.semantickernel.exceptions.SKException;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
public class InputVariable {
|
||||
|
||||
@@ -10,27 +12,31 @@ public class InputVariable {
|
||||
@JsonProperty("type")
|
||||
private String type;
|
||||
|
||||
@Nullable
|
||||
@JsonProperty("description")
|
||||
private String description;
|
||||
|
||||
@JsonProperty("default")
|
||||
@Nullable
|
||||
private String defaultValue;
|
||||
|
||||
@JsonProperty("is_required")
|
||||
private boolean isRequired;
|
||||
|
||||
public InputVariable() {
|
||||
}
|
||||
|
||||
public InputVariable(
|
||||
String name) {
|
||||
public InputVariable(String name) {
|
||||
this.name = name;
|
||||
this.type = String.class.getName();
|
||||
this.description = null;
|
||||
this.defaultValue = null;
|
||||
this.isRequired = true;
|
||||
}
|
||||
|
||||
public InputVariable(
|
||||
String name,
|
||||
String type,
|
||||
@Nullable
|
||||
String description,
|
||||
@Nullable
|
||||
String defaultValue,
|
||||
boolean isRequired) {
|
||||
this.name = name;
|
||||
@@ -56,6 +62,7 @@ public class InputVariable {
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public String getDescription() {
|
||||
return description;
|
||||
}
|
||||
@@ -64,6 +71,7 @@ public class InputVariable {
|
||||
this.description = description;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public String getDefaultValue() {
|
||||
return defaultValue;
|
||||
}
|
||||
@@ -79,4 +87,14 @@ public class InputVariable {
|
||||
public void setRequired(boolean required) {
|
||||
isRequired = required;
|
||||
}
|
||||
|
||||
public Class<?> getTypeClass() {
|
||||
try {
|
||||
return Thread.currentThread().getContextClassLoader().loadClass(type);
|
||||
} catch (ClassNotFoundException e) {
|
||||
throw new SKException(
|
||||
"Could not load class for type: " + type + " when for input variable " + name +
|
||||
", note this needs to be a fully qualified class name, i.e 'java.lang.String'.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+109
-110
@@ -16,20 +16,18 @@ import com.microsoft.semantickernel.orchestration.MethodDetails;
|
||||
import com.microsoft.semantickernel.orchestration.contextvariables.ContextVariable;
|
||||
import com.microsoft.semantickernel.orchestration.contextvariables.ContextVariableType;
|
||||
import com.microsoft.semantickernel.orchestration.contextvariables.ContextVariableTypeConverter;
|
||||
import com.microsoft.semantickernel.orchestration.contextvariables.ContextVariableTypeConverter.NoopConverter;
|
||||
import com.microsoft.semantickernel.orchestration.contextvariables.ContextVariableTypes;
|
||||
import com.microsoft.semantickernel.orchestration.contextvariables.KernelArguments;
|
||||
import com.microsoft.semantickernel.plugin.KernelParameterMetadata;
|
||||
import com.microsoft.semantickernel.plugin.KernelReturnParameterMetadata;
|
||||
import com.microsoft.semantickernel.plugin.annotations.DefineKernelFunction;
|
||||
import com.microsoft.semantickernel.plugin.annotations.KernelFunctionParameter;
|
||||
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Parameter;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.function.Function;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
@@ -49,8 +47,10 @@ public class KernelFunctionFromMethod extends DefaultKernelFunction {
|
||||
private KernelFunctionFromMethod(
|
||||
ImplementationFunc implementationFunc,
|
||||
String functionName,
|
||||
@Nullable
|
||||
String description,
|
||||
List<KernelParameterMetadata> parameters,
|
||||
@Nullable
|
||||
List<KernelParameterMetadata<?>> parameters,
|
||||
KernelReturnParameterMetadata<?> returnParameter) {
|
||||
super(
|
||||
new KernelFunctionMetadata(
|
||||
@@ -68,8 +68,12 @@ public class KernelFunctionFromMethod extends DefaultKernelFunction {
|
||||
public <T> Mono<FunctionResult<T>> invokeAsync(
|
||||
Kernel kernel,
|
||||
@Nullable KernelArguments arguments,
|
||||
ContextVariableType<T> variableType) {
|
||||
return function.invoke(kernel, this, arguments, kernel.getHookService());
|
||||
@Nullable ContextVariableType<T> variableType) {
|
||||
return invokeAsync(
|
||||
kernel,
|
||||
arguments,
|
||||
kernel.getHookService(),
|
||||
variableType);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -77,13 +81,41 @@ public class KernelFunctionFromMethod extends DefaultKernelFunction {
|
||||
Kernel kernel,
|
||||
@Nullable KernelArguments arguments,
|
||||
@Nullable KernelHooks kernelHooks,
|
||||
ContextVariableType<T> variableType) {
|
||||
@Nullable ContextVariableType<T> variableType) {
|
||||
|
||||
if (kernelHooks == null) {
|
||||
kernelHooks = kernel.getHookService();
|
||||
}
|
||||
|
||||
return function.invoke(kernel, this, arguments, kernelHooks);
|
||||
return function
|
||||
.invoke(kernel, this, arguments, kernelHooks)
|
||||
.map(it -> convertVariableToReturnType(variableType, it));
|
||||
}
|
||||
|
||||
private <T> FunctionResult convertVariableToReturnType(
|
||||
@Nullable ContextVariableType<T> variableType,
|
||||
FunctionResult<Object> it) {
|
||||
if (variableType != null) {
|
||||
return new FunctionResult<>(
|
||||
variableType.of(it.getResult()),
|
||||
it.getMetadata());
|
||||
} else {
|
||||
Object result = it.getResult();
|
||||
if (result != null && getMetadata().getReturnParameter().getParameterType()
|
||||
.isAssignableFrom(result.getClass())) {
|
||||
return new FunctionResult<>(
|
||||
new ContextVariable<>(
|
||||
new ContextVariableType<>(
|
||||
new NoopConverter(
|
||||
getMetadata().getReturnParameter().getParameterType()),
|
||||
getMetadata().getReturnParameter().getParameterType()
|
||||
),
|
||||
it.getResult())
|
||||
);
|
||||
}
|
||||
throw new RuntimeException("Unable to convert result to type: "
|
||||
+ getMetadata().getReturnParameter().getParameterType().getName());
|
||||
}
|
||||
}
|
||||
|
||||
public interface ImplementationFunc {
|
||||
@@ -100,9 +132,13 @@ public class KernelFunctionFromMethod extends DefaultKernelFunction {
|
||||
public static KernelFunction create(
|
||||
Method method,
|
||||
Object target,
|
||||
@Nullable
|
||||
String functionName,
|
||||
@Nullable
|
||||
String description,
|
||||
List<KernelParameterMetadata> parameters,
|
||||
@Nullable
|
||||
List<KernelParameterMetadata<?>> parameters,
|
||||
@Nullable
|
||||
KernelReturnParameterMetadata<?> returnParameter) {
|
||||
|
||||
MethodDetails methodDetails = getMethodDetails(functionName, method, target);
|
||||
@@ -129,6 +165,7 @@ public class KernelFunctionFromMethod extends DefaultKernelFunction {
|
||||
|
||||
|
||||
private static MethodDetails getMethodDetails(
|
||||
@Nullable
|
||||
String functionName,
|
||||
Method method,
|
||||
Object target) {
|
||||
@@ -142,12 +179,19 @@ public class KernelFunctionFromMethod extends DefaultKernelFunction {
|
||||
returnDescription = annotation.returnDescription();
|
||||
}
|
||||
|
||||
if (functionName == null) {
|
||||
functionName = method.getName();
|
||||
}
|
||||
|
||||
return new MethodDetails(
|
||||
functionName,
|
||||
description,
|
||||
getFunction(method, target),
|
||||
getParameters(method),
|
||||
new KernelReturnParameterMetadata<>(returnDescription, method.getReturnType())
|
||||
new KernelReturnParameterMetadata<>(
|
||||
returnDescription,
|
||||
method.getReturnType()
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
@@ -193,11 +237,30 @@ public class KernelFunctionFromMethod extends DefaultKernelFunction {
|
||||
});
|
||||
|
||||
return r
|
||||
.map(it -> new FunctionResult<>(ContextVariable.of(it)))
|
||||
.map(it -> {
|
||||
return new FunctionResult(
|
||||
new ContextVariable(
|
||||
new ContextVariableType(
|
||||
new NoopConverter(
|
||||
function
|
||||
.getMetadata()
|
||||
.getReturnParameter()
|
||||
.getParameterType()
|
||||
),
|
||||
function
|
||||
.getMetadata()
|
||||
.getReturnParameter()
|
||||
.getParameterType()
|
||||
),
|
||||
it));
|
||||
})
|
||||
.map(it -> {
|
||||
FunctionInvokedEvent<T> updatedResult = kernelHooks
|
||||
.executeHooks(
|
||||
new FunctionInvokedEvent<>(function, updatedArguments, it));
|
||||
new FunctionInvokedEvent<>(
|
||||
function,
|
||||
updatedArguments,
|
||||
it));
|
||||
return updatedResult.getResult();
|
||||
});
|
||||
} catch (Exception e) {
|
||||
@@ -239,6 +302,7 @@ public class KernelFunctionFromMethod extends DefaultKernelFunction {
|
||||
.subscribeOn(Schedulers.boundedElastic()));
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static Function<Parameter, Object> getParameters(
|
||||
Method method,
|
||||
@Nullable
|
||||
@@ -253,6 +317,7 @@ public class KernelFunctionFromMethod extends DefaultKernelFunction {
|
||||
};
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static Object getArgumentValue(
|
||||
Method method,
|
||||
@Nullable KernelArguments context,
|
||||
@@ -265,15 +330,16 @@ public class KernelFunctionFromMethod extends DefaultKernelFunction {
|
||||
KernelFunctionParameter annotation =
|
||||
parameter.getAnnotation(KernelFunctionParameter.class);
|
||||
if (annotation != null) {
|
||||
// Convert from the defaultValue, which is a String to the argument type
|
||||
// Expectation here is that the fromPromptString method will be able to handle a null or empty string
|
||||
Class<?> type = annotation.type();
|
||||
ContextVariableType<?> cvType = ContextVariableTypes.getDefaultVariableTypeForClass(type);
|
||||
if (cvType != null) {
|
||||
String defaultValue = annotation.defaultValue();
|
||||
Object value = cvType.getConverter().fromPromptString(defaultValue);
|
||||
arg = ContextVariable.of(value);
|
||||
}
|
||||
// Convert from the defaultValue, which is a String to the argument type
|
||||
// Expectation here is that the fromPromptString method will be able to handle a null or empty string
|
||||
Class<?> type = annotation.type();
|
||||
ContextVariableType<?> cvType = ContextVariableTypes.getDefaultVariableTypeForClass(
|
||||
type);
|
||||
if (cvType != null) {
|
||||
String defaultValue = annotation.defaultValue();
|
||||
Object value = cvType.getConverter().fromPromptString(defaultValue);
|
||||
arg = ContextVariable.untypedOf(value, cvType.getConverter());
|
||||
}
|
||||
|
||||
if (arg != null && NO_DEFAULT_VALUE.equals(arg.getValue())) {
|
||||
if (!annotation.required()) {
|
||||
@@ -331,19 +397,22 @@ public class KernelFunctionFromMethod extends DefaultKernelFunction {
|
||||
|
||||
Object value = arg;
|
||||
|
||||
if (parameter.getType().isAssignableFrom(arg.getValue().getClass())) {
|
||||
return arg.getValue();
|
||||
}
|
||||
if (arg != null) {
|
||||
|
||||
if (isPrimative(arg.getValue().getClass(), parameter.getType())) {
|
||||
return arg.getValue();
|
||||
}
|
||||
if (parameter.getType().isAssignableFrom(arg.getType().getClazz())) {
|
||||
return arg.getValue();
|
||||
}
|
||||
|
||||
ContextVariableTypeConverter<?> c = arg.getType().getConverter();
|
||||
if (isPrimative(arg.getType().getClazz(), parameter.getType())) {
|
||||
return arg.getValue();
|
||||
}
|
||||
|
||||
Object converted = c.toObject(arg.getValue(), parameter.getType());
|
||||
if (converted != null) {
|
||||
return converted;
|
||||
ContextVariableTypeConverter<?> c = arg.getType().getConverter();
|
||||
|
||||
Object converted = c.toObject(arg.getValue(), parameter.getType());
|
||||
if (converted != null) {
|
||||
return converted;
|
||||
}
|
||||
}
|
||||
|
||||
// Well-known types only
|
||||
@@ -366,6 +435,7 @@ public class KernelFunctionFromMethod extends DefaultKernelFunction {
|
||||
return value;
|
||||
}
|
||||
|
||||
@SuppressWarnings("OperatorPrecedence")
|
||||
private static boolean isPrimative(Class<?> argType, Class<?> param) {
|
||||
return (argType == Byte.class || argType == byte.class) && (param == Byte.class
|
||||
|| param == byte.class) ||
|
||||
@@ -415,81 +485,7 @@ public class KernelFunctionFromMethod extends DefaultKernelFunction {
|
||||
+ " was invoked with a required context variable missing and no default value.";
|
||||
}
|
||||
|
||||
private static Set<Parameter> determineInputArgs(Method method) {
|
||||
// Something is bound to the input if either:
|
||||
// - it is annotated with @SKFunctionInputAttribute
|
||||
// - SKFunctionParameters annotation has a name of "input"
|
||||
// - the arg name is "input"
|
||||
// - there is only 1 string argument to the function
|
||||
|
||||
// Get all parameters annotated with SKFunctionInputAttribute
|
||||
List<Parameter> annotated =
|
||||
Arrays.stream(method.getParameters())
|
||||
.filter(it -> it.isAnnotationPresent(KernelFunctionParameter.class))
|
||||
.collect(Collectors.toList());
|
||||
|
||||
if (annotated.size() > 1) {
|
||||
LOGGER.warn(
|
||||
"Multiple arguments of "
|
||||
+ method.getName()
|
||||
+ " have the @SKFunctionInputAttribute annotation. This is likely an"
|
||||
+ " error.");
|
||||
}
|
||||
|
||||
// Get all parameters annotated with SKFunctionParameters with a name of "input"
|
||||
List<Parameter> annotatedWithName =
|
||||
Arrays.stream(method.getParameters())
|
||||
.filter(it -> it.isAnnotationPresent(KernelFunctionParameter.class))
|
||||
.filter(it -> it.getName().equals("input"))
|
||||
.collect(Collectors.toList());
|
||||
|
||||
if (annotatedWithName.size() > 1) {
|
||||
LOGGER.warn(
|
||||
"Multiple arguments of "
|
||||
+ method.getName()
|
||||
+ " have the name input. This is likely an error.");
|
||||
}
|
||||
|
||||
// Get all parameters named "input", this will frequently fail as compilers strip out
|
||||
// argument names
|
||||
List<Parameter> calledInput =
|
||||
Arrays.stream(method.getParameters())
|
||||
.filter(it -> getGetVariableName(it).equals("input"))
|
||||
.collect(Collectors.toList());
|
||||
|
||||
// Get parameter if there is only 1 string, and it has not been annotated with
|
||||
// SKFunctionParameters
|
||||
List<Parameter> soloString =
|
||||
Arrays.stream(method.getParameters())
|
||||
.filter(it -> it.getType().equals(String.class))
|
||||
.filter(
|
||||
it ->
|
||||
!(it.isAnnotationPresent(KernelFunctionParameter.class)
|
||||
&& !it.getAnnotation(KernelFunctionParameter.class)
|
||||
.name()
|
||||
.isEmpty()))
|
||||
.collect(Collectors.toList());
|
||||
if (soloString.size() > 1) {
|
||||
soloString.clear();
|
||||
}
|
||||
|
||||
Set<Parameter> params = new HashSet<>();
|
||||
params.addAll(annotated);
|
||||
params.addAll(annotatedWithName);
|
||||
params.addAll(calledInput);
|
||||
params.addAll(soloString);
|
||||
|
||||
if (params.size() > 1) {
|
||||
LOGGER.warn(
|
||||
"Multiple arguments of "
|
||||
+ method.getName()
|
||||
+ " are bound to the input variable. This is likely an error.");
|
||||
}
|
||||
|
||||
return params;
|
||||
}
|
||||
|
||||
private static List<KernelParameterMetadata> getParameters(Method method) {
|
||||
private static List<KernelParameterMetadata<?>> getParameters(Method method) {
|
||||
return
|
||||
Arrays.stream(method
|
||||
.getParameters())
|
||||
@@ -497,7 +493,7 @@ public class KernelFunctionFromMethod extends DefaultKernelFunction {
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
private static KernelParameterMetadata toKernelParameterMetadata(Parameter parameter) {
|
||||
private static KernelParameterMetadata<?> toKernelParameterMetadata(Parameter parameter) {
|
||||
KernelFunctionParameter annotation = parameter.getAnnotation(
|
||||
KernelFunctionParameter.class);
|
||||
|
||||
@@ -505,19 +501,22 @@ public class KernelFunctionFromMethod extends DefaultKernelFunction {
|
||||
String description = null;
|
||||
String defaultValue = null;
|
||||
boolean isRequired = true;
|
||||
Class<?> type = parameter.getType();
|
||||
|
||||
if (annotation != null) {
|
||||
name = annotation.name();
|
||||
description = annotation.description();
|
||||
defaultValue = annotation.defaultValue();
|
||||
isRequired = annotation.required();
|
||||
type = annotation.type();
|
||||
}
|
||||
|
||||
return new KernelParameterMetadata(
|
||||
return new KernelParameterMetadata<>(
|
||||
name,
|
||||
description,
|
||||
null,
|
||||
defaultValue, isRequired
|
||||
type,
|
||||
defaultValue,
|
||||
isRequired
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
+106
-30
@@ -8,6 +8,7 @@ import com.microsoft.semantickernel.Kernel;
|
||||
import com.microsoft.semantickernel.TextAIService;
|
||||
import com.microsoft.semantickernel.chatcompletion.AuthorRole;
|
||||
import com.microsoft.semantickernel.chatcompletion.ChatCompletionService;
|
||||
import com.microsoft.semantickernel.exceptions.SKException;
|
||||
import com.microsoft.semantickernel.hooks.FunctionInvokedEvent;
|
||||
import com.microsoft.semantickernel.hooks.FunctionInvokingEvent;
|
||||
import com.microsoft.semantickernel.hooks.KernelHooks;
|
||||
@@ -21,6 +22,7 @@ import com.microsoft.semantickernel.orchestration.PromptExecutionSettings;
|
||||
import com.microsoft.semantickernel.orchestration.contextvariables.ContextVariable;
|
||||
import com.microsoft.semantickernel.orchestration.contextvariables.ContextVariableType;
|
||||
import com.microsoft.semantickernel.orchestration.contextvariables.ContextVariableTypes;
|
||||
import com.microsoft.semantickernel.orchestration.contextvariables.DefaultKernelArguments;
|
||||
import com.microsoft.semantickernel.orchestration.contextvariables.KernelArguments;
|
||||
import com.microsoft.semantickernel.services.AIServiceSelection;
|
||||
import com.microsoft.semantickernel.textcompletion.TextGenerationService;
|
||||
@@ -28,6 +30,7 @@ import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
import javax.annotation.Nullable;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
@@ -47,7 +50,7 @@ public class KernelFunctionFromPrompt extends DefaultKernelFunction {
|
||||
Map<String, PromptExecutionSettings> executionSettings) {
|
||||
super(
|
||||
new KernelFunctionMetadata(
|
||||
promptConfig.getName(),
|
||||
getName(promptConfig),
|
||||
promptConfig.getDescription(),
|
||||
promptConfig.getKernelParametersMetadata(),
|
||||
promptConfig.getKernelReturnParameterMetadata()
|
||||
@@ -57,6 +60,14 @@ public class KernelFunctionFromPrompt extends DefaultKernelFunction {
|
||||
this.template = template;
|
||||
}
|
||||
|
||||
private static String getName(PromptTemplateConfig promptConfig) {
|
||||
if (promptConfig.getName() == null) {
|
||||
return UUID.randomUUID().toString();
|
||||
} else {
|
||||
return promptConfig.getName();
|
||||
}
|
||||
}
|
||||
|
||||
public static KernelFunction create(
|
||||
PromptTemplateConfig promptConfig
|
||||
) {
|
||||
@@ -91,10 +102,17 @@ public class KernelFunctionFromPrompt extends DefaultKernelFunction {
|
||||
|
||||
private <T> Flux<FunctionResult<T>> invokeInternalAsync(
|
||||
Kernel kernel,
|
||||
@Nullable KernelArguments arguments,
|
||||
@Nullable KernelArguments argumentsIn,
|
||||
KernelHooks kernelHooks,
|
||||
ContextVariableType<T> variableType) {
|
||||
|
||||
KernelArguments arguments;
|
||||
if (argumentsIn == null) {
|
||||
arguments = new DefaultKernelArguments();
|
||||
} else {
|
||||
arguments = argumentsIn;
|
||||
}
|
||||
|
||||
PromptRenderingEvent preRenderingHookResult = kernelHooks
|
||||
.executeHooks(new PromptRenderingEvent(this, arguments));
|
||||
|
||||
@@ -121,6 +139,11 @@ public class KernelFunctionFromPrompt extends DefaultKernelFunction {
|
||||
args
|
||||
);
|
||||
|
||||
if (aiServiceSelection == null) {
|
||||
throw new IllegalStateException(
|
||||
"Failed to initialise aiService, could not find any TextAIService implementations");
|
||||
}
|
||||
|
||||
AIService client = aiServiceSelection.getService();
|
||||
if (client == null) {
|
||||
throw new IllegalStateException(
|
||||
@@ -162,8 +185,13 @@ public class KernelFunctionFromPrompt extends DefaultKernelFunction {
|
||||
);
|
||||
} else if (chatMessageContent.getAuthorRole()
|
||||
== AuthorRole.TOOL) {
|
||||
String content = chatMessageContent.getContent();
|
||||
if (content == null || content.isEmpty()) {
|
||||
return Flux.error(new IllegalStateException(
|
||||
"Tool message content is empty"));
|
||||
}
|
||||
Mono<FunctionResult<String>> toolResult = invokeTool(kernel,
|
||||
chatMessageContent.getContent());
|
||||
content);
|
||||
return toolResult.flux();
|
||||
}
|
||||
return Flux.empty();
|
||||
@@ -238,8 +266,8 @@ public class KernelFunctionFromPrompt extends DefaultKernelFunction {
|
||||
if (ex instanceof HttpResponseException
|
||||
&& ((HttpResponseException) ex).getResponse().getStatusCode()
|
||||
== 400
|
||||
&& ex.getMessage()
|
||||
.contains("parameters are not available" + " on")) {
|
||||
&& ex.getMessage() != null
|
||||
&& ex.getMessage().contains("parameters are not available" + " on")) {
|
||||
LOGGER.warn(
|
||||
"This error indicates that you have attempted"
|
||||
+ " to use a chat completion model in a"
|
||||
@@ -258,7 +286,13 @@ public class KernelFunctionFromPrompt extends DefaultKernelFunction {
|
||||
Kernel kernel,
|
||||
@Nullable KernelArguments arguments,
|
||||
@Nullable KernelHooks kernelHooks,
|
||||
@Nullable
|
||||
ContextVariableType<T> variableType) {
|
||||
if (variableType == null) {
|
||||
return Mono.error(new SKException(
|
||||
"Cannot invoke a prompt function without a variable return type. Ensure that there is"
|
||||
+ " a valid type converter for the return type of the prompt function."));
|
||||
}
|
||||
if (kernelHooks == null) {
|
||||
kernelHooks = kernel.getHookService();
|
||||
}
|
||||
@@ -270,7 +304,9 @@ public class KernelFunctionFromPrompt extends DefaultKernelFunction {
|
||||
@Override
|
||||
public <T> Mono<FunctionResult<T>> invokeAsync(
|
||||
Kernel kernel,
|
||||
@Nullable KernelArguments arguments,
|
||||
@Nullable
|
||||
KernelArguments arguments,
|
||||
@Nullable
|
||||
ContextVariableType<T> variableType) {
|
||||
return invokeAsync(kernel, arguments, null, variableType);
|
||||
}
|
||||
@@ -300,6 +336,7 @@ public class KernelFunctionFromPrompt extends DefaultKernelFunction {
|
||||
/*
|
||||
* The jsonNode should represent: {"name":"search-search", "parameters": {"query":"Banksy"}}}
|
||||
*/
|
||||
@SuppressWarnings("StringSplitter")
|
||||
private Mono<FunctionResult<String>> invokeFunction(Kernel kernel, JsonNode jsonNode) {
|
||||
String name = jsonNode.get("name").asText();
|
||||
String[] parts = name.split("-");
|
||||
@@ -307,22 +344,25 @@ public class KernelFunctionFromPrompt extends DefaultKernelFunction {
|
||||
String fnName = parts.length > 1 ? parts[1] : "";
|
||||
JsonNode parameters = jsonNode.get("parameters");
|
||||
if (parameters != null) {
|
||||
KernelFunction kernelFunction = kernel.getPlugins().getFunction(pluginName, fnName);
|
||||
if (kernelFunction == null) {
|
||||
return Mono.empty();
|
||||
try {
|
||||
KernelFunction kernelFunction = kernel.getPlugins().getFunction(pluginName, fnName);
|
||||
|
||||
ContextVariableType<String> variableType = ContextVariableTypes.getDefaultVariableTypeForClass(
|
||||
String.class);
|
||||
Map<String, ContextVariable<?>> variables = new HashMap<>();
|
||||
parameters.fields().forEachRemaining(entry -> {
|
||||
String paramName = entry.getKey();
|
||||
String paramValue = entry.getValue().asText();
|
||||
ContextVariable<String> contextVariable = new ContextVariable<>(variableType,
|
||||
paramValue);
|
||||
variables.put(paramName, contextVariable);
|
||||
});
|
||||
KernelArguments arguments = KernelArguments.builder().withVariables(variables)
|
||||
.build();
|
||||
return kernelFunction.invokeAsync(kernel, arguments, variableType);
|
||||
} catch (Exception e) {
|
||||
return Mono.error(e);
|
||||
}
|
||||
ContextVariableType<String> variableType = ContextVariableTypes.getDefaultVariableTypeForClass(
|
||||
String.class);
|
||||
Map<String, ContextVariable<?>> variables = new HashMap<>();
|
||||
parameters.fields().forEachRemaining(entry -> {
|
||||
String paramName = entry.getKey();
|
||||
String paramValue = entry.getValue().asText();
|
||||
ContextVariable<String> contextVariable = new ContextVariable<>(variableType,
|
||||
paramValue);
|
||||
variables.put(paramName, contextVariable);
|
||||
});
|
||||
KernelArguments arguments = KernelArguments.builder().withVariables(variables).build();
|
||||
return kernelFunction.invokeAsync(kernel, arguments, variableType);
|
||||
}
|
||||
return Mono.empty();
|
||||
}
|
||||
@@ -351,6 +391,14 @@ public class KernelFunctionFromPrompt extends DefaultKernelFunction {
|
||||
@Nullable String templateFormat,
|
||||
@Nullable PromptTemplateFactory promptTemplateFactory) {
|
||||
|
||||
if (templateFormat == null) {
|
||||
templateFormat = PromptTemplateConfig.SEMANTIC_KERNEL_TEMPLATE_FORMAT;
|
||||
}
|
||||
|
||||
if (functionName == null) {
|
||||
functionName = UUID.randomUUID().toString();
|
||||
}
|
||||
|
||||
return new KernelFunctionFromPrompt.Builder()
|
||||
.withName(functionName)
|
||||
.withDescription(description)
|
||||
@@ -367,37 +415,50 @@ public class KernelFunctionFromPrompt extends DefaultKernelFunction {
|
||||
|
||||
public static final class Builder implements FromPromptBuilder {
|
||||
|
||||
@Nullable
|
||||
private PromptTemplate promptTemplate;
|
||||
@Nullable
|
||||
private String name;
|
||||
private Map<String, PromptExecutionSettings> executionSettings;
|
||||
@Nullable
|
||||
private Map<String, PromptExecutionSettings> executionSettings = null;
|
||||
@Nullable
|
||||
private String description;
|
||||
@Nullable
|
||||
private List<InputVariable> inputVariables;
|
||||
@Nullable
|
||||
private String template;
|
||||
private String templateFormat = PromptTemplateConfig.SEMANTIC_KERNEL_TEMPLATE_FORMAT;
|
||||
@Nullable
|
||||
private OutputVariable outputVariable;
|
||||
@Nullable
|
||||
private PromptTemplateFactory promptTemplateFactory;
|
||||
|
||||
|
||||
@Override
|
||||
public FromPromptBuilder withName(String name) {
|
||||
public FromPromptBuilder withName(@Nullable String name) {
|
||||
this.name = name;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public FromPromptBuilder withInputParameters(List<InputVariable> inputVariables) {
|
||||
this.inputVariables = new ArrayList<>(inputVariables);
|
||||
public FromPromptBuilder withInputParameters(@Nullable List<InputVariable> inputVariables) {
|
||||
if (inputVariables != null) {
|
||||
this.inputVariables = new ArrayList<>(inputVariables);
|
||||
} else {
|
||||
this.inputVariables = null;
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public FromPromptBuilder withPromptTemplate(PromptTemplate promptTemplate) {
|
||||
public FromPromptBuilder withPromptTemplate(@Nullable PromptTemplate promptTemplate) {
|
||||
this.promptTemplate = promptTemplate;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public FromPromptBuilder withExecutionSettings(
|
||||
@Nullable
|
||||
Map<String, PromptExecutionSettings> executionSettings) {
|
||||
if (this.executionSettings == null) {
|
||||
this.executionSettings = new HashMap<>();
|
||||
@@ -411,7 +472,11 @@ public class KernelFunctionFromPrompt extends DefaultKernelFunction {
|
||||
|
||||
@Override
|
||||
public FromPromptBuilder withDefaultExecutionSettings(
|
||||
PromptExecutionSettings executionSettings) {
|
||||
@Nullable PromptExecutionSettings executionSettings) {
|
||||
if (executionSettings == null) {
|
||||
return this;
|
||||
}
|
||||
|
||||
if (this.executionSettings == null) {
|
||||
this.executionSettings = new HashMap<>();
|
||||
}
|
||||
@@ -426,13 +491,13 @@ public class KernelFunctionFromPrompt extends DefaultKernelFunction {
|
||||
}
|
||||
|
||||
@Override
|
||||
public FromPromptBuilder withDescription(String description) {
|
||||
public FromPromptBuilder withDescription(@Nullable String description) {
|
||||
this.description = description;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public FromPromptBuilder withTemplate(String template) {
|
||||
public FromPromptBuilder withTemplate(@Nullable String template) {
|
||||
this.template = template;
|
||||
return this;
|
||||
}
|
||||
@@ -445,13 +510,14 @@ public class KernelFunctionFromPrompt extends DefaultKernelFunction {
|
||||
}
|
||||
|
||||
@Override
|
||||
public FromPromptBuilder withOutputVariable(OutputVariable outputVariable) {
|
||||
public FromPromptBuilder withOutputVariable(@Nullable OutputVariable outputVariable) {
|
||||
this.outputVariable = outputVariable;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public FromPromptBuilder withPromptTemplateFactory(
|
||||
@Nullable
|
||||
PromptTemplateFactory promptTemplateFactory) {
|
||||
this.promptTemplateFactory = promptTemplateFactory;
|
||||
return this;
|
||||
@@ -459,6 +525,11 @@ public class KernelFunctionFromPrompt extends DefaultKernelFunction {
|
||||
|
||||
@Override
|
||||
public KernelFunction build() {
|
||||
|
||||
if (template == null) {
|
||||
throw new IllegalStateException("Template must be provided");
|
||||
}
|
||||
|
||||
PromptTemplateConfig config = new PromptTemplateConfig(
|
||||
name,
|
||||
template,
|
||||
@@ -483,6 +554,11 @@ public class KernelFunctionFromPrompt extends DefaultKernelFunction {
|
||||
}
|
||||
|
||||
public KernelFunction build(PromptTemplateConfig functionModel) {
|
||||
|
||||
if (promptTemplate == null) {
|
||||
throw new IllegalStateException("Template must be provided");
|
||||
}
|
||||
|
||||
return new KernelFunctionFromPrompt(
|
||||
promptTemplate,
|
||||
functionModel,
|
||||
|
||||
+2
-1
@@ -5,11 +5,12 @@ import static com.microsoft.semantickernel.semanticfunctions.PromptTemplateConfi
|
||||
|
||||
import com.microsoft.semantickernel.templateengine.handlebars.HandlebarsPromptTemplate;
|
||||
import com.microsoft.semantickernel.templateengine.semantickernel.DefaultPromptTemplate;
|
||||
import javax.annotation.Nonnull;
|
||||
import java.util.Locale;
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
public class KernelPromptTemplateFactory implements PromptTemplateFactory {
|
||||
|
||||
@Override
|
||||
public PromptTemplate tryCreate(@Nonnull PromptTemplateConfig templateConfig) {
|
||||
switch (templateConfig.getTemplateFormat().toLowerCase(Locale.ROOT)) {
|
||||
case SEMANTIC_KERNEL_TEMPLATE_FORMAT:
|
||||
|
||||
+32
-12
@@ -23,18 +23,15 @@ public class PromptTemplateConfig {
|
||||
|
||||
public static final int CURRENT_SCHEMA = 1;
|
||||
public static final String DEFAULT_CONFIG_NAME = "default";
|
||||
@Nullable
|
||||
private String name;
|
||||
|
||||
private String template;
|
||||
|
||||
private String templateFormat;
|
||||
|
||||
@Nullable
|
||||
private String description;
|
||||
|
||||
private List<InputVariable> inputVariables;
|
||||
|
||||
@Nullable
|
||||
private OutputVariable outputVariable;
|
||||
|
||||
private Map<String, PromptExecutionSettings> executionSettings;
|
||||
|
||||
public static final String SEMANTIC_KERNEL_TEMPLATE_FORMAT = "semantic-kernel";
|
||||
@@ -56,6 +53,7 @@ public class PromptTemplateConfig {
|
||||
public PromptTemplateConfig(
|
||||
@JsonProperty("schema")
|
||||
int schema,
|
||||
@Nullable
|
||||
@JsonProperty("name")
|
||||
String name,
|
||||
@JsonProperty("template")
|
||||
@@ -65,12 +63,16 @@ public class PromptTemplateConfig {
|
||||
value = "template_format",
|
||||
defaultValue = SEMANTIC_KERNEL_TEMPLATE_FORMAT)
|
||||
String templateFormat,
|
||||
@Nullable
|
||||
@JsonProperty("description")
|
||||
String description,
|
||||
@Nullable
|
||||
@JsonProperty("input_variables")
|
||||
List<InputVariable> inputVariables,
|
||||
@Nullable
|
||||
@JsonProperty("output_variable")
|
||||
OutputVariable outputVariable,
|
||||
@Nullable
|
||||
@JsonProperty("execution_settings")
|
||||
Map<String, PromptExecutionSettings> executionSettings) {
|
||||
this.name = name;
|
||||
@@ -104,8 +106,19 @@ public class PromptTemplateConfig {
|
||||
* @param outputVariable Output variable
|
||||
* @param executionSettings Execution settings
|
||||
*/
|
||||
public PromptTemplateConfig(String name, String template, String templateFormat,
|
||||
String description, List<InputVariable> inputVariables, OutputVariable outputVariable,
|
||||
public PromptTemplateConfig(
|
||||
@Nullable
|
||||
String name,
|
||||
String template,
|
||||
@Nullable
|
||||
String templateFormat,
|
||||
@Nullable
|
||||
String description,
|
||||
@Nullable
|
||||
List<InputVariable> inputVariables,
|
||||
@Nullable
|
||||
OutputVariable outputVariable,
|
||||
@Nullable
|
||||
Map<String, PromptExecutionSettings> executionSettings) {
|
||||
this(
|
||||
CURRENT_SCHEMA,
|
||||
@@ -131,16 +144,16 @@ public class PromptTemplateConfig {
|
||||
);
|
||||
}
|
||||
|
||||
public List<KernelParameterMetadata> getKernelParametersMetadata() {
|
||||
public List<KernelParameterMetadata<?>> getKernelParametersMetadata() {
|
||||
if (inputVariables == null) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
return inputVariables
|
||||
.stream()
|
||||
.map(inputVariable -> new KernelParameterMetadata(
|
||||
.map(inputVariable -> new KernelParameterMetadata<>(
|
||||
inputVariable.getName(),
|
||||
inputVariable.getDescription(),
|
||||
null,
|
||||
inputVariable.getTypeClass(),
|
||||
inputVariable.getDefaultValue(), inputVariable.isRequired()
|
||||
))
|
||||
.collect(Collectors.toList());
|
||||
@@ -161,6 +174,7 @@ public class PromptTemplateConfig {
|
||||
inputVariables.add(inputVariable);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
@@ -186,6 +200,7 @@ public class PromptTemplateConfig {
|
||||
this.templateFormat = templateFormat;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public String getDescription() {
|
||||
return description;
|
||||
}
|
||||
@@ -202,6 +217,7 @@ public class PromptTemplateConfig {
|
||||
this.inputVariables = new ArrayList<>(inputVariables);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public OutputVariable getOutputVariable() {
|
||||
return outputVariable;
|
||||
}
|
||||
@@ -210,8 +226,12 @@ public class PromptTemplateConfig {
|
||||
this.outputVariable = outputVariable;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public Map<String, PromptExecutionSettings> getExecutionSettings() {
|
||||
return Collections.unmodifiableMap(executionSettings);
|
||||
if (executionSettings != null) {
|
||||
return Collections.unmodifiableMap(executionSettings);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
|
||||
+3
-1
@@ -1,5 +1,7 @@
|
||||
package com.microsoft.semantickernel.semanticfunctions;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
public interface PromptTemplateFactory {
|
||||
|
||||
PromptTemplate tryCreate(PromptTemplateConfig templateConfig);
|
||||
@@ -10,7 +12,7 @@ public interface PromptTemplateFactory {
|
||||
|
||||
class UnknownTemplateFormatException extends IllegalArgumentException {
|
||||
|
||||
public UnknownTemplateFormatException(String templateFormat) {
|
||||
public UnknownTemplateFormatException(@Nullable String templateFormat) {
|
||||
super("Unknown template format: " + templateFormat);
|
||||
}
|
||||
}
|
||||
|
||||
+4
-1
@@ -3,6 +3,7 @@ package com.microsoft.semantickernel.services;
|
||||
|
||||
import com.microsoft.semantickernel.AIService;
|
||||
import com.microsoft.semantickernel.orchestration.PromptExecutionSettings;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
/**
|
||||
* The result of an AI service selection.
|
||||
@@ -10,6 +11,7 @@ import com.microsoft.semantickernel.orchestration.PromptExecutionSettings;
|
||||
public class AIServiceSelection<T extends AIService> {
|
||||
|
||||
private final T service;
|
||||
@Nullable
|
||||
private final PromptExecutionSettings settings;
|
||||
|
||||
/**
|
||||
@@ -19,7 +21,7 @@ public class AIServiceSelection<T extends AIService> {
|
||||
* @param settings The settings associated with the selected service. This may be null even if a
|
||||
* service is selected..
|
||||
*/
|
||||
public AIServiceSelection(T service, PromptExecutionSettings settings) {
|
||||
public AIServiceSelection(T service, @Nullable PromptExecutionSettings settings) {
|
||||
this.service = service;
|
||||
this.settings = settings;
|
||||
}
|
||||
@@ -39,6 +41,7 @@ public class AIServiceSelection<T extends AIService> {
|
||||
* @return The settings associated with the selected service. This may be null even if a service
|
||||
* is selected.
|
||||
*/
|
||||
@Nullable
|
||||
public PromptExecutionSettings getSettings() {
|
||||
return settings;
|
||||
}
|
||||
|
||||
+15
-12
@@ -29,6 +29,7 @@ public class OrderedAIServiceSelector extends BaseAIServiceSelector {
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public <T extends AIService> AIServiceSelection<T> trySelectAIService(
|
||||
Class<T> serviceType,
|
||||
@Nullable
|
||||
@@ -38,9 +39,7 @@ public class OrderedAIServiceSelector extends BaseAIServiceSelector {
|
||||
Map<Class<? extends AIService>, AIService> services) {
|
||||
|
||||
// Allow the execution settings from the kernel arguments to take precedence
|
||||
Map<String, PromptExecutionSettings> executionSettings = null;
|
||||
|
||||
executionSettings = settingsFromArguments(arguments, executionSettings);
|
||||
Map<String, PromptExecutionSettings> executionSettings = settingsFromArguments(arguments);
|
||||
executionSettings = settingsFromFunctionSettings(function, executionSettings);
|
||||
|
||||
if (executionSettings == null || executionSettings.isEmpty()) {
|
||||
@@ -105,12 +104,14 @@ public class OrderedAIServiceSelector extends BaseAIServiceSelector {
|
||||
}
|
||||
|
||||
AIService service = getAnyService(serviceType);
|
||||
PromptExecutionSettings settings;
|
||||
PromptExecutionSettings settings = null;
|
||||
|
||||
if (executionSettings.containsKey(PromptExecutionSettings.DEFAULT_SERVICE_ID)) {
|
||||
settings = executionSettings.get(PromptExecutionSettings.DEFAULT_SERVICE_ID);
|
||||
} else {
|
||||
settings = executionSettings.values().stream().findFirst().orElseGet(() -> null);
|
||||
if (executionSettings != null && !executionSettings.isEmpty()) {
|
||||
if (executionSettings.containsKey(PromptExecutionSettings.DEFAULT_SERVICE_ID)) {
|
||||
settings = executionSettings.get(PromptExecutionSettings.DEFAULT_SERVICE_ID);
|
||||
} else {
|
||||
settings = executionSettings.values().stream().findFirst().orElseGet(() -> null);
|
||||
}
|
||||
}
|
||||
|
||||
if (service != null) {
|
||||
@@ -125,6 +126,7 @@ public class OrderedAIServiceSelector extends BaseAIServiceSelector {
|
||||
private static Map<String, PromptExecutionSettings> settingsFromFunctionSettings(
|
||||
@Nullable
|
||||
KernelFunction function,
|
||||
@Nullable
|
||||
Map<String, PromptExecutionSettings> executionSettings) {
|
||||
if (executionSettings == null || executionSettings.isEmpty()) {
|
||||
if (function != null) {
|
||||
@@ -136,12 +138,12 @@ public class OrderedAIServiceSelector extends BaseAIServiceSelector {
|
||||
|
||||
@Nullable
|
||||
private static Map<String, PromptExecutionSettings> settingsFromArguments(
|
||||
@Nullable KernelArguments arguments,
|
||||
Map<String, PromptExecutionSettings> executionSettings) {
|
||||
@Nullable
|
||||
KernelArguments arguments) {
|
||||
if (arguments != null) {
|
||||
executionSettings = arguments.getExecutionSettings();
|
||||
return arguments.getExecutionSettings();
|
||||
}
|
||||
return executionSettings;
|
||||
return null;
|
||||
}
|
||||
|
||||
private AIService getServiceByModelId(String modelId) {
|
||||
@@ -189,6 +191,7 @@ public class OrderedAIServiceSelector extends BaseAIServiceSelector {
|
||||
return service;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
AIService getAnyService(Class<? extends AIService> serviceType) {
|
||||
List<AIService> services = getServices(serviceType);
|
||||
if (services.isEmpty()) {
|
||||
|
||||
+25
-11
@@ -10,6 +10,7 @@ import com.microsoft.semantickernel.chatcompletion.ChatHistory;
|
||||
import com.microsoft.semantickernel.chatcompletion.ChatMessageContent;
|
||||
import com.microsoft.semantickernel.orchestration.KernelFunction;
|
||||
import com.microsoft.semantickernel.orchestration.contextvariables.ContextVariable;
|
||||
import com.microsoft.semantickernel.orchestration.contextvariables.DefaultKernelArguments;
|
||||
import com.microsoft.semantickernel.orchestration.contextvariables.KernelArguments;
|
||||
import com.microsoft.semantickernel.plugin.KernelParameterMetadata;
|
||||
import com.microsoft.semantickernel.plugin.KernelPlugin;
|
||||
@@ -42,22 +43,27 @@ public class HandlebarsPromptTemplate implements PromptTemplate {
|
||||
|
||||
|
||||
@Override
|
||||
public Mono<String> renderAsync(Kernel kernel,
|
||||
public Mono<String> renderAsync(
|
||||
Kernel kernel,
|
||||
@Nullable KernelArguments arguments) {
|
||||
HandleBarsPromptTemplateHandler handler =
|
||||
new HandleBarsPromptTemplateHandler(kernel, promptTemplate.getTemplate());
|
||||
|
||||
if (arguments == null) {
|
||||
arguments = new DefaultKernelArguments();
|
||||
}
|
||||
return handler.render(arguments);
|
||||
}
|
||||
|
||||
private static class MessageResolver implements ValueResolver {
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("NullAway")
|
||||
public Object resolve(Object context, String name) {
|
||||
if (context instanceof ChatMessageContent) {
|
||||
if ("role".equals(name.toLowerCase())) {
|
||||
if ("role".equalsIgnoreCase(name)) {
|
||||
return ((ChatMessageContent) context).getAuthorRole().name();
|
||||
} else if ("content".equals(name.toLowerCase())) {
|
||||
} else if ("content".equalsIgnoreCase(name)) {
|
||||
return ((ChatMessageContent) context).getContent();
|
||||
}
|
||||
}
|
||||
@@ -67,7 +73,8 @@ public class HandlebarsPromptTemplate implements PromptTemplate {
|
||||
@Override
|
||||
public Object resolve(Object context) {
|
||||
if (context instanceof ChatMessageContent) {
|
||||
return ((ChatMessageContent) context).getContent();
|
||||
String result = ((ChatMessageContent) context).getContent();
|
||||
return result != null ? result : UNRESOLVED;
|
||||
}
|
||||
return UNRESOLVED;
|
||||
}
|
||||
@@ -93,9 +100,9 @@ public class HandlebarsPromptTemplate implements PromptTemplate {
|
||||
}
|
||||
if (context instanceof KernelFunction) {
|
||||
KernelFunction function = (KernelFunction) context;
|
||||
if ("pluginname".equals(name.toLowerCase())) {
|
||||
if ("pluginname".equalsIgnoreCase(name)) {
|
||||
return function.getSkillName();
|
||||
} else if ("name".equals(name.toLowerCase())) {
|
||||
} else if ("name".equalsIgnoreCase(name)) {
|
||||
return function.getName();
|
||||
}
|
||||
}
|
||||
@@ -120,19 +127,26 @@ public class HandlebarsPromptTemplate implements PromptTemplate {
|
||||
|
||||
@Override
|
||||
public Object resolve(Object context, String name) {
|
||||
Object value = null;
|
||||
if (context instanceof KernelArguments) {
|
||||
return ((KernelArguments) context).get(name).getValue();
|
||||
ContextVariable<?> variable = ((KernelArguments) context).get(name);
|
||||
value = variable != null ? variable.getValue() : null;
|
||||
}
|
||||
if (context instanceof ContextVariable) {
|
||||
return ((ContextVariable<?>) context).getValue();
|
||||
value = ((ContextVariable<?>) context).getValue();
|
||||
}
|
||||
if (value == null) {
|
||||
return UNRESOLVED;
|
||||
} else {
|
||||
return value;
|
||||
}
|
||||
return UNRESOLVED;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object resolve(Object context) {
|
||||
if (context instanceof ContextVariable) {
|
||||
return ((ContextVariable<?>) context).getValue();
|
||||
Object result = ((ContextVariable<?>) context).getValue();
|
||||
return result != null ? result : UNRESOLVED;
|
||||
}
|
||||
return UNRESOLVED;
|
||||
}
|
||||
@@ -182,7 +196,7 @@ public class HandlebarsPromptTemplate implements PromptTemplate {
|
||||
String.format(
|
||||
"<function pluginName=\"%s\" name=\"%s\" description=\"%s\">",
|
||||
pluginName, functionName, description));
|
||||
List<KernelParameterMetadata> parameters = function.getMetadata().getParameters();
|
||||
List<KernelParameterMetadata<?>> parameters = function.getMetadata().getParameters();
|
||||
parameters.forEach(p -> {
|
||||
sb.append(String.format(
|
||||
"<parameter name=\"%s\" description=\"%s\" defaultValue=\"%s\" isRequired=\"%s\" type=\"%s\"/>",
|
||||
|
||||
+5
-5
@@ -116,7 +116,7 @@ public class CodeTokenizer {
|
||||
for (int nextCharCursor = 1; nextCharCursor < text.length(); nextCharCursor++) {
|
||||
char currentChar = nextChar;
|
||||
nextChar = text.charAt(nextCharCursor);
|
||||
|
||||
|
||||
if (skipNextChar) {
|
||||
skipNextChar = false;
|
||||
continue;
|
||||
@@ -161,7 +161,7 @@ public class CodeTokenizer {
|
||||
spaceSeparatorFound = false;
|
||||
} else if (currentChar == namedArgValuePrefix
|
||||
&& currentTokenType == TokenTypes.NamedArg) {
|
||||
blocks.add(new NamedArgBlock(currentTokenContent.toString()));
|
||||
blocks.add(NamedArgBlock.from(currentTokenContent.toString()));
|
||||
currentTokenContent = new StringBuilder();
|
||||
currentTokenType = TokenTypes.None;
|
||||
spaceSeparatorFound = false;
|
||||
@@ -195,7 +195,7 @@ public class CodeTokenizer {
|
||||
} else if (currentTokenType == TokenTypes.NamedArg && namedArgSeparatorFound
|
||||
&& namedArgValuePrefix != 0) {
|
||||
blocks.add(
|
||||
new NamedArgBlock(currentTokenContent.toString()));
|
||||
NamedArgBlock.from(currentTokenContent.toString()));
|
||||
currentTokenContent = new StringBuilder();
|
||||
namedArgSeparatorFound = false;
|
||||
namedArgValuePrefix = '\0';
|
||||
@@ -277,7 +277,7 @@ public class CodeTokenizer {
|
||||
|
||||
break;
|
||||
case NamedArg:
|
||||
blocks.add(new NamedArgBlock(currentTokenContent.toString()));
|
||||
blocks.add(NamedArgBlock.from(currentTokenContent.toString()));
|
||||
break;
|
||||
|
||||
case None:
|
||||
@@ -305,6 +305,7 @@ public class CodeTokenizer {
|
||||
return c == Symbols.DblQuote || c == Symbols.SglQuote || c == Symbols.EscapeChar;
|
||||
}
|
||||
|
||||
@SuppressWarnings("NullAway")
|
||||
@Nullable
|
||||
private static NamedArgBlock getNamedArg(String tokenContent) {
|
||||
|
||||
@@ -314,7 +315,6 @@ public class CodeTokenizer {
|
||||
if (Verify.isNullOrEmpty(name) || Verify.isNullOrEmpty(value)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
NamedArgBlock block = new NamedArgBlock(tokenContent, name, value);
|
||||
if (block.isValid()) {
|
||||
return block;
|
||||
|
||||
+3
-4
@@ -63,10 +63,8 @@ public class DefaultPromptTemplate implements PromptTemplate {
|
||||
/// Augments <paramref name="config"/>'s <see cref="PromptTemplateConfig.InputVariables"/> with any variables
|
||||
/// not already contained there but that are referenced in the prompt template.
|
||||
/// </summary>
|
||||
@SuppressWarnings("NullAway")
|
||||
private void addMissingInputVariables(List<Block> blocks) {
|
||||
if (promptTemplate == null) {
|
||||
return;
|
||||
}
|
||||
// Add all of the existing input variables to our known set. We'll avoid adding any
|
||||
// dynamically discovered input variables with the same name.
|
||||
Set<String> seen = new HashSet<>();
|
||||
@@ -84,7 +82,8 @@ public class DefaultPromptTemplate implements PromptTemplate {
|
||||
if (block.getType() == BlockTypes.Variable) {
|
||||
name = ((VarBlock) block).getName();
|
||||
} else if (block.getType() == BlockTypes.NamedArg) {
|
||||
name = ((NamedArgBlock) block).getVarBlock().getName();
|
||||
VarBlock blockName = ((NamedArgBlock) block).getVarBlock();
|
||||
name = blockName == null ? null : blockName.getName();
|
||||
}
|
||||
|
||||
if (!Verify.isNullOrEmpty(name) && !seen.contains(name)) {
|
||||
|
||||
+14
-23
@@ -7,10 +7,12 @@ import javax.annotation.Nullable;
|
||||
|
||||
public class TemplateException extends SKException {
|
||||
|
||||
@Nonnull private final ErrorCodes errorCode;
|
||||
@Nonnull
|
||||
private final ErrorCodes errorCode;
|
||||
|
||||
/**
|
||||
* Initializes a new instance of the {@code TemplateException} class with a provided error code.
|
||||
* Initializes a new instance of the {@code TemplateException} class with a provided error
|
||||
* code.
|
||||
*
|
||||
* @param error The error code.
|
||||
*/
|
||||
@@ -19,26 +21,28 @@ public class TemplateException extends SKException {
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes a new instance of the {@code TemplateException} class with a provided error code.
|
||||
* Initializes a new instance of the {@code TemplateException} class with a provided error
|
||||
* code.
|
||||
*
|
||||
* @param errorCode The error code.
|
||||
* @param message The exception message.
|
||||
* @param message The exception message.
|
||||
*/
|
||||
public TemplateException(@Nonnull ErrorCodes errorCode, @Nullable String message) {
|
||||
this(errorCode, message, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes a new instance of the {@code TemplateException} class with a provided error code.
|
||||
* Initializes a new instance of the {@code TemplateException} class with a provided error
|
||||
* code.
|
||||
*
|
||||
* @param errorCode The error code.
|
||||
* @param message The exception message.
|
||||
* @param errorCode The error code.
|
||||
* @param message The exception message.
|
||||
* @param innerException The exception that is the cause of the current exception.
|
||||
*/
|
||||
public TemplateException(
|
||||
@Nonnull ErrorCodes errorCode,
|
||||
@Nullable String message,
|
||||
@Nullable Throwable innerException) {
|
||||
@Nonnull ErrorCodes errorCode,
|
||||
@Nullable String message,
|
||||
@Nullable Throwable innerException) {
|
||||
super(formatDefaultMessage(errorCode.getMessage(), message), innerException);
|
||||
this.errorCode = errorCode;
|
||||
}
|
||||
@@ -57,19 +61,6 @@ public class TemplateException extends SKException {
|
||||
|
||||
// spotless:off
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Translate the error code into a default message.
|
||||
* </p>
|
||||
*/
|
||||
//spotless:on
|
||||
private static String getDefaultMessage(
|
||||
@Nonnull ErrorCodes errorCode, @Nullable String message) {
|
||||
return String.format("%s: %s", errorCode.getMessage(), message);
|
||||
}
|
||||
|
||||
// spotless:off
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Error codes for {@code TemplateException}.
|
||||
|
||||
+10
-3
@@ -1,8 +1,13 @@
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
package com.microsoft.semantickernel.templateengine.semantickernel.blocks;
|
||||
|
||||
/** Base class for blocks parsed from a prompt template */
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
/**
|
||||
* Base class for blocks parsed from a prompt template
|
||||
*/
|
||||
public abstract class Block {
|
||||
|
||||
private final String content;
|
||||
private final BlockTypes type;
|
||||
|
||||
@@ -10,9 +15,11 @@ public abstract class Block {
|
||||
* Base constructor
|
||||
*
|
||||
* @param content Block content
|
||||
* @param type Block type
|
||||
* @param type Block type
|
||||
*/
|
||||
public Block(String content, BlockTypes type) {
|
||||
public Block(
|
||||
@Nullable String content,
|
||||
BlockTypes type) {
|
||||
if (content == null) {
|
||||
content = "";
|
||||
}
|
||||
|
||||
+15
-9
@@ -3,19 +3,18 @@ package com.microsoft.semantickernel.templateengine.semantickernel.blocks;
|
||||
|
||||
import com.microsoft.semantickernel.Kernel;
|
||||
import com.microsoft.semantickernel.exceptions.SKException;
|
||||
import com.microsoft.semantickernel.orchestration.FunctionResult;
|
||||
import com.microsoft.semantickernel.orchestration.KernelFunctionMetadata;
|
||||
import com.microsoft.semantickernel.orchestration.contextvariables.ContextVariable;
|
||||
import com.microsoft.semantickernel.orchestration.contextvariables.ContextVariableType;
|
||||
import com.microsoft.semantickernel.orchestration.contextvariables.ContextVariableTypes;
|
||||
import com.microsoft.semantickernel.orchestration.contextvariables.DefaultKernelArguments;
|
||||
import com.microsoft.semantickernel.orchestration.FunctionResult;
|
||||
import com.microsoft.semantickernel.orchestration.contextvariables.KernelArguments;
|
||||
import com.microsoft.semantickernel.templateengine.semantickernel.TemplateException;
|
||||
import java.util.ArrayList;
|
||||
import com.microsoft.semantickernel.templateengine.semantickernel.TemplateException.ErrorCodes;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Optional;
|
||||
import javax.annotation.Nullable;
|
||||
import org.slf4j.Logger;
|
||||
@@ -81,7 +80,7 @@ public final class CodeBlock extends Block implements CodeRendering {
|
||||
@Override
|
||||
public Mono<String> renderCodeAsync(Kernel kernel, @Nullable KernelArguments arguments) {
|
||||
if (!this.isValid()) {
|
||||
throw new TemplateException(TemplateException.ErrorCodes.SYNTAX_ERROR);
|
||||
throw new TemplateException(ErrorCodes.SYNTAX_ERROR);
|
||||
}
|
||||
|
||||
// this.Log.LogTrace("Rendering code: `{0}`", this.Content);
|
||||
@@ -149,7 +148,9 @@ public final class CodeBlock extends Block implements CodeRendering {
|
||||
/// <param name="arguments">The prompt rendering arguments.</param>
|
||||
/// <returns>The function arguments.</returns>
|
||||
/// <exception cref="KernelException">Occurs when any argument other than the first is not a named argument.</exception>
|
||||
private KernelArguments enrichFunctionArguments(Kernel kernel, FunctionIdBlock fBlock,
|
||||
private KernelArguments enrichFunctionArguments(
|
||||
Kernel kernel,
|
||||
FunctionIdBlock fBlock,
|
||||
KernelArguments arguments) {
|
||||
Block firstArg = this.tokens.get(1);
|
||||
|
||||
@@ -158,14 +159,14 @@ public final class CodeBlock extends Block implements CodeRendering {
|
||||
.getFunction(fBlock.getPluginName(), fBlock.getFunctionName()).getMetadata();
|
||||
|
||||
// Check if the function has parameters to be set
|
||||
if (functionMetadata.getParameters().size() == 0) {
|
||||
if (functionMetadata.getParameters().isEmpty()) {
|
||||
throw new IllegalArgumentException(
|
||||
"Function " + fBlock.getPluginName() + "." + fBlock.getFunctionName()
|
||||
+ " does not take any arguments but it is being called in the template with {this._tokens.Count - 1} arguments.");
|
||||
}
|
||||
|
||||
String firstPositionalParameterName = null;
|
||||
Object firstPositionalInputValue = null;
|
||||
Object firstPositionalInputValue;
|
||||
int namedArgsStartIndex = 1;
|
||||
|
||||
if (firstArg.getType() != BlockTypes.NamedArg) {
|
||||
@@ -175,6 +176,12 @@ public final class CodeBlock extends Block implements CodeRendering {
|
||||
firstPositionalInputValue = ((TextRendering) tokens.get(1)).render(arguments);
|
||||
// Type check is avoided and marshalling is done by the function itself
|
||||
|
||||
if (firstPositionalInputValue == null) {
|
||||
throw new SKException(
|
||||
"Unexpected null value for first positional argument: " + tokens.get(1)
|
||||
.getContent());
|
||||
}
|
||||
|
||||
// Keep previous trust information when updating the input
|
||||
arguments.put(firstPositionalParameterName,
|
||||
ContextVariable.of(firstPositionalInputValue));
|
||||
@@ -191,8 +198,7 @@ public final class CodeBlock extends Block implements CodeRendering {
|
||||
|
||||
// Check if the positional parameter clashes with a named parameter
|
||||
if (firstPositionalParameterName != null &&
|
||||
firstPositionalParameterName.toLowerCase(Locale.ROOT)
|
||||
.equals(arg.getName().toLowerCase(Locale.ROOT))) {
|
||||
firstPositionalParameterName.equalsIgnoreCase(arg.getName())) {
|
||||
throw new SKException(
|
||||
"Ambiguity found as a named parameter '{arg.Name}' cannot be set for the first parameter when there is also a positional value: '{firstPositionalInputValue}' provided. Function: {fBlock.PluginName}.{fBlock.FunctionName}");
|
||||
}
|
||||
|
||||
+1
-1
@@ -31,7 +31,7 @@ public final class FunctionIdBlock extends Block implements TextRendering {
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public String render(KernelArguments variables) {
|
||||
public String render(@Nullable KernelArguments variables) {
|
||||
return this.getContent();
|
||||
}
|
||||
|
||||
|
||||
+41
-8
@@ -12,10 +12,14 @@ public class NamedArgBlock extends Block implements TextRendering {
|
||||
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(NamedArgBlock.class);
|
||||
|
||||
@Nullable
|
||||
private final String name;
|
||||
@Nullable
|
||||
private final String value;
|
||||
private final VarBlock argNameAsVarBlock;
|
||||
@Nullable
|
||||
private final VarBlock varBlock;
|
||||
@Nullable
|
||||
private final ValBlock valBlock;
|
||||
|
||||
public NamedArgBlock(String content, String name, String value) {
|
||||
@@ -34,20 +38,47 @@ public class NamedArgBlock extends Block implements TextRendering {
|
||||
}
|
||||
}
|
||||
|
||||
public NamedArgBlock(String content) {
|
||||
protected NamedArgBlock(
|
||||
String content,
|
||||
@Nullable
|
||||
String name,
|
||||
@Nullable
|
||||
String value,
|
||||
VarBlock argNameAsVarBlock,
|
||||
@Nullable
|
||||
VarBlock varBlock,
|
||||
@Nullable
|
||||
ValBlock valBlock) {
|
||||
super(content, NamedArg);
|
||||
this.name = name;
|
||||
this.value = value;
|
||||
this.argNameAsVarBlock = argNameAsVarBlock;
|
||||
this.varBlock = varBlock;
|
||||
this.valBlock = valBlock;
|
||||
}
|
||||
|
||||
this.name = tryGetName(content);
|
||||
this.value = tryGetValue(content);
|
||||
this.argNameAsVarBlock = new VarBlock(Symbols.VarPrefix + name);
|
||||
public static NamedArgBlock from(String content) {
|
||||
|
||||
String name = tryGetName(content);
|
||||
String value = tryGetValue(content);
|
||||
VarBlock argNameAsVarBlock = new VarBlock(Symbols.VarPrefix + name);
|
||||
|
||||
if (value == null) {
|
||||
throw new IllegalArgumentException("Unable to extract value from: " + content);
|
||||
}
|
||||
|
||||
VarBlock varBlock;
|
||||
ValBlock valBlock;
|
||||
|
||||
if (value.startsWith(String.valueOf(Symbols.VarPrefix))) {
|
||||
this.varBlock = new VarBlock(value);
|
||||
varBlock = new VarBlock(value);
|
||||
valBlock = null;
|
||||
} else {
|
||||
this.valBlock = new ValBlock(value);
|
||||
valBlock = new ValBlock(value);
|
||||
varBlock = null;
|
||||
}
|
||||
|
||||
return new NamedArgBlock(content, name, value, argNameAsVarBlock, varBlock, valBlock);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -72,9 +103,8 @@ public class NamedArgBlock extends Block implements TextRendering {
|
||||
return this.argNameAsVarBlock.isValid();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public String render(KernelArguments variables) {
|
||||
public String render(@Nullable KernelArguments variables) {
|
||||
return getContent();
|
||||
}
|
||||
|
||||
@@ -84,10 +114,12 @@ public class NamedArgBlock extends Block implements TextRendering {
|
||||
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
@SuppressWarnings("NullAway")
|
||||
public String getValue(KernelArguments arguments) {
|
||||
boolean valueIsValidValBlock = this.valBlock != null && this.valBlock.isValid();
|
||||
if (valueIsValidValBlock) {
|
||||
@@ -120,6 +152,7 @@ public class NamedArgBlock extends Block implements TextRendering {
|
||||
return splitAndGetPart(text, 1);
|
||||
}
|
||||
|
||||
@SuppressWarnings("StringSplitter")
|
||||
@Nullable
|
||||
private static String splitAndGetPart(String text, int x) {
|
||||
if (Verify.isNullOrEmpty(text)) {
|
||||
|
||||
+2
-1
@@ -2,6 +2,7 @@
|
||||
package com.microsoft.semantickernel.templateengine.semantickernel.blocks;
|
||||
|
||||
import com.microsoft.semantickernel.orchestration.contextvariables.KernelArguments;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
public final class TextBlock extends Block implements TextRendering {
|
||||
|
||||
@@ -15,7 +16,7 @@ public final class TextBlock extends Block implements TextRendering {
|
||||
}
|
||||
|
||||
@Override
|
||||
public String render(KernelArguments variables) {
|
||||
public String render(@Nullable KernelArguments variables) {
|
||||
return super.getContent();
|
||||
}
|
||||
|
||||
|
||||
+1
-2
@@ -31,8 +31,7 @@ public final class ValBlock extends Block implements TextRendering {
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public String render(KernelArguments variables) {
|
||||
public String render(@Nullable KernelArguments variables) {
|
||||
return value;
|
||||
}
|
||||
|
||||
|
||||
+2
-1
@@ -6,6 +6,7 @@ import com.microsoft.semantickernel.orchestration.contextvariables.KernelArgumen
|
||||
import com.microsoft.semantickernel.templateengine.semantickernel.TemplateException;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
public final class VarBlock extends Block implements TextRendering {
|
||||
|
||||
@@ -23,7 +24,7 @@ public final class VarBlock extends Block implements TextRendering {
|
||||
}
|
||||
|
||||
@Override
|
||||
public String render(KernelArguments variables) {
|
||||
public String render(@Nullable KernelArguments variables) {
|
||||
if (variables == null) {
|
||||
return "";
|
||||
}
|
||||
|
||||
-4
@@ -8,8 +8,4 @@ public class StreamingTextContent extends StreamingKernelContent<TextContent> {
|
||||
super(content, 0, null, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] toByteArray() {
|
||||
return new byte[0];
|
||||
}
|
||||
}
|
||||
|
||||
+1
-2
@@ -2,8 +2,6 @@ package com.microsoft.semantickernel.textcompletion;
|
||||
|
||||
import com.microsoft.semantickernel.KernelContent;
|
||||
import com.microsoft.semantickernel.orchestration.FunctionResultMetadata;
|
||||
import com.microsoft.semantickernel.orchestration.contextvariables.ContextVariable;
|
||||
import java.util.Map;
|
||||
|
||||
public class TextContent extends KernelContent<TextContent> {
|
||||
|
||||
@@ -21,6 +19,7 @@ public class TextContent extends KernelContent<TextContent> {
|
||||
return content;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getContent() {
|
||||
return content;
|
||||
}
|
||||
|
||||
+4
@@ -58,8 +58,11 @@ public interface TextGenerationService extends Buildable, TextAIService {
|
||||
*/
|
||||
abstract class Builder implements SemanticKernelBuilder<TextGenerationService> {
|
||||
|
||||
@Nullable
|
||||
protected String modelId;
|
||||
@Nullable
|
||||
protected OpenAIAsyncClient client;
|
||||
@Nullable
|
||||
protected String serviceId;
|
||||
|
||||
public Builder withModelId(String modelId) {
|
||||
@@ -77,6 +80,7 @@ public interface TextGenerationService extends Buildable, TextAIService {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public abstract TextGenerationService build();
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user