Migrate existing Skills to Bot Framework Skills GA
Explains the steps required to migrate an older Skill version to use the new GA Skill capabilities provided by the Bot Framework.
Updated Skill Template
As part of the 0.8 release, we published a new version of the Virtual Assistant template. In this new version, we have transitioned the Skills capabilities into the 4.7 release of BotBuilder SDK as it reached the General Availability milestone in C# and JS as well.
This is expected to be our last major template change ahead of the General Availability milestone planned for March 2020.
One migration step will be to create a new Skill project and migrate your customisation manually. If however you wish to to upgrade your in-place project, this documentation page explains how to migrate your existing Skill to take advantage of the GA Bot Framework Skills capability.
Prerequisites
- An existing Bot Framework Skill built from using Skill Template v4.6.0.1 and below.
- Review the
Dialogs\MainDialog.cs
file. IfMainDialog
derives fromRouterDialog
rather thanActivityHandlerDialog
follow these instructions to reflect dialog routing changes made in the last release.
Solution and Package Changes
C#
-
Open your old Skill solution using Visual Studio. Right click your (.csproj) project file in Solution Explorer and Choose
Edit Project
. Change the project to a .net core 3.0 app as shown below.<PropertyGroup> <TargetFramework>netcoreapp3.0</TargetFramework> <NoWarn>NU1701</NoWarn> </PropertyGroup>
-
Update all BotBuilder package references to 4.7.2. The easiest way to do this is right click the Solution and choose
Edit Project File
and replace with the fragment below. The accompanying test project also needs one library version update. If you haveMicrosoft.Bot.Builder.LanguageGeneration
referenced, please use version4.7.2-preview
<PackageReference Include="Microsoft.Bot.Builder" Version="4.7.2" /> <PackageReference Include="Microsoft.Bot.Builder.AI.Luis" Version="4.7.2" /> <PackageReference Include="Microsoft.Bot.Builder.AI.QnA" Version="4.7.2" /> <PackageReference Include="Microsoft.Bot.Builder.ApplicationInsights" Version="4.7.2" /> <PackageReference Include="Microsoft.Bot.Builder.Azure" Version="4.7.2" /> <PackageReference Include="Microsoft.Bot.Builder.Dialogs" Version="4.7.2" /> <PackageReference Include="Microsoft.Bot.Builder.Integration.ApplicationInsights.Core" Version="4.7.2" /> <PackageReference Include="Microsoft.Bot.Builder.Integration.AspNet.Core" Version="4.7.2" /> <PackageReference Include="Microsoft.Bot.Configuration" Version="4.7.2" /> <PackageReference Include="Microsoft.Bot.Schema" Version="4.7.2" />
-
Change all
Microsoft.Bot.Builder.Solutions package
references to the newMicrosoft.Bot.Solutions
package with version0.8.0-preview1
. If your Virtual Assistant project has a reference to the packageMicrosoft.Bot.Builder.Skills
remove this as it’s now part of the core BotBuilder SDK.<PackageReference Include="Microsoft.Bot.Builder.Solutions->Microsoft.Bot.Solutions" Version="0.8.0-preview1" />
-
Change all namespace statements across the project to use
Microsoft.Bot.Solutions
instead ofMicrosoft.Bot.Builder.Solutions
TypeScript
-
Open the
package.json
of your old Virtual Assistant using Visual Studio Code. Update all BotBuilder package references to 4.8.0. The easiest way to do this is by replacing your BotBuilder package references with the fragment below."botbuilder": "^4.8.0", "botbuilder-ai": "^4.8.0", "botbuilder-applicationinsights": "^4.8.0", "botbuilder-azure": "^4.8.0", "botbuilder-dialogs": "^4.8.0", "botframework-config": "^4.8.0", "botframework-connector": "^4.8.0",
-
Remove
botbuilder-skills
library from the package.json, which will require to change all the references tobot-solutions
.
Note: Take into account that botbuilder-solutions
will be deprecated and it should be bot-solutions@1.0.0
instead following the C# pattern.
BotController changes
C#
-
Update BotController.cs
Within your Skill project,
Controller\BotController.cs
implementsSkillController
which includes capabilities of standing up new APIs for skill invocation. This requirement has now been removed, therefore a default controller can now be used.Change the
BotController.cs
as shown below:using System.Threading.Tasks; using Microsoft.AspNetCore.Mvc; using Microsoft.Bot.Builder; using Microsoft.Bot.Builder.Integration.AspNet.Core; namespace {YourSkill}.Controllers { [Route("api/messages")] [ApiController] public class BotController : ControllerBase { private readonly IBotFrameworkHttpAdapter _adapter; private readonly IBot _bot; public BotController(IBotFrameworkHttpAdapter httpAdapter, IBot bot) { _adapter = httpAdapter; _bot = bot; } [HttpPost] [HttpGet] public async Task PostAsync() { await _adapter.ProcessAsync(Request, Response, _bot); } } }
TypeScript
-
Update index.ts
Add the GET api/messages endpoint in
index.ts
for incoming requests as shown below:server.get('/api/messages', async (req: restify.Request, res: restify.Response): Promise<void> => { // Route received a request to adapter for processing await defaultAdapter.processActivity(req, res, async (turnContext: TurnContext): Promise<void> => { // route to bot activity handler. await bot.run(turnContext); }); });
Removal Steps
C#
-
Remove registrations in
Startup.cs
In the current Skill Template, there are registrations for classes that are no longer needed in the 4.7 Skill protocol. They should be removed from Startup.cs within your Skill project.
Remove these lines:
services.AddTransient<SkillWebSocketBotAdapter, CustomSkillAdapter>(); services.AddTransient<SkillWebSocketAdapter>(); // Register WhiteListAuthProvider services.AddSingleton<IWhitelistAuthenticationProvider, WhitelistAuthenticationProvider>();
-
Remove CustomSkillAdapter.cs
A custom adapter is no longer needed, you can therefore remove
Adapters\CustomSkillAdapter.cs
from your project. -
Remove custom implementation of
IWhitelistAuthenticationProvider
If you have implemented your own class for the interface
IWhitelistAuthenticationProvider
instead of using the WhitelistAuthenticationProvider class from the Solutions lib this can be removed.
TypeScript
-
Remove
WhitelistAuthenticationProvider
registrations inindex.ts
In the current Skill Template, there are registrations for WhitelistAuthenticatoinProvider that are no longer needed in the Skill protocol. They should be removed from index.ts within your Skill project.
Remove this line:
const whitelistAuthenticationProvider: WhitelistAuthenticationProvider = new WhitelistAuthenticationProvider(botSettings);
Update the defaultAdapter’s initialization removing the whitelistAuthenticationProvider parameter
const defaultAdapter: DefaultAdapter = new DefaultAdapter( botSettings, adapterSettings, localeTemplateEngine, telemetryInitializerMiddleware, telemetryClient);
-
Remove customSkillAdapter.ts
A custom adapter is no longer needed, you can therefore remove
adapters\customSkillAdapter.ts
from your project.
Handle EndOfConversation
C#
-
Add code to handle the
EndOfConversation
activity from parent botIn Skill invocation, a skill needs to handle an
EndOfConversation
activity in order to support cancellation for interruption scenarios at the parent bot level. This capability will be included in the next release of theMicrosoft.Bot.Builder.Solutions
package and eventually as part of the core Bot Builder SDK. For now, add these lines of code at the top of theOnTurnAsync
handler in yourIBot
implementation class (within the Bots folder of your project) to handle theEndOfConversation
activity:var activity = turnContext.Activity; var dialogState = _conversationState.CreateProperty<DialogState>(nameof(DialogState)); if (activity != null && activity.Type == ActivityTypes.EndOfConversation) { await dialogState.DeleteAsync(turnContext).ConfigureAwait(false); await _conversationState.ClearStateAsync(turnContext).ConfigureAwait(false); await _conversationState.SaveChangesAsync(turnContext, force: true).ConfigureAwait(false); return; }
With this block of code, when your skill receives an EndOfConversation activity it will clear out the existing dialog state so the skill will be in a clean state ready for the next conversation.
-
Update to use
EndOfConversation
instead of Handoff when a conversation completedIn the
OnDialogCompleteAsync
function ofMainDialog.cs
, instead of sending back a ‘Handoff’ activity, update it to beEndOfConversation
inline with the new Skills changes. Replace the entire contents with the code below.// Retrieve the prior dialogs result if provided to return on the Skill EndOfConversation event. ObjectPath.TryGetPathValue<object>(outerDc.Context.TurnState, TurnPath.LASTRESULT, out object dialogResult); var endOfConversation = new Activity(ActivityTypes.EndOfConversation) { Code = EndOfConversationCodes.CompletedSuccessfully, Value = dialogResult }; await outerDc.Context.SendActivityAsync(endOfConversation, cancellationToken); await outerDc.EndDialogAsync(result);
-
Add code in the exception handler of the adapter to send an EndOfConversation activity back
In the exception handler of the
DefaultAdapter
normally located in theAdapters
folder, add code to send anEndOfConversation
activity back to complete a conversation when an exception is thrown:```csharp OnTurnError = async (turnContext, exception) => { // Send and EndOfConversation activity to the skill caller with the error to end the conversation // and let the caller decide what to do. var endOfConversation = Activity.CreateEndOfConversationActivity(); endOfConversation.Code = “SkillError”; endOfConversation.Text = exception.Message; await context.SendActivityAsync(endOfConversation); … };
TypeScript
-
The EndOfConversation activity is handled by
botbuilder@4.8.0
. Just be sure to override theonMessageActivity
method in theDefaultActivityHandler
bot as follows:protected onMessageActivity(turnContext: TurnContext): Promise<void> { return DialogEx.run(this.dialog, turnContext, this.dialogStateAccessor); }
-
Add code in the exception handler of the adapter to send an EndOfConversation activity back
In the exception handler of the
defaultAdapter
normally located in theadapters
folder, add code to send anEndOfConversation
activity back to complete a conversation when an exception is thrown:this.onTurnError = async (context: TurnContext, error: Error): Promise<void> => { const endOfconversation: Partial<Activity> = { type: ActivityTypes.EndOfConversation, code: 'SkillError', text: error.message } await context.sendActivity(endOfconversation); };
MultiProviderAuthDialog
-
Keep using the MultiProviderAuthDialog (No action needed)
In the previous model the parent bot (VA) is the responsible for performing OAuth tasks by acting on behalf of a skill thus ensuring a common, shared authentication experience across an assistant. With this new release, Skills can now perform their own authentication requests and still benefit from a shared trust boundary.
The existing
MultiProviderAuthDialog
if used will automatically adapt to this change and no changes are required. As required you can switch to using theOAuthPrompt
directly.
LocaleTemplateManager
The class LocaleTemplateEngineManager
has been remamed to LocaleTemplateManager
and its constructor has been slightly modified.
-
Make sure rename the instances of
localeTemplateEngineManager
tolocaleTemplateManager
. -
In
index.ts
, update the incialization ofLocaleTemplateManager
with the localized responses to this:// Configure localized responses const localizedTemplates: Map<string, string> = new Map<string, string>(); const templateFile = 'AllResponses'; const supportedLocales: string[] = ['en-us', 'de-de', 'es-es', 'fr-fr', 'it-it', 'zh-cn']; supportedLocales.forEach((locale: string) => { // LG template for en-us does not include locale in file extension. const localTemplateFile = locale === 'en-us' ? join(__dirname, 'responses', `${ templateFile }.lg`) : join(__dirname, 'responses', `${ templateFile }.${ locale }.lg`); localizedTemplates.set(locale, localTemplateFile); }); const localeTemplateManager: LocaleTemplateManager = new LocaleTemplateManager(localizedTemplates, botSettings.defaultLocale || 'en-us');
Manifest Changes
The provided manifestTemplate.json
schema type has been retired, therefore these steps will create a new Manifest supporting the new schema.
-
Create a folder called
Manifest
within thewwwroot
of your Skill project and create a new file calledmanifest-1.0.json
with the JSON fragment below. Ensure the Build Action on the file is set to Content. Extend this to incorporate any additional intents or LU files you have created using your existingmanifestTemplate.json
as a reference.{ "$schema": "https://schemas.botframework.com/schemas/skills/skill-manifest-2.1.preview-0.json", "$id": "SampleSkill", "name": "SampleSkill", "description": "Sample Skill description", "publisherName": "Your Company", "version": "1.0", "iconUrl": "https://{YOUR_SKILL_URL}/sampleSkill.png", "copyright": "Copyright (c) Microsoft Corporation. All rights reserved.", "license": "", "privacyUrl": "https://{YOUR_SKILL_URL}/privacy.html", "tags": [ "sample", "skill" ], "endpoints": [ { "name": "production", "protocol": "BotFrameworkV3", "description": "Production endpoint for the Sample Skill", "endpointUrl": "https://{YOUR_SKILL_URL}/api/messages", "msAppId": "{YOUR_SKILL_APPID}" } ], "dispatchModels": { "languages": { "en-us": [ { "id": "SampleSkillLuModel-en", "name": "SampleSkill LU (English)", "contentType": "application/lu", "url": "file://SampleSkill.lu", "description": "English language model for the skill" } ] }, "intents": { "Sample": "#/activities/message", "*": "#/activities/message" } "activities": { "message": { "type": "message", "description": "Receives the users utterance and attempts to resolve it using the skill's LU models" } }, "definitions": { } }
At the time of writing Power Virtual Agents only supports the 2.0 manifest rather than the extended 2.1 version. Therefore, in Power Virtual Agent scenarios ensure you adjust the above manifest in the following ways:
- Change schema version to https://schemas.botframework.com/schemas/skills/skill-manifest-2.0.0.json
- Remove the entire dispatchModels section which is not required or supported by Power Virtual Agents.
-
Update
{YOUR_SKILL_URL}
with the URL of your deployed Skill endpoint, this must be prefixed with https. -
Update
{YOUR_SKILL_APPID}
with the Active Directory AppID of your deployed Skill, you can find this within yourappSettings.json
file. -
Publish the changes to your Skill endpoint and validate that you can retrieve the manifest using the browser (
/manifest/manifest.json
) -
Update your
botskills
CLI tool to ensure it supports the new Manifest schema:npm install -g botskills
Once complete you have transitioned your exiting Skill to support the new Generally Available Bot Framework Skills capability.