Skip to content

Commit

Permalink
Plugin refactor (microsoft#424)
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.
-->
Currently, plugin development using Chat Copilot has the following
drawbacks:
1. CC requires the end user to configure the plugins and once
configured, the plugins are transient, meaning that they disappear when
the user refreshes the app.
2. Plugins are scoped to the user who configured them, meaning that all
users who want to use plugins must independently configure them.

### Description

<!-- Describe your changes, the overall approach, the underlying design.
These notes will help understanding how your code works. Thanks! -->
This PR addresses the above issues by:
1. Move plugin configuration to the backend. Plugins are configured and
added in the backend.
2. Save the states of plugins within chat sessions.
3. Create a new plugin call WebSearcher that performs Bing search. This
plugin can be deployed to Azure.
4. Move the Klarna plugin to the backend.

This is not a breaking change.

This will be the first of a couple more PRs to come. The follow-up PRs
will move the existing plugins that are still managed by the frontend to
this new pattern.


![image](https://github.com/microsoft/chat-copilot/assets/12570346/ab1a293b-5d25-4b8e-9272-ba42ff0850c2)


### Contribution Checklist

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

- [ ] The code builds clean without any errors or warnings
- [ ] 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
- [ ] All unit tests pass, and I have added new tests where possible
- [ ] I didn't break anyone 😄
  • Loading branch information
TaoChenOSU authored Oct 2, 2023
1 parent 19868f0 commit 67ca4fe
Show file tree
Hide file tree
Showing 59 changed files with 2,119 additions and 149 deletions.
74 changes: 74 additions & 0 deletions .github/workflows/copilot-build-plugins.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
name: copilot-build-plugins

on:
workflow_dispatch:
pull_request:
branches: ["main"]
paths:
- "plugins/**"
workflow_call:
outputs:
artifact:
description: "The name of the uploaded plugin artifacts."
value: ${{jobs.plugins.outputs.artifact}}

permissions:
contents: read

jobs:
plugins:
runs-on: windows-latest

env:
NUGET_CERT_REVOCATION_MODE: offline

outputs:
artifact: ${{steps.artifactoutput.outputs.artifactname}}

steps:
- uses: actions/checkout@v4
with:
clean: true
fetch-depth: 0

- name: Install GitVersion
uses: gittools/actions/gitversion/setup@v0
with:
versionSpec: "5.x"

- name: Determine version
id: gitversion
uses: gittools/actions/gitversion/execute@v0

- name: Set version tag
id: versiontag
run: |
$VERSION_TAG = "${{ steps.gitversion.outputs.Major }}."
$VERSION_TAG += "${{ steps.gitversion.outputs.Minor }}."
$VERSION_TAG += "${{ steps.gitversion.outputs.CommitsSinceVersionSource }}"
echo $VERSION_TAG
Write-Output "versiontag=$VERSION_TAG" >> $env:GITHUB_OUTPUT
- name: Set .Net Core version
uses: actions/setup-dotnet@v3
with:
dotnet-version: 6.0.x

- name: Package Copilot Chat Plugins
run: |
scripts\deploy\package-plugins.ps1 `
-BuildConfiguration Release `
-DotNetFramework net6.0 `
-OutputDirectory ${{ github.workspace }}\scripts\deploy `
-Version ${{ steps.versiontag.outputs.versiontag }} `
-InformationalVersion "Built from commit ${{ steps.gitversion.outputs.ShortSha }} on $(Get-Date -Format "yyyy-MM-dd")"
- name: Upload packages to artifacts
uses: actions/upload-artifact@v3
with:
name: copilotchat-plugins-${{ steps.versiontag.outputs.versiontag }}
path: ${{ github.workspace }}\scripts\deploy\out\plugins

- name: "Set outputs"
id: artifactoutput
run: Write-Output "artifactname=copilotchat-plugins-${{ steps.versiontag.outputs.versiontag }}" >> $env:GITHUB_OUTPUT
15 changes: 15 additions & 0 deletions .github/workflows/copilot-deploy-environment.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,9 @@ on:
MEMORYPIPELINE_ARTIFACT_NAME:
required: true
type: string
PLUGINS_ARTIFACT_NAME:
required: true
type: string
secrets:
AZURE_CLIENT_ID:
required: true
Expand Down Expand Up @@ -65,6 +68,18 @@ jobs:
AZURE_TENANT_ID: ${{secrets.AZURE_TENANT_ID}}
AZURE_SUBSCRIPTION_ID: ${{secrets.AZURE_SUBSCRIPTION_ID}}

deploy-plugins:
needs: [deploy-infra]
uses: ./.github/workflows/copilot-deploy-plugins.yml
with:
ARTIFACT_NAME: ${{inputs.PLUGINS_ARTIFACT_NAME}}
DEPLOYMENT_NAME: ${{needs.deploy-infra.outputs.deployment-id}}
ENVIRONMENT: ${{inputs.ENVIRONMENT}}
secrets:
AZURE_CLIENT_ID: ${{secrets.AZURE_CLIENT_ID}}
AZURE_TENANT_ID: ${{secrets.AZURE_TENANT_ID}}
AZURE_SUBSCRIPTION_ID: ${{secrets.AZURE_SUBSCRIPTION_ID}}

deploy-frontend:
needs: [deploy-infra]
uses: ./.github/workflows/copilot-deploy-frontend.yml
Expand Down
7 changes: 6 additions & 1 deletion .github/workflows/copilot-deploy-pipeline.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,17 @@ jobs:
build-memorypipeline:
uses: ./.github/workflows/copilot-build-memorypipeline.yml

build-plugins:
uses: ./.github/workflows/copilot-build-plugins.yml

int:
needs: [build-webapi, build-memorypipeline]
needs: [build-webapi, build-memorypipeline, build-plugins]
uses: ./.github/workflows/copilot-deploy-environment.yml
with:
ENVIRONMENT: int
WEBAPI_ARTIFACT_NAME: ${{needs.build-webapi.outputs.artifact}}
MEMORYPIPELINE_ARTIFACT_NAME: ${{needs.build-memorypipeline.outputs.artifact}}
PLUGINS_ARTIFACT_NAME: ${{needs.build-plugins.outputs.artifact}}
secrets:
AZURE_CLIENT_ID: ${{secrets.AZURE_CLIENT_ID}}
AZURE_TENANT_ID: ${{secrets.AZURE_TENANT_ID}}
Expand All @@ -47,6 +51,7 @@ jobs:
ENVIRONMENT: stable
WEBAPI_ARTIFACT_NAME: ${{needs.build-webapi.outputs.artifact}}
MEMORYPIPELINE_ARTIFACT_NAME: ${{needs.build-memorypipeline.outputs.artifact}}
PLUGINS_ARTIFACT_NAME: ${{needs.build-plugins.outputs.artifact}}
secrets:
AZURE_CLIENT_ID: ${{secrets.AZURE_CLIENT_ID}}
AZURE_TENANT_ID: ${{secrets.AZURE_TENANT_ID}}
Expand Down
66 changes: 66 additions & 0 deletions .github/workflows/copilot-deploy-plugins.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
name: copilot-deploy-memorypipeline

on:
workflow_call:
inputs:
ARTIFACT_NAME:
required: true
type: string
ENVIRONMENT:
required: true
type: string
DEPLOYMENT_NAME:
required: true
type: string
secrets:
AZURE_CLIENT_ID:
required: true
AZURE_TENANT_ID:
required: true
AZURE_SUBSCRIPTION_ID:
required: true

permissions:
contents: read

jobs:
memorypipeline:
environment: ${{inputs.ENVIRONMENT}}
permissions:
id-token: write
strategy:
fail-fast: false
matrix:
include:
- { dotnet: "6.0", configuration: Release, os: ubuntu-latest }

runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v4
with:
clean: true

- uses: actions/download-artifact@v3
with:
name: ${{inputs.ARTIFACT_NAME}}
path: "${{ github.workspace }}/${{inputs.ARTIFACT_NAME}}"

- name: "Display downloaded content"
run: ls -R
working-directory: "${{ github.workspace }}/${{inputs.ARTIFACT_NAME}}"

- name: "Azure login"
uses: azure/login@v1
with:
client-id: ${{secrets.AZURE_CLIENT_ID}}
tenant-id: ${{secrets.AZURE_TENANT_ID}}
subscription-id: ${{secrets.AZURE_SUBSCRIPTION_ID}}
enable-AzPSSession: false

- name: "Deploy"
run: |
scripts/deploy/deploy-plugins.sh \
--deployment-name ${{inputs.DEPLOYMENT_NAME}} \
--subscription ${{secrets.AZURE_SUBSCRIPTION_ID}} \
--resource-group ${{vars.CC_DEPLOYMENT_GROUP_NAME}} \
--packages "${{ github.workspace }}/${{inputs.ARTIFACT_NAME}}/plugins"
11 changes: 11 additions & 0 deletions CopilotChat.sln
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@ EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ChatCopilotIntegrationTests", "integration-tests\ChatCopilotIntegrationTests.csproj", "{0CD2CD95-536B-455F-B74A-772A455FA607}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CopilotChatShared", "shared\CopilotChatShared.csproj", "{94F12185-FAF9-43E3-B153-28A1708AC918}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WebSearcher", "plugins\web-searcher\WebSearcher.csproj", "{F83C857D-3080-4DEA-B3D1-978E2BC64BFB}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PluginShared", "plugins\shared\PluginShared.csproj", "{9D03913A-21FF-4D0A-9883-95C4B3D6F65A}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Expand Down Expand Up @@ -38,6 +41,14 @@ Global
{94F12185-FAF9-43E3-B153-28A1708AC918}.Debug|Any CPU.Build.0 = Debug|Any CPU
{94F12185-FAF9-43E3-B153-28A1708AC918}.Release|Any CPU.ActiveCfg = Release|Any CPU
{94F12185-FAF9-43E3-B153-28A1708AC918}.Release|Any CPU.Build.0 = Release|Any CPU
{F83C857D-3080-4DEA-B3D1-978E2BC64BFB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{F83C857D-3080-4DEA-B3D1-978E2BC64BFB}.Debug|Any CPU.Build.0 = Debug|Any CPU
{F83C857D-3080-4DEA-B3D1-978E2BC64BFB}.Release|Any CPU.ActiveCfg = Release|Any CPU
{F83C857D-3080-4DEA-B3D1-978E2BC64BFB}.Release|Any CPU.Build.0 = Release|Any CPU
{9D03913A-21FF-4D0A-9883-95C4B3D6F65A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{9D03913A-21FF-4D0A-9883-95C4B3D6F65A}.Debug|Any CPU.Build.0 = Debug|Any CPU
{9D03913A-21FF-4D0A-9883-95C4B3D6F65A}.Release|Any CPU.ActiveCfg = Release|Any CPU
{9D03913A-21FF-4D0A-9883-95C4B3D6F65A}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand Down
51 changes: 51 additions & 0 deletions plugins/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
# Plugins

> **IMPORTANT:** This sample is for educational purposes only and is not recommended for production deployments.
Plugins are cool! They allow Chat Copilot to talk to the internet. Read more about plugins here [Understanding AI plugins in Semantic Kernel](https://learn.microsoft.com/en-us/semantic-kernel/ai-orchestration/plugins/?tabs=Csharp) and here [ChatGPT Plugins](https://platform.openai.com/docs/plugins/introduction).

## Available Plugins

> These plugins in this project can be optionally deployed with the Chat Copilot [WebApi](../webapi/README.md)
- [WebSearcher](./web-searcher/README.md): A plugin that allows the chat bot to perform Bing search.
- More to come. Stay tuned!

## Third Party plugins

You can also configure Chat Copilot to use third party plugins.

> All no-auth plugins will be supported.
> All service-level-auth and user-level-auth plugins will be supported.
> OAuth plugins will NOT be supported.
Read more about plugin authentication here: [Plugin authentication](https://platform.openai.com/docs/plugins/authentication)

## Plugin Configuration in Chat Copilot

### Prerequisites

1. The name of your plugin. This should be identical to the `NameForHuman` in your plugin manifest.
> Please refer to OpenAI for the [manifest requirements](https://platform.openai.com/docs/plugins/getting-started/plugin-manifest).
2. Url of your plugin.
> This should be the root url to your API. Not the manifest url nor the OpenAPI spec url.
3. (Optional) Key of the plugin if it requires one.

### Local dev

In `appsettings.json` or `appsettings.development.json` under `../webapi/`, add your plugin to the existing **Plugins** list with the required information.

### Deployment

1. Go to your webapi resource in Azure portal.
2. Go to **Configuration** -> **Application settings**.
3. Look for Plugins:[*index*]:\* in the names that has the largest index.
4. Add the following names and their corresponding values:

```
Plugins[*index+1*]:Name
Plugins[*index+1*]:Url
Plugins[*index+1*]:Key (only if the plugin requires it)
```
19 changes: 19 additions & 0 deletions plugins/shared/PluginApi.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
// Copyright (c) Microsoft. All rights reserved.

namespace Plugins.PluginShared;

/// <summary>
/// This class represents the plugin API specification.
/// </summary>
public class PluginApi
{
/// <summary>
/// The API specification
/// </summary>
public string Type { get; set; } = "openapi";

/// <summary>
/// URL used to fetch the specification
/// </summary>
public string Url { get; set; } = string.Empty;
}
40 changes: 40 additions & 0 deletions plugins/shared/PluginAuth.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
// Copyright (c) Microsoft. All rights reserved.

using System.Text.Json.Serialization;

namespace Plugins.PluginShared;

/// <summary>
/// This class represents the OpenAI plugin authentication schema.
/// </summary>
public class PluginAuth
{
/// <summary>
/// Tokens for API key authentication
/// </summary>
public class VerificationTokens
{
/// <summary>
/// The API key
/// </summary>
public string OpenAI { get; set; } = string.Empty;
}

/// <summary>
/// The authentication schema
/// Supported values: none, service_http, user_http
/// </summary>
public string Type { get; set; } = "none";

/// <summary>
/// Manifest schema version
/// </summary>
[JsonPropertyName("authorization_type")]
public string AuthorizationType { get; } = "bearer";

/// <summary>
/// Tokens for API key authentication
/// </summary>
[JsonPropertyName("verification_tokens")]
public VerificationTokens Tokens { get; set; } = new VerificationTokens();
}
Loading

0 comments on commit 67ca4fe

Please sign in to comment.