8000 Added 05.multi-turn-prompt (#368) · sherlock666/botbuilder-python@0987404 · GitHub
[go: up one dir, main page]

Skip to content

Commit 0987404

Browse files
tracyboehreraxelsrz
authored andcommitted
Added 05.multi-turn-prompt (microsoft#368)
* Added 05.multi-turn-prompt * Removed LUIS keys from settings. * PR corrections
1 parent 5bc70b2 commit 0987404

File tree

12 files changed

+436
-0
lines changed

12 files changed

+436
-0
lines changed
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
# multi-turn prompt
2+
3+
Bot Framework v4 welcome users bot sample
4+
5+
This bot has been created using [Bot Framework](https://dev.botframework.com), it shows how to use the prompts classes included in `botbuilder-dialogs`. This bot will ask for the user's name and age, then store the responses. It demonstrates a multi-turn dialog flow using a text prompt, a number prompt, and state accessors to store and retrieve values.
6+
7+
## Running the sample
8+
- Clone the repository
9+
```bash
10+
git clone https://github.com/Microsoft/botbuilder-python.git
11+
```
12+
- Run `pip install -r requirements.txt` to install all dependencies
13+
- Run `python app.py`
14+
- Alternatively to the last command, you can set the file in an environment variable with `set FLASK_APP=app.py` in windows (`export FLASK_APP=app.py` in mac/linux) and then run `flask run --host=127.0.0.1 --port=3978`
15+
16+
17+
### Visual studio code
18+
- Activate your desired virtual environment
19+
- Open `botbuilder-python\samples\45.state-management` folder
20+
- Bring up a terminal, navigate to `botbuilder-python\samples\05.multi-turn-prompt` folder
21+
- In the terminal, type `pip install -r requirements.txt`
22+
- In the terminal, type `python app.py`
23+
24+
## Testing the bot using Bot Framework Emulator
25+
[Microsoft Bot Framework Emulator](https://github.com/microsoft/botframework-emulator) is a desktop application that allows bot developers to test and debug their bots on localhost or running remotely through a tunnel.
26+
27+
- Install the Bot Framework emulator from [here](https://github.com/Microsoft/BotFramework-Emulator/releases)
28+
29+
### Connect to bot using Bot Framework Emulator
30+
- Launch Bot Framework Emulator
31+
- Paste this URL in the emulator window - http://localhost:3978/api/messages
32+
33+
34+
## Prompts
35+
36+
A conversation between a bot and a user often involves asking (prompting) the user for information, parsing the user's response,
37+
and then acting on that information. This sample demonstrates how to prompt users for information using the different prompt types
38+
included in the [botbuilder-dialogs](https://docs.microsoft.com/en-us/azure/bot-service/bot-builder-concept-dialog?view=azure-bot-service-4.0) library
39+
and supported by the SDK.
40+
41+
The `botbuilder-dialogs` library includes a variety of pre-built prompt classes, including text, number, and datetime types. This
42+
sample demonstrates using a text prompt to collect the user's name, then using a number prompt to collect an age.
43+
44+
# Further reading
45+
46+
- [Bot Framework Documentation](https://docs.botframework.com)
47+
- [Bot Basics](https://docs.microsoft.com/azure/bot-service/bot-builder-basics?view=azure-bot-service-4.0)
48+
- [Dialogs](https://docs.microsoft.com/en-us/azure/bot-service/bot-builder-concept-dialog?view=azure-bot-service-4.0)
49+
- [Gathering Input Using Prompts](https://docs.microsoft.com/en-us/azure/bot-service/bot-builder-prompts?view=azure-bot-service-4.0&tabs=csharp)
50+
- [Activity processing](https://docs.microsoft.com/en-us/azure/bot-service/bot-builder-concept-activity-processing?view=azure-bot-service-4.0)

samples/05.multi-turn-prompt/app.py

Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
# Copyright (c) Microsoft Corporation. All rights reserved.
2+
# Licensed under the MIT License.
3+
4+
import asyncio
5+
import sys
6+
from datetime import datetime
7+
from types import MethodType
8+
9+
from flask import Flask, request, Response
10+
from botbuilder.core import (
11+
BotFrameworkAdapter,
12+
BotFrameworkAdapterSettings,
13+
ConversationState,
14+
MemoryStorage,
15+
TurnContext,
16+
UserState,
17+
)
18+
from botbuilder.schema import Activity, ActivityTypes
19+
20+
from dialogs import UserProfileDialog
21+
from bots import DialogBot
22+
23+
# Create the loop and Flask app
24+
LOOP = asyncio.get_event_loop()
25+
APP = Flask(__name__, instance_relative_config=True)
26+
APP.config.from_object("config.DefaultConfig")
27+
28+
# Create adapter.
29+
# See https://aka.ms/about-bot-adapter to learn more about how bots work.
30+
SETTINGS = BotFrameworkAdapterSettings(APP.config["APP_ID"], APP.config["APP_PASSWORD"])
31+
ADAPTER = BotFrameworkAdapter(SETTINGS)
32+
33+
34+
# Catch-all for errors.
35+
async def on_error(context: TurnContext, error: Exception):
36+
# This check writes out errors to console log
37+
# NOTE: In production environment, you should consider logging this to Azure
38+
# application insights.
39+
print(f"\n [on_turn_error]: { error }", file=sys.stderr)
40+
41+
# Send a message to the user
42+
await context.send_activity("The bot encountered an error or bug.")
43+
await context.send_activity("To continue to run this bot, please fix the bot source code.")
44+
# Send a trace activity if we're talking to the Bot Framework Emulator
45+
if context.activity.channel_id == 'emulator':
46+
# Create a trace activity that contains the error object
47+
trace_activity = Activity(
48+
label="TurnError",
49+
name="on_turn_error Trace",
50+
timestamp=datetime.utcnow(),
51+
type=ActivityTypes.trace,
52+
value=f"{error}",
53+
value_type="https://www.botframework.com/schemas/error"
54+
)
55+
56+
# Send a trace activity, which will be displayed in Bot Framework Emulator
57+
await context.send_activity(trace_activity)
58+
59+
# Clear out state
60+
await CONVERSATION_STATE.delete(context)
61+
62+
ADAPTER.on_turn_error = MethodType(on_error, ADAPTER)
63+
64+
# Create MemoryStorage, UserState and ConversationState
65+
MEMORY = MemoryStorage()
66+
CONVERSATION_STATE = ConversationState(MEMORY)
67+
USER_STATE = UserState(MEMORY)
68+
69+
# create main dialog and bot
70+
DIALOG = UserProfileDialog(USER_STATE)
71+
BOT = DialogBot(CONVERSATION_STATE, USER_STATE, DIALOG)
72+
73+
74+
# Listen for incoming requests on /api/messages.
75+
@APP.route("/api/messages", methods=["POST"])
76+
def messages():
77+
# Main bot message handler.s
78+
if "application/json" in request.headers["Content-Type"]:
79+
body = request.json
80+
else:
81+
return Response(status=415)
82+
83+
activity = Activity().deserialize(body)
84+
auth_header = (
85+
request.headers["Authorization"] if "Authorization" in request.headers else ""
86+
)
87+
88+
try:
89+
task = LOOP.create_task(
90+
ADAPTER.process_activity(activity, auth_header, BOT.on_turn)
91+
)
92+
LOOP.run_until_complete(task)
93+
return Response(status=201)
94+
except Exception as exception:
95+
raise exception
96+
97+
98+
if __name__ == "__main__":
99+
try:
100+
APP.run(debug=False, port=APP.config["PORT"]) # nosec debug
101+
except Exception as exception:
102+
raise exception
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
# Copyright (c) Microsoft Corporation. All rights reserved.
2+
# Licensed under the MIT License.
3+
4+
from .dialog_bot import DialogBot
5+
6+
__all__ = ["DialogBot"]
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
# Copyright (c) Microsoft Corporation. All rights reserved.
2+
# Licensed under the MIT License.
3+
4+
from botbuilder.core import ActivityHandler, ConversationState, TurnContext, UserState
5+
from botbuilder.dialogs import Dialog
6+
from helpers.dialog_helper import DialogHelper
7+
8+
"""
9+
This Bot implementation can run any type of Dialog. The use of type parameterization is to allows multiple
10+
different bots to be run at different endpoints within the same project. This can be achieved by defining distinct
11+
Controller types each with dependency on distinct Bot types. The ConversationState is used by the Dialog system. The
12+
UserState isn't, however, it might have been used in a Dialog implementation, and the requirement is that all
13+
BotState objects are saved at the end of a turn.
14+
"""
15+
16+
17+
class DialogBot(ActivityHandler):
18+
def __init__(
19+
self,
20+
conversation_state: ConversationState,
21+
user_state: UserState,
22+
dialog: Dialog
23+
):
24+
if conversation_state is None:
25+
raise TypeError(
26+
"[DialogBot]: Missing parameter. conversation_state is required but None was given"
27+
)
28+
if user_state is None:
29+
raise TypeError(
30+
"[DialogBot]: Missing parameter. user_state is required but None was given"
31+
)
32+
if dialog is None:
33+
raise Exception("[DialogBot]: Missing parameter. dialog is required")
34+
35+
self.conversation_state = conversation_state
36+
self.user_state = user_state
37+
self.dialog = dialog
38+
39+
async def on_turn(self, turn_context: TurnContext):
40+
await super().on_turn(turn_context)
41+
42+
# Save any state changes that might have ocurred during the turn.
43+
await self.conversation_state.save_changes(turn_context)
44+
await self.user_state.save_changes(turn_context)
45+
46+
async def on_message_activity(self, turn_context: TurnContext):
47+
await DialogHelper.run_dialog(
48+
self.dialog,
49+
turn_context,
50+
self.conversation_state.create_property("DialogState"),
51+
)
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
#!/usr/bin/env python3
2+
# Copyright (c) Microsoft Corporation. All rights reserved.
3+
# Licensed under the MIT License.
4+
5+
import os
6+
7+
""" Bot Configuration """
8+
9+
10+
class DefaultConfig:
11+
""" Bot Configuration """
12+
13+
PORT = 3978
14+
APP_ID = os.environ.get("MicrosoftAppId", "")
15+
APP_PASSWORD = os.environ.get("MicrosoftAppPassword", "")
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
# Copyright (c) Microsoft Corporation. All rights reserved.
2+
# Licensed under the MIT License.
3+
4+
from .user_profile import UserProfile
5+
6+
__all__ = ["UserProfile"]
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
# Copyright (c) Microsoft Corporation. All rights reserved.
2+
# Licensed under the MIT License.
3+
4+
"""
5+
This is our application state. Just a regular serializable Python class.
6+
"""
7+
8+
9+
class UserProfile:
10+
def __init__(self, name: str = None, transport: str = None, age: int = 0):
11+
self.name = name
12+
self.transport = transport
13+
self.age = age
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
# Copyright (c) Microsoft Corporation. All rights reserved.
2+
# Licensed under the MIT License.
3+
4+
from .user_profile_dialog import UserProfileDialog
5+
6+
__all__ = ["UserProfileDialog"]

0 commit comments

Comments
 (0)
0