aws-solutions / qnabot-on-aws

AWS QnABot is a multi-channel, multi-language conversational interface (chatbot) that responds to your customer's questions, answers, and feedback. The solution allows you to deploy a fully functional chatbot across multiple channels including chat, voice, SMS and Amazon Alexa.
https://aws.amazon.com/solutions/implementations/aws-qnabot
Apache License 2.0
392 stars 251 forks source link

Using a lambda function for conditional chaining seems to not trigger the response bot on the next question #176

Closed Dudemanguy closed 4 years ago

Dudemanguy commented 4 years ago

Maybe someone can point me out in the right direction here. Let's say I have these questions in the qnabot.

  1. What is your favorite number?
  2. What is your name?
  3. What is your age?

Based on the answer to the first question, we want to use some fancy formula to determine whether to direct the person to question 2 or question 3, so we use a lambda function. This redirection seems to work and you conditionally advance to 2 or 3 (the bot replies "What is your name?" or "What is your age?"). However, when the user responds to the question (with either their name or age in this case), the bot always replies with the empty message prompt as if it's unable to interpret the user's reponse. To make matters stranger, if the user replies with the exact string in the "question" field, the QnAbot asks the question again and then you can proceed as normal.

I experience this behavior even when using an extremely simple function like so:

exports.handler = async (event) => {
    return "name";
};

The documentation seems to indicate that simply returning the string of the question is all that's required here and it almost works except for the response breaking. If I am missing something here, it would be much appreciated if someone could let me know.

rstrahan commented 4 years ago

Hi @Dudemanguy - I'm having a hard time reproducing this. Can you please provide explicit steps to repro.. include any screen shots that might help. Also 1 - export and provide the questions 2 - provide code for simplest case lambda routing functions uses in your questions 3- if you're using custom response bots with elicit response, please export from lex and provide as well so i can repro exactly what you have. Cheers Bob

Dudemanguy commented 4 years ago

Sure thing. Here's a minimally reproducible example.

json for the questions ``` { "qna": [ { "args": [ "" ], "next": "", "a": "What is your user id?", "r": { "buttons": [ { "text": "", "value": "" } ], "subTitle": "", "imageUrl": "", "title": "" }, "t": "", "elicitResponse": { "response_sessionattr_namespace": "user_id", "responsebot_hook": "QNANumber" }, "alt": { "markdown": "", "ssml": "" }, "conditionalChaining": "Lambda::qna-Auth", "l": "", "qid": "ElicitResponse.001", "type": "qna", "selected": false, "q": [ "begin" ] }, { "args": [ "" ], "next": "", "a": "What is your pin number?", "r": { "buttons": [ { "text": "", "value": "" } ], "subTitle": "", "imageUrl": "", "title": "" }, "t": "", "elicitResponse": { "response_sessionattr_namespace": "pin", "responsebot_hook": "QNANumber" }, "alt": { "markdown": "", "ssml": "" }, "conditionalChaining": "\"name\"", "l": "", "qid": "ElicitResponse.002", "type": "qna", "selected": false, "q": [ "pin" ] }, { "args": [ "" ], "next": "", "a": "What is your name?", "r": { "buttons": [ { "text": "", "value": "" } ], "subTitle": "", "imageUrl": "", "title": "" }, "t": "", "elicitResponse": { "response_sessionattr_namespace": "name", "responsebot_hook": "QNAName" }, "alt": { "markdown": "", "ssml": "" }, "conditionalChaining": "", "l": "", "qid": "ElicitResponse.003", "type": "qna", "selected": false, "q": [ "name" ] } ] } ```

In there, we use a lambda function called "qna-Auth". For this example, we'll just skip directly to "name". Perhaps in other cases we would rather go to "pin" but let's just keep it simple for now. So the function is:

exports.handler = async (event) => {
    return "name"
};

Here are a couple of screenshots of the behavior. example1 example2

I did a little bit of debugging and it looks to me what's going on here is that for some reason, the elicitResponseProgress in query.js doesn't get set to Fulfilled if you use a lambda function in the conditional chain for the previous step. So what happens instead is that get_hit is run which executes another elastic search using what you just typed in. That's why in the second picture, typing in "name" works because the elastic search picks up the qid and then asks the question again.

rstrahan commented 4 years ago

Thanks @Dudemanguy! I can reproduce the problem, and it is indeed a bug. The fix will be in our next release, hopefully in the next couple of weeks.

Details:
The bug is in the function evaluateConditionalChaining in file /aws-ai-qna-bot/lambda/proxy-es/lib/query.js, in the block:

        var res=await lambda.invoke({
            FunctionName:lambdaName,
            InvocationType:"RequestResponse",
            Payload:JSON.stringify({
                req:req,
                res:res
            })
        }).promise();
        next_q=res.Payload;

This code overwrote the global res object with the locally declared variable of the same name. The fix is to change the variable name:

        var lambdares=await lambda.invoke({
            FunctionName:lambdaName,
            InvocationType:"RequestResponse",
            Payload:JSON.stringify({
                req:req,
                res:res
            })
        }).promise();
        next_q=lambdares.Payload;

Apologies for the inconvenience, and thanks for reporting the problem!

Cheers Bob

Dudemanguy commented 4 years ago

I just tested out your branch with the fix and it does indeed work as expected. Thanks for quick fix!

Dudemanguy commented 4 years ago

Looks like this is in master now and there's a new release. I just tested it and all is well. Thanks again!