Skip to content

Commit

Permalink
Multi-turn prompt: Summarizing the collected information before askin…
Browse files Browse the repository at this point in the history
…g to confirm if collected info is Ok. (#3957)

* Multi-turn prompt: Summarizing the collected information before asking to confirm if collected info is Ok.

* Fixing lint errors
  • Loading branch information
gandiddi authored Apr 8, 2024
1 parent 93bf764 commit 9fd77a1
Show file tree
Hide file tree
Showing 3 changed files with 106 additions and 93 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,8 @@ public UserProfileDialog(UserState userState)
NameConfirmStepAsync,
AgeStepAsync,
PictureStepAsync,
ConfirmStepAsync,
SummaryStepAsync,
ConfirmStepAsync,
};

// Add named dialogs to the DialogSet. These names are saved in the dialog state.
Expand Down Expand Up @@ -127,54 +127,60 @@ private static async Task<DialogTurnResult> PictureStepAsync(WaterfallStepContex

private async Task<DialogTurnResult> ConfirmStepAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken)
{
stepContext.Values["picture"] = ((IList<Attachment>)stepContext.Result)?.FirstOrDefault();
var msg = $"Thanks. ";

// WaterfallStep always finishes with the end of the Waterfall or with another dialog; here it is a Prompt Dialog.
return await stepContext.PromptAsync(nameof(ConfirmPrompt), new PromptOptions { Prompt = MessageFactory.Text("Is this ok?") }, cancellationToken);
if ((bool)stepContext.Result)
{
msg += $" Your profile saved successfully.";
}
else
{
msg += $" Your profile will not be kept.";
}

await stepContext.Context.SendActivityAsync(MessageFactory.Text(msg), cancellationToken);

// WaterfallStep always finishes with the end of the Waterfall or with another dialog; here it is the end.
return await stepContext.EndDialogAsync(cancellationToken: cancellationToken);
}

private async Task<DialogTurnResult> SummaryStepAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken)
{
if ((bool)stepContext.Result)
{
// Get the current profile object from user state.
var userProfile = await _userProfileAccessor.GetAsync(stepContext.Context, () => new UserProfile(), cancellationToken);
stepContext.Values["picture"] = ((IList<Attachment>)stepContext.Result)?.FirstOrDefault();

userProfile.Transport = (string)stepContext.Values["transport"];
userProfile.Name = (string)stepContext.Values["name"];
userProfile.Age = (int)stepContext.Values["age"];
userProfile.Picture = (Attachment)stepContext.Values["picture"];
// Get the current profile object from user state.
var userProfile = await _userProfileAccessor.GetAsync(stepContext.Context, () => new UserProfile(), cancellationToken);

var msg = $"I have your mode of transport as {userProfile.Transport} and your name as {userProfile.Name}";
userProfile.Transport = (string)stepContext.Values["transport"];
userProfile.Name = (string)stepContext.Values["name"];
userProfile.Age = (int)stepContext.Values["age"];
userProfile.Picture = (Attachment)stepContext.Values["picture"];

if (userProfile.Age != -1)
{
msg += $" and your age as {userProfile.Age}";
}
var msg = $"I have your mode of transport as {userProfile.Transport} and your name as {userProfile.Name}";

msg += ".";
if (userProfile.Age != -1)
{
msg += $" and your age as {userProfile.Age}";
}

await stepContext.Context.SendActivityAsync(MessageFactory.Text(msg), cancellationToken);
msg += ".";

await stepContext.Context.SendActivityAsync(MessageFactory.Text(msg), cancellationToken);

if (userProfile.Picture != null)
if (userProfile.Picture != null)
{
try
{
try
{
await stepContext.Context.SendActivityAsync(MessageFactory.Attachment(userProfile.Picture, "This is your profile picture."), cancellationToken);
}
catch
{
await stepContext.Context.SendActivityAsync(MessageFactory.Text("A profile picture was saved but could not be displayed here."), cancellationToken);
}
await stepContext.Context.SendActivityAsync(MessageFactory.Attachment(userProfile.Picture, "This is your profile picture."), cancellationToken);
}
catch
{
await stepContext.Context.SendActivityAsync(MessageFactory.Text("A profile picture was saved but could not be displayed here."), cancellationToken);
}
}
else
{
await stepContext.Context.SendActivityAsync(MessageFactory.Text("Thanks. Your profile will not be kept."), cancellationToken);
}

// WaterfallStep always finishes with the end of the Waterfall or with another dialog; here it is the end.
return await stepContext.EndDialogAsync(cancellationToken: cancellationToken);
// WaterfallStep always finishes with the end of the Waterfall or with another dialog; here it is a Prompt Dialog.
return await stepContext.PromptAsync(nameof(ConfirmPrompt), new PromptOptions { Prompt = MessageFactory.Text("Is this ok?") }, cancellationToken);
}

private static Task<bool> AgePromptValidatorAsync(PromptValidatorContext<int> promptContext, CancellationToken cancellationToken)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,8 +43,8 @@ class UserProfileDialog extends ComponentDialog {
this.nameConfirmStep.bind(this),
this.ageStep.bind(this),
this.pictureStep.bind(this),
this.confirmStep.bind(this),
this.summaryStep.bind(this)
this.summaryStep.bind(this),
this.confirmStep.bind(this)
]));

this.initialDialogId = WATERFALL_DIALOG;
Expand Down Expand Up @@ -128,42 +128,47 @@ class UserProfileDialog extends ComponentDialog {
}

