1

I am creating a Questionnaire bot for smart speakers that asks a user several questions. And the answers are stored in firebase. I am using node.js, actions-on-google.

It conversation looks like this:

  • Bot: Hi user, Are you ready to answer the questions? [welcome intent, with Yes/No folllow up intents]{output context = q1}
  • User: Yes
  • Bot: What is your answer for Q1? [intent q1]{input context = q1, output context = q2}
  • User: Here is my answer.
  • Bot: What is your answer for Q2? [intent q2]{input context = q2, output context = q2}
  • ...
  • ...
  • User: Here is my answer.
  • Bot: Thank you for answering. Bye.

In this conversation, if the the user does not respond (no.input), I want to handle it in fulfillment.

For no.input, I am using the code from: https://developers.google.com/assistant/conversational/reprompts#dialogflow_2. I have an intent 'No Input Prompt' with 'action and parameters' no.input. This intent does not have follow up intents or any contexts.

My question is: How can I provide a custom re-prompt for each intent on the event of no.input?

I am doing something like this:

const { dialogflow, SignIn, Confirmation } = require('actions-on-google');
const functions = require('firebase-functions');
const app = dialogflow({
    debug: true
});
const admin = require("firebase-admin");
admin.initializeApp({
    credential: admin.credential.applicationDefault(),
    databaseURL: 'DATABASE URL',
});
const db = admin.firestore();
const db_ref = db.collection('users');

let current_context = '';

app.intent('q1', (conv, {response}) =>{
    const payload = conv.user.profile.payload
    db_ref.doc(`${payload.email}`).collection(today).doc('q1').set({
        answer: response,
        timestamp: time
    });
    current_context='q1';
    conv.ask(question('Whats your answer for q1?'));
})


app.intent('No Input Prompt', (conv) => {
  const repromptCount = parseInt(conv.arguments.get('REPROMPT_COUNT'));
  if(current_context === 'q1'){
        if (repromptCount === 0) {
              conv.ask("What is your answer for Q1?");
        } else if (repromptCount === 1) {
              conv.ask("What is your answer for Q1? You can say option 1,2,3 or 4");
        } else if (conv.arguments.get('IS_FINAL_REPROMPT')) {
              conv.close("Lets talk some other time.");
        }
  }

})

With this code, when I simulate no input, I get this error:

Failed to parse Dialogflow response into AppResponse because of invalid platform response: Could not find a RichResponse or SystemIntent in the platform response for agentId: 12ae4038-8f17-4005-9bfa-1691ffcde5e5 and intentId: 218605f2-e718-47e0-97f6-e1925755fa7c

maharrx
  • 65
  • 1
  • 2
  • 11
  • Was there a question or problem that you're having? If so, can you [update your question](https://stackoverflow.com/posts/58914612/edit) to more clearly state what issue you're having, if you're getting any errors, or what is happening that you don't expect? – Prisoner Nov 18 '19 at 14:08

1 Answers1

2

There are three major issues with the code you've posted

  1. In JavaScript, variable names can't have a dash in them. So current-context isn't a valid variable name.
  2. You're declaring current-context as a constant. Which, as the name suggests, means it can't change. You then try to change it.
  3. You don't indicate what the execution environment is, but if you're using something like Firebase Cloud Functions, you have no guarantee that you'll be using the same server when the "q1" Intent Handler runs and when the "No Input Prompt" Intent Handler runs.

To get around (3), you need to pass information between calls to any Intent Handler. In most cases, you'd do this using the actions-on-google conv.data storage or a Dialogflow Context.

However, since what you're trying to detect is a Context, you can do better and just look at what Contexts are currently active. You can see if a Context is active by calling conv.contexts.get('name') to get a context. If it is active, you'll get a result. If it is not active, it will be undefined.

So your code might look something like

app.intent('No Input Prompt', (conv) => {
  const repromptCount = parseInt(conv.arguments.get('REPROMPT_COUNT'));
  if(conv.contexts.get('q1')){
        if (repromptCount === 0) {
              conv.ask("What is your answer for Q1?");
        } else if (repromptCount === 1) {
              conv.ask("What is your answer for Q1? You can say option 1,2,3 or 4");
        } else if (conv.arguments.get('IS_FINAL_REPROMPT')) {
              conv.close("Lets talk some other time.");
        }
  }

})

Update based on the question in your comment:

Cloud Functions for Firebase use a "serverless" technology such that there is no single server that always has your code loaded and ready to be executed. Instead, when you make a request to the URL that should run the function, Firebase determines if it needs to start a new copy of your code to run and then runs the request. It may start a new copy if there are no copies currently running (it may have shut them all down for whatever reason) or that there are more requests than the current instances can currently handle. Even if there are copies running, two separate requests from you may go to different servers, and they're not sharing variable instances.

Each Intent that gets triggered will result in another call to the Firebase function, so there is no guarantee that you'll get the same server, or even if the same server is running from one time to the next.

Not using global variables are also important when you start getting more than one user. If you were using global variables, then two users saying something at the same time could overlap each other, so what you save for one user might be used for the other.

This is why anything that you want to remember in between calls to Intent Handlers must be saved in some way besides using variables.

Prisoner
  • 49,922
  • 7
  • 53
  • 105
  • 1) and 2) Thank you for pointing out the issues in my code. I have made the changes. Const to Var and "-" to "_". 3) Can you please elaborate on this? What do you mean by "you have no guarantee that you'll be using the same server when the 'q1' Intent Handler runs and when the 'No Input Prompt' Intent Handler run". I am using Firebase. I have updated the code. – maharrx Nov 19 '19 at 10:14
  • I see what you mean by "you have no guarantee that you'll be using the same server when the 'q1' Intent Handler runs and when the 'No Input Prompt' Intent Handler run". At times the data is set, and sometimes not. – maharrx Nov 25 '19 at 16:22
  • If the answer has helped, upvoting and/or accepting it are always appreciated. – Prisoner Nov 25 '19 at 17:11