From fe40db687d9f1729ec2d17de759e85ab9c0fd200 Mon Sep 17 00:00:00 2001 From: xbotter Date: Thu, 23 Nov 2023 19:12:06 +0800 Subject: [PATCH 1/5] =?UTF-8?q?=E2=9C=A8=20Add=20ExecuteCommand=20to=20SKo?= =?UTF-8?q?nsole?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add a new command called ExecuteCommand to SKonsole. This command allows users to execute semantic functions directly. The command takes a prompt argument and an optional template option. The template option specifies the template to use for the semantic function, with a default value of "{{$input}}". The command uses the KernelProvider to get an instance of the semantic kernel and creates a semantic function based on the template. It then runs the semantic function with the provided prompt and outputs the result. The result is printed to the console. --- apps/SKonsole/Commands/ExecuteCommand.cs | 61 ++++++++++++++++++++++++ apps/SKonsole/Program.cs | 3 +- 2 files changed, 63 insertions(+), 1 deletion(-) create mode 100644 apps/SKonsole/Commands/ExecuteCommand.cs diff --git a/apps/SKonsole/Commands/ExecuteCommand.cs b/apps/SKonsole/Commands/ExecuteCommand.cs new file mode 100644 index 0000000..0d53319 --- /dev/null +++ b/apps/SKonsole/Commands/ExecuteCommand.cs @@ -0,0 +1,61 @@ +using System.CommandLine; +using Microsoft.Extensions.Logging; +using Microsoft.SemanticKernel; +using SKonsole.Utils; + +namespace SKonsole.Commands; +internal sealed class ExecuteCommand : Command +{ + private ILogger _logger; + + public ExecuteCommand(ConfigurationProvider config, ILogger? logger = null) : base("exec", "Execute semantic function directly.") + { + if (logger is null) + { + using var loggerFactory = Logging.GetFactory(); + this._logger = loggerFactory.CreateLogger(this.GetType()); + } + else + { + this._logger = logger; + } + + var promptArgument = new Argument + ("prompt", "An argument that is parsed as a string."); + + this.AddArgument(promptArgument); + + var templateOption = new Option( + new string[] { "--template", "-t" }, + () => { return "{{$input}}"; }, + "The template to use for the semantic function."); + + this.AddOption(templateOption); + + this.SetHandler(async context => await RunExecuteAsync(context.GetCancellationToken(), + context.BindingContext.ParseResult.GetValueForArgument(promptArgument), + context.BindingContext.ParseResult.GetValueForOption(templateOption), + this._logger)); + } + + private static async Task RunExecuteAsync(CancellationToken cancellationToken, + string prompt, + string? template = null, + ILogger? logger = null) + { + var kernel = KernelProvider.Instance.Get(); + + if (string.IsNullOrWhiteSpace(template)) + { + template = "{{$input}}"; + } + + var func = kernel.CreateSemanticFunction(template); + + var output = await kernel.RunAsync(prompt, func); + + var result = output.GetValue(); + + Console.WriteLine(result); + } +} diff --git a/apps/SKonsole/Program.cs b/apps/SKonsole/Program.cs index d9e5f68..4376e39 100644 --- a/apps/SKonsole/Program.cs +++ b/apps/SKonsole/Program.cs @@ -13,7 +13,8 @@ new PRCommand(ConfigurationProvider.Instance), new PlannerCommand(ConfigurationProvider.Instance), new StepwisePlannerCommand(ConfigurationProvider.Instance), - new PromptChatCommand(ConfigurationProvider.Instance) + new PromptChatCommand(ConfigurationProvider.Instance), + new ExecuteCommand(ConfigurationProvider.Instance), }; return await rootCommand.InvokeAsync(args); From b2e32c5cf2d511430487b3b099830c1718ea933a Mon Sep 17 00:00:00 2001 From: xbotter Date: Thu, 23 Nov 2023 19:41:08 +0800 Subject: [PATCH 2/5] =?UTF-8?q?=E2=9C=A8=20Update=20ExecuteCommand.cs?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Redirect console output to StreamWriter for better control - Write result to console using StreamWriter --- apps/SKonsole/Commands/ExecuteCommand.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/apps/SKonsole/Commands/ExecuteCommand.cs b/apps/SKonsole/Commands/ExecuteCommand.cs index 0d53319..b2b0e5b 100644 --- a/apps/SKonsole/Commands/ExecuteCommand.cs +++ b/apps/SKonsole/Commands/ExecuteCommand.cs @@ -56,6 +56,9 @@ private static async Task RunExecuteAsync(CancellationToken cancellationToken, var result = output.GetValue(); - Console.WriteLine(result); + using var streamWriter = new StreamWriter(Console.OpenStandardOutput()); + streamWriter.AutoFlush = true; + Console.SetOut(streamWriter); + Console.Write(result); } } From ae4763c9510d118eefed0e989784f8af5607dacd Mon Sep 17 00:00:00 2001 From: xbotter Date: Fri, 24 Nov 2023 09:16:47 +0800 Subject: [PATCH 3/5] =?UTF-8?q?=E2=9C=A8=20Update=20ExecuteCommand.cs?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add support for specifying an output file for the result - Load template from file if specified - Create output file directory if it doesn't exist - Write result to output file if specified, otherwise print to console --- apps/SKonsole/Commands/ExecuteCommand.cs | 35 ++++++++++++++++++++---- 1 file changed, 30 insertions(+), 5 deletions(-) diff --git a/apps/SKonsole/Commands/ExecuteCommand.cs b/apps/SKonsole/Commands/ExecuteCommand.cs index b2b0e5b..379701f 100644 --- a/apps/SKonsole/Commands/ExecuteCommand.cs +++ b/apps/SKonsole/Commands/ExecuteCommand.cs @@ -28,19 +28,27 @@ public ExecuteCommand(ConfigurationProvider config, ILogger? logger = null) : ba var templateOption = new Option( new string[] { "--template", "-t" }, () => { return "{{$input}}"; }, - "The template to use for the semantic function."); + "The template (file) to use for the semantic function."); this.AddOption(templateOption); + var outputOption = new Option( + new string[] { "--output", "-o" }, + () => { return "{{$output}}"; }, + "Output the result to the specified file."); + this.AddOption(outputOption); + this.SetHandler(async context => await RunExecuteAsync(context.GetCancellationToken(), context.BindingContext.ParseResult.GetValueForArgument(promptArgument), context.BindingContext.ParseResult.GetValueForOption(templateOption), + context.BindingContext.ParseResult.GetValueForOption(outputOption), this._logger)); } private static async Task RunExecuteAsync(CancellationToken cancellationToken, string prompt, string? template = null, + string? outputFile = null, ILogger? logger = null) { var kernel = KernelProvider.Instance.Get(); @@ -49,6 +57,14 @@ private static async Task RunExecuteAsync(CancellationToken cancellationToken, { template = "{{$input}}"; } + else if (Path.Exists(template)) + { + template = await File.ReadAllTextAsync(template, cancellationToken); + } + else if (Path.Exists(Path.Combine(template, "skprompt.txt"))) + { + template = await File.ReadAllTextAsync(Path.Combine(template, "skprompt.txt"), cancellationToken); + } var func = kernel.CreateSemanticFunction(template); @@ -56,9 +72,18 @@ private static async Task RunExecuteAsync(CancellationToken cancellationToken, var result = output.GetValue(); - using var streamWriter = new StreamWriter(Console.OpenStandardOutput()); - streamWriter.AutoFlush = true; - Console.SetOut(streamWriter); - Console.Write(result); + if (!string.IsNullOrWhiteSpace(outputFile)) + { + var directory = Path.GetDirectoryName(outputFile); + if (!string.IsNullOrEmpty(directory)) + { + Directory.CreateDirectory(directory); + } + System.IO.File.WriteAllText(outputFile, result); + } + else + { + Console.WriteLine(result); + } } } From e72b51abee9b0be9e6a7f9ccebc95f953620d157 Mon Sep 17 00:00:00 2001 From: xbotter Date: Fri, 24 Nov 2023 09:22:20 +0800 Subject: [PATCH 4/5] update command help description --- apps/SKonsole/Commands/ExecuteCommand.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/apps/SKonsole/Commands/ExecuteCommand.cs b/apps/SKonsole/Commands/ExecuteCommand.cs index 379701f..f90254a 100644 --- a/apps/SKonsole/Commands/ExecuteCommand.cs +++ b/apps/SKonsole/Commands/ExecuteCommand.cs @@ -8,7 +8,7 @@ internal sealed class ExecuteCommand : Command { private ILogger _logger; - public ExecuteCommand(ConfigurationProvider config, ILogger? logger = null) : base("exec", "Execute semantic function directly.") + public ExecuteCommand(ConfigurationProvider config, ILogger? logger = null) : base("exec", "Execute semantic function.") { if (logger is null) { @@ -21,7 +21,7 @@ public ExecuteCommand(ConfigurationProvider config, ILogger? logger = null) : ba } var promptArgument = new Argument - ("prompt", "An argument that is parsed as a string."); + ("prompt", "The semantic function prompt or input for direct execution"); this.AddArgument(promptArgument); @@ -34,7 +34,7 @@ public ExecuteCommand(ConfigurationProvider config, ILogger? logger = null) : ba var outputOption = new Option( new string[] { "--output", "-o" }, - () => { return "{{$output}}"; }, + () => { return ""; }, "Output the result to the specified file."); this.AddOption(outputOption); From a23dc44b3d92d1432b89741755aae3b309f687ba Mon Sep 17 00:00:00 2001 From: xbotter Date: Fri, 24 Nov 2023 09:23:00 +0800 Subject: [PATCH 5/5] update output file option --- apps/SKonsole/Commands/ExecuteCommand.cs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/apps/SKonsole/Commands/ExecuteCommand.cs b/apps/SKonsole/Commands/ExecuteCommand.cs index f90254a..ede547e 100644 --- a/apps/SKonsole/Commands/ExecuteCommand.cs +++ b/apps/SKonsole/Commands/ExecuteCommand.cs @@ -33,9 +33,8 @@ public ExecuteCommand(ConfigurationProvider config, ILogger? logger = null) : ba this.AddOption(templateOption); var outputOption = new Option( - new string[] { "--output", "-o" }, - () => { return ""; }, - "Output the result to the specified file."); + new string[] { "--outputFile", "-o" }, + description: "Output the result to the specified file."); this.AddOption(outputOption); this.SetHandler(async context => await RunExecuteAsync(context.GetCancellationToken(),