-
Notifications
You must be signed in to change notification settings - Fork 281
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
add skillDialog and associated classes (#1771)
* add skillDialog and associated classes * move non-impl Skill code to core for use in dialogs * scaffold dialogRootBot and dialogSkillBot * apply SkillDialog updates from microsoft/botbuilder-dotnet#3474 * rename SkillDialogArgs to BeginSkillDialogOptions Co-authored-by: Steven Ickman <stevenic@microsoft.com>
- Loading branch information
Showing
24 changed files
with
341 additions
and
17 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
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
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
File renamed without changes.
25 changes: 25 additions & 0 deletions
25
libraries/botbuilder-core/src/skills/botFrameworkClient.ts
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,25 @@ | ||
/** | ||
* @module botbuilder | ||
*/ | ||
/** | ||
* Copyright (c) Microsoft Corporation. All rights reserved. | ||
* Licensed under the MIT License. | ||
*/ | ||
|
||
import { Activity } from 'botframework-schema'; | ||
import { InvokeResponse } from '../invokeResponse'; | ||
|
||
export abstract class BotFrameworkClient { | ||
/** | ||
* Forwards an activity to a another bot. | ||
* @remarks | ||
* | ||
* @param fromBotId The MicrosoftAppId of the bot sending the activity. | ||
* @param toBotId The MicrosoftAppId of the bot receiving the activity. | ||
* @param toUrl The URL of the bot receiving the activity. | ||
* @param serviceUrl The callback Url for the skill host. | ||
* @param conversationId A conversation ID to use for the conversation with the skill. | ||
* @param activity Activity to forward. | ||
*/ | ||
public abstract postActivity(fromBotId: string, toBotId: string, toUrl: string, serviceUrl: string, conversationId: string, activity: Activity): Promise<InvokeResponse> | ||
} |
2 changes: 1 addition & 1 deletion
2
...otbuilder/src/skills/botFrameworkSkill.ts → ...lder-core/src/skills/botFrameworkSkill.ts
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
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,11 @@ | ||
/** | ||
* @module botbuilder-core | ||
*/ | ||
/** | ||
* Copyright (c) Microsoft Corporation. All rights reserved. | ||
* Licensed under the MIT License. | ||
*/ | ||
|
||
export { BotFrameworkClient } from './botFrameworkClient'; | ||
export { BotFrameworkSkill } from './botFrameworkSkill'; | ||
export { SkillConversationIdFactoryBase } from './skillConversationIdFactoryBase'; |
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
19 changes: 19 additions & 0 deletions
19
libraries/botbuilder-dialogs/src/beginSkillDialogOptions.ts
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,19 @@ | ||
/** | ||
* @module botbuilder-dialogs | ||
*/ | ||
/** | ||
* Copyright (c) Microsoft Corporation. All rights reserved. | ||
* Licensed under the MIT License. | ||
*/ | ||
|
||
import { Activity } from 'botbuilder-core'; | ||
|
||
/** | ||
* A class with dialog arguments for a SkillDialog. | ||
*/ | ||
export interface BeginSkillDialogOptions { | ||
/** | ||
* The Activity to send to the skill. | ||
*/ | ||
activity: Activity; | ||
} |
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
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,140 @@ | ||
/** | ||
* @module botbuilder-dialogs | ||
*/ | ||
/** | ||
* Copyright (c) Microsoft Corporation. All rights reserved. | ||
* Licensed under the MIT License. | ||
*/ | ||
|
||
import { | ||
Activity, | ||
ActivityTypes, | ||
ConversationReference, | ||
TurnContext | ||
} from 'botbuilder-core'; | ||
import { | ||
Dialog, | ||
DialogInstance, | ||
DialogReason, | ||
DialogTurnResult | ||
} from './dialog'; | ||
import { DialogContext } from './dialogContext'; | ||
import { BeginSkillDialogOptions } from './beginSkillDialogOptions'; | ||
import { SkillDialogOptions } from './skillDialogOptions'; | ||
|
||
export class SkillDialog extends Dialog { | ||
protected dialogOptions: SkillDialogOptions; | ||
|
||
/** | ||
* A sample dialog that can wrap remote calls to a skill. | ||
* | ||
* @remarks | ||
* The options parameter in `beginDialog()` must be a `SkillDialogArgs` object with the initial parameters | ||
* for the dialog. | ||
* | ||
* @param dialogOptions | ||
* @param dialogId | ||
*/ | ||
public constructor(dialogOptions: SkillDialogOptions, dialogId?: string) { | ||
super(dialogId); | ||
if (!dialogOptions) { | ||
throw new TypeError('Missing dialogOptions parameter'); | ||
} | ||
this.dialogOptions = dialogOptions; | ||
} | ||
|
||
public async beginDialog(dc: DialogContext, options?: {}): Promise<DialogTurnResult> { | ||
const dialogArgs = SkillDialog.validateBeginDialogArgs(options); | ||
|
||
await dc.context.sendTraceActivity(`${ this.id }.beginDialog()`, undefined, undefined, `Using activity of type: ${ dialogArgs.activity.type }`); | ||
|
||
// Create deep clone of the original activity to avoid altering it before forwarding it. | ||
const clonedActivity = this.cloneActivity(dialogArgs.activity); | ||
|
||
// Apply conversation reference and common properties from incoming activity before sending. | ||
const skillActivity = TurnContext.applyConversationReference(clonedActivity, TurnContext.getConversationReference(dc.context.activity), true) as Activity; | ||
|
||
// Send the activity to the skill. | ||
await this.sendToSkill(dc.context, skillActivity); | ||
return Dialog.EndOfTurn; | ||
} | ||
|
||
public async continueDialog(dc: DialogContext): Promise<DialogTurnResult> { | ||
await dc.context.sendTraceActivity(`${ this.id }.continueDialog()`, undefined, undefined, `ActivityType: ${ dc.context.activity.type }`); | ||
|
||
|
||
// Handle EndOfConversation from the skill (this will be sent to the this dialog by the SkillHandler if received from the Skill) | ||
if (dc.context.activity.type === ActivityTypes.EndOfConversation) { | ||
await dc.context.sendTraceActivity(`${ this.id }.continueDialog()`, undefined, undefined, `Got ${ ActivityTypes.EndOfConversation }`); | ||
return await dc.endDialog(dc.context.activity.value); | ||
} | ||
|
||
// Forward only Message and Event activities to the skill | ||
if (dc.context.activity.type === ActivityTypes.Message || dc.context.activity.type === ActivityTypes.Event) { | ||
// Just forward to the remote skill | ||
await this.sendToSkill(dc.context, dc.context.activity); | ||
} | ||
|
||
return Dialog.EndOfTurn; | ||
} | ||
|
||
public async endDialog(context: TurnContext, instance: DialogInstance, reason: DialogReason): Promise<void> { | ||
// Send of of conversation to the skill if the dialog has been cancelled. | ||
if (reason == DialogReason.cancelCalled || reason == DialogReason.replaceCalled) { | ||
await context.sendTraceActivity(`${ this.id }.EndDialogAsync()`, undefined, undefined, `ActivityType: ${ context.activity.type }`); | ||
|
||
const reference = TurnContext.getConversationReference(context.activity); | ||
// Apply conversation reference and common properties from incoming activity before sending. | ||
const activity = TurnContext.applyConversationReference({ type: ActivityTypes.EndOfConversation }, reference, true); | ||
activity.channelData = context.activity.channelData; | ||
|
||
await this.sendToSkill(context, activity as Activity); | ||
} | ||
|
||
await super.endDialog(context, instance, reason); | ||
} | ||
|
||
/** | ||
* Clones the Activity entity. | ||
* @param activity Activity to clone. | ||
*/ | ||
private cloneActivity(activity: Partial<Activity>): Activity { | ||
return Object.assign({} as Activity, activity); | ||
} | ||
|
||
private static validateBeginDialogArgs(options: any): BeginSkillDialogOptions { | ||
if (!options) { | ||
throw new TypeError('Missing options parameter'); | ||
} | ||
|
||
const dialogArgs = options as BeginSkillDialogOptions; | ||
|
||
if (!dialogArgs.activity) { | ||
throw new TypeError(`"activity" is undefined or null in options.`); | ||
} | ||
|
||
// Only accept Message or Event activities | ||
if (dialogArgs.activity.type !== ActivityTypes.Message && dialogArgs.activity.type !== ActivityTypes.Event) { | ||
// Just forward to the remote skill | ||
throw new TypeError(`Only ${ ActivityTypes.Message } and ${ ActivityTypes.Event } activities are supported. Received activity of type ${ dialogArgs.activity.type } in options.`); | ||
} | ||
|
||
return dialogArgs; | ||
} | ||
|
||
private async sendToSkill(context: TurnContext, activity: Activity): Promise<void> { | ||
// Create a conversationId to interact with the skill and send the activity | ||
const skillConversationId = await this.dialogOptions.conversationIdFactory.createSkillConversationId(TurnContext.getConversationReference(activity) as ConversationReference); | ||
|
||
// Always save state before forwarding | ||
// (the dialog stack won't get updated with the skillDialog and things won't work if you don't) | ||
await this.dialogOptions.conversationState.saveChanges(context, true); | ||
const skillInfo = this.dialogOptions.skill; | ||
const response = await this.dialogOptions.skillClient.postActivity(this.dialogOptions.botId, skillInfo.appId, skillInfo.skillEndpoint, skillInfo.skillEndpoint, skillConversationId, activity); | ||
|
||
// Inspect the skill response status | ||
if (!(response.status >= 200 && response.status <= 299)) { | ||
throw new Error(`Error invoking the skill id: "${ skillInfo.id }" at "${ skillInfo.skillEndpoint }" (status is ${ response.status }). \r\n ${ response.body }`); | ||
} | ||
} | ||
} |
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,46 @@ | ||
/** | ||
* @module botbuilder-dialogs | ||
*/ | ||
/** | ||
* Copyright (c) Microsoft Corporation. All rights reserved. | ||
* Licensed under the MIT License. | ||
*/ | ||
|
||
import { | ||
BotFrameworkClient, | ||
BotFrameworkSkill, | ||
ConversationState, | ||
SkillConversationIdFactoryBase | ||
} from 'botbuilder-core'; | ||
|
||
export interface SkillDialogOptions { | ||
/** | ||
* The the Microsoft app ID of the bot calling the skill. | ||
*/ | ||
botId: string; | ||
|
||
/** | ||
* The BotFrameworkSkill that the dialog will call. | ||
*/ | ||
conversationIdFactory: SkillConversationIdFactoryBase; | ||
|
||
/** | ||
* The ConversationState to be used by the Dialog. | ||
*/ | ||
conversationState: ConversationState; | ||
|
||
/** | ||
* The BotFrameworkSkill the dialog will call. | ||
*/ | ||
skill: BotFrameworkSkill; | ||
|
||
/** | ||
* The BotFrameworkClient used to call the remote skill. | ||
*/ | ||
skillClient: BotFrameworkClient; | ||
|
||
/** | ||
* The callback Url for the skill host. | ||
*/ | ||
skillHostEndpoint: string; | ||
} |
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
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
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
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
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
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
Oops, something went wrong.