forked from microsoft/botframework-solutions
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[TypeScript] Update How-To docs (microsoft#1314)
* Adding Skills Support to a V4 Bot (not based on Virtual Assistant Template) * Skill Enabling a V4 Bot (not based on Skill Template)
- Loading branch information
1 parent
4afe143
commit 9f0f726
Showing
2 changed files
with
210 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,107 @@ | ||
# Adding Skills Support to a V4 Bot (not based on Virtual Assistant Template) | ||
|
||
## Table of Contents | ||
- [Adding Skills Support to a V4 Bot (not based on Virtual Assistant Template)](#adding-skills-support-to-a-v4-bot-not-based-on-virtual-assistant-template) | ||
- [Table of Contents](#table-of-contents) | ||
- [Overview](#overview) | ||
- [Libraries](#libraries) | ||
- [Skill Configuration](#skill-configuration) | ||
- [Skill Dialog Registration](#skill-dialog-registration) | ||
- [Routing utterances to Skills](#routing-utterances-to-skills) | ||
|
||
## Overview | ||
|
||
Creating a Bot Framework Bot through the [Virtual Assistant template](/docs/overview/virtualassistant.md) is the easiest way to get started with using Skills. If you have an existing v4 based Bot, the recommended approach would be to take the resulting project from this template and bring across your custom dialogs to get started quickly. | ||
|
||
If, however you have an existing V4 Bot that you wish to add Skill capability then please follow the steps below. | ||
|
||
## Libraries | ||
|
||
- Add `botbuilder-solutions` and `botbuilder-skills` npm packages to your solution. | ||
|
||
## Skill Configuration | ||
|
||
The 'botbuilder-skills' package provides a `ISkillManifest` interface that describes a Skill. Your bot should maintain a collection of registered Skills typically serialized into a `JSON` configuration file. The Virtual Assistant template uses a `skills.json` file for this purpose that can be found in the `src` directory. | ||
|
||
That file must have the following structure: | ||
```json | ||
{ | ||
"skills": [] | ||
} | ||
``` | ||
|
||
As part of your Configuration processing you should construct a collection of registered Skills by deserializing this file, for example: | ||
```typescript | ||
import { skills as skillsRaw } from './skills.json'; | ||
const skills: ISkillManifest[] = skillsRaw; | ||
``` | ||
|
||
> NOTE: The `botbuilder-skills` package also provides a `IBotSettings` interface that can be used to storage the keys/secrets of the services that will be used to connect services to the bot. | ||
## Skill Dialog Registration | ||
In your `index.ts` file register a `SkillDialog` for each registered skill as shown below, this uses the collection of Skills that you created in the previous step. | ||
```typescript | ||
// Register skill dialogs | ||
const skillDialogs: SkillDialog[] = skills.map((skill: ISkillManifest) => { | ||
const authDialog: MultiProviderAuthDialog|undefined = buildAuthDialog(skill, botSettings); | ||
const credentials: MicrosoftAppCredentialsEx = new MicrosoftAppCredentialsEx( | ||
botSettings.microsoftAppId || '', | ||
botSettings.microsoftAppPassword || '', | ||
skill.msAppId); | ||
|
||
return new SkillDialog(skill, credentials, adapter.telemetryClient, skillContextAccessor, authDialog); | ||
}); | ||
``` | ||
|
||
For scenarios where Skills require authentication connections you need to create an associated `MultiProviderAuthDialog` | ||
```typescript | ||
// This method creates a MultiProviderAuthDialog based on a skill manifest. | ||
function buildAuthDialog(skill: ISkillManifest, settings: Partial<IBotSettings>): MultiProviderAuthDialog|undefined { | ||
if (skill.authenticationConnections !== undefined && skill.authenticationConnections.length > 0) { | ||
if (settings.oauthConnections !== undefined) { | ||
const oauthConnections: IOAuthConnection[] | undefined = settings.oauthConnections.filter( | ||
(oauthConnection: IOAuthConnection) => { | ||
return skill.authenticationConnections.some((authenticationConnection: IAuthenticationConnection) => { | ||
return authenticationConnection.serviceProviderId === oauthConnection.provider; | ||
}); | ||
}); | ||
if (oauthConnections !== undefined) { | ||
return new MultiProviderAuthDialog(oauthConnections); | ||
} | ||
} else { | ||
throw new Error(`You must configure at least one supported OAuth connection to use this skill: ${skill.name}.`); | ||
} | ||
} | ||
|
||
return undefined; | ||
} | ||
``` | ||
|
||
## Routing utterances to Skills | ||
|
||
Within your Main/Router dialog you firstly need to ensure the SkillDialogs registered previously are added to the dialog stack: | ||
```typescript | ||
skillDialogs.forEach((skillDialog: SkillDialog) => { | ||
this.addDialog(skillDialog); | ||
}); | ||
``` | ||
|
||
Add the following code after your Dispatcher has executed passing the registered Skills and the Intent returned from the Dispatcher. If the `isSkill` method returns true then you start the appropriate SkillDialog instance passing the Skill Manifest Id and the matching intent. | ||
```typescript | ||
// Identify if the dispatch intent matches any Action within a Skill if so, we pass to the appropriate SkillDialog to hand-off | ||
const identifiedSkill: ISkillManifest | undefined = SkillRouter.isSkill(this.settings.skills, intent); | ||
if (identifiedSkill !== undefined) { | ||
// We have identiifed a skill so initialize the skill connection with the target skill | ||
// the dispatch intent is the Action ID of the Skill enabling us to resolve the specific action and identify slots | ||
await dc.beginDialog(identifiedSkill.id); | ||
|
||
// Pass the activity we have | ||
const result: DialogTurnResult = await dc.continueDialog(); | ||
|
||
if (result.status === DialogTurnStatus.complete) { | ||
await this.complete(dc); | ||
} | ||
} else { | ||
// Your normal intent routing logic | ||
} | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,103 @@ | ||
# Skill Enabling a V4 Bot (not based on Skill Template) | ||
|
||
## Table of Contents | ||
- [Table of Contents](#table-of-contents) | ||
- [Overview](#overview) | ||
- [Libraries](#libraries) | ||
- [Adapter](#adapter) | ||
- [Startup](#startup) | ||
- [Add Skill Endpoint](#add-skill-endpoint) | ||
- [Manifest Template](#manifest-template) | ||
|
||
## Overview | ||
|
||
Creating a Skill through the [Skill template](/docs/tutorials/typescript/skill.md#create-your-skill) is the easiest way to get started with creating a new Skill. If you have an existing v4 based Bot, the recommended approach would be to take the resulting project from this template and bring across your custom dialogs to get started quickly. | ||
|
||
If however you want to manually enable your Bot to be called as a Skill follow the steps below. | ||
|
||
## Libraries | ||
|
||
- Add `botbuilder-solutions` and `botbuilder-skills` npm packages to your solution. | ||
|
||
## Adapter | ||
|
||
Create a Custom Adapter that derives from the `SkillHttpBotAdapter` and ensure the `SkillMiddleware` is added | ||
```typescript | ||
export class CustomSkillAdapter extends SkillHttpBotAdapter { | ||
constructor( | ||
telemetryClient: TelemetryClient, | ||
conversationState: ConversationState, | ||
skillContextAccessor: StatePropertyAccessor<SkillContext>, | ||
dialogStateAccessor: StatePropertyAccessor<DialogState>, | ||
... | ||
) { | ||
super(telemetryClient); | ||
[...] | ||
this.use(new SkillMiddleware(conversationState, skillContextAccessor, dialogStateAccessor)); | ||
[...] | ||
} | ||
} | ||
``` | ||
|
||
## Startup | ||
|
||
Add the new adapter to your `index.ts` file. | ||
|
||
```typescript | ||
const skillBotAdapter: CustomSkillAdapter = new CustomSkillAdapter( | ||
telemetryClient, | ||
conversationState, | ||
skillContextAccessor, | ||
dialogStateAccessor, | ||
...); | ||
const skillAdapter: SkillHttpAdapter = new SkillHttpAdapter( | ||
skillBotAdapter | ||
); | ||
``` | ||
|
||
## Add Skill Endpoint | ||
|
||
Update your `index.ts` to handle messages to interact with the bot as a skill. | ||
|
||
```typescript | ||
// Listen for incoming assistant requests | ||
server.post('/api/skill/messages', (req: restify.Request, res: restify.Response) => { | ||
// Route received a request to adapter for processing | ||
skillAdapter.processActivity(req, res, async (turnContext: TurnContext) => { | ||
// route to bot activity handler. | ||
await bot.run(turnContext); | ||
}); | ||
}); | ||
``` | ||
|
||
## Manifest Template | ||
|
||
Create a `manifestTemplate.json` file in the root of your Bot. Ensure at a minimum the root level `id`, `name`, `description` and action details are completed. This file should be shared to the bot that will use this bot as a skill. | ||
```json | ||
{ | ||
"id": "", | ||
"name": "", | ||
"description": "", | ||
"iconUrl": "", | ||
"authenticationConnections": [ ], | ||
"actions": [ | ||
{ | ||
"id": "", | ||
"definition": { | ||
"description": "", | ||
"slots": [ ], | ||
"triggers": { | ||
"utteranceSources": [ | ||
{ | ||
"locale": "en", | ||
"source": [ | ||
"luisModel#intent" | ||
] | ||
} | ||
] | ||
} | ||
} | ||
} | ||
] | ||
} | ||
``` |