Skip to content

Commit

Permalink
Replace "Skills" term, as appropriate. (microsoft#587)
Browse files Browse the repository at this point in the history
### Motivation and Context

<!-- Thank you for your contribution to the chat-copilot 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.
-->

Since late May 2023, we've been transitioning away from the term `Skills
`in favor of `Plugins` in the semantic-kernel ecosystem. This completes
that transition for chat-copiilot.

> NOTE: There is one definition where the `skill` term cannot be
replaced since its linked to the Plan class definition in
semantic-kernel.


![image](https://github.com/microsoft/chat-copilot/assets/66376200/bc0dd9ba-297e-468f-8078-fb8b187335f8)

![image](https://github.com/microsoft/chat-copilot/assets/66376200/2079825b-528d-4556-8899-0084ee63abd6)

### Contribution Checklist

<!-- Before submitting this PR, please make sure: -->

- [x] The code builds clean without any errors or warnings
- [x] The PR follows the [Contribution
Guidelines](https://github.com/microsoft/chat-copilot/blob/main/CONTRIBUTING.md)
and the [pre-submission formatting
script](https://github.com/microsoft/chat-copilot/blob/main/CONTRIBUTING.md#development-scripts)
raises no violations
- [x] All unit tests pass, and I have added new tests where possible
- [x] I didn't break anyone 😄
  • Loading branch information
crickman authored Nov 8, 2023
1 parent 2af2c63 commit 8c89901
Show file tree
Hide file tree
Showing 38 changed files with 205 additions and 203 deletions.
60 changes: 30 additions & 30 deletions webapi/Controllers/ChatController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,8 @@
using CopilotChat.WebApi.Models.Response;
using CopilotChat.WebApi.Models.Storage;
using CopilotChat.WebApi.Options;
using CopilotChat.WebApi.Plugins.Chat;
using CopilotChat.WebApi.Services;
using CopilotChat.WebApi.Skills.ChatSkills;
using CopilotChat.WebApi.Storage;
using CopilotChat.WebApi.Utilities;
using Microsoft.AspNetCore.Http;
Expand Down Expand Up @@ -51,7 +51,7 @@ public class ChatController : ControllerBase, IDisposable
private readonly PlannerOptions _plannerOptions;
private readonly IDictionary<string, Plugin> _plugins;

private const string ChatSkillName = "ChatSkill";
private const string ChatPluginName = nameof(ChatPlugin);
private const string ChatFunctionName = "Chat";
private const string ProcessPlanFunctionName = "ProcessPlan";
private const string GeneratingResponseClientCall = "ReceiveBotResponseStatus";
Expand All @@ -74,7 +74,7 @@ public ChatController(
}

/// <summary>
/// Invokes the chat skill to get a response from the bot.
/// Invokes the chat function to get a response from the bot.
/// </summary>
/// <param name="kernel">Semantic kernel obtained through dependency injection.</param>
/// <param name="messageRelayHubContext">Message Hub that performs the real time relay service.</param>
Expand Down Expand Up @@ -110,7 +110,7 @@ public async Task<IActionResult> ChatAsync(
}

/// <summary>
/// Invokes the chat skill to process and/or execute plan.
/// Invokes the chat function to process and/or execute plan.
/// </summary>
/// <param name="kernel">Semantic kernel obtained through dependency injection.</param>
/// <param name="messageRelayHubContext">Message Hub that performs the real time relay service.</param>
Expand Down Expand Up @@ -146,9 +146,9 @@ public async Task<IActionResult> ProcessPlanAsync(
}

/// <summary>
/// Invokes given function of ChatSkill.
/// Invokes given function of ChatPlugin.
/// </summary>
/// <param name="functionName">Name of the ChatSkill function to invoke.</param>
/// <param name="functionName">Name of the ChatPlugin function to invoke.</param>
/// <param name="kernel">Semantic kernel obtained through dependency injection.</param>
/// <param name="messageRelayHubContext">Message Hub that performs the real time relay service.</param>
/// <param name="planner">Planner to use to create function sequences.</param>
Expand Down Expand Up @@ -187,22 +187,22 @@ private async Task<IActionResult> HandleRequest(
}

// Register plugins that have been enabled
var openApiSkillsAuthHeaders = this.GetPluginAuthHeaders(this.HttpContext.Request.Headers);
await this.RegisterPlannerSkillsAsync(planner, openApiSkillsAuthHeaders, contextVariables);
var openApiPluginAuthHeaders = this.GetPluginAuthHeaders(this.HttpContext.Request.Headers);
await this.RegisterPlannerFunctionsAsync(planner, openApiPluginAuthHeaders, contextVariables);

// Register hosted plugins that have been enabled
await this.RegisterPlannerHostedSkillsAsync(planner, chat!.EnabledPlugins);
await this.RegisterPlannerHostedFunctionsUsedAsync(planner, chat!.EnabledPlugins);

// Get the function to invoke
ISKFunction? function = null;
try
{
function = kernel.Functions.GetFunction(ChatSkillName, functionName);
function = kernel.Functions.GetFunction(ChatPluginName, functionName);
}
catch (SKException ex)
{
this._logger.LogError("Failed to find {SkillName}/{FunctionName} on server: {Exception}", ChatSkillName, functionName, ex);
return this.NotFound($"Failed to find {ChatSkillName}/{functionName} on server");
this._logger.LogError("Failed to find {PluginName}/{FunctionName} on server: {Exception}", ChatPluginName, functionName, ex);
return this.NotFound($"Failed to find {ChatPluginName}/{functionName} on server");
}

// Run the function.
Expand All @@ -215,7 +215,7 @@ private async Task<IActionResult> HandleRequest(
: null;

result = await kernel.RunAsync(function!, contextVariables, cts?.Token ?? default);
this._telemetryService.TrackSkillFunction(ChatSkillName, functionName, true);
this._telemetryService.TrackPluginFunction(ChatPluginName, functionName, true);
}
catch (Exception ex)
{
Expand All @@ -226,11 +226,11 @@ private async Task<IActionResult> HandleRequest(
return this.StatusCode(StatusCodes.Status504GatewayTimeout, $"The chat {functionName} timed out.");
}

this._telemetryService.TrackSkillFunction(ChatSkillName, functionName, false);
this._telemetryService.TrackPluginFunction(ChatPluginName, functionName, false);
throw ex;
}

AskResult chatSkillAskResult = new()
AskResult chatAskResult = new()
{
Value = result.GetValue<string>() ?? string.Empty,
Variables = contextVariables.Select(v => new KeyValuePair<string, string>(v.Key, v.Value))
Expand All @@ -239,7 +239,7 @@ private async Task<IActionResult> HandleRequest(
// Broadcast AskResult to all users
await messageRelayHubContext.Clients.Group(chatId).SendAsync(GeneratingResponseClientCall, chatId, null);

return this.Ok(chatSkillAskResult);
return this.Ok(chatAskResult);
}

/// <summary>
Expand All @@ -251,7 +251,7 @@ private Dictionary<string, string> GetPluginAuthHeaders(IHeaderDictionary header
var regex = new Regex("x-sk-copilot-(.*)-auth", RegexOptions.IgnoreCase);

// Create a dictionary to store the matched headers and values
var openApiSkillsAuthHeaders = new Dictionary<string, string>();
var authHeaders = new Dictionary<string, string>();

// Loop through the request headers and add the matched ones to the dictionary
foreach (var header in headers)
Expand All @@ -260,44 +260,44 @@ private Dictionary<string, string> GetPluginAuthHeaders(IHeaderDictionary header
if (match.Success)
{
// Use the first capture group as the key and the header value as the value
openApiSkillsAuthHeaders.Add(match.Groups[1].Value.ToUpperInvariant(), header.Value!);
authHeaders.Add(match.Groups[1].Value.ToUpperInvariant(), header.Value!);
}
}

return openApiSkillsAuthHeaders;
return authHeaders;
}

/// <summary>
/// Register skills with the planner's kernel.
/// Register functions with the planner's kernel.
/// </summary>
private async Task RegisterPlannerSkillsAsync(CopilotChatPlanner planner, Dictionary<string, string> openApiSkillsAuthHeaders, ContextVariables variables)
private async Task RegisterPlannerFunctionsAsync(CopilotChatPlanner planner, Dictionary<string, string> authHeaders, ContextVariables variables)
{
// Register authenticated skills with the planner's kernel only if the request includes an auth header for the skill.
// Register authenticated functions with the planner's kernel only if the request includes an auth header for the plugin.

// GitHub
if (openApiSkillsAuthHeaders.TryGetValue("GITHUB", out string? GithubAuthHeader))
if (authHeaders.TryGetValue("GITHUB", out string? GithubAuthHeader))
{
this._logger.LogInformation("Enabling GitHub plugin.");
BearerAuthenticationProvider authenticationProvider = new(() => Task.FromResult(GithubAuthHeader));
await planner.Kernel.ImportPluginFunctionsAsync(
pluginName: "GitHubPlugin",
filePath: Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location)!, "Skills", "OpenApiPlugins/GitHubPlugin/openapi.json"),
filePath: Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location)!, "Plugins", "OpenApi/GitHubPlugin/openapi.json"),
new OpenApiFunctionExecutionParameters
{
AuthCallback = authenticationProvider.AuthenticateRequestAsync,
});
}

// Jira
if (openApiSkillsAuthHeaders.TryGetValue("JIRA", out string? JiraAuthHeader))
if (authHeaders.TryGetValue("JIRA", out string? JiraAuthHeader))
{
this._logger.LogInformation("Registering Jira plugin");
var authenticationProvider = new BasicAuthenticationProvider(() => { return Task.FromResult(JiraAuthHeader); });
var hasServerUrlOverride = variables.TryGetValue("jira-server-url", out string? serverUrlOverride);

await planner.Kernel.ImportPluginFunctionsAsync(
pluginName: "JiraPlugin",
filePath: Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location)!, "Skills", "OpenApiPlugins/JiraPlugin/openapi.json"),
filePath: Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location)!, "Plugins", "OpenApi/JiraPlugin/openapi.json"),
new OpenApiFunctionExecutionParameters
{
AuthCallback = authenticationProvider.AuthenticateRequestAsync,
Expand All @@ -306,9 +306,9 @@ await planner.Kernel.ImportPluginFunctionsAsync(
}

// Microsoft Graph
if (openApiSkillsAuthHeaders.TryGetValue("GRAPH", out string? GraphAuthHeader))
if (authHeaders.TryGetValue("GRAPH", out string? GraphAuthHeader))
{
this._logger.LogInformation("Enabling Microsoft Graph skill(s).");
this._logger.LogInformation("Enabling Microsoft Graph plugin(s).");
BearerAuthenticationProvider authenticationProvider = new(() => Task.FromResult(GraphAuthHeader));
GraphServiceClient graphServiceClient = this.CreateGraphServiceClient(authenticationProvider.AuthenticateRequestAsync);

Expand All @@ -325,7 +325,7 @@ await planner.Kernel.ImportPluginFunctionsAsync(
{
foreach (CustomPlugin plugin in customPlugins)
{
if (openApiSkillsAuthHeaders.TryGetValue(plugin.AuthHeaderTag.ToUpperInvariant(), out string? PluginAuthValue))
if (authHeaders.TryGetValue(plugin.AuthHeaderTag.ToUpperInvariant(), out string? PluginAuthValue))
{
// Register the ChatGPT plugin with the planner's kernel.
this._logger.LogInformation("Enabling {0} plugin.", plugin.NameForHuman);
Expand Down Expand Up @@ -373,7 +373,7 @@ private GraphServiceClient CreateGraphServiceClient(AuthenticateRequestAsyncDele
return graphServiceClient;
}

private async Task RegisterPlannerHostedSkillsAsync(CopilotChatPlanner planner, HashSet<string> enabledPlugins)
private async Task RegisterPlannerHostedFunctionsUsedAsync(CopilotChatPlanner planner, HashSet<string> enabledPlugins)
{
foreach (string enabledPlugin in enabledPlugins)
{
Expand Down
2 changes: 1 addition & 1 deletion webapi/Controllers/ChatHistoryController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
using CopilotChat.WebApi.Models.Response;
using CopilotChat.WebApi.Models.Storage;
using CopilotChat.WebApi.Options;
using CopilotChat.WebApi.Skills.Utils;
using CopilotChat.WebApi.Plugins.Utils;
using CopilotChat.WebApi.Storage;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http;
Expand Down
4 changes: 2 additions & 2 deletions webapi/CopilotChatWebApi.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -84,10 +84,10 @@
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<!--
<Content Include="Skills\NativePlugins\*.*">
<Content Include="Plugins\NativePlugins\*.*">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="Skills\SemanticPlugins\*.*">
<Content Include="Plugins\SemanticPlugins\*.*">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
-->
Expand Down
42 changes: 21 additions & 21 deletions webapi/Extensions/SemanticKernelExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@
using CopilotChat.WebApi.Hubs;
using CopilotChat.WebApi.Models.Response;
using CopilotChat.WebApi.Options;
using CopilotChat.WebApi.Plugins.Chat;
using CopilotChat.WebApi.Services;
using CopilotChat.WebApi.Skills.ChatSkills;
using CopilotChat.WebApi.Storage;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.SignalR;
Expand All @@ -31,9 +31,9 @@ namespace CopilotChat.WebApi.Extensions;
internal static class SemanticKernelExtensions
{
/// <summary>
/// Delegate to register plugins with a Semantic Kernel
/// Delegate to register functions with a Semantic Kernel
/// </summary>
public delegate Task RegisterSkillsWithKernel(IServiceProvider sp, IKernel kernel);
public delegate Task RegisterFunctionsWithKernel(IServiceProvider sp, IKernel kernel);

/// <summary>
/// Delegate for any complimentary setup of the kernel, i.e., registering custom plugins, etc.
Expand All @@ -45,7 +45,7 @@ internal static class SemanticKernelExtensions
/// Delegate to register plugins with the planner's kernel (i.e., omits plugins not required to generate bot response).
/// See webapi/README.md#Add-Custom-Plugin-Registration-to-the-Planner's-Kernel for more details.
/// </summary>
public delegate Task RegisterSkillsWithPlannerHook(IServiceProvider sp, IKernel kernel);
public delegate Task RegisterFunctionsWithPlannerHook(IServiceProvider sp, IKernel kernel);

/// <summary>
/// Add Semantic Kernel services
Expand All @@ -61,7 +61,7 @@ public static WebApplicationBuilder AddSemanticKernelServices(this WebApplicatio
var provider = sp.GetRequiredService<SemanticKernelProvider>();
var kernel = provider.GetCompletionKernel();

sp.GetRequiredService<RegisterSkillsWithKernel>()(sp, kernel);
sp.GetRequiredService<RegisterFunctionsWithKernel>()(sp, kernel);

// If KernelSetupHook is not null, invoke custom kernel setup.
sp.GetService<KernelSetupHook>()?.Invoke(sp, kernel);
Expand All @@ -72,7 +72,7 @@ public static WebApplicationBuilder AddSemanticKernelServices(this WebApplicatio
builder.Services.AddContentSafety();

// Register plugins
builder.Services.AddScoped<RegisterSkillsWithKernel>(sp => RegisterChatCopilotSkillsAsync);
builder.Services.AddScoped<RegisterFunctionsWithKernel>(sp => RegisterChatCopilotFunctionsAsync);

// Add any additional setup needed for the kernel.
// Uncomment the following line and pass in a custom hook for any complimentary setup of the kernel.
Expand All @@ -97,7 +97,7 @@ public static WebApplicationBuilder AddPlannerServices(this WebApplicationBuilde
var plannerKernel = provider.GetPlannerKernel();

// Invoke custom plugin registration for planner's kernel.
sp.GetService<RegisterSkillsWithPlannerHook>()?.Invoke(sp, plannerKernel);
sp.GetService<RegisterFunctionsWithPlannerHook>()?.Invoke(sp, plannerKernel);

return new CopilotChatPlanner(plannerKernel, plannerOptions?.Value, sp.GetRequiredService<ILogger<CopilotChatPlanner>>());
});
Expand Down Expand Up @@ -132,27 +132,27 @@ public static IServiceCollection AddKernelSetupHook(this IServiceCollection serv
/// <summary>
/// Register custom hook for registering plugins with the planner's kernel.
/// These plugins will be persistent and available to the planner on every request.
/// Transient plugins requiring auth or configured by the webapp should be registered in RegisterPlannerSkillsAsync of ChatController.
/// Transient plugins requiring auth or configured by the webapp should be registered in RegisterPlannerFunctionsAsync of ChatController.
/// </summary>
/// <param name="registerPluginsHook">The delegate to register plugins with the planner's kernel. If null, defaults to local runtime plugin registration using RegisterPluginsAsync.</param>
public static IServiceCollection AddPlannerSetupHook(this IServiceCollection services, RegisterSkillsWithPlannerHook? registerPluginsHook = null)
public static IServiceCollection AddPlannerSetupHook(this IServiceCollection services, RegisterFunctionsWithPlannerHook? registerPluginsHook = null)
{
// Default to local runtime plugin registration.
registerPluginsHook ??= RegisterPluginsAsync;

// Add the hook to the service collection
services.AddScoped<RegisterSkillsWithPlannerHook>(sp => registerPluginsHook);
services.AddScoped<RegisterFunctionsWithPlannerHook>(sp => registerPluginsHook);
return services;
}

/// <summary>
/// Register the chat skill with the kernel.
/// Register the chat plugin with the kernel.
/// </summary>
public static IKernel RegisterChatSkill(this IKernel kernel, IServiceProvider sp)
public static IKernel RegisterChatPlugin(this IKernel kernel, IServiceProvider sp)
{
// Chat skill
// Chat plugin
kernel.ImportFunctions(
new ChatSkill(
new ChatPlugin(
kernel,
memoryClient: sp.GetRequiredService<IKernelMemory>(),
chatMessageRepository: sp.GetRequiredService<ChatMessageRepository>(),
Expand All @@ -162,8 +162,8 @@ public static IKernel RegisterChatSkill(this IKernel kernel, IServiceProvider sp
documentImportOptions: sp.GetRequiredService<IOptions<DocumentMemoryOptions>>(),
contentSafety: sp.GetService<AzureContentSafety>(),
planner: sp.GetRequiredService<CopilotChatPlanner>(),
logger: sp.GetRequiredService<ILogger<ChatSkill>>()),
nameof(ChatSkill));
logger: sp.GetRequiredService<ILogger<ChatPlugin>>()),
nameof(ChatPlugin));

return kernel;
}
Expand All @@ -174,14 +174,14 @@ private static void InitializeKernelProvider(this WebApplicationBuilder builder)
}

/// <summary>
/// Register skills with the main kernel responsible for handling Chat Copilot requests.
/// Register functions with the main kernel responsible for handling Chat Copilot requests.
/// </summary>
private static Task RegisterChatCopilotSkillsAsync(IServiceProvider sp, IKernel kernel)
private static Task RegisterChatCopilotFunctionsAsync(IServiceProvider sp, IKernel kernel)
{
// Chat Copilot skills
kernel.RegisterChatSkill(sp);
// Chat Copilot functions
kernel.RegisterChatPlugin(sp);

// Time skill
// Time plugin
kernel.ImportFunctions(new TimePlugin(), nameof(TimePlugin));

return Task.CompletedTask;
Expand Down
10 changes: 5 additions & 5 deletions webapi/Models/Response/PlanExecutionMetadata.cs
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,10 @@ public class PlanExecutionMetadata
public string TimeTaken { get; set; } = string.Empty;

/// <summary>
/// Skills used execution stat.
/// Functions used execution stat.
/// </summary>
[JsonPropertyName("skillsUsed")]
public string SkillsUsed { get; set; } = string.Empty;
[JsonPropertyName("functionsUsed")]
public string FunctionsUsed { get; set; } = string.Empty;

/// <summary>
/// Planner type.
Expand All @@ -40,11 +40,11 @@ public class PlanExecutionMetadata
[JsonIgnore]
public string RawResult { get; set; } = string.Empty;

public PlanExecutionMetadata(string stepsTaken, string timeTaken, string skillsUsed, string rawResult)
public PlanExecutionMetadata(string stepsTaken, string timeTaken, string functionsUsed, string rawResult)
{
this.StepsTaken = stepsTaken;
this.TimeTaken = timeTaken;
this.SkillsUsed = skillsUsed;
this.FunctionsUsed = functionsUsed;
this.RawResult = rawResult;
}
}
Loading

0 comments on commit 8c89901

Please sign in to comment.