Skip to content

Commit ec7ff2d

Browse files
authored
Fluent Dialog (#1)
* Initial implementation
1 parent 87d0ad1 commit ec7ff2d

25 files changed

+2134
-28
lines changed
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
/**/node_modules
2+
/**/.vscode
3+
/**/lib/*
4+
coverage
5+
.nyc_output
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
{
2+
"extension": [
3+
".js"
4+
],
5+
"include": [
6+
"lib/**/*.js"
7+
],
8+
"exclude": [
9+
"**/node_modules/**",
10+
"**/tests/**",
11+
"**/coverage/**",
12+
"**/*.d.ts"
13+
],
14+
"reporter": [
15+
"html"
16+
],
17+
"all": true,
18+
"cache": true
19+
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
MIT License
2+
3+
Copyright (c) Microsoft Corporation. All rights reserved.
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy
6+
of this software and associated documentation files (the "Software"), to deal
7+
in the Software without restriction, including without limitation the rights
8+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
copies of the Software, and to permit persons to whom the Software is
10+
furnished to do so, subject to the following conditions:
11+
12+
The above copyright notice and this permission notice shall be included in all
13+
copies or substantial portions of the Software.
14+
15+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+
SOFTWARE
Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,132 @@
1+
# Bot Builder Fluent Dialog
2+
3+
A Microsoft BotBuilder dialog implementation using event sourcing.
4+
5+
- [Installing](#installing)
6+
- [Basic Use](#use)
7+
- [Learn More](#learn-more)
8+
- [Documentation](https://docs.microsoft.com/en-us/azure/bot-service/bot-service-overview-introduction?view=azure-bot-service-4.0)
9+
- [Class Reference](https://docs.microsoft.com/en-us/javascript/api/botbuilder-dialogs/)
10+
- [GitHub Repo](https://github.com/Microsoft/botbuilder-js)
11+
- [Report Issues](https://github.com/Microsoft/botbuilder-js/issues)
12+
13+
## Installing
14+
To add the latest version of this package to your bot:
15+
16+
```bash
17+
npm install --save botbuilder-dialogs-fluent
18+
```
19+
20+
#### How to Use Daily Builds
21+
If you want to play with the very latest versions of botbuilder, you can opt in to working with the daily builds. This is not meant to be used in a production environment and is for advanced development. Quality will vary and you should only use daily builds for exploratory purposes.
22+
23+
To get access to the daily builds of this library, configure npm to use the MyGet feed before installing.
24+
25+
```bash
26+
npm config set registry https://botbuilder.myget.org/F/botbuilder-v4-js-daily/npm/
27+
```
28+
29+
To reset the registry in order to get the latest published version, run:
30+
```bash
31+
npm config set registry https://registry.npmjs.org/
32+
```
33+
34+
## What's included?
35+
36+
This module includes a dialog implementation using an approach similar to a durable function. The FluentDialog uses event sourcing to enable arbitrarily complex user interactions in a seemingly uninterrupted execution flow.
37+
Behind the scenes, the yield operator in the dialog flow function yields control of the execution thread back to a dialog flow dispatcher. The dispatcher then commits any new actions that the dialog flow function scheduled (such as starting a child dialog, receiving an activity or making an async call) to storage.
38+
The transparent commit action updates the execution history of the dialog flow by appending all new events into the dialog state, much like an append-only log.
39+
Once the history is updated, the dialog ends its turn and, when it is later resumed, the dispatcher re-executes the entire function from the start to rebuild the local state.
40+
During the replay, if the code tries to begin a child dialog (or do any async work), the dispatcher consults the execution history, replays that result and the function code continues to run.
41+
The replay continues until the function code is finished or until it yields a new suspension task.
42+
43+
## Use
44+
45+
After adding the module to your application, modify your app's code to import the multi-turn dialog management capabilities. Near your other `import` and `require` statements, add:
46+
47+
```javascript
48+
// Import some of the capabities from the module.
49+
const { DialogSet, TextPrompt, ConfirmPrompt } = require("botbuilder-dialogs");
50+
const { FluentDialog } = require("botbuilder-dialogs-fluent");
51+
```
52+
53+
Then, create one or more `DialogSet` objects to manage the dialogs used in your bot.
54+
A DialogSet is used to collect and execute dialogs. A bot may have more than one
55+
DialogSet, which can be used to group dialogs logically and avoid name collisions.
56+
57+
Then, create one or more dialogs and add them to the DialogSet. Use the WaterfallDialog
58+
class to construct dialogs defined by a series of functions for sending and receiving input
59+
that will be executed in order.
60+
61+
More sophisticated multi-dialog sets can be created using the `ComponentDialog` class, which
62+
contains a DialogSet, is itself also a dialog that can be triggered like any other. By building on top ComponentDialog,
63+
developer can bundle multiple dialogs into a single unit which can then be packaged, distributed and reused.
64+
65+
```javascript
66+
// Set up a storage system that will capture the conversation state.
67+
const storage = new MemoryStorage();
68+
const convoState = new ConversationState(storage);
69+
70+
// Define a property associated with the conversation state.
71+
const dialogState = convoState.createProperty('dialogState');
72+
73+
// Initialize a DialogSet, passing in a property used to capture state.
74+
const dialogs = new DialogSet(dialogState);
75+
76+
// Each dialog is identified by a unique name used to invoke the dialog later.
77+
const MAIN_DIALOG = 'MAIN_DIALOG';
78+
const TEXT_PROMPT = 'TEXT_PROMPT'
79+
const CONFIRM_PROMPT = 'CONFIRM_PROMPT'
80+
81+
// Implement the dialog flow function
82+
function *dialogFlow(context) {
83+
84+
let response = yield context.prompt(DIALOG_PROMPT, 'say something');
85+
yield context.sendActivity(`you said: ${response}`);
86+
87+
let shouldContinue = yield context.prompt(CONFIRM_PROMPT, 'play another round?', ['yes', 'no'])
88+
if (shouldContinue) {
89+
yield context.restart();
90+
}
91+
92+
yield context.sendActivity('good bye!');
93+
}
94+
95+
// Add a dialog. Use the included FluentDialog type, initialized with the dialog flow function
96+
dialogs.add(new FluentDialog(MAIN_DIALOG, dialogFlow));
97+
dialogs.add(new TextPrompt(DIALOG_PROMPT));
98+
dialogs.add(new ConfirmPrompt(CONFIRM_PROMPT));
99+
100+
```
101+
102+
Finally, from somewhere in your bot's code, invoke your dialog by name:
103+
104+
```javascript
105+
// Receive and process incoming events into TurnContext objects in the normal way
106+
adapter.processActivity(req, res, async (turnContext) => {
107+
// Create a DialogContext object from the incoming TurnContext
108+
const dc = await dialogs.createContext(turnContext);
109+
110+
// ...evaluate message and do other bot logic...
111+
112+
// If the bot hasn't yet responded, try to continue any active dialog
113+
if (!turnContext.responded) {
114+
const results = await dc.continueDialog();
115+
if (results.status === DialogTurnStatus.empty) {
116+
await dialogContext.beginDialog(MAIN_DIALOG);
117+
}
118+
}
119+
});
120+
```
121+
122+
## Examples
123+
124+
See this module in action in these example apps:
125+
126+
# Learn More
127+
128+
[Prompts](https://docs.microsoft.com/en-us/azure/bot-service/bot-builder-prompts?view=azure-bot-service-4.0&tabs=javascript) This module contains several types of built-in prompt that can be used to create dialogs that capture and validate specific data types like dates, numbers and multiple-choice answers.
129+
130+
[DialogSet](https://docs.microsoft.com/en-us/javascript/api/botbuilder-dialogs/dialogset) DialogSet is a container for multiple dialogs. Once added to a DialogSet, dialogs can be called and interlinked.
131+
132+
[ComponentDialog](https://docs.microsoft.com/en-us/javascript/api/botbuilder-dialogs/componentdialog) ComponentDialogs are containers that encapsulate multiple sub-dialogs, but can be invoked like normal dialogs. This is useful for re-usable dialogs, or creating multiple dialogs with similarly named sub-dialogs that would otherwise collide.
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
{
2+
"extends": "../../api-extractor.json",
3+
"mainEntryPointFilePath": "./lib/index.d.ts"
4+
}
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
{
2+
"name": "botbuilder-dialogs-fluent",
3+
"author": "Microsoft Corp.",
4+
"description": "Fluent execution flow library for the Microsoft BotBuilder dialog system.",
5+
"version": "4.1.6",
6+
"preview": true,
7+
"license": "MIT",
8+
"keywords": [
9+
"botbuilder",
10+
"botframework",
11+
"bots",
12+
"chatbots"
13+
],
14+
"bugs": {
15+
"url": "https://github.com/Microsoft/botbuilder-js/issues"
16+
},
17+
"repository": {
18+
"type": "git",
19+
"url": "https://github.com/Microsoft/botbuilder-js.git"
20+
},
21+
"main": "./lib/index.js",
22+
"types": "./lib/index.d.ts",
23+
"typesVersions": {
24+
"<3.9": {
25+
"*": [
26+
"_ts3.4/*"
27+
]
28+
}
29+
},
30+
"dependencies": {
31+
"botbuilder-core": "4.1.6",
32+
"botbuilder-dialogs": "4.1.6",
33+
"botbuilder-stdlib": "4.1.6",
34+
"chokidar": "^3.6.0",
35+
"zod": "^3.23.8",
36+
"type-fest": "3.13.1"
37+
},
38+
"scripts": {
39+
"build": "tsc -b",
40+
"build-docs": "typedoc --theme markdown --entryPoint botbuilder-dialogs-fluent --excludePrivate --includeDeclarations --ignoreCompilerErrors --module amd --out ..\\..\\doc\\botbuilder-dialogs .\\lib\\index.d.ts --hideGenerator --name \"Bot Builder SDK - Dialogs\" --readme none",
41+
"clean": "rimraf _ts3.4 lib tsconfig.tsbuildinfo",
42+
"depcheck": "depcheck --config ../../.depcheckrc --ignores botbuilder-ai,botbuilder-dialogs-adaptive",
43+
"lint": "eslint . --config ../../eslint.config.cjs",
44+
"postbuild": "downlevel-dts lib _ts3.4/lib --checksum",
45+
"test": "yarn build && nyc mocha tests/**/*.test.js --exit",
46+
"test:compat": "api-extractor run --verbose"
47+
},
48+
"files": [
49+
"_ts3.4",
50+
"lib",
51+
"schemas",
52+
"src"
53+
]
54+
}
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
// Copyright (c) Microsoft Corporation.
2+
// Licensed under the MIT License.
3+
4+
import { JsonValue } from "type-fest";
5+
6+
7+
/**
8+
* Interface for a callable that is bound to a dialog flow context.
9+
* @template A The parameter types of the bound function.
10+
* @template R The return type of the bound function.
11+
*/
12+
export interface DialogFlowBoundCallable<A extends any[], R extends JsonValue> {
13+
14+
/**
15+
* Invokes the bound function with the given arguments.
16+
* @param args The arguments to pass to the function.
17+
* @returns The observable result of the function call.
18+
*/
19+
(...args: A): R;
20+
21+
/**
22+
* Gets a new function that has the same arguments as the bound function and returns an observable
23+
* value of a different type.
24+
*
25+
* @template T The type of the observable value produced by the projector
26+
* @param projector The callback used to convert the deserialized result to its observable value
27+
* @returns The projected function.
28+
*/
29+
project<T>(
30+
projector: (value: R) => T
31+
): (...args: A) => T;
32+
}

0 commit comments

Comments
 (0)