I have an intent-A which is triggered by some user input. When the response is given to user I have used ConfirmIntent instead of Close so that I can switch/chain another intent (lets say intent-B).
Ideally if user type "yes" then intent should be triggered and if user type "no" then it should not. Problem is that intent-B is being triggered no matter what I type.
I have read about ConfirmIntent from here, here and here.
Calling Code:
session_attributes = {"confirmationContext": "AutoPopulate"}
return confirm_intent(session_attributes , 'intent-B', slots, 'Do you want to invoke intent-B')
ConfirmIntent Code:
def confirm_intent(session_attributes, intent_name, slots, message):
return {
'sessionAttributes': session_attributes,
'dialogAction': {
'type': 'ConfirmIntent',
'intentName': intent_name,
'slots': slots,
'message': {
'contentType': 'PlainText',
'content': message
}
}
}
In the logs I can see that confirmationStatus': 'Denied' when I type "no" but even then intent-B is being called.
Am I missing something or is it designed this way?
NOTE: For workaround I am adding below code in the DialogCodeHook of intent-B
if 'confirmationStatus' in intent_request['currentIntent'] and intent_request['currentIntent']['confirmationStatus'] == 'Denied':
return close("Ok, let me know if you need anything else.", session_attributes)
You are handling this correctly.
When you are passing intent-B into your confirm_intent request. You are telling Lex to pass the users response to intent-B. When the user responds with "no", the Denied value is correctly passed on.
Alternatively, you could have intent-A in your confirm_intent request. Then when the response hits intent-A you can use Close on denial and Delegate on confirmation to pass the flow to intent-B. This is "more correct" but will result in additional computation, so it's a trade off.
If you have multiple follow up requests, you could consider using ElicitIntent to instead ask the user "What can I help you with?". Having an an intent with utterances such as "nothing", "goodbye" will catch negative responses. This is a slightly different use case and may not be appropriate for you.
Related
I have a problem with input password in webchat.I used Sample - Customize Web Chat with Password Input Activity for password input card when I enter wrong password it show a message like wrong password please try again but when i enter password out of form i need to show another message.
I used this code.
if (card.activity.type === 'message') {
if (
card.activity.from.role === 'bot' &&
(card.activity.text === getLoginMessage(this.props.language) ||
card.activity.text === getLoginRetryMessage(this.props.language))
) {
let message = card.activity.text;
if (!this.hasSubmittedPassword && (card.activity.text === getLoginRetryMessage(this.props.language))) {
message = "Please fill the form and click enter in order to complete your request.";
}
return children => (
<ConnectedPasswordInputActivity
promptMessage={message}
passwordPlaceholder={this.props.literals.password}
language={this.props.language}
handlePasswordSubmit={this.handlePasswordSubmit}
>
{next(card)(children)}
</ConnectedPasswordInputActivity>
);
}
Refer to the image please
This is hard to know for certain as there is a lot of hidden code you are referencing, however I suspect the issue is tied to your hasSubmittedPassword function and your check against it.
You are matching on
A message
From a bot
Where the text equals either the getLoginMessage or getLoginRetryMessage return value
These are passing your check (based on the attached image).
You then check against hasSubmittedPassword which (apparently) is passing and then check a second time on getLoginRetryMessage (???). This second check is unnecessary as you wouldn't be in this if statement if the first check (card.activity.text = this.getLoginRetryMessage(this.props.language) hadn't succeeded.
This can be simplified to:
if (!this.hasSubmittedPassword) {
message = "Please fill the form and click enter in order to complete your request.";
}
That being said, your use of if(!this.hasSubmittedPassword) {...} is only checking if there is a returned value or not. This function may or may not be returning the correct value, but your check doesn't care. It only wants to know IF there is a value. If your logic is setup to always return something (i.e. true, false, yes, no, try again), then it will always pass.
From what I can see, if your first three checks pass, then you will always get the secondary message.
Hope of help!
I am trying to implement a FormAction here, and I’ve overridden validate method.
Here is the code for the same:
def validate(self, dispatcher, tracker, domain):
logger.info("Validate of single entity called")
document_number = tracker.get_slot("document_number")
# Run regex on latest_message
extracted = re.findall(regexp, tracker.latest_message['text'])
document_array = []
for e in extracted:
document_array.append(e[0])
# generate set for needed things and
document_set = set(document_array)
document_array = list(document_set)
logger.info(document_set)
if len(document_set) > 0:
if document_number and len(document_number):
document_array = list(set(document_array + document_number))
return [SlotSet("document_number", document_array)]
else:
if document_number and len(document_number):
document_array = list(set(document_array + document_number))
return [SlotSet("document_number", document_array)]
else:
# Here it doesn't have previously set slot
# So Raise an error
raise ActionExecutionRejection(self.name(),
"Please provide document number")
So, ideally as per the docs, when ActionExecutionRejection occurs, it should utter a template with name utter_ask_{slotname} but it doesn’t trigger that action.
Here is my domain.yml templates
templates:
utter_greet:
- text: "Hi, hope you are having a good day! How can I help?"
utter_ask_document_number:
- text: "Please provide document number"
utter_help:
- text: "To find the document, please say the ID of a single document or multiple documents"
utter_goodbye:
- text: "Talk to you later!"
utter_thanks:
- text: "My pleasure."
The ActionExecutionRejection doesn't by default utter a template with the name utter_ask_{slotname}, but rather leaves the form logic to allow other policies (e.g. FallbackPolicy) to take action. The utter_ask_{slotname} is the default for the happy path in which it's trying to get a required slot for the first time. This default implementation of the action rejection is there in order to handle certain unhappy paths such as if a user decides they want to exit the flow by denying, or take a detour by chatting, etc.
If you want to implement the template to re-ask for the required slot using the utterance, you could replace the ActionExecutionRejection with dispatcher.utter_template(<desired template name>, tracker). However, this will leave you with no way to exit the form action without validation -- I don't know what your intents are, but perhaps you want to also incorporate some logic based on the intent (i.e. if it's something like "deny", let the ActionExecutionRejection happen so it can exit, it it's an "enter data" type of intent make sure it asks again).
I have added retrySpeak property to builder.prompt.text incase if the user not responds for a while. But only the text in speak property will be spoken and not retrySpeak text. the below is the dialog which I am triggering from default dialog. I am testing this from cortana mobile app. Is there any property I missed here?
bot.dialog("mainServices", [
function(session){
builder.Prompts.text(session, 'How can I help you?', {
speak: 'How can I help you?',
retrySpeak: 'How can I help you,please say something',
inputHint: builder.InputHint.expectingInput
});
}
])
.triggerAction({
// The user can request this at any time.
// Once triggered, it clears the stack and prompts the main services again.
matches: /^start over$|^go back to main services$|^begin again$|start over services/i,
confirmPrompt: "This will cancel your request. Are you sure?"
});
IPrompt Options retrySpeak
This property is actually a re-prompt for if the response from the user was invalid, as opposed to a lack of response. For example, if speak prompted for a time (in my example a number), and the bot receives a string it cannot parse instead, retrySpeak is prompted asking for a valid message. This is where you can tell the user what input type you are expecting, or to enter a value within a set range, etc.
bot.dialog("/", [
function(session){
builder.Prompts.number(session, 'Please enter a time for your reservation', {
speak: 'Please enter a time, 1-4, for your reservation.',
retryPrompt: 'That is not a number!', //what bot types
retrySpeak: 'I\'m sorry. That is not a valid time. Please enter a time between 1:00 and 4:00', //what Cortana says
inputHint: builder.InputHint.expectingInput
});
},
function(session, results){
console.log(results)
session.endConversation();
}
])
If you are particularly looking for a timeout check, take a look at this botbuilder-timeout package, which reprompts after a certain amount of time. Please keep in mind that it's a third party app and I haven't tested it myself. :)
I have a Intent where I have a parameter called age and its value I want it to be the user input.
And in the response I would like to responde with the same input as user given ex:
User Input: Hello, haw are you.
Bot: You said: Hello, haw are you.
So I would need the user input first to store in a parameter and from the Text Response section I can call the parameter but I just don't know how to catch the user input at this moment!
So Text response would be like: You said: $input.
Assuming that you are using Javascript, you can find many useful details here.
Depending on the way you get the input, the solution is totally different.
For instance, in this link you get the input via a click (this.button.addEventListener("click", this.handleClick.bind(this))) or in the following code you get the input as a query.
import {ApiAiClient} from "api-ai-javascript";
import {IRequestOptions, IServerResponse, ApiAiConstants} from "api-ai-javascript/ApiAiClient"
const client = new ApiAiClient({accessToken: 'YOUR_ACCESS_TOKEN'})
queryInput.addEventListener("keydown", queryInputKeyDown);
.textRequest('Hello!')
.then((response) => {console.log(queryInput.value);})
Thus, in the above example, you can use:
value = queryInput.value
you can try it in Inline Editor Fulfillment
function userInput(agent) {
let user_input = agent.query;
//test it
agent.add(user_input);
}
var response = await App.PhrasespageCS.DisplayAlert("Reset score",
"You have more than 50 points. Are you sure you want to reset it to 0?",
"Yes", "No");
if (response == true)
{
App.DB.ResetPointsForSelectedPhrase(App.cfs);
}
I am not clear on the use of await here and would appreciate advice. Also could this be combined into one statement?
Await means the app has created a dialog for you and is gonna wait till the answer comes in return.
And that's the correct way to write it. Also if you wanna reduce the code you can put it as below,
if (await App.PhrasespageCS.DisplayAlert("Reset score", "You have more than 50 points.Are you sure you want to reset it to 0 ? ", "Yes", "No"))
App.DB.ResetPointsForSelectedPhrase(App.cfs);
As described in the example you should use await. Check this also. The code for your purpose is given above in the answer.