Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Enhancements, Refactoring, and Documentation Updates #27

Merged
merged 8 commits into from
Oct 16, 2023
4 changes: 3 additions & 1 deletion .vscode/launch.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,9 @@
"request": "launch",
"preLaunchTask": "build",
"program": "${workspaceFolder}/apps/SKonsole/bin/Debug/net7.0/SKonsole.dll",
"args": ["stepwise", "optionset", "bing++"],
"args": ["stepwise", "optionset", "git"],
// "args": ["git"],

"cwd": "${workspaceFolder}",
"console": "integratedTerminal",
"stopAtEntry": false
Expand Down
52 changes: 52 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
# Contributing

We welcome contributions to improve SKonsole. If you have any suggestions or bug reports, please feel free to open an issue or submit a pull request.

## Structure

The repository is organized into the following main directories:

- `apps`: Contains the SKonsole application.
- `plugins`: Contains the plugins for the SKonsole application, including the CondensePlugin and PRPlugin.

## Getting Started

To get started with the SKonsole application, follow these steps:

1. Clone the repository.

### Using Visual Studio
2. Open the `skonsole.sln` solution file in Visual Studio.
3. Build and run the SKonsole application.

### Using Terminal

*apps\SKonsole*
```Copy code
dotnet build
dotnet run
```
This should build and run the SKonsole app. Note that you may need to configure your environment variables with your Azure OpenAI credentials before running the app.

## Structure

The repository is organized into the following main directories:

- `apps`: Contains the SKonsole application.
- `plugins`: Contains the plugins for the SKonsole application, including the CondensePlugin and PRPlugin.

## Projects and Classes
This repository contains several projects and classes, including:

- PRPlugin: A plugin that can generate feedback, commit messages, and pull request descriptions based on git diff or git show output. The PRPlugin uses the CondensePlugin as a dependency and implements chunking and aggregation mechanisms to handle large inputs.

- CondensePlugin: A plugin built on the Semantic Kernel that can condense multiple chunks of text into a single chunk. The plugin uses a semantic function defined with prompt templates and a completion engine.

- CommitParser and CommitChunker: Two utility classes that split and parse the input based on commit and file information. These classes are useful for generating content and responses based on large text results.

## Dependencies
This project requires the following dependencies:

