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

CopilotChat: External skills and planner #659

Merged
merged 31 commits into from
Apr 26, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
09aacf5
Investigation WIP
adrianwyatt Apr 25, 2023
699fa7b
wip
adrianwyatt Apr 25, 2023
2c1ac32
Merge remote-tracking branch 'origin/main' into planner
adrianwyatt Apr 25, 2023
ab5ea75
Merge remote-tracking branch 'origin/main' into planner
adrianwyatt Apr 25, 2023
ea36145
Working planner
adrianwyatt Apr 26, 2023
d61c8e2
Working planner
adrianwyatt Apr 26, 2023
8698feb
Prepping for draft PR
adrianwyatt Apr 26, 2023
b57137b
Prep for PR
adrianwyatt Apr 26, 2023
3457ef3
Merge branch 'main' into planner
adrianwyatt Apr 26, 2023
a76edd8
Add planner enable flag
adrianwyatt Apr 26, 2023
271c1d6
Add planner enable flag
adrianwyatt Apr 26, 2023
5f5bb3c
Add some core and semantic functions to planner, refactored to use a …
adrianwyatt Apr 26, 2023
df97886
Cleaned up copilotchatapi proejct file
adrianwyatt Apr 26, 2023
5d705ee
Minor fixes from PR comments
adrianwyatt Apr 26, 2023
07419c0
Minor fixes from PR comments
adrianwyatt Apr 26, 2023
fd2ea34
Minor fixes from PR comments
adrianwyatt Apr 26, 2023
8c18a51
Renamed PlannerFactory to include Async, updated plan invoke to remov…
adrianwyatt Apr 26, 2023
5e8d9e9
Renamed PlannerFactory to include Async, updated plan invoke to remov…
adrianwyatt Apr 26, 2023
0790b5b
Minor comment on relevancy in appsettings
adrianwyatt Apr 26, 2023
5c2666d
Minor comment on relevancy in appsettings
adrianwyatt Apr 26, 2023
571b63d
Merge branch 'main' into planner
adrianwyatt Apr 26, 2023
157b7ed
wip
adrianwyatt Apr 26, 2023
7531fc6
wip
adrianwyatt Apr 26, 2023
c1b1486
wip
adrianwyatt Apr 26, 2023
244e598
wip
adrianwyatt Apr 26, 2023
37c65b3
Merge remote-tracking branch 'origin/main' into planner
adrianwyatt Apr 26, 2023
544b2a6
Merge branch 'main' into planner
adrianwyatt Apr 26, 2023
a213a1f
Fixed up project file and renamed planner options to SequentialPlanne…
adrianwyatt Apr 26, 2023
91bf5d7
Merge branch 'planner' of https://github.com/adrianwyatt/semantic-ker…
adrianwyatt Apr 26, 2023
864246b
Fix planner input
adrianwyatt Apr 26, 2023
ef980e7
Merge branch 'main' into planner
adrianwyatt Apr 26, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
// Copyright (c) Microsoft. All rights reserved.

using System.ComponentModel.DataAnnotations;
using Microsoft.SemanticKernel.Planning.Sequential;

namespace SemanticKernel.Service.Config;