async confirmStep(step) {
step.values.picture = step.result && step.result[0];
let msg = 'Thanks.';
if (step.result) {
msg += ' Your profile saved successfully.';
} else {
msg += ' Your profile will not be kept.';
}

await step.context.sendActivity(msg);

// WaterfallStep always finishes with the end of the Waterfall or with another dialog; here it is a Prompt Dialog.
return await step.prompt(CONFIRM_PROMPT, { prompt: 'Is this okay?' });
return await step.endDialog();
}

async summaryStep(step) {
if (step.result) {
// Get the current profile object from user state.
const userProfile = await this.userProfile.get(step.context, new UserProfile());
step.values.picture = step.result && step.result[0];

userProfile.transport = step.values.transport;
userProfile.name = step.values.name;
userProfile.age = step.values.age;
userProfile.picture = step.values.picture;
// Get the current profile object from user state.
const userProfile = await this.userProfile.get(step.context, new UserProfile());

let msg = `I have your mode of transport as ${ userProfile.transport } and your name as ${ userProfile.name }`;
if (userProfile.age !== -1) {
msg += ` and your age as ${ userProfile.age }`;
}
userProfile.transport = step.values.transport;
userProfile.name = step.values.name;
userProfile.age = step.values.age;
userProfile.picture = step.values.picture;

msg += '.';
await step.context.sendActivity(msg);
if (userProfile.picture) {
try {
await step.context.sendActivity(MessageFactory.attachment(userProfile.picture, 'This is your profile picture.'));
} catch {
await step.context.sendActivity('A profile picture was saved but could not be displayed here.');
}
let msg = `I have your mode of transport as ${ userProfile.transport } and your name as ${ userProfile.name }`;
if (userProfile.age !== -1) {
msg += ` and your age as ${ userProfile.age }`;
}

msg += '.';
await step.context.sendActivity(msg);
if (userProfile.picture) {
try {
await step.context.sendActivity(MessageFactory.attachment(userProfile.picture, 'This is your profile picture.'));
} catch {
await step.context.sendActivity('A profile picture was saved but could not be displayed here.');
}
} else {
await step.context.sendActivity('Thanks. Your profile will not be kept.');
}

// WaterfallStep always finishes with the end of the Waterfall or with another dialog; here it is the end.
return await step.endDialog();
return await step.prompt(CONFIRM_PROMPT, { prompt: 'Is this okay?' });
}

async agePromptValidator(promptContext) {
Expand Down
68 changes: 35 additions & 33 deletions samples/python/05.multi-turn-prompt/dialogs/user_profile_dialog.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,8 @@ def __init__(self, user_state: UserState):
self.name_confirm_step,
self.age_step,
self.picture_step,
self.confirm_step,
self.summary_step,
self.confirm_step,
],
)
)
Expand Down Expand Up @@ -152,56 +152,58 @@ async def picture_step(
async def confirm_step(
self, step_context: WaterfallStepContext
) -> DialogTurnResult:
step_context.values["picture"] = (
None if not step_context.result else step_context.result[0]
)
msg = f"Thanks."
if step_context.result:
msg += f" Your profile saved successfully."
else:
msg += f" Your profile will not be kept."

await step_context.context.send_activity(MessageFactory.text(msg))

# WaterfallStep always finishes with the end of the Waterfall or
# with another dialog; here it is a Prompt Dialog.
return await step_context.prompt(
ConfirmPrompt.__name__,
PromptOptions(prompt=MessageFactory.text("Is this ok?")),
)
return await step_context.end_dialog()

async def summary_step(
self, step_context: WaterfallStepContext
) -> DialogTurnResult:
if step_context.result:
# Get the current profile object from user state. Changes to it
# will saved during Bot.on_turn.
user_profile = await self.user_profile_accessor.get(
step_context.context, UserProfile
)
step_context.values["picture"] = (
None if not step_context.result else step_context.result[0]
)
# Get the current profile object from user state. Changes to it
# will saved during Bot.on_turn.
user_profile = await self.user_profile_accessor.get(
step_context.context, UserProfile
)

user_profile.transport = step_context.values["transport"]
user_profile.name = step_context.values["name"]
user_profile.age = step_context.values["age"]
user_profile.picture = step_context.values["picture"]
user_profile.transport = step_context.values["transport"]
user_profile.name = step_context.values["name"]
user_profile.age = step_context.values["age"]
user_profile.picture = step_context.values["picture"]

msg = f"I have your mode of transport as {user_profile.transport} and your name as {user_profile.name}."
if user_profile.age != -1:
msg += f" And age as {user_profile.age}."
msg = f"I have your mode of transport as {user_profile.transport} and your name as {user_profile.name}."
if user_profile.age != -1:
msg += f" And age as {user_profile.age}."

await step_context.context.send_activity(MessageFactory.text(msg))
await step_context.context.send_activity(MessageFactory.text(msg))

if user_profile.picture:
await step_context.context.send_activity(
MessageFactory.attachment(
user_profile.picture, "This is your profile picture."
)
)
else:
await step_context.context.send_activity(
"A profile picture was saved but could not be displayed here."
if user_profile.picture:
await step_context.context.send_activity(
MessageFactory.attachment(
user_profile.picture, "This is your profile picture."
)
)
else:
await step_context.context.send_activity(
MessageFactory.text("Thanks. Your profile will not be kept.")
"A profile picture was saved but could not be displayed here."
)

# WaterfallStep always finishes with the end of the Waterfall or with another
# dialog, here it is the end.
return await step_context.end_dialog()
return await step_context.prompt(
ConfirmPrompt.__name__,
PromptOptions(prompt=MessageFactory.text("Is this ok?")),
)

@staticmethod
async def age_prompt_validator(prompt_context: PromptValidatorContext) -> bool:
Expand Down

0 comments on commit 9fd77a1

Please sign in to comment.