mirror of
https://github.com/microsoft/semantic-kernel.git
synced 2026-10-12 02:19:06 +00:00
Java: Initial implementation of Example09_FunctionTypes (#4493)
### Motivation and Context <!-- Thank you for your contribution to the semantic-kernel repo! Please help reviewers and future users, providing the following information: 1. Why is this change required? 2. What problem does it solve? 3. What scenario does it contribute to? 4. If it fixes an open issue, please link to the issue here. --> ### Description <!-- Describe your changes, the overall approach, the underlying design. These notes will help understanding how your code works. Thanks! --> ### Contribution Checklist <!-- Before submitting this PR, please make sure: --> - [ ] The code builds clean without any errors or warnings - [ ] The PR follows the [SK Contribution Guidelines](https://github.com/microsoft/semantic-kernel/blob/main/CONTRIBUTING.md) and the [pre-submission formatting script](https://github.com/microsoft/semantic-kernel/blob/main/CONTRIBUTING.md#development-scripts) raises no violations - [ ] All unit tests pass, and I have added new tests where possible - [ ] I didn't break anyone 😄
This commit is contained in:
+29
-6
@@ -3,6 +3,9 @@ package com.microsoft.semantickernel.samples.syntaxexamples;
|
||||
|
||||
|
||||
import com.azure.ai.openai.OpenAIAsyncClient;
|
||||
import com.azure.ai.openai.OpenAIClientBuilder;
|
||||
import com.azure.core.credential.AzureKeyCredential;
|
||||
import com.azure.core.credential.KeyCredential;
|
||||
import com.microsoft.semantickernel.DefaultKernel;
|
||||
import com.microsoft.semantickernel.Kernel;
|
||||
import com.microsoft.semantickernel.aiservices.azureopenai.AzureOpenAITextGenerationService;
|
||||
@@ -11,7 +14,6 @@ import com.microsoft.semantickernel.orchestration.PromptExecutionSettings;
|
||||
import com.microsoft.semantickernel.orchestration.contextvariables.ContextVariable;
|
||||
import com.microsoft.semantickernel.orchestration.contextvariables.KernelArguments;
|
||||
import com.microsoft.semantickernel.plugin.KernelFunctionFactory;
|
||||
import com.microsoft.semantickernel.samples.SamplesConfig;
|
||||
import com.microsoft.semantickernel.semanticfunctions.KernelFunctionFromPrompt;
|
||||
import com.microsoft.semantickernel.textcompletion.TextGenerationService;
|
||||
import java.time.Instant;
|
||||
@@ -20,14 +22,35 @@ import java.time.format.DateTimeFormatter;
|
||||
|
||||
public class Example05_InlineFunctionDefinition {
|
||||
|
||||
private static final boolean USE_AZURE_CLIENT = Boolean.parseBoolean(
|
||||
System.getenv("USE_AZURE_CLIENT"));
|
||||
private static final String CLIENT_KEY = System.getenv("CLIENT_KEY");
|
||||
|
||||
// Only required if USE_AZURE_CLIENT is true
|
||||
private static final String CLIENT_ENDPOINT = System.getenv("CLIENT_ENDPOINT");
|
||||
|
||||
public static void main(String[] args) throws ConfigurationException {
|
||||
|
||||
OpenAIAsyncClient client = SamplesConfig.getClient();
|
||||
TextGenerationService textGenerationService;
|
||||
|
||||
AzureOpenAITextGenerationService textGenerationService = AzureOpenAITextGenerationService.builder()
|
||||
.withOpenAIAsyncClient(client)
|
||||
.withModelId("text-davinci-003")
|
||||
.build();
|
||||
if (USE_AZURE_CLIENT) {
|
||||
OpenAIAsyncClient client = new OpenAIClientBuilder()
|
||||
.credential(new AzureKeyCredential(CLIENT_KEY))
|
||||
.endpoint(CLIENT_ENDPOINT)
|
||||
.buildAsyncClient();
|
||||
|
||||
textGenerationService = AzureOpenAITextGenerationService.builder()
|
||||
.withOpenAIAsyncClient(client)
|
||||
.withModelId("text-davinci-003")
|
||||
.build();
|
||||
} else {
|
||||
OpenAIAsyncClient client = new OpenAIClientBuilder()
|
||||
.credential(new KeyCredential(CLIENT_KEY))
|
||||
.buildAsyncClient();
|
||||
|
||||
// TODO: Add support for OpenAI API
|
||||
textGenerationService = null;
|
||||
}
|
||||
|
||||
Kernel kernel = new DefaultKernel.Builder()
|
||||
.withDefaultAIService(TextGenerationService.class, textGenerationService)
|
||||
|
||||
+1
-2
@@ -8,7 +8,6 @@ import com.azure.core.http.policy.RetryOptions;
|
||||
import com.microsoft.semantickernel.DefaultKernel;
|
||||
import com.microsoft.semantickernel.Kernel;
|
||||
import com.microsoft.semantickernel.aiservices.azureopenai.AzureOpenAITextGenerationService;
|
||||
import com.microsoft.semantickernel.exceptions.ConfigurationException;
|
||||
import com.microsoft.semantickernel.orchestration.KernelFunction;
|
||||
import com.microsoft.semantickernel.semanticfunctions.KernelFunctionFromPrompt;
|
||||
import com.microsoft.semantickernel.textcompletion.TextGenerationService;
|
||||
@@ -16,7 +15,7 @@ import java.time.Duration;
|
||||
|
||||
public class Example08_RetryHandler {
|
||||
|
||||
public static void main(String[] args) throws ConfigurationException {
|
||||
public static void main(String[] args) {
|
||||
// Create a Kernel with the HttpClient
|
||||
RetryOptions retryOptions = new RetryOptions(new ExponentialBackoffOptions()
|
||||
.setMaxDelay(Duration.ofSeconds(10))
|
||||
|
||||
+477
@@ -0,0 +1,477 @@
|
||||
package com.microsoft.semantickernel.samples.syntaxexamples;
|
||||
|
||||
import static com.microsoft.semantickernel.orchestration.contextvariables.ContextVariableTypes.convert;
|
||||
|
||||
import com.azure.ai.openai.OpenAIAsyncClient;
|
||||
import com.azure.ai.openai.OpenAIClientBuilder;
|
||||
import com.azure.core.credential.AzureKeyCredential;
|
||||
import com.azure.core.credential.KeyCredential;
|
||||
import com.microsoft.semantickernel.DefaultKernel;
|
||||
import com.microsoft.semantickernel.Kernel;
|
||||
import com.microsoft.semantickernel.aiservices.azureopenai.AzureOpenAITextGenerationService;
|
||||
import com.microsoft.semantickernel.orchestration.contextvariables.ContextVariable;
|
||||
import com.microsoft.semantickernel.orchestration.contextvariables.ContextVariableTypeConverter;
|
||||
import com.microsoft.semantickernel.orchestration.contextvariables.ContextVariableTypeConverter.NoopConverter;
|
||||
import com.microsoft.semantickernel.orchestration.contextvariables.KernelArguments;
|
||||
import com.microsoft.semantickernel.plugin.KernelPlugin;
|
||||
import com.microsoft.semantickernel.plugin.KernelPluginFactory;
|
||||
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.ZonedDateTime;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
public class Example09_FunctionTypes {
|
||||
|
||||
private static final boolean USE_AZURE_CLIENT = Boolean.parseBoolean(
|
||||
System.getenv("USE_AZURE_CLIENT"));
|
||||
private static final String CLIENT_KEY = System.getenv("CLIENT_KEY");
|
||||
|
||||
// Only required if USE_AZURE_CLIENT is true
|
||||
private static final String CLIENT_ENDPOINT = System.getenv("CLIENT_ENDPOINT");
|
||||
|
||||
|
||||
private static class DateTimeContextVariableTypeConverter extends
|
||||
ContextVariableTypeConverter<ZonedDateTime> {
|
||||
|
||||
private static final List<Converter<ZonedDateTime, ?>> converters = List.of(
|
||||
new DefaultConverter<>(ZonedDateTime.class, Date.class) {
|
||||
@Override
|
||||
public Date toObject(ZonedDateTime zonedDateTime) {
|
||||
return new Date(zonedDateTime.toInstant().toEpochMilli());
|
||||
}
|
||||
},
|
||||
new DefaultConverter<>(ZonedDateTime.class, String.class) {
|
||||
@Override
|
||||
public String toObject(ZonedDateTime zonedDateTime) {
|
||||
return zonedDateTime.format(DateTimeFormatter.ISO_DATE_TIME);
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
public DateTimeContextVariableTypeConverter() {
|
||||
super(
|
||||
ZonedDateTime.class,
|
||||
(x) -> convert(x, ZonedDateTime.class),
|
||||
zonedDateTime -> zonedDateTime.format(DateTimeFormatter.ISO_DATE_TIME),
|
||||
promptString -> ZonedDateTime.parse(promptString, DateTimeFormatter.ISO_DATE_TIME),
|
||||
converters
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
|
||||
System.out.println("======== Method Function types ========");
|
||||
|
||||
TextGenerationService textGenerationService;
|
||||
|
||||
if (USE_AZURE_CLIENT) {
|
||||
OpenAIAsyncClient client = new OpenAIClientBuilder()
|
||||
.credential(new AzureKeyCredential(CLIENT_KEY))
|
||||
.endpoint(CLIENT_ENDPOINT)
|
||||
.buildAsyncClient();
|
||||
|
||||
textGenerationService = AzureOpenAITextGenerationService.builder()
|
||||
.withOpenAIAsyncClient(client)
|
||||
.withModelId("text-davinci-003")
|
||||
.build();
|
||||
} else {
|
||||
OpenAIAsyncClient client = new OpenAIClientBuilder()
|
||||
.credential(new KeyCredential(CLIENT_KEY))
|
||||
.buildAsyncClient();
|
||||
|
||||
// TODO: Add support for OpenAI API
|
||||
textGenerationService = null;
|
||||
}
|
||||
|
||||
// Load native plugin into the kernel function collection, sharing its functions with prompt templates
|
||||
KernelPlugin plugin = KernelPluginFactory
|
||||
.createFromObject(new LocalExamplePlugin(), "Examples");
|
||||
|
||||
KernelPlugin summarize = KernelPluginFactory
|
||||
.importPluginFromDirectory(
|
||||
Path.of("java/samples/sample-code/src/main/resources/Plugins"),
|
||||
"SummarizePlugin",
|
||||
null);
|
||||
|
||||
KernelPlugin examplePlugin = KernelPluginFactory
|
||||
.importPluginFromResourcesDirectory(
|
||||
"Plugins",
|
||||
"ExamplePlugins",
|
||||
"ExampleFunction",
|
||||
null,
|
||||
Example09_FunctionTypes.class
|
||||
);
|
||||
|
||||
Kernel kernel = new DefaultKernel.Builder()
|
||||
.withDefaultAIService(TextGenerationService.class, textGenerationService)
|
||||
.withPlugin(plugin)
|
||||
.withPlugin(summarize)
|
||||
.withPlugin(examplePlugin)
|
||||
.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();
|
||||
|
||||
kernel
|
||||
.invokeAsync(
|
||||
plugin.get("InputDateTimeWithStringResult"),
|
||||
KernelArguments
|
||||
.builder()
|
||||
.withVariable("currentDate",
|
||||
ContextVariable.of(
|
||||
ZonedDateTime.now(),
|
||||
new DateTimeContextVariableTypeConverter()
|
||||
)
|
||||
)
|
||||
.build(),
|
||||
String.class
|
||||
).block();
|
||||
|
||||
kernel.invokeAsync(plugin.get("NoInputTaskWithStringResult"), null, String.class).block();
|
||||
|
||||
kernel.invokeAsync(plugin.get("MultipleInputsWithVoidResult"),
|
||||
KernelArguments
|
||||
.builder()
|
||||
.withVariable("x", "x string")
|
||||
.withVariable("y", 100)
|
||||
.withVariable("z", 1.5)
|
||||
.build(),
|
||||
String.class)
|
||||
.block();
|
||||
|
||||
kernel
|
||||
.invokeAsync(plugin.get("ComplexInputWithStringResult"),
|
||||
KernelArguments
|
||||
.builder()
|
||||
.withVariable(
|
||||
"complexObject",
|
||||
ContextVariable.of(
|
||||
new Object() {
|
||||
@Override
|
||||
public String toString() {
|
||||
return "A complex object";
|
||||
}
|
||||
},
|
||||
new NoopConverter<>(Object.class)
|
||||
)
|
||||
)
|
||||
.build(),
|
||||
String.class)
|
||||
.block();
|
||||
|
||||
kernel
|
||||
.invokeAsync(plugin.get("InputStringTaskWithStringResult"),
|
||||
KernelArguments
|
||||
.builder()
|
||||
.withVariable("echoInput", "return this")
|
||||
.build(),
|
||||
String.class)
|
||||
.block();
|
||||
|
||||
kernel
|
||||
.invokeAsync(plugin.get("InputStringTaskWithVoidResult"),
|
||||
KernelArguments
|
||||
.builder()
|
||||
.withVariable("x", "x input")
|
||||
.build(),
|
||||
Void.class)
|
||||
.block();
|
||||
|
||||
/*
|
||||
TODO: support FunctionResult
|
||||
kernel
|
||||
.invokeAsync(plugin.get("NoInputWithFunctionResult"),
|
||||
null,
|
||||
Void.class)
|
||||
.block();
|
||||
|
||||
*/
|
||||
|
||||
/*
|
||||
TODO: support injection
|
||||
// Injecting Parameters Examples
|
||||
await kernel.InvokeAsync(plugin[nameof(LocalExamplePlugin.TaskInjectingKernelFunctionWithStringResult)]);
|
||||
await kernel.InvokeAsync(plugin[nameof(LocalExamplePlugin.TaskInjectingLoggerWithNoResult)]);
|
||||
await kernel.InvokeAsync(plugin[nameof(LocalExamplePlugin.TaskInjectingLoggerFactoryWithNoResult)]);
|
||||
await kernel.InvokeAsync(plugin[nameof(LocalExamplePlugin.TaskInjectingCultureInfoOrIFormatProviderWithStringResult)]);
|
||||
await kernel.InvokeAsync(plugin[nameof(LocalExamplePlugin.TaskInjectingCancellationTokenWithStringResult)]);
|
||||
await kernel.InvokeAsync(plugin[nameof(LocalExamplePlugin.TaskInjectingServiceSelectorWithStringResult)]);
|
||||
await kernel.InvokeAsync(plugin[nameof(LocalExamplePlugin.TaskInjectingKernelWithInputTextAndStringResult)],
|
||||
new()
|
||||
{
|
||||
["textToSummarize"] = @"C# is a modern, versatile language by Microsoft, blending the efficiency of C++
|
||||
with Visual Basic's simplicity. It's ideal for a wide range of applications,
|
||||
emphasizing type safety, modularity, and modern programming paradigms."
|
||||
});
|
||||
|
||||
*/
|
||||
|
||||
// You can also use the kernel.Plugins collection to invoke a function
|
||||
kernel
|
||||
.invokeAsync(
|
||||
kernel.getPlugins()
|
||||
.getFunction("Examples", "NoInputWithVoidResult"),
|
||||
null,
|
||||
Void.class)
|
||||
.block();
|
||||
}
|
||||
|
||||
|
||||
public static class LocalExamplePlugin {
|
||||
|
||||
/// <summary>
|
||||
/// Example of using a void function with no input
|
||||
/// </summary>
|
||||
@DefineKernelFunction(name = "NoInputWithVoidResult")
|
||||
public void NoInputWithVoidResult() {
|
||||
System.out.println("Running this.NoInputWithVoidResult) -> No input");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Example of using a void task function with no input
|
||||
/// </summary>
|
||||
@DefineKernelFunction(name = "NoInputTaskWithVoidResult")
|
||||
public Mono<Void> NoInputTaskWithVoidResult() {
|
||||
return Mono.fromRunnable(
|
||||
() -> System.out.println("Running this.NoInputTaskWithVoidResult) -> No input"));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Example of using a function with a DateTime input and a string result
|
||||
/// </summary>
|
||||
@DefineKernelFunction(name = "InputDateTimeWithStringResult")
|
||||
public String InputDateTimeWithStringResult(
|
||||
@KernelFunctionParameter(
|
||||
name = "currentDate",
|
||||
description = "Current date time",
|
||||
type = ZonedDateTime.class
|
||||
)
|
||||
ZonedDateTime currentDate) {
|
||||
var result = currentDate.format(DateTimeFormatter.ISO_DATE_TIME);
|
||||
System.out.println(
|
||||
"Running {nameof(this.InputDateTimeWithStringResult)} -> [currentDate = {"
|
||||
+ currentDate + "}] -> result: {" + result + "}");
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Example of using a Task function with no input and a string result
|
||||
/// </summary>
|
||||
@DefineKernelFunction(name = "NoInputTaskWithStringResult")
|
||||
public Mono<String> NoInputTaskWithStringResult() {
|
||||
return Mono.fromCallable(() -> {
|
||||
var result = "string result";
|
||||
System.out.println(
|
||||
"Running {nameof(this.NoInputTaskWithStringResult)} -> No input -> result: {"
|
||||
+ result + "}");
|
||||
return result;
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Example passing multiple parameters with multiple types
|
||||
/// </summary>
|
||||
@DefineKernelFunction(name = "MultipleInputsWithVoidResult")
|
||||
public void MultipleInputsWithVoidResult(
|
||||
@KernelFunctionParameter(
|
||||
name = "x"
|
||||
)
|
||||
String x,
|
||||
|
||||
@KernelFunctionParameter(
|
||||
name = "y",
|
||||
type = int.class
|
||||
)
|
||||
int y,
|
||||
|
||||
@KernelFunctionParameter(
|
||||
name = "z",
|
||||
type = double.class
|
||||
)
|
||||
double z) {
|
||||
System.out.println(
|
||||
"Running {nameof(this.MultipleInputsWithVoidResult)} -> input: [x = {" + x
|
||||
+ "}, y = {" + y + "}, z = {" + z + "}]");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Example passing a complex object and returning a string result
|
||||
/// </summary>
|
||||
@DefineKernelFunction(name = "ComplexInputWithStringResult")
|
||||
public String ComplexInputWithStringResult(
|
||||
@KernelFunctionParameter(
|
||||
name = "complexObject",
|
||||
type = Object.class
|
||||
)
|
||||
Object complexObject
|
||||
) {
|
||||
String result = complexObject.toString();
|
||||
System.out.println(
|
||||
"Running {nameof(this.ComplexInputWithStringResult)} -> input: [complexObject = {complexObject}] -> result: {"
|
||||
+ result + "}");
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Example using an async task function echoing the input
|
||||
/// </summary>
|
||||
@DefineKernelFunction(name = "InputStringTaskWithStringResult")
|
||||
public Mono<String> InputStringTaskWithStringResult(
|
||||
|
||||
@KernelFunctionParameter(
|
||||
name = "echoInput"
|
||||
)
|
||||
String echoInput) {
|
||||
return Mono.fromCallable(() -> {
|
||||
System.out.println(
|
||||
"Running {nameof(this.InputStringTaskWithStringResult)} -> input: [echoInput = {echoInput}] -> result: {"
|
||||
+ echoInput + "}");
|
||||
return echoInput;
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Example using an async void task with string input
|
||||
/// </summary>
|
||||
@DefineKernelFunction(name = "InputStringTaskWithVoidResult")
|
||||
public Mono<Void> InputStringTaskWithVoidResult(
|
||||
@KernelFunctionParameter(
|
||||
name = "x"
|
||||
)
|
||||
String x) {
|
||||
return Mono.fromRunnable(() -> {
|
||||
System.out.println(
|
||||
"Running {nameof(this.InputStringTaskWithVoidResult)} -> input: [x = {" + x
|
||||
+ "}]");
|
||||
});
|
||||
}
|
||||
|
||||
/*
|
||||
/// <summary>
|
||||
/// Example using a function to return the result of another inner function
|
||||
/// </summary>
|
||||
@DefineKernelFunction(name = "InputStringTaskWithVoidResult")
|
||||
public FunctionResult NoInputWithFunctionResult()
|
||||
{
|
||||
var myInternalFunction = KernelFunctionFactory.CreateFromMethod(() => { });
|
||||
var result = new FunctionResult(myInternalFunction);
|
||||
Console.WriteLine($"Running {nameof(this.NoInputWithFunctionResult)} -> No input -> result: {result.GetType().Name}");
|
||||
return result;
|
||||
}
|
||||
*/
|
||||
|
||||
/*
|
||||
|
||||
/// <summary>
|
||||
/// Example using a task function to return the result of another kernel function
|
||||
/// </summary>
|
||||
[KernelFunction]
|
||||
public async Task<FunctionResult> NoInputTaskWithFunctionResult(Kernel kernel)
|
||||
{
|
||||
var result = await kernel.InvokeAsync(kernel.Plugins["Examples"][nameof(this.NoInputWithVoidResult)]);
|
||||
Console.WriteLine($"Running {nameof(this.NoInputTaskWithFunctionResult)} -> Injected kernel -> result: {result.GetType().Name}");
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Example how to inject Kernel in your function
|
||||
/// This example uses the injected kernel to invoke a plugin from within another function
|
||||
/// </summary>
|
||||
[KernelFunction]
|
||||
public async Task<string> TaskInjectingKernelWithInputTextAndStringResult(Kernel kernel, string textToSummarize)
|
||||
{
|
||||
var summary = await kernel.InvokeAsync<string>(kernel.Plugins["SummarizePlugin"]["Summarize"], new() { ["input"] = textToSummarize });
|
||||
Console.WriteLine($"Running {nameof(this.TaskInjectingKernelWithInputTextAndStringResult)} -> Injected kernel + input: [textToSummarize: {textToSummarize[..15]}...{textToSummarize[^15..]}] -> result: {summary}");
|
||||
return summary!;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Example how to inject the executing KernelFunction as a parameter
|
||||
/// </summary>
|
||||
[KernelFunction, Description("Example function injecting itself as a parameter")]
|
||||
public async Task<string> TaskInjectingKernelFunctionWithStringResult(KernelFunction executingFunction)
|
||||
{
|
||||
var result = $"Name: {executingFunction.Name}, Description: {executingFunction.Description}";
|
||||
Console.WriteLine($"Running {nameof(this.TaskInjectingKernelWithInputTextAndStringResult)} -> Injected Function -> result: {result}");
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Example how to inject ILogger in your function
|
||||
/// </summary>
|
||||
[KernelFunction]
|
||||
public Task TaskInjectingLoggerWithNoResult(ILogger logger)
|
||||
{
|
||||
logger.LogWarning("Running {FunctionName} -> Injected Logger", nameof(this.TaskInjectingLoggerWithNoResult));
|
||||
Console.WriteLine($"Running {nameof(this.TaskInjectingKernelWithInputTextAndStringResult)} -> Injected Logger");
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Example how to inject ILoggerFactory in your function
|
||||
/// </summary>
|
||||
[KernelFunction]
|
||||
public Task TaskInjectingLoggerFactoryWithNoResult(ILoggerFactory loggerFactory)
|
||||
{
|
||||
loggerFactory
|
||||
.CreateLogger<LocalExamplePlugin>()
|
||||
.LogWarning("Running {FunctionName} -> Injected Logger", nameof(this.TaskInjectingLoggerWithNoResult));
|
||||
|
||||
Console.WriteLine($"Running {nameof(this.TaskInjectingKernelWithInputTextAndStringResult)} -> Injected Logger");
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Example how to inject a service selector in your function and use a specific service
|
||||
/// </summary>
|
||||
[KernelFunction]
|
||||
public async Task<string> TaskInjectingServiceSelectorWithStringResult(Kernel kernel, KernelFunction function, KernelArguments arguments, IAIServiceSelector serviceSelector)
|
||||
{
|
||||
ChatMessageContent? chatMessageContent = null;
|
||||
if (serviceSelector.TrySelectAIService<IChatCompletionService>(kernel, function, arguments, out var chatCompletion, out var executionSettings))
|
||||
{
|
||||
chatMessageContent = await chatCompletion.GetChatMessageContentAsync(new ChatHistory("How much is 5 + 5 ?"), executionSettings);
|
||||
}
|
||||
|
||||
var result = chatMessageContent?.Content;
|
||||
Console.WriteLine($"Running {nameof(this.TaskInjectingKernelWithInputTextAndStringResult)} -> Injected Kernel, KernelFunction, KernelArguments, Service Selector -> result: {result}");
|
||||
return result ?? string.Empty;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Example how to inject CultureInfo or IFormatProvider in your function
|
||||
/// </summary>
|
||||
public async Task<string> TaskInjectingCultureInfoOrIFormatProviderWithStringResult(CultureInfo cultureInfo, IFormatProvider formatProvider)
|
||||
{
|
||||
var result = $"Culture Name: {cultureInfo.Name}, FormatProvider Equals CultureInfo?: {formatProvider.Equals(cultureInfo)}";
|
||||
Console.WriteLine($"Running {nameof(this.TaskInjectingCultureInfoOrIFormatProviderWithStringResult)} -> Injected CultureInfo, IFormatProvider -> result: {result}");
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Example how to inject current CancellationToken in your function
|
||||
/// </summary>
|
||||
[KernelFunction]
|
||||
public async Task<string> TaskInjectingCancellationTokenWithStringResult(CancellationToken cancellationToken)
|
||||
{
|
||||
var result = $"Cancellation resquested: {cancellationToken.IsCancellationRequested}";
|
||||
Console.WriteLine($"Running {nameof(this.TaskInjectingCultureInfoOrIFormatProviderWithStringResult)} -> Injected Cancellation Token -> result: {result}");
|
||||
return result;
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return "Complex type result ToString override";
|
||||
}
|
||||
|
||||
*/
|
||||
}
|
||||
}
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"schema": 1,
|
||||
"description": "Given a scientific white paper abstract, rewrite it to make it more readable",
|
||||
"execution_settings": {
|
||||
"default": {
|
||||
"max_tokens": 4000,
|
||||
"temperature": 0.0,
|
||||
"top_p": 1.0,
|
||||
"presence_penalty": 0.0,
|
||||
"frequency_penalty": 2.0
|
||||
}
|
||||
}
|
||||
}
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
{{$input}}
|
||||
|
||||
==
|
||||
Summarize, using a user friendly, using simple grammar. Don't use subjects like "we" "our" "us" "your".
|
||||
==
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"schema": 1,
|
||||
"description": "Automatically generate compact notes for any text or text document.",
|
||||
"execution_settings": {
|
||||
"default": {
|
||||
"max_tokens": 256,
|
||||
"temperature": 0.0,
|
||||
"top_p": 0.0,
|
||||
"presence_penalty": 0.0,
|
||||
"frequency_penalty": 0.0
|
||||
}
|
||||
}
|
||||
}
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
Analyze the following extract taken from a document.
|
||||
- Produce key points for memory.
|
||||
- Give memory a name.
|
||||
- Extract only points worth remembering.
|
||||
- Be brief. Conciseness is very important.
|
||||
- Use broken English.
|
||||
You will use this memory to analyze the rest of this document, and for other relevant tasks.
|
||||
|
||||
[Input]
|
||||
My name is Macbeth. I used to be King of Scotland, but I died. My wife's name is Lady Macbeth and we were married for 15 years. We had no children. Our beloved dog Toby McDuff was a famous hunter of rats in the forest.
|
||||
My story was immortalized by Shakespeare in a play.
|
||||
+++++
|
||||
Family History
|
||||
- Macbeth, King Scotland
|
||||
- Wife Lady Macbeth, No Kids
|
||||
- Dog Toby McDuff. Hunter, dead.
|
||||
- Shakespeare play
|
||||
|
||||
[Input]
|
||||
[[{{$input}}]]
|
||||
+++++
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
{
|
||||
"schema": 1,
|
||||
"description": "Summarize given text or any text document",
|
||||
"execution_settings": {
|
||||
"default": {
|
||||
"max_tokens": 512,
|
||||
"temperature": 0.0,
|
||||
"top_p": 0.0,
|
||||
"presence_penalty": 0.0,
|
||||
"frequency_penalty": 0.0
|
||||
}
|
||||
},
|
||||
"input_variables": [
|
||||
{
|
||||
"name": "input",
|
||||
"description": "Text to summarize",
|
||||
"default": "",
|
||||
"is_required": true
|
||||
}
|
||||
]
|
||||
}
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
[SUMMARIZATION RULES]
|
||||
DONT WASTE WORDS
|
||||
USE SHORT, CLEAR, COMPLETE SENTENCES.
|
||||
DO NOT USE BULLET POINTS OR DASHES.
|
||||
USE ACTIVE VOICE.
|
||||
MAXIMIZE DETAIL, MEANING
|
||||
FOCUS ON THE CONTENT
|
||||
|
||||
[BANNED PHRASES]
|
||||
This article
|
||||
This document
|
||||
This page
|
||||
This material
|
||||
[END LIST]
|
||||
|
||||
Summarize:
|
||||
Hello how are you?
|
||||
+++++
|
||||
Hello
|
||||
|
||||
Summarize this
|
||||
{{$input}}
|
||||
+++++
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"schema": 1,
|
||||
"description": "Analyze given text or document and extract key topics worth remembering",
|
||||
"execution_settings": {
|
||||
"default": {
|
||||
"max_tokens": 128,
|
||||
"temperature": 0.0,
|
||||
"top_p": 0.0,
|
||||
"presence_penalty": 0.0,
|
||||
"frequency_penalty": 0.0
|
||||
}
|
||||
}
|
||||
}
|
||||
+28
@@ -0,0 +1,28 @@
|
||||
Analyze the following extract taken from a document and extract key topics.
|
||||
- Topics only worth remembering.
|
||||
- Be brief. Short phrases.
|
||||
- Can use broken English.
|
||||
- Conciseness is very important.
|
||||
- Topics can include names of memories you want to recall.
|
||||
- NO LONG SENTENCES. SHORT PHRASES.
|
||||
- Return in JSON
|
||||
[Input]
|
||||
My name is Macbeth. I used to be King of Scotland, but I died. My wife's name is Lady Macbeth and we were married for 15 years. We had no children. Our beloved dog Toby McDuff was a famous hunter of rats in the forest.
|
||||
My tragic story was immortalized by Shakespeare in a play.
|
||||
[Output]
|
||||
{
|
||||
"topics": [
|
||||
"Macbeth",
|
||||
"King of Scotland",
|
||||
"Lady Macbeth",
|
||||
"Dog",
|
||||
"Toby McDuff",
|
||||
"Shakespeare",
|
||||
"Play",
|
||||
"Tragedy"
|
||||
]
|
||||
}
|
||||
+++++
|
||||
[Input]
|
||||
{{$input}}
|
||||
[Output]
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"schema": 1,
|
||||
"description": "Sample plugin loaded via resources",
|
||||
"execution_settings": {
|
||||
"default": {
|
||||
"max_tokens": 150,
|
||||
"temperature": 0.9,
|
||||
"top_p": 0.0,
|
||||
"presence_penalty": 0.6,
|
||||
"frequency_penalty": 0.0
|
||||
}
|
||||
}
|
||||
}
|
||||
+1
@@ -0,0 +1 @@
|
||||
This is an example
|
||||
+8
-7
@@ -1,12 +1,13 @@
|
||||
{
|
||||
"schema": 1,
|
||||
"description": "Sample plugin loaded via resources",
|
||||
"type": "completion",
|
||||
"completion": {
|
||||
"max_tokens": 150,
|
||||
"temperature": 0.9,
|
||||
"top_p": 0.0,
|
||||
"presence_penalty": 0.6,
|
||||
"frequency_penalty": 0.0
|
||||
"execution_settings": {
|
||||
"default": {
|
||||
"max_tokens": 150,
|
||||
"temperature": 0.9,
|
||||
"top_p": 0.0,
|
||||
"presence_penalty": 0.6,
|
||||
"frequency_penalty": 0.0
|
||||
}
|
||||
}
|
||||
}
|
||||
-21
@@ -1,21 +0,0 @@
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
package com.microsoft.semantickernel;
|
||||
|
||||
import com.microsoft.semantickernel.builders.Buildable;
|
||||
import com.microsoft.semantickernel.builders.BuildersSingleton;
|
||||
import com.microsoft.semantickernel.builders.SemanticKernelBuilder;
|
||||
|
||||
// Currently the kernel config does not have and function within Java
|
||||
@Deprecated
|
||||
public final class KernelConfig implements Buildable {
|
||||
|
||||
public static Builder builder() {
|
||||
return BuildersSingleton.INST.getInstance(KernelConfig.Builder.class);
|
||||
}
|
||||
|
||||
public static class Builder implements SemanticKernelBuilder<KernelConfig> {
|
||||
public KernelConfig build() {
|
||||
return new KernelConfig();
|
||||
}
|
||||
}
|
||||
}
|
||||
-49
@@ -1,49 +0,0 @@
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
package com.microsoft.semantickernel;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
/** Provides the base exception from which all CHECKED Semantic Kernel exceptions derive. */
|
||||
public class SKCheckedException extends Exception {
|
||||
|
||||
/**
|
||||
* Initializes a new instance of the {@code SKCheckedException} class with a default message.
|
||||
*/
|
||||
protected SKCheckedException() {
|
||||
super();
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes a new instance of the {@code SKCheckedException} class with its message set to
|
||||
* {@code message}.
|
||||
*
|
||||
* @param message A string that describes the error.
|
||||
*/
|
||||
protected SKCheckedException(@Nullable String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes a new instance of the {@code SKCheckedException} class with its message set to
|
||||
* {@code message}.
|
||||
*
|
||||
* @param message A string that describes the error.
|
||||
* @param cause The exception that is the cause of the current exception.
|
||||
*/
|
||||
protected SKCheckedException(@Nullable String message, @Nullable Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
|
||||
/**
|
||||
* Translate the error code into a default message format.
|
||||
*
|
||||
* @param errorMessage The error message from an error code
|
||||
* @param message The message from the code which throws the exception
|
||||
* @return A formatted message
|
||||
*/
|
||||
protected static String formatDefaultMessage(
|
||||
@Nonnull String errorMessage, @Nullable String message) {
|
||||
return SKException.formatDefaultMessage(errorMessage, message);
|
||||
}
|
||||
}
|
||||
-47
@@ -1,47 +0,0 @@
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
package com.microsoft.semantickernel;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
/** Provides the base exception from which all Semantic Kernel exceptions derive. */
|
||||
public class SKException extends RuntimeException {
|
||||
|
||||
/** Initializes a new instance of the {@code SKException} class with a default message. */
|
||||
protected SKException() {
|
||||
super();
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes a new instance of the {@code SKException} class with its message set to {@code
|
||||
* message}.
|
||||
*
|
||||
* @param message A string that describes the error.
|
||||
*/
|
||||
protected SKException(@Nullable String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes a new instance of the {@code SKException} class with its message set to {@code
|
||||
* message}.
|
||||
*
|
||||
* @param message A string that describes the error.
|
||||
* @param cause The exception that is the cause of the current exception.
|
||||
*/
|
||||
protected SKException(@Nullable String message, @Nullable Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
|
||||
/**
|
||||
* Translate the error code into a default message format.
|
||||
*
|
||||
* @param errorMessage The error message from an error code
|
||||
* @param message The message from the code which throws the exception
|
||||
* @return A formatted message
|
||||
*/
|
||||
protected static String formatDefaultMessage(
|
||||
@Nonnull String errorMessage, @Nullable String message) {
|
||||
return String.format("%s: %s", errorMessage, message);
|
||||
}
|
||||
}
|
||||
-56
@@ -1,56 +0,0 @@
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
package com.microsoft.semantickernel.exceptions;
|
||||
|
||||
import com.microsoft.semantickernel.SKCheckedException;
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
/** Kernel logic exception */
|
||||
public class ConfigurationException extends SKCheckedException {
|
||||
|
||||
@Nonnull private final ErrorCodes errorCode;
|
||||
|
||||
public ConfigurationException(@Nonnull ErrorCodes error) {
|
||||
this(error, null, null);
|
||||
}
|
||||
|
||||
public ConfigurationException(@Nonnull ErrorCodes errorCode, @Nullable String message) {
|
||||
this(errorCode, message, null);
|
||||
}
|
||||
|
||||
public ConfigurationException(
|
||||
@Nonnull ErrorCodes errorCode,
|
||||
@Nullable String message,
|
||||
@Nullable Throwable innerException) {
|
||||
super(formatDefaultMessage(errorCode.getMessage(), message), innerException);
|
||||
this.errorCode = errorCode;
|
||||
}
|
||||
|
||||
public ErrorCodes getErrorCode() {
|
||||
return errorCode;
|
||||
}
|
||||
|
||||
public enum ErrorCodes {
|
||||
UNKNOWN_ERROR("Unknown error"),
|
||||
|
||||
CONFIGURATION_NOT_FOUND("Could not find configuration file"),
|
||||
|
||||
COULD_NOT_READ_CONFIGURATION("Could not parse or load configuration file"),
|
||||
NO_VALID_CONFIGURATIONS_FOUND("Could not find any valid configuration settings"),
|
||||
VALUE_NOT_FOUND("Could not find value for configuration key");
|
||||
|
||||
private final String message;
|
||||
|
||||
ErrorCodes(String message) {
|
||||
this.message = message;
|
||||
}
|
||||
|
||||
public String getMessage() {
|
||||
return message;
|
||||
}
|
||||
|
||||
public String getMessage(String param) {
|
||||
return String.format(message, param);
|
||||
}
|
||||
}
|
||||
}
|
||||
-43
@@ -1,43 +0,0 @@
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
package com.microsoft.semantickernel.exceptions;
|
||||
|
||||
import com.microsoft.semantickernel.SKException;
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
public class NotSupportedException extends SKException {
|
||||
|
||||
private final ErrorCodes errorCode;
|
||||
|
||||
public NotSupportedException(@Nonnull ErrorCodes errorCode) {
|
||||
this(errorCode, null, null);
|
||||
}
|
||||
|
||||
public NotSupportedException(@Nonnull ErrorCodes errorCode, @Nullable String message) {
|
||||
this(errorCode, message, null);
|
||||
}
|
||||
|
||||
public NotSupportedException(
|
||||
@Nonnull ErrorCodes errorCode, @Nullable String message, @Nullable Throwable cause) {
|
||||
super(formatDefaultMessage(errorCode.getMessage(), message), cause);
|
||||
this.errorCode = errorCode;
|
||||
}
|
||||
|
||||
public ErrorCodes getErrorCode() {
|
||||
return errorCode;
|
||||
}
|
||||
|
||||
public enum ErrorCodes {
|
||||
NOT_SUPPORTED("Not Supported");
|
||||
|
||||
final String message;
|
||||
|
||||
ErrorCodes(String message) {
|
||||
this.message = message;
|
||||
}
|
||||
|
||||
String getMessage() {
|
||||
return message;
|
||||
}
|
||||
}
|
||||
}
|
||||
-43
@@ -1,43 +0,0 @@
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
package com.microsoft.semantickernel.exceptions;
|
||||
|
||||
import com.microsoft.semantickernel.SKException;
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
-189
@@ -1,189 +0,0 @@
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
package com.microsoft.semantickernel.extensions;
|
||||
|
||||
import com.fasterxml.jackson.databind.JsonMappingException;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.microsoft.semantickernel.KernelException;
|
||||
import com.microsoft.semantickernel.KernelException.ErrorCodes;
|
||||
import com.microsoft.semantickernel.SKBuilders;
|
||||
import com.microsoft.semantickernel.semanticfunctions.PromptConfig;
|
||||
import com.microsoft.semantickernel.semanticfunctions.PromptTemplate;
|
||||
import com.microsoft.semantickernel.semanticfunctions.SemanticFunctionConfig;
|
||||
import com.microsoft.semantickernel.templateengine.PromptTemplateEngine;
|
||||
import com.microsoft.semantickernel.util.EmbeddedResourceLoader;
|
||||
import com.microsoft.semantickernel.util.EmbeddedResourceLoader.ResourceLocation;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.Charset;
|
||||
import java.nio.file.Files;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import javax.annotation.Nullable;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
public class KernelExtensions {
|
||||
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(KernelExtensions.class);
|
||||
private static final String CONFIG_FILE = "config.json";
|
||||
private static final String PROMPT_FILE = "skprompt.txt";
|
||||
|
||||
private KernelExtensions() {}
|
||||
|
||||
public static Map<String, SemanticFunctionConfig> importSemanticSkillFromDirectory(
|
||||
String parentDirectory,
|
||||
String skillDirectoryName,
|
||||
PromptTemplateEngine promptTemplateEngine) {
|
||||
|
||||
// Verify.ValidSkillName(skillDirectoryName);
|
||||
File skillDir = new File(parentDirectory, skillDirectoryName);
|
||||
// Verify.DirectoryExists(skillDir);
|
||||
|
||||
File[] files = skillDir.listFiles();
|
||||
if (files == null) {
|
||||
throw new KernelException(
|
||||
ErrorCodes.FUNCTION_NOT_AVAILABLE,
|
||||
"No Skills found in directory " + skillDir.getAbsolutePath());
|
||||
}
|
||||
|
||||
HashMap<String, SemanticFunctionConfig> skills = new HashMap<>();
|
||||
|
||||
for (File dir : files) {
|
||||
try {
|
||||
// Continue only if prompt template exists
|
||||
File promptPath = new File(dir, PROMPT_FILE);
|
||||
if (!promptPath.exists()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Load prompt configuration. Note: the configuration is
|
||||
// optional.
|
||||
PromptConfig config = new PromptConfig("", "", null);
|
||||
|
||||
File configPath = new File(dir, CONFIG_FILE);
|
||||
if (configPath.exists()) {
|
||||
config = new ObjectMapper().readValue(configPath, PromptConfig.class);
|
||||
|
||||
// Verify.NotNull(config, $"Invalid prompt template
|
||||
// configuration, unable to parse {configPath}");
|
||||
}
|
||||
|
||||
// kernel.Log.LogTrace("Config {0}: {1}", functionName,
|
||||
// config.ToJson());
|
||||
|
||||
// Load prompt template
|
||||
String template =
|
||||
new String(
|
||||
Files.readAllBytes(promptPath.toPath()), Charset.defaultCharset());
|
||||
|
||||
PromptTemplate promptTemplate =
|
||||
SKBuilders.promptTemplate()
|
||||
.withPromptTemplate(template)
|
||||
.withPromptTemplateConfig(config)
|
||||
.withPromptTemplateEngine(promptTemplateEngine)
|
||||
.build();
|
||||
|
||||
skills.put(dir.getName(), new SemanticFunctionConfig(config, promptTemplate));
|
||||
} catch (IOException e) {
|
||||
LOGGER.error("Failed to read file", e);
|
||||
}
|
||||
}
|
||||
|
||||
return skills;
|
||||
}
|
||||
|
||||
public static Map<String, SemanticFunctionConfig> importSemanticSkillFromResourcesDirectory(
|
||||
String pluginDirectory,
|
||||
String pluginName,
|
||||
String functionName,
|
||||
@Nullable Class clazz,
|
||||
PromptTemplateEngine promptTemplateEngine)
|
||||
throws KernelException {
|
||||
|
||||
PromptConfig config =
|
||||
getPromptTemplateConfig(pluginDirectory, pluginName, functionName, clazz);
|
||||
|
||||
if (config == null) {
|
||||
config = new PromptConfig("", "", null);
|
||||
}
|
||||
|
||||
String template = getTemplatePrompt(pluginDirectory, pluginName, functionName, clazz);
|
||||
|
||||
HashMap<String, SemanticFunctionConfig> skills = new HashMap<>();
|
||||
|
||||
PromptTemplate promptTemplate =
|
||||
SKBuilders.promptTemplate()
|
||||
.withPromptTemplate(template)
|
||||
.withPromptTemplateConfig(config)
|
||||
.withPromptTemplateEngine(promptTemplateEngine)
|
||||
.build();
|
||||
|
||||
skills.put(functionName, new SemanticFunctionConfig(config, promptTemplate));
|
||||
|
||||
return skills;
|
||||
}
|
||||
|
||||
private static String getTemplatePrompt(
|
||||
String pluginDirectory, String pluginName, String functionName, @Nullable Class clazz) {
|
||||
String promptFileName =
|
||||
pluginDirectory
|
||||
+ File.separator
|
||||
+ pluginName
|
||||
+ File.separator
|
||||
+ functionName
|
||||
+ File.separator
|
||||
+ PROMPT_FILE;
|
||||
|
||||
try {
|
||||
return EmbeddedResourceLoader.readFile(
|
||||
promptFileName,
|
||||
clazz,
|
||||
ResourceLocation.CLASSPATH_ROOT,
|
||||
ResourceLocation.CLASSPATH,
|
||||
ResourceLocation.FILESYSTEM);
|
||||
} catch (IOException e) {
|
||||
LOGGER.error("Failed to read file " + promptFileName, e);
|
||||
|
||||
throw new KernelException(
|
||||
ErrorCodes.FUNCTION_NOT_AVAILABLE,
|
||||
"No Skills found in directory " + promptFileName);
|
||||
}
|
||||
}
|
||||
|
||||
private static PromptConfig getPromptTemplateConfig(
|
||||
String pluginDirectory, String pluginName, String functionName, @Nullable Class clazz)
|
||||
throws KernelException {
|
||||
String configFileName =
|
||||
pluginDirectory
|
||||
+ File.separator
|
||||
+ pluginName
|
||||
+ File.separator
|
||||
+ functionName
|
||||
+ File.separator
|
||||
+ CONFIG_FILE;
|
||||
|
||||
try {
|
||||
String config =
|
||||
EmbeddedResourceLoader.readFile(
|
||||
configFileName,
|
||||
clazz,
|
||||
ResourceLocation.CLASSPATH_ROOT,
|
||||
ResourceLocation.CLASSPATH,
|
||||
ResourceLocation.FILESYSTEM);
|
||||
|
||||
return new ObjectMapper().readValue(config, PromptConfig.class);
|
||||
} catch (IOException e) {
|
||||
if (e instanceof JsonMappingException) {
|
||||
LOGGER.error("Failed to parse config file " + configFileName, e);
|
||||
|
||||
throw new KernelException(
|
||||
ErrorCodes.FUNCTION_CONFIGURATION_ERROR,
|
||||
"Failed to parse config file " + configFileName,
|
||||
e);
|
||||
} else {
|
||||
LOGGER.debug("No config for " + functionName + " in " + pluginName);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
-22
@@ -1,22 +0,0 @@
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
package com.microsoft.semantickernel.skilldefinition.annotations;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/** Annotation that defines a method that can be invoked as a native function */
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(ElementType.METHOD)
|
||||
public @interface DefineSKFunction {
|
||||
String description() default "";
|
||||
|
||||
String name() default "";
|
||||
|
||||
String returnType() default "void";
|
||||
|
||||
String returnDescription() default "";
|
||||
|
||||
SKSample[] samples() default {};
|
||||
}
|
||||
-14
@@ -1,14 +0,0 @@
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
package com.microsoft.semantickernel.skilldefinition.annotations;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/** Annotates a parameter binding it to the "input" context variable */
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(ElementType.PARAMETER)
|
||||
public @interface SKFunctionInputAttribute {
|
||||
String description();
|
||||
}
|
||||
-29
@@ -1,29 +0,0 @@
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
package com.microsoft.semantickernel.skilldefinition.annotations;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/** Annotates a parameter to a native function */
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(ElementType.PARAMETER)
|
||||
public @interface SKFunctionParameters {
|
||||
|
||||
String NO_DEFAULT_VALUE = "SKFunctionParameters__NO_INPUT_PROVIDED";
|
||||
|
||||
String description() default "";
|
||||
|
||||
String name();
|
||||
|
||||
String defaultValue() default NO_DEFAULT_VALUE;
|
||||
|
||||
/**
|
||||
* If required is false, no value is set, and no default value is provided, null will be passed
|
||||
* as this argument.
|
||||
*/
|
||||
boolean required() default true;
|
||||
|
||||
Class<?> type() default String.class;
|
||||
}
|
||||
-18
@@ -1,18 +0,0 @@
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
package com.microsoft.semantickernel.skilldefinition.annotations;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* @since 1.0.0
|
||||
*/
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(ElementType.METHOD)
|
||||
public @interface SKSample {
|
||||
String inputs();
|
||||
|
||||
String output();
|
||||
}
|
||||
+4
-2
@@ -53,8 +53,10 @@ public class DefaultKernel implements Kernel {
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> Mono<ContextVariable<T>> invokeAsync(KernelFunction function,
|
||||
@Nullable KernelArguments arguments, Class<T> resultType) {
|
||||
public <T> Mono<ContextVariable<T>> invokeAsync(
|
||||
KernelFunction function,
|
||||
@Nullable KernelArguments arguments,
|
||||
Class<T> resultType) {
|
||||
return function.invokeAsync(this, arguments,
|
||||
ContextVariableTypes.getDefaultVariableTypeForClass(resultType));
|
||||
}
|
||||
|
||||
+55
-3
@@ -2,11 +2,12 @@ package com.microsoft.semantickernel.orchestration;
|
||||
|
||||
import static com.microsoft.semantickernel.plugin.annotations.KernelFunctionParameter.NO_DEFAULT_VALUE;
|
||||
|
||||
import com.microsoft.semantickernel.Kernel;
|
||||
import com.microsoft.semantickernel.exceptions.AIException;
|
||||
import com.microsoft.semantickernel.exceptions.AIException.ErrorCodes;
|
||||
import com.microsoft.semantickernel.Kernel;
|
||||
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.ContextVariableTypes;
|
||||
import com.microsoft.semantickernel.orchestration.contextvariables.KernelArguments;
|
||||
import com.microsoft.semantickernel.plugin.KernelParameterMetadata;
|
||||
@@ -84,6 +85,7 @@ public class KernelFunctionFromMethod extends DefaultKernelFunction {
|
||||
<T> Mono<T> invoke(
|
||||
Kernel kernel,
|
||||
KernelFunction function,
|
||||
@Nullable
|
||||
KernelArguments arguments);
|
||||
}
|
||||
|
||||
@@ -148,6 +150,7 @@ public class KernelFunctionFromMethod extends DefaultKernelFunction {
|
||||
return new ImplementationFunc() {
|
||||
@Override
|
||||
public <T> Mono<T> invoke(Kernel kernel, KernelFunction function,
|
||||
@Nullable
|
||||
KernelArguments arguments) {
|
||||
|
||||
//Set<Parameter> inputArgs = determineInputArgs(method);
|
||||
@@ -227,7 +230,9 @@ public class KernelFunctionFromMethod extends DefaultKernelFunction {
|
||||
}
|
||||
|
||||
private static Function<Parameter, Object> getParameters(
|
||||
Method method, KernelArguments context) {
|
||||
Method method,
|
||||
@Nullable
|
||||
KernelArguments context) {
|
||||
return parameter -> {
|
||||
if (KernelArguments.class.isAssignableFrom(parameter.getType())) {
|
||||
return context;
|
||||
@@ -238,7 +243,12 @@ public class KernelFunctionFromMethod extends DefaultKernelFunction {
|
||||
}
|
||||
|
||||
private static Object getArgumentValue(
|
||||
Method method, KernelArguments context, Parameter parameter) {
|
||||
Method method,
|
||||
@Nullable KernelArguments context,
|
||||
Parameter parameter) {
|
||||
if (context == null) {
|
||||
return context;
|
||||
}
|
||||
String variableName = getGetVariableName(parameter);
|
||||
|
||||
ContextVariable<?> arg = context.get(variableName);
|
||||
@@ -303,7 +313,31 @@ public class KernelFunctionFromMethod extends DefaultKernelFunction {
|
||||
|
||||
Class<?> type = annotation.type();
|
||||
|
||||
if (!parameter.getType().isAssignableFrom(type)) {
|
||||
throw new AIException(
|
||||
AIException.ErrorCodes.INVALID_CONFIGURATION,
|
||||
"Annotation on method: " + method.getName() + " requested conversion to type: "
|
||||
+ type.getName() + ", however this cannot be assigned to parameter of type: "
|
||||
+ parameter.getType());
|
||||
}
|
||||
|
||||
Object value = arg;
|
||||
|
||||
if (parameter.getType().isAssignableFrom(arg.getValue().getClass())) {
|
||||
return arg.getValue();
|
||||
}
|
||||
|
||||
if (isPrimative(arg.getValue().getClass(), parameter.getType())) {
|
||||
return arg.getValue();
|
||||
}
|
||||
|
||||
ContextVariableTypeConverter<?> c = arg.getType().getConverter();
|
||||
|
||||
Object converted = c.toObject(arg.getValue(), parameter.getType());
|
||||
if (converted != null) {
|
||||
return converted;
|
||||
}
|
||||
|
||||
// Well-known types only
|
||||
ContextVariableType<?> converter = ContextVariableTypes.getDefaultVariableTypeForClass(
|
||||
type);
|
||||
@@ -324,6 +358,24 @@ public class KernelFunctionFromMethod extends DefaultKernelFunction {
|
||||
return value;
|
||||
}
|
||||
|
||||
private static boolean isPrimative(Class<?> argType, Class<?> param) {
|
||||
return (argType == Byte.class || argType == byte.class) && (param == Byte.class
|
||||
|| param == byte.class) ||
|
||||
(argType == Integer.class || argType == int.class) && (param == Integer.class
|
||||
|| param == int.class) ||
|
||||
(argType == Long.class || argType == long.class) && (param == Long.class
|
||||
|| param == long.class) ||
|
||||
(argType == Double.class || argType == double.class) && (param == Double.class
|
||||
|| param == double.class) ||
|
||||
(argType == Float.class || argType == float.class) && (param == Float.class
|
||||
|| param == float.class) ||
|
||||
(argType == Short.class || argType == short.class) && (param == Short.class
|
||||
|| param == short.class) ||
|
||||
(argType == Boolean.class || argType == boolean.class) && (param == Boolean.class
|
||||
|| param == boolean.class) ||
|
||||
(argType == Character.class || argType == char.class) && (param == Character.class
|
||||
|| param == char.class);
|
||||
}
|
||||
|
||||
private static String getGetVariableName(Parameter parameter) {
|
||||
KernelFunctionParameter annotation = parameter.getAnnotation(KernelFunctionParameter.class);
|
||||
|
||||
+5
-4
@@ -1,10 +1,6 @@
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
package com.microsoft.semantickernel.orchestration.contextvariables;
|
||||
|
||||
import com.microsoft.semantickernel.orchestration.contextvariables.ContextVariableType;
|
||||
import com.microsoft.semantickernel.orchestration.contextvariables.ContextVariableTypeConverter;
|
||||
import com.microsoft.semantickernel.orchestration.contextvariables.ContextVariableTypes;
|
||||
|
||||
public class ContextVariable<T> {
|
||||
|
||||
private final ContextVariableType<T> type;
|
||||
@@ -45,4 +41,9 @@ public class ContextVariable<T> {
|
||||
return new ContextVariable<T>(type, value);
|
||||
}
|
||||
|
||||
|
||||
public static <T> ContextVariable<T> of(T value, ContextVariableTypeConverter<T> converter) {
|
||||
ContextVariableType<T> type = new ContextVariableType<>(converter, converter.getType());
|
||||
return new ContextVariable<>(type, value);
|
||||
}
|
||||
}
|
||||
|
||||
+81
@@ -1,24 +1,105 @@
|
||||
package com.microsoft.semantickernel.orchestration.contextvariables;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.function.Function;
|
||||
import javax.annotation.Nullable;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
public class ContextVariableTypeConverter<T> {
|
||||
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(
|
||||
ContextVariableTypeConverter.class);
|
||||
|
||||
private final Class<T> clazz;
|
||||
private final Function<Object, T> fromObject;
|
||||
private final Function<T, String> toPromptString;
|
||||
private final Function<String, T> fromPromptString;
|
||||
private final List<Converter<T, ?>> toObjects;
|
||||
|
||||
public interface Converter<T, U> {
|
||||
|
||||
U toObject(T t);
|
||||
|
||||
Class<U> getTargetType();
|
||||
}
|
||||
|
||||
public static class NoopConverter<T> extends ContextVariableTypeConverter<T> {
|
||||
|
||||
public NoopConverter(Class<T> clazz) {
|
||||
super(
|
||||
clazz,
|
||||
x -> {
|
||||
throw new RuntimeException("Noop converter should not be called");
|
||||
},
|
||||
x -> {
|
||||
throw new RuntimeException("Noop converter should not be called");
|
||||
},
|
||||
x -> {
|
||||
throw new RuntimeException("Noop converter should not be called");
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public static abstract class DefaultConverter<T, U> implements Converter<T, U> {
|
||||
|
||||
private final Class<U> targetType;
|
||||
|
||||
protected DefaultConverter(Class<T> sourceType, Class<U> targetType) {
|
||||
this.targetType = targetType;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<U> getTargetType() {
|
||||
return targetType;
|
||||
}
|
||||
}
|
||||
|
||||
public ContextVariableTypeConverter(
|
||||
Class<T> clazz,
|
||||
Function<Object, T> fromObject,
|
||||
Function<T, String> toPromptString,
|
||||
Function<String, T> fromPromptString) {
|
||||
this(clazz, fromObject, toPromptString, fromPromptString, Collections.emptyList());
|
||||
}
|
||||
|
||||
public ContextVariableTypeConverter(
|
||||
Class<T> clazz,
|
||||
Function<Object, T> fromObject,
|
||||
Function<T, String> toPromptString,
|
||||
Function<String, T> fromPromptString,
|
||||
List<Converter<T, ?>> toObjects) {
|
||||
this.clazz = clazz;
|
||||
this.fromObject = fromObject;
|
||||
this.toPromptString = toPromptString;
|
||||
this.fromPromptString = fromPromptString;
|
||||
this.toObjects = toObjects;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public <U> U toObject(Object t, Class<U> clazz) {
|
||||
Optional<Converter<T, ?>> converter = toObjects
|
||||
.stream()
|
||||
.filter(c -> c.getTargetType().equals(clazz))
|
||||
.findFirst();
|
||||
|
||||
if (converter.isPresent()) {
|
||||
return (U) converter.get().toObject((T) t);
|
||||
}
|
||||
|
||||
converter = toObjects
|
||||
.stream()
|
||||
.filter(c -> clazz.isAssignableFrom(c.getTargetType()))
|
||||
.findFirst();
|
||||
|
||||
if (converter.isPresent()) {
|
||||
return (U) converter.get().toObject((T) t);
|
||||
}
|
||||
|
||||
LOGGER.warn("No converter found for {} to {}", t.getClass(), clazz);
|
||||
return null;
|
||||
}
|
||||
|
||||
public T fromObject(Object s) {
|
||||
|
||||
+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.NumberVariableContextVariableTypeConverter;
|
||||
import com.microsoft.semantickernel.orchestration.contextvariables.converters.StringVariableContextVariableTypeConverter;
|
||||
import com.microsoft.semantickernel.orchestration.contextvariables.converters.VoidVariableContextVariableTypeConverter;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
@@ -20,6 +21,7 @@ public class ContextVariableTypes {
|
||||
new BooleanVariableContextVariableTypeConverter(),
|
||||
new ChatHistoryVariableContextVariableTypeConverter(),
|
||||
new StringVariableContextVariableTypeConverter(),
|
||||
new VoidVariableContextVariableTypeConverter(),
|
||||
|
||||
new NumberVariableContextVariableTypeConverter<>(Byte.class, Byte::parseByte),
|
||||
new NumberVariableContextVariableTypeConverter<>(byte.class, Byte::parseByte),
|
||||
|
||||
+7
@@ -107,6 +107,13 @@ public interface KernelArguments extends Buildable, Map<String, ContextVariable<
|
||||
*/
|
||||
<T> Builder withVariable(String key, ContextVariable<T> value);
|
||||
|
||||
/**
|
||||
* Set variable
|
||||
*
|
||||
* @param key variable name
|
||||
* @param value variable value
|
||||
* @return builder for fluent chaining
|
||||
*/
|
||||
Builder withVariable(String key, Object value);
|
||||
}
|
||||
}
|
||||
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
package com.microsoft.semantickernel.orchestration.contextvariables.converters;
|
||||
|
||||
import com.microsoft.semantickernel.orchestration.contextvariables.ContextVariableTypeConverter;
|
||||
|
||||
public class VoidVariableContextVariableTypeConverter extends
|
||||
ContextVariableTypeConverter<Void> {
|
||||
|
||||
public VoidVariableContextVariableTypeConverter() {
|
||||
super(
|
||||
Void.class,
|
||||
s -> null,
|
||||
s -> null,
|
||||
s -> null);
|
||||
}
|
||||
|
||||
}
|
||||
+214
-1
@@ -1,19 +1,42 @@
|
||||
package com.microsoft.semantickernel.plugin;
|
||||
|
||||
import com.microsoft.semantickernel.ServiceProvider;
|
||||
import com.fasterxml.jackson.databind.JsonMappingException;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.microsoft.semantickernel.Todo;
|
||||
import com.microsoft.semantickernel.exceptions.SKException;
|
||||
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.KernelPromptTemplateFactory;
|
||||
import com.microsoft.semantickernel.semanticfunctions.PromptTemplate;
|
||||
import com.microsoft.semantickernel.semanticfunctions.PromptTemplateConfig;
|
||||
import com.microsoft.semantickernel.semanticfunctions.PromptTemplateFactory;
|
||||
import com.microsoft.semantickernel.util.EmbeddedResourceLoader;
|
||||
import com.microsoft.semantickernel.util.EmbeddedResourceLoader.ResourceLocation;
|
||||
import java.io.File;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.nio.charset.Charset;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
import javax.annotation.Nullable;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
public class KernelPluginFactory {
|
||||
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(KernelPluginFactory.class);
|
||||
private static final String CONFIG_FILE = "config.json";
|
||||
private static final String PROMPT_FILE = "skprompt.txt";
|
||||
|
||||
/// <summary>Creates a plugin that wraps the specified target object.</summary>
|
||||
/// <param name="target">The instance of the class to be wrapped.</param>
|
||||
/// <param name="pluginName">
|
||||
@@ -112,4 +135,194 @@ public class KernelPluginFactory {
|
||||
})
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
|
||||
public static KernelPlugin importPluginFromDirectory(
|
||||
Path parentDirectory,
|
||||
String pluginDirectoryName,
|
||||
PromptTemplateFactory promptTemplateFactory) {
|
||||
|
||||
// Verify.ValidSkillName(pluginDirectoryName);
|
||||
File pluginDir = new File(parentDirectory.toFile(), pluginDirectoryName);
|
||||
// Verify.DirectoryExists(pluginDir);
|
||||
if (!pluginDir.exists() || !pluginDir.isDirectory()) {
|
||||
throw new SKException(
|
||||
"Could not find directory " + pluginDir.getAbsolutePath());
|
||||
}
|
||||
File[] files = pluginDir.listFiles(File::isDirectory);
|
||||
if (files == null) {
|
||||
throw new SKException(
|
||||
"No Plugins found in directory " + pluginDir.getAbsolutePath());
|
||||
}
|
||||
|
||||
Map<String, KernelFunction> plugins = new CaseInsensitiveMap<>();
|
||||
|
||||
for (File dir : files) {
|
||||
try {
|
||||
// Continue only if prompt template exists
|
||||
File promptPath = new File(dir, PROMPT_FILE);
|
||||
if (!promptPath.exists()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
File configPath = new File(dir, CONFIG_FILE);
|
||||
if (!configPath.exists()) {
|
||||
continue;
|
||||
// Verify.NotNull(config, $"Invalid prompt template
|
||||
// configuration, unable to parse {configPath}");
|
||||
}
|
||||
|
||||
KernelFunction plugin = getKernelFunction(pluginDirectoryName,
|
||||
promptTemplateFactory, configPath, promptPath);
|
||||
|
||||
plugins.put(dir.getName(), plugin);
|
||||
} catch (IOException e) {
|
||||
LOGGER.error("Failed to read file", e);
|
||||
}
|
||||
}
|
||||
|
||||
return new DefaultKernelPlugin(
|
||||
pluginDirectoryName,
|
||||
null,
|
||||
plugins
|
||||
);
|
||||
}
|
||||
|
||||
private static KernelFunction getKernelFunction(
|
||||
String pluginDirectoryName,
|
||||
PromptTemplateFactory promptTemplateFactory,
|
||||
File configPath,
|
||||
File promptPath)
|
||||
throws IOException {
|
||||
PromptTemplateConfig config = new ObjectMapper()
|
||||
.readValue(configPath, PromptTemplateConfig.class);
|
||||
|
||||
// Load prompt template
|
||||
String template = new String(Files.readAllBytes(promptPath.toPath()),
|
||||
Charset.defaultCharset());
|
||||
|
||||
return getKernelFunction(pluginDirectoryName, promptTemplateFactory, config, template);
|
||||
}
|
||||
|
||||
private static KernelFunction getKernelFunction(
|
||||
String pluginDirectoryName,
|
||||
PromptTemplateFactory promptTemplateFactory,
|
||||
PromptTemplateConfig config,
|
||||
String template) {
|
||||
PromptTemplate promptTemplate;
|
||||
|
||||
if (promptTemplateFactory != null) {
|
||||
promptTemplate = promptTemplateFactory.tryCreate(config);
|
||||
} else {
|
||||
promptTemplate = new KernelPromptTemplateFactory().tryCreate(config);
|
||||
}
|
||||
|
||||
return new Builder()
|
||||
.withName(config.getName())
|
||||
.withDescription(config.getDescription())
|
||||
.withExecutionSettings(config.getExecutionSettings())
|
||||
.withInputParameters(config.getInputVariables())
|
||||
.withPromptTemplate(promptTemplate)
|
||||
.withPluginName(pluginDirectoryName)
|
||||
.withTemplate(template)
|
||||
.withTemplateFormat(config.getTemplateFormat())
|
||||
.withOutputVariable(config.getOutputVariable())
|
||||
.withPromptTemplateFactory(promptTemplateFactory)
|
||||
.build();
|
||||
}
|
||||
|
||||
public static KernelPlugin importPluginFromResourcesDirectory(
|
||||
String parentDirectory,
|
||||
String pluginDirectoryName,
|
||||
String functionName,
|
||||
PromptTemplateFactory promptTemplateFactory,
|
||||
@Nullable Class<?> clazz) {
|
||||
|
||||
String template = getTemplatePrompt(parentDirectory, pluginDirectoryName, functionName,
|
||||
clazz);
|
||||
|
||||
PromptTemplateConfig promptTemplateConfig = getPromptTemplateConfig(parentDirectory,
|
||||
pluginDirectoryName,
|
||||
functionName,
|
||||
clazz);
|
||||
|
||||
KernelFunction function = getKernelFunction(pluginDirectoryName, promptTemplateFactory,
|
||||
promptTemplateConfig, template);
|
||||
|
||||
HashMap<String, KernelFunction> plugins = new HashMap<>();
|
||||
|
||||
plugins.put(functionName, function);
|
||||
|
||||
return new DefaultKernelPlugin(
|
||||
pluginDirectoryName,
|
||||
null,
|
||||
plugins
|
||||
);
|
||||
}
|
||||
|
||||
private static String getTemplatePrompt(
|
||||
String pluginDirectory, String pluginName, String functionName, @Nullable Class clazz) {
|
||||
String promptFileName =
|
||||
pluginDirectory
|
||||
+ File.separator
|
||||
+ pluginName
|
||||
+ File.separator
|
||||
+ functionName
|
||||
+ File.separator
|
||||
+ PROMPT_FILE;
|
||||
|
||||
try {
|
||||
return getFileContents(
|
||||
promptFileName,
|
||||
clazz);
|
||||
} catch (IOException e) {
|
||||
LOGGER.error("Failed to read file " + promptFileName, e);
|
||||
|
||||
throw new SKException(
|
||||
"No Skills found in directory " + promptFileName);
|
||||
}
|
||||
}
|
||||
|
||||
private static String getFileContents(
|
||||
String file,
|
||||
@Nullable 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) {
|
||||
String configFileName =
|
||||
pluginDirectory
|
||||
+ File.separator
|
||||
+ pluginName
|
||||
+ File.separator
|
||||
+ functionName
|
||||
+ File.separator
|
||||
+ CONFIG_FILE;
|
||||
|
||||
try {
|
||||
String config = getFileContents(configFileName, clazz);
|
||||
|
||||
return new ObjectMapper().readValue(config, PromptTemplateConfig.class);
|
||||
} catch (IOException e) {
|
||||
if (e instanceof JsonMappingException) {
|
||||
LOGGER.error("Failed to parse config file " + configFileName, e);
|
||||
|
||||
throw new SKException(
|
||||
"Failed to parse config file " + configFileName,
|
||||
e);
|
||||
} else {
|
||||
LOGGER.debug("No config for " + functionName + " in " + pluginName);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -13,7 +13,7 @@ public class InputVariable {
|
||||
@JsonProperty("description")
|
||||
private String description;
|
||||
|
||||
@JsonProperty("default_value")
|
||||
@JsonProperty("default")
|
||||
private String defaultValue;
|
||||
|
||||
@JsonProperty("is_required")
|
||||
|
||||
-12
@@ -3,7 +3,6 @@ package com.microsoft.semantickernel.semanticfunctions;
|
||||
|
||||
import com.microsoft.semantickernel.Kernel;
|
||||
import com.microsoft.semantickernel.builders.Buildable;
|
||||
import com.microsoft.semantickernel.builders.BuildersSingleton;
|
||||
import com.microsoft.semantickernel.builders.SemanticKernelBuilder;
|
||||
import com.microsoft.semantickernel.orchestration.contextvariables.KernelArguments;
|
||||
import javax.annotation.Nullable;
|
||||
@@ -24,15 +23,4 @@ public interface PromptTemplate extends Buildable {
|
||||
Mono<String> renderAsync(Kernel kernel,
|
||||
@Nullable KernelArguments arguments);
|
||||
|
||||
static Builder builder() {
|
||||
return BuildersSingleton.INST.getInstance(Builder.class);
|
||||
}
|
||||
|
||||
interface Builder extends SemanticKernelBuilder<PromptTemplate> {
|
||||
|
||||
Builder withPromptTemplate(String promptTemplate);
|
||||
|
||||
Builder withPromptTemplateConfig(PromptTemplateConfig config);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
+38
-28
@@ -6,17 +6,16 @@ import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import com.microsoft.semantickernel.orchestration.PromptExecutionSettings;
|
||||
import com.microsoft.semantickernel.plugin.KernelParameterMetadata;
|
||||
import com.microsoft.semantickernel.plugin.KernelReturnParameterMetadata;
|
||||
import com.microsoft.semantickernel.templateengine.handlebars.HandlebarsPromptTemplate;
|
||||
import com.microsoft.semantickernel.templateengine.semantickernel.DefaultPromptTemplate;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class PromptTemplateConfig {
|
||||
|
||||
public static final int CURRENT_SCHEMA = 1;
|
||||
private String name;
|
||||
|
||||
private String template;
|
||||
@@ -33,6 +32,7 @@ public class PromptTemplateConfig {
|
||||
|
||||
public PromptTemplateConfig(String template) {
|
||||
this(
|
||||
CURRENT_SCHEMA,
|
||||
"default",
|
||||
template,
|
||||
"semantic-kernel",
|
||||
@@ -43,28 +43,10 @@ public class PromptTemplateConfig {
|
||||
);
|
||||
}
|
||||
|
||||
public PromptTemplateConfig(
|
||||
String name,
|
||||
String template,
|
||||
String templateFormat,
|
||||
String description,
|
||||
List<InputVariable> inputVariables,
|
||||
OutputVariable outputVariable,
|
||||
Map<String, PromptExecutionSettings> executionSettings) {
|
||||
this.name = name;
|
||||
this.template = template;
|
||||
this.templateFormat = templateFormat;
|
||||
this.description = description;
|
||||
this.inputVariables = inputVariables;
|
||||
if (inputVariables == null) {
|
||||
this.inputVariables = Collections.emptyList();
|
||||
}
|
||||
this.outputVariable = outputVariable;
|
||||
this.executionSettings = executionSettings;
|
||||
}
|
||||
|
||||
@JsonCreator
|
||||
public PromptTemplateConfig(
|
||||
@JsonProperty("schema")
|
||||
int schema,
|
||||
@JsonProperty("name")
|
||||
String name,
|
||||
@JsonProperty("template")
|
||||
@@ -78,16 +60,44 @@ public class PromptTemplateConfig {
|
||||
@JsonProperty("output_variable")
|
||||
OutputVariable outputVariable,
|
||||
@JsonProperty("execution_settings")
|
||||
List<PromptExecutionSettings> executionSettings) {
|
||||
Map<String, PromptExecutionSettings> executionSettings) {
|
||||
this.name = name;
|
||||
this.template = template;
|
||||
this.templateFormat = templateFormat;
|
||||
this.description = description;
|
||||
this.inputVariables = inputVariables;
|
||||
if (inputVariables == null) {
|
||||
this.inputVariables = Collections.emptyList();
|
||||
} else {
|
||||
this.inputVariables = new ArrayList<>(inputVariables);
|
||||
}
|
||||
this.outputVariable = outputVariable;
|
||||
this.executionSettings = executionSettings
|
||||
.stream()
|
||||
.collect(Collectors.toMap(PromptExecutionSettings::getModelId, e -> e));
|
||||
this.executionSettings = executionSettings;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor for a prompt template config
|
||||
*
|
||||
* @param name Name of the template
|
||||
* @param template Template string
|
||||
* @param templateFormat Template format
|
||||
* @param description Description of the template
|
||||
* @param inputVariables Input variables
|
||||
* @param outputVariable Output variable
|
||||
* @param executionSettings Execution settings
|
||||
*/
|
||||
public PromptTemplateConfig(String name, String template, String templateFormat,
|
||||
String description, List<InputVariable> inputVariables, OutputVariable outputVariable,
|
||||
Map<String, PromptExecutionSettings> executionSettings) {
|
||||
this(
|
||||
CURRENT_SCHEMA,
|
||||
name,
|
||||
template,
|
||||
templateFormat,
|
||||
description,
|
||||
inputVariables,
|
||||
outputVariable,
|
||||
executionSettings
|
||||
);
|
||||
}
|
||||
|
||||
public List<KernelParameterMetadata> getKernelParametersMetadata() {
|
||||
|
||||
Reference in New Issue
Block a user