- [Semantic Kernel](https://github.com/microsoft/semantic-kernel)

*Powered by [Microsoft Semantic Kernel](https://github.com/microsoft/semantic-kernel)*
75 changes: 48 additions & 27 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,24 +1,44 @@

# SKonsole - A Console App built on Semantic Plugins
This repository contains a demo of a console application called SKonsole, which uses the Semantic Kernel to run various plugins. The app currently supports two plugins: generating commit messages and generating pull request feedback. The app uses environment variables to configure the Azure OpenAI backend.
Welcome to the SKonsole repository! SKonsole is a powerful command-line tool that leverages AI to assist you with various tasks. It provides a simple interface to interact with the AI model and perform operations like reading and writing files, searching for files, and even sending emails. The repository contains the source code for the SKonsole application and its plugins.

## Features

- AI-powered command-line interface
- Customizable configuration
- Generate commit messages, pull request descriptions, and feedback.
- Engage in chat conversations that are powered by various Plugins.

## Usage

### General Commands

These commands will execute and return a result from the LLM.

`skonsole commit <commitHash>`: Generate commit messages based on the provided commit hash.

## Getting Started
To get started, simply run the following commands in the terminal:
`skonsole pr feedback`: Generate valuable feedback for pull requests using git diff or git show output.

```Copy code
apps\SKonsole> dotnet build
apps\SKonsole> dotnet run
```
This should build and run the SKonsole app. Note that you may need to configure your environment variables with your Azure OpenAI credentials before running the app.
`skonsole pr description`: Generate detailed descriptions for pull requests using git diff or git show output.

## Projects and Classes
This repository contains several projects and classes, including:
### Chat Commands

- PRPlugin: A plugin that can generate feedback, commit messages, and pull request descriptions based on git diff or git show output. The PRPlugin uses the CondensePlugin as a dependency and implements chunking and aggregation mechanisms to handle large inputs.
These commands will start a chat conversation with the LLM.

- CondensePlugin: A plugin built on the Semantic Kernel that can condense multiple chunks of text into a single chunk. The plugin uses a semantic function defined with prompt templates and a completion engine.
`skonsole stepwise [options]`: Engage in a StepwisePlanner powered chat session. Use `optionSet` option to specify which optionSets should be used for planning.

- CommitParser and CommitChunker: Two utility classes that split and parse the input based on commit and file information. These classes are useful for generating pull request descriptions and feedback based on the git diff output.
`skonsole createPlan <message>`: Create plans using a Planner by providing a message and then execute the plan.

`skonsole promptChat`: Engage in interactive prompt chat sessions for building semantic prompts using the LLM.

### Other Commands

These commands are other utilities that do not directly leverage LLMs.

`skonsole config [command] [options]`: Configure SKonsole application settings like LLM endpoints, keys, etc.

## Configuration

You can customize the behavior of SKonsole by modifying the configuration settings. In addition to the `config` command, the configuration file is located at `.skonsole` in your user profile directory. You can also set environment variables to override the default settings.

## Installing SKonsole Tool

Expand All @@ -36,27 +56,28 @@ Install the SKonsole Tool globally with a few quick steps:
skonsole --version
```

## Available Commands
## Plugins

The repository includes the following plugins:

- `skonsole commit <commitHash>`: Generate commit messages based on the provided commit hash.
### CondensePlugin

- `skonsole pr feedback`: Generate valuable feedback for pull requests using git diff or git show output.
The CondensePlugin is designed to help condense text by using the LLM to merge multiple chunks of text.

- `skonsole pr description`: Generate detailed descriptions for pull requests using git diff or git show output.
### PRPlugin

- `skonsole createPlan <message>`: Create plans using the Planner subcommand by providing a message.
The PRPlugin is designed to help generate pull request summaries and change lists from `git diff` output.

- `skonsole promptChat`: Engage in interactive prompt chat sessions.
### SuperFileIOPlugin

## Dependencies
This project requires the following dependencies:
The SuperFileIOPlugin is an extension of the FileIOPlugin in Semantic Kernel. It includes additional capabilities for reading and writing from the file system.

- [Semantic Kernel](https://github.com/microsoft/semantic-kernel)

## Future Work
In the future, the SKonsole app could be expanded to support more plugins and to parse arguments on launch. Additionally, the repository could include instructions for setting up NuGet credentials and using a GitHub Package source.
## Contributing
See [Contributing](CONTRIBUTING.md).

I hope this README is helpful for you and others who may use your repository in the future. Let me know if there's anything else I can do to help!
## License

SKonsole is licensed under the MIT License.

####_This README was generated using Semantic Kernel_
*Powered by [Microsoft Semantic Kernel](https://github.com/microsoft/semantic-kernel)*
2 changes: 1 addition & 1 deletion apps/SKonsole/Commands/CommitCommand.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ public CommitCommand(ConfigurationProvider config, ILogger? logger = null) : bas
if (logger is null)
{
using var loggerFactory = Logging.GetFactory();
this._logger = loggerFactory.CreateLogger<CommitCommand>();
this._logger = loggerFactory.CreateLogger(this.GetType());
}
else
{
Expand Down
2 changes: 1 addition & 1 deletion apps/SKonsole/Commands/PRCommand.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ public PRCommand(ConfigurationProvider config, ILogger? logger = null) : base("p
if (logger is null)
{
using var loggerFactory = Logging.GetFactory();
this._logger = loggerFactory.CreateLogger<PRCommand>();
this._logger = loggerFactory.CreateLogger(this.GetType());
}
else
{
Expand Down
2 changes: 1 addition & 1 deletion apps/SKonsole/Commands/PlannerCommand.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ public PlannerCommand(ConfigurationProvider config, ILogger? logger = null) : ba
if (logger is null)
{
using var loggerFactory = Logging.GetFactory();
this._logger = loggerFactory.CreateLogger<PlannerCommand>();
this._logger = loggerFactory.CreateLogger(this.GetType());
}
else
{
Expand Down
2 changes: 1 addition & 1 deletion apps/SKonsole/Commands/PromptChatCommand.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ public PromptChatCommand(ConfigurationProvider config, ILogger? logger = null) :
if (logger is null)
{
using var loggerFactory = Logging.GetFactory();
this._logger = loggerFactory.CreateLogger<PromptChatCommand>();
this._logger = loggerFactory.CreateLogger(this.GetType());
}
else
{
Expand Down
78 changes: 69 additions & 9 deletions apps/SKonsole/Commands/StepwisePlannerCommand.cs
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
using System.CommandLine;
using System.ComponentModel;
using System.Text;
using System.Text.Json;
using Microsoft.Extensions.Logging;
using Microsoft.SemanticKernel;
using Microsoft.SemanticKernel.Orchestration;
using Microsoft.SemanticKernel.Planners;
using Microsoft.SemanticKernel.Planning;
using Microsoft.SemanticKernel.Plugins.Core;
using Microsoft.SemanticKernel.Plugins.Web;
using Microsoft.SemanticKernel.Plugins.Web.Bing;
Expand All @@ -21,7 +23,7 @@ public StepwisePlannerCommand(ConfigurationProvider config, ILogger? logger = nu
if (logger is null)
{
using var loggerFactory = Logging.GetFactory();
this._logger = loggerFactory.CreateLogger<StepwisePlannerCommand>();
this._logger = loggerFactory.CreateLogger(this.GetType());
}
else
{
Expand All @@ -35,50 +37,93 @@ public StepwisePlannerCommand(ConfigurationProvider config, ILogger? logger = nu

private static async Task RunCreatePlan(CancellationToken token, ILogger logger, string message = "", string optionSet = "")
{
IKernel kernel = LoadOptionSet(optionSet);
(IKernel kernel, var validPlugins) = LoadOptionSet(optionSet);

var stepKernel = KernelProvider.Instance.Get();
var functions = stepKernel.ImportFunctions(new StepwisePlugin(kernel), "stepwise");

await RunChat(stepKernel, null, functions["RespondTo"]).ConfigureAwait(false);
await RunChat(stepKernel, validPlugins, null, functions["RespondTo"]).ConfigureAwait(false);
}

private static IKernel LoadOptionSet(string optionSet)
private static (IKernel, List<string>) LoadOptionSet(string optionSet)
{
var kernel = KernelProvider.Instance.Get();
List<string> validPlugins = new();

if (optionSet.Contains("bing"))
{
var bingConnector = new BingConnector(Configuration.ConfigVar("BING_API_KEY"));
var bing = new WebSearchEnginePlugin(bingConnector);
var search = kernel.ImportFunctions(bing, "bing");
validPlugins.Add("bing");
}

if (optionSet.Contains("++"))
{
kernel.ImportFunctions(new TimePlugin(), "time");
validPlugins.Add("time");
kernel.ImportFunctions(new ConversationSummaryPlugin(kernel), "summary");
validPlugins.Add("summary");
kernel.ImportFunctions(new SuperFileIOPlugin(), "file");
validPlugins.Add("file");
}
else
{
if (optionSet.Contains("time"))
{
kernel.ImportFunctions(new TimePlugin(), "time");
validPlugins.Add("time");
}

if (optionSet.Contains("summary"))
{
kernel.ImportFunctions(new ConversationSummaryPlugin(kernel), "summary");
validPlugins.Add("summary");
}

if (optionSet.Contains("file"))
{
kernel.ImportFunctions(new SuperFileIOPlugin(), "file");
validPlugins.Add("file");
}

if (optionSet.Contains("git"))
{
var gitPlugin = kernel.ImportFunctions(new GitPlugin(kernel), "git");

Plan gitProcessPlan = new("Execute a 'git diff' command and execute semantic reasoning over the output.");
gitProcessPlan.Name = "GenerateDynamicGitDiffResult";

// It'd be nice if I could even just use this to set default values. An option -- doesn't work right now.
// gitProcessPlan.Parameters.Set("filter", "-- . \":!*.md\" \":!*skprompt.txt\" \":!*encoder.json\" \":!*vocab.bpe\" \":!*dict.txt\"");
// gitProcessPlan.Parameters.Set("target", "HEAD");
// gitProcessPlan.Parameters.Set("source", "4b825dc642cb6eb9a060e54bf8d69288fbee4904");
// gitProcessPlan.Parameters.Set("instructions", "");
gitProcessPlan.Parameters.Set("filter", "");
gitProcessPlan.Parameters.Set("target", "");
gitProcessPlan.Parameters.Set("source", "");
gitProcessPlan.Parameters.Set("instructions", "");

gitProcessPlan.AddSteps(gitPlugin["GitDiffDynamic"]);
gitProcessPlan.Steps[0].Outputs.Add("gitDiffResult");

gitProcessPlan.AddSteps(gitPlugin["GenerateDynamic"]);

// This is the only way to connect to parameters. It's not great.
// Since they are optional, if nothing is supplied `Plan` will not replace the parameter. Should file an issue.
gitProcessPlan.Steps[0].Parameters.Set("filter", "$filter");
gitProcessPlan.Steps[0].Parameters.Set("target", "$target");
gitProcessPlan.Steps[0].Parameters.Set("source", "$source");
gitProcessPlan.Steps[1].Parameters.Set("fullDiff", "$gitDiffResult");
gitProcessPlan.Steps[1].Parameters.Set("instructions", "$instructions");

var p = gitProcessPlan.Describe();
kernel.ImportPlan(gitProcessPlan);
validPlugins.Add("Plan");
}
}

return kernel;
return (kernel, validPlugins);
}

public class StepwisePlugin
Expand All @@ -90,9 +135,23 @@ public StepwisePlugin(IKernel kernel)
}

[SKFunction, Description("Respond to a message.")]
public async Task<SKContext?> RespondTo(string message, string history)
public async Task<SKContext?> RespondTo(string message, string history, string validPlugins, CancellationToken cancellationToken = default)
{
var planner = new StepwisePlanner(this._kernel);
var config = new StepwisePlannerConfig();
var plugins = JsonSerializer.Deserialize<List<string>>(validPlugins);

config.GetAvailableFunctionsAsync = (plannerConfig, filter, token) =>
{
var functions = this._kernel.Functions;
var functionViews = functions.GetFunctionViews();

var filteredViews = functionViews
.Where(f => plugins is null || plugins.Contains(f.PluginName))
.OrderBy(f => $"{f.PluginName}.{f.Name}");

return Task.FromResult(filteredViews);
};
var planner = new StepwisePlanner(this._kernel, config);

// Option 1 - Respond to just the message
// var plan = planner.CreatePlan(message);
Expand All @@ -104,7 +163,7 @@ public StepwisePlugin(IKernel kernel)

// Option 3 - Respond to the history with prompt
var plan = planner.CreatePlan($"{history}\n---\nGiven the conversation history, respond to the most recent message.");
var result = await this._kernel.RunAsync(plan);
var result = await this._kernel.RunAsync(plan, cancellationToken: cancellationToken);

// Extract metadata and result string into new SKContext -- Is there a better way?
var functionResult = result?.FunctionResults?.FirstOrDefault();
Expand All @@ -124,14 +183,15 @@ public StepwisePlugin(IKernel kernel)
}
}

private static async Task RunChat(IKernel kernel, ILogger? logger, ISKFunction chatFunction)
private static async Task RunChat(IKernel kernel, IList<string> validPlugins, ILogger? logger, ISKFunction chatFunction, CancellationToken cancellationToken = default)
{
AnsiConsole.MarkupLine("[grey]Press Enter twice to send a message.[/]");
AnsiConsole.MarkupLine("[grey]Enter 'exit' to exit.[/]");
var contextVariables = new ContextVariables();

var history = string.Empty;
contextVariables.Set("history", history);
contextVariables.Set("validPlugins", JsonSerializer.Serialize(validPlugins));

KernelResult botMessage = KernelResult.FromFunctionResults("Hello!", new List<FunctionResult>());

Expand Down
Binary file added apps/SKonsole/Images/icon.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion apps/SKonsole/KernelProvider.cs
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ public IKernel Get()
.WithLoggerFactory(s_loggerFactory)
.Build();

_kernel.LoggerFactory.CreateLogger<KernelProvider>().LogTrace("KernelProvider.Instance: Added Azure OpenAI backends");
_kernel.LoggerFactory.CreateLogger(this.GetType()).LogTrace("KernelProvider.Instance: Added Azure OpenAI backends");

return _kernel;
}
Expand Down
Loading