/// <summary>
/// Configuration options for the planner.
/// </summary>
public class SequentialPlannerOptions
{
public const string PropertyName = "Planner";

/// <summary>
/// Whether to enable the planner.
/// </summary>
public bool Enabled { get; set; } = false;

/// <summary>
/// The directory containing semantic skills to include in the planner's list of available functions.
/// </summary>
public string? SemanticSkillsDirectory { get; set; }

/// <summary>
/// The minimum relevancy score for a function to be considered
/// </summary>
public double? RelevancyThreshold { get; set; }

/// <summary>
/// The maximum number of relevant functions to include in the plan.
/// </summary>
public int MaxRelevantFunctions { get; set; } = 100;

/// <summary>
/// A list of skills to exclude from the plan creation request.
/// </summary>
public HashSet<string> ExcludedSkills { get; set; } = new() { };

/// <summary>
/// A list of functions to exclude from the plan creation request.
/// </summary>
public HashSet<string> ExcludedFunctions { get; set; } = new() { };

/// <summary>
/// A list of functions to include in the plan creation request.
/// </summary>
public HashSet<string> IncludedFunctions { get; set; } = new() { };

/// <summary>
/// The maximum number of tokens to allow in a plan.
/// </summary>
[Range(1, int.MaxValue)]
public int MaxTokens { get; set; } = 1024;

/// <summary>
/// Convert to a <see cref="SequentialPlannerConfig"/> instance.
/// </summary>
public SequentialPlannerConfig ToSequentialPlannerConfig()
{
SequentialPlannerConfig config = new()
{
RelevancyThreshold = RelevancyThreshold,
MaxRelevantFunctions = MaxRelevantFunctions,
MaxTokens = MaxTokens,
};

this.ExcludedSkills.Clear();
foreach (var excludedSkill in this.ExcludedSkills)
{
config.ExcludedSkills.Add(excludedSkill);
}

this.ExcludedFunctions.Clear();
foreach (var excludedFunction in this.ExcludedFunctions)
{
config.ExcludedFunctions.Add(excludedFunction);
}

this.IncludedFunctions.Clear();
foreach (var includedFunction in this.IncludedFunctions)
{
config.IncludedFunctions.Add(includedFunction);
}

return config;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,8 @@ public class SemanticKernelController : ControllerBase
/// <param name="chatRepository">Storage repository to store chat sessions</param>
/// <param name="chatMessageRepository">Storage repository to store chat messages</param>
/// <param name="documentMemoryOptions">Options for document memory handling.</param>
/// <param name="plannerFactory">Factory for planners to use to create function sequences.</param>
/// <param name="plannerOptions">Options for the planner.</param>
/// <param name="ask">Prompt along with its parameters</param>
/// <param name="skillName">Skill in which function to invoke resides</param>
/// <param name="functionName">Name of function to invoke</param>
Expand All @@ -59,6 +61,8 @@ public class SemanticKernelController : ControllerBase
[FromServices] ChatSessionRepository chatRepository,
[FromServices] ChatMessageRepository chatMessageRepository,
[FromServices] IOptions<DocumentMemoryOptions> documentMemoryOptions,
[FromServices] PlannerFactoryAsync plannerFactory,
[FromServices] IOptions<SequentialPlannerOptions> plannerOptions,
[FromBody] Ask ask,
string skillName, string functionName)
{
Expand All @@ -69,12 +73,20 @@ public class SemanticKernelController : ControllerBase
return this.BadRequest("Input is required.");
}

// Not required for Copilot Chat, but this is how to register additional skills for the service to provide.
if (!string.IsNullOrWhiteSpace(this._options.SemanticSkillsDirectory))
{
kernel.RegisterSemanticSkills(this._options.SemanticSkillsDirectory, this._logger);
}

kernel.RegisterNativeSkills(chatRepository, chatMessageRepository, this._promptSettings, documentMemoryOptions.Value, this._logger);
kernel.RegisterNativeSkills(
chatSessionRepository: chatRepository,
chatMessageRepository: chatMessageRepository,
promptSettings: this._promptSettings,
plannerFactory: plannerFactory,
plannerOptions: plannerOptions.Value,
documentMemoryOptions: documentMemoryOptions.Value,
logger: this._logger);

ISKFunction? function = null;
try
Expand Down
22 changes: 20 additions & 2 deletions samples/apps/copilot-chat-app/webapi/CopilotChatApi.csproj
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<Project Sdk="Microsoft.NET.Sdk.Web">
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<RepoRoot>$([System.IO.Path]::GetDirectoryName($([MSBuild]::GetPathOfFileAbove('.gitignore', '$(MSBuildThisFileDirectory)'))))</RepoRoot>
</PropertyGroup>
Expand All @@ -13,22 +13,40 @@
<UserSecretsId>aspnet-SKWebApi-1581687a-bee4-40ea-a886-ce22524aea88</UserSecretsId>
</PropertyGroup>

<ItemGroup>
adrianwyatt marked this conversation as resolved.
Show resolved Hide resolved
<None Include="PlannerSkills\Summarize\config.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="PlannerSkills\Summarize\skprompt.txt">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Include="PlannerSkills\Translate\config.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="PlannerSkills\Translate\skprompt.txt">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
</ItemGroup>

<ItemGroup>
<PackageReference Include="Azure.Extensions.AspNetCore.Configuration.Secrets" Version="1.2.2" />
<PackageReference Include="Azure.Identity" Version="1.8.2" />
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="6.0.14" />
<PackageReference Include="Microsoft.AspNetCore.Authentication.OpenIdConnect" Version="6.0.14" />
<PackageReference Include="Microsoft.Azure.Cosmos" Version="3.32.3" />
<PackageReference Include="Microsoft.CognitiveServices.Speech" Version="1.27.0" />
<PackageReference Include="Microsoft.Extensions.Options.DataAnnotations" Version="7.0.0" />
<PackageReference Include="Microsoft.Identity.Web" Version="2.9.0" />
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.5.0" />
<PackageReference Include="System.Linq.Async" Version="6.0.1" />
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\..\..\..\dotnet\src\Connectors\Connectors.AI.OpenAI\Connectors.AI.OpenAI.csproj" />
<ProjectReference Include="..\..\..\..\dotnet\src\Connectors\Connectors.Memory.Qdrant\Connectors.Memory.Qdrant.csproj" />
<ProjectReference Include="..\..\..\..\dotnet\src\Extensions\Planning.SequentialPlanner\Planning.SequentialPlanner.csproj" />
<ProjectReference Include="..\..\..\..\dotnet\src\SemanticKernel\SemanticKernel.csproj" />
<ProjectReference Include="..\..\..\..\dotnet\src\Skills\Skills.OpenAPI\Skills.OpenAPI.csproj" />
<ProjectReference Include="..\..\..\..\dotnet\src\Skills\Skills.Web\Skills.Web.csproj" />
</ItemGroup>

</Project>
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,8 @@ internal static class FunctionLoadingExtensions
ChatSessionRepository chatSessionRepository,
ChatMessageRepository chatMessageRepository,
PromptSettings promptSettings,
PlannerFactoryAsync plannerFactory,
SequentialPlannerOptions plannerOptions,
DocumentMemoryOptions documentMemoryOptions,
ILogger logger)
{
Expand All @@ -51,10 +53,13 @@ internal static class FunctionLoadingExtensions
kernel.ImportSkill(timeSkill, nameof(TimeSkill));

var chatSkill = new ChatSkill(
kernel,
chatMessageRepository,
chatSessionRepository,
promptSettings
kernel: kernel,
chatMessageRepository: chatMessageRepository,
chatSessionRepository: chatSessionRepository,
promptSettings: promptSettings,
plannerFactory: plannerFactory,
adrianwyatt marked this conversation as resolved.
Show resolved Hide resolved
plannerOptions: plannerOptions,
logger: logger
);
kernel.ImportSkill(chatSkill, nameof(ChatSkill));

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
{
"schema": 1,
"type": "completion",
"description": "Summarize given text or any text document",
"completion": {
"max_tokens": 512,
"temperature": 0.0,
"top_p": 0.0,
"presence_penalty": 0.0,
"frequency_penalty": 0.0
},
"input": {
"parameters": [
{
"name": "input",
"description": "Text to summarize",
"defaultValue": ""
}
]
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
[SUMMARIZATION RULES]
DONT WASTE WORDS
adrianwyatt marked this conversation as resolved.
Show resolved Hide resolved
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}}
+++++
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
{
"schema": 1,
"type": "completion",
"description": "Translate the input into a language of your choice",
"completion": {
"max_tokens": 256,
"temperature": 0.7,
"top_p": 0.0,
"presence_penalty": 0.0,
"frequency_penalty": 0.0,
"stop_sequences": [
"[done]"
]
adrianwyatt marked this conversation as resolved.
Show resolved Hide resolved
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
Translate the input below into {{$language}}

MAKE SURE YOU ONLY USE {{$language}}.

{{$input}}

Translation:
45 changes: 42 additions & 3 deletions samples/apps/copilot-chat-app/webapi/SemanticKernelExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@
using Microsoft.SemanticKernel.Connectors.AI.OpenAI.TextEmbedding;
using Microsoft.SemanticKernel.Connectors.Memory.Qdrant;
using Microsoft.SemanticKernel.Memory;
using Microsoft.SemanticKernel.Planning;
using Microsoft.SemanticKernel.Planning.Sequential;
using Microsoft.SemanticKernel.Reliability;
using Microsoft.SemanticKernel.SkillDefinition;
using Microsoft.SemanticKernel.TemplateEngine;
Expand All @@ -34,6 +36,7 @@ internal static IServiceCollection AddSemanticKernelServices(this IServiceCollec
});
services.AddSingleton<PromptSettings>();

// Add the semantic memory with backing memory store.
services.AddSingleton<IMemoryStore>(serviceProvider =>
{
MemoriesStoreOptions config = serviceProvider.GetRequiredService<IOptions<MemoriesStoreOptions>>().Value;
Expand Down Expand Up @@ -62,9 +65,12 @@ internal static IServiceCollection AddSemanticKernelServices(this IServiceCollec
});

services.AddScoped<ISemanticTextMemory>(serviceProvider => new SemanticTextMemory(
serviceProvider.GetRequiredService<IMemoryStore>(),
serviceProvider.GetRequiredService<IOptionsSnapshot<AIServiceOptions>>().Get(AIServiceOptions.EmbeddingPropertyName)
.ToTextEmbeddingsService(serviceProvider.GetRequiredService<ILogger<AIServiceOptions>>())));
serviceProvider.GetRequiredService<IMemoryStore>(),
serviceProvider.GetRequiredService<IOptionsSnapshot<AIServiceOptions>>().Get(AIServiceOptions.EmbeddingPropertyName)
.ToTextEmbeddingsService(serviceProvider.GetRequiredService<ILogger<AIServiceOptions>>())));

// Add the planner factory.
services.AddPlannerFactory();

// Add the Semantic Kernel
services.AddSingleton<IPromptTemplateEngine, PromptTemplateEngine>();
Expand All @@ -77,6 +83,39 @@ internal static IServiceCollection AddSemanticKernelServices(this IServiceCollec
return services;
}

/// <summary>
/// Add the planner factory.
/// </summary>
internal static IServiceCollection AddPlannerFactory(this IServiceCollection services)
{
// TODO Replace sequential planner with a custom CopilotChat planner tuned to chat scenarios.

services.AddSingleton<SequentialPlannerConfig>(sp => sp.GetRequiredService<IOptions<SequentialPlannerOptions>>().Value.ToSequentialPlannerConfig());
services.AddScoped<PlannerFactoryAsync>(sp => async (IKernel kernel) =>
{
// Create a kernel for the planner with the same contexts as the chat's kernel but with only skills we want available to the planner.
IKernel plannerKernel = new Kernel(new SkillCollection(), kernel.PromptTemplateEngine, kernel.Memory, kernel.Config, kernel.Log);

//
// Add skills to the planner here.
//
await plannerKernel.ImportChatGptPluginSkillFromUrlAsync("Klarna", new Uri("https://www.klarna.com/.well-known/ai-plugin.json")); // Klarna
plannerKernel.ImportSkill(new Microsoft.SemanticKernel.CoreSkills.TextSkill(), "text");
plannerKernel.ImportSkill(new Microsoft.SemanticKernel.CoreSkills.TimeSkill(), "time");
plannerKernel.ImportSkill(new Microsoft.SemanticKernel.CoreSkills.MathSkill(), "math");

SequentialPlannerOptions plannerOptions = sp.GetRequiredService<IOptions<SequentialPlannerOptions>>().Value;
if (!string.IsNullOrWhiteSpace(plannerOptions.SemanticSkillsDirectory))
{
plannerKernel.RegisterSemanticSkills(plannerOptions.SemanticSkillsDirectory, sp.GetRequiredService<ILogger>());
}

return new SequentialPlanner(plannerKernel, plannerOptions.ToSequentialPlannerConfig());
});

return services;
}

/// <summary>
/// Add the completion backend to the kernel config
/// </summary>
Expand Down
8 changes: 7 additions & 1 deletion samples/apps/copilot-chat-app/webapi/ServiceExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -47,15 +47,21 @@ internal static IServiceCollection AddOptions(this IServiceCollection services,
.Bind(configuration.GetSection(AzureSpeechOptions.PropertyName))
.ValidateDataAnnotations().ValidateOnStart();

// Azure speech token configuration
// Bot schema configuration
services.AddOptions<BotSchemaOptions>()
.Bind(configuration.GetSection(BotSchemaOptions.PropertyName))
.ValidateDataAnnotations().ValidateOnStart();

// Document memory options
services.AddOptions<DocumentMemoryOptions>()
.Bind(configuration.GetSection(DocumentMemoryOptions.PropertyName))
.ValidateDataAnnotations().ValidateOnStart();

// Planner options
services.AddOptions<SequentialPlannerOptions>()
.Bind(configuration.GetSection(SequentialPlannerOptions.PropertyName))
.ValidateDataAnnotations().ValidateOnStart();

return services;
}

Expand Down