google-cloud-functionsdialogflow-es-fulfillment

Chatbot breaks when using Enable beta features and APIs option and agent.setFollowupEvent() in the fulfillment code


The situation: I'm developing the chatbot. Today I noticed that the fulfillment code isn't working as expected. The fulfillment code is pretty basic, mostly just triggering custom followup events (e.g. agent.setFollowupEvent('someCustomName')), which then triggers specific intent.

I'm using the fulfillment with the Inline editor for Google Cloud so the webhook server is in Google Cloud.

I'm also using 2 integrations (DF messenger & FB messenger).

Everything worked fine, but today I'm very often seeing an error (specifics below).  
 

Steps I did today and which led to the problem:

  1. Snce I need to develop the bot further and the DF messenger integration is already published, I created 2 different environments in project settings: "Production" and "Development".
  2. I set the appropriate environment for each of the integrations in the integrations settings - "Production" for DF messenger integration and "Development" for FB messenger integration (FB integration isn't public yet).
  3. Tested both integrations and saw that they ignore the environment selection and they actually work with the Draft version (whatever the curent intents, entities etc. in the Draft are).
  4. I read that enabling the option 'Enable beta features and APIs' in the project settings might do the trick (serving the correct environment to specific integration). So I enabled this option and the integrations started working as expected (every integration with its own environment).
  5. So, everything seemed fine until I found out that if I enable the 'Enable beta features and APIs' option, this causes another problem - see below.  
     

'The real' problem: the bot breaks when using Enable beta features and APIs option and agent.setFollowupEvent() in the fulfillment code

My bot is using very simple event triggering. I think the part of the code below is self-explanatory enough, so here it is:

  // Dummies
  function D_99_01_01(agent) {
    agent.add(`...`);
    agent.setFollowupEvent('custom_990101');
  }
  
  let intentMap = new Map();
  
  // Dummies
  intentMap.set('02.01 > some intent to trigger the event (D99.01.01)', D_99_01_01);
  
  agent.handleRequest(intentMap);

The easiest way to explain the problem is: if I don't enable the 'Enable beta features and APIs' option, this code works fine. If I do enable the 'Enable beta features and APIs' option, it doesn't work - in that case I get the error response:

Webhook call failed. Error: UNAVAILABLE, State: URL_UNREACHABLE, Reason: UNREACHABLE_5xx, HTTP status code: 500.

The error happens regardless of how I'm testing/using the bot (while testing inside Dialgoflow itself, while testing on https://console.actions.google.com/, while using the chatbot on production website (integration with DF messenger) or while testing FB messenger integration (app on FB page)).  
 

My partial solution and questions:

  1. First of all, if I want to use different environemnts for different integrations and if I want to be able to develop the bot further, I definitely need the 'Enable beta features and APIs'option enabled, right? But then I need to solve the second part of the problem ...
  2. I suspect that the fulfillment code could be an issue? If it's true, I need someone to point me in the right direction with a bit of rewritten code or with some examples of how to trigger the events in a proper way.
  3. Is the code itself OK and the issue actually lies in the Dialogflow itself (the way it works)? What can I do in that case?  
     

Edit: adding the whole code

Package.json (default, didn't touch it):

{
  "name": "dialogflowFirebaseFulfillment",
  "description": "This is the default fulfillment for a Dialogflow agents using Cloud Functions for Firebase",
  "version": "0.0.1",
  "private": true,
  "license": "Apache Version 2.0",
  "author": "Google Inc.",
  "engines": {
    "node": "10"
  },
  "scripts": {
    "start": "firebase serve --only functions:dialogflowFirebaseFulfillment",
    "deploy": "firebase deploy --only functions:dialogflowFirebaseFulfillment"
  },
  "dependencies": {
    "actions-on-google": "^2.2.0",
    "firebase-admin": "^5.13.1",
    "firebase-functions": "^2.0.2",
    "dialogflow": "^0.6.0",
    "dialogflow-fulfillment": "^0.5.0"
  }
}

index.js:

// See https://github.com/dialogflow/dialogflow-fulfillment-nodejs
// for Dialogflow fulfillment library docs, samples, and to report issues
'use strict';

const functions = require('firebase-functions');
const {WebhookClient} = require('dialogflow-fulfillment');
const {Card, Suggestion} = require('dialogflow-fulfillment');

process.env.DEBUG = 'dialogflow:debug'; // enables lib debugging statements

exports.dialogflowFirebaseFulfillment = functions.https.onRequest((request, response) => {
  const agent = new WebhookClient({ request, response });
  console.log('Dialogflow Request headers: ' + JSON.stringify(request.headers));
  console.log('Dialogflow Request body: ' + JSON.stringify(request.body));

  /*
  function welcome(agent) {
    agent.add(`Pozdravljeni!`);
  }

  function fallback(agent) {
    agent.add(`Oprostite, nisem razumela.`);
    agent.add(`Lahko ponovite, prosim?`);
  }
  */

  // Dummies
  function D_99_01_01(agent) {
    //agent.add(`Text reponse`);
    //console.log('Dummy custom_990101 triggered');
    //agent.setContext({ name: 'outputCOntext', lifespan: 1, parameters: {}});
    agent.add(`...`);
    agent.setFollowupEvent('custom_990101');
  }
  function D_99_01_02(agent) {
    agent.add(`...`);
    agent.setFollowupEvent('custom_990102');
  }
  function D_99_01_03(agent) {
    agent.add(`...`);
    agent.setFollowupEvent('custom_990103');
  }
  function D_99_02_01(agent) {
    agent.add(`...`);
    agent.setFollowupEvent('custom_990201');
  }
  function D_99_03_01(agent) {
    agent.add(`...`);
    agent.setFollowupEvent('custom_990301');
  }

  function D_01(agent) {
    agent.add(`...`);
    agent.setContext({ name: 'Nov_narocnik', lifespan: 0, parameters: {}});
    agent.setContext({ name: 'Obstojec_narocnik', lifespan: 0, parameters: {}});
    agent.setFollowupEvent('custom_01');
  }
  function D_02_02(agent) {
    agent.add(`...`);
    agent.setContext({ name: '01Uvod-followup', lifespan: 2, parameters: {}});
    agent.setFollowupEvent('custom_0202');
  }
  function D_02_02_01(agent) {
    agent.add(`...`);
    agent.setContext({ name: 'Obstojec_narocnik', lifespan: 999, parameters: {}});
    agent.setContext({ name: '0202Obstojenaronik-followup', lifespan: 2, parameters: {}});
    agent.setFollowupEvent('custom_020201');
  }
  function D_02_02_02(agent) {
    agent.add(`...`);
    agent.setContext({ name: 'Obstojec_narocnik', lifespan: 999, parameters: {}});
    agent.setContext({ name: '0202Obstojenaronik-followup', lifespan: 2, parameters: {}});
    agent.setFollowupEvent('custom_020202');
  }
  function D_02_02_02_SERVICE_NO(agent) {
    agent.add(`...`);
    agent.setFollowupEvent('custom_020202_SERVICE_NO');
  }
  function D_02_02_02_SERVICE_NO_CLIENT(agent) {
    agent.add(`...`);
    agent.setContext({ name: '020202Podpora-STORITEV-ne-followup', lifespan: 2, parameters: {}});
    agent.setFollowupEvent('custom_020202_SERVICE_NO_CLIENT');
  }
  function D_02_02_02_SERVICE_NO_FINISH(agent) {
    agent.add(`...`);
    agent.setContext({ name: '020202Podpora-STORITEV-ne-followup', lifespan: 2, parameters: {}});
    agent.setFollowupEvent('custom_020202_SERVICE_NO_FINISH');
  }
  function D_02_02_02_SERVICE_NO_SUPPORT(agent) {
    agent.add(`...`);
    agent.setContext({ name: '020202Podpora-STORITEV-ne-followup', lifespan: 2, parameters: {}});
    agent.setFollowupEvent('custom_020202_SERVICE_NO_SUPPORT');
  }
  function D_02_02_03(agent) {
    agent.add(`...`);
    agent.setContext({ name: 'Obstojec_narocnik', lifespan: 999, parameters: {}});
    agent.setContext({ name: '0202Obstojenaronik-followup', lifespan: 2, parameters: {}});
    agent.setFollowupEvent('custom_020203');
  }
  function D_02_02_03_NO(agent) {
    agent.add(`...`);
    agent.setContext({ name: '020203Obstojenaronik-klub-followup', lifespan: 2, parameters: {}});
    agent.setFollowupEvent('custom_020203_NO');
  }


  // // Uncomment and edit to make your own intent handler
  // // uncomment `intentMap.set('your intent name here', yourFunctionHandler);`
  // // below to get this function to be run when a Dialogflow intent is matched
  // function yourFunctionHandler(agent) {
  //   agent.add(`This message is from Dialogflow's Cloud Functions for Firebase editor!`);
  //   agent.add(new Card({
  //       title: `Title: this is a card title`,
  //       imageUrl: 'https://developers.google.com/actions/images/badges/XPM_BADGING_GoogleAssistant_VER.png',
  //       text: `This is the body text of a card.  You can even use line\n  breaks and emoji! 💁`,
  //       buttonText: 'This is a button',
  //       buttonUrl: 'https://assistant.google.com/'
  //     })
  //   );
  //   agent.add(new Suggestion(`Quick Reply`));
  //   agent.add(new Suggestion(`Suggestion`));
  //   agent.setContext({ name: 'weather', lifespan: 2, parameters: { city: 'Rome' }});
  // }

  // // Uncomment and edit to make your own Google Assistant intent handler
  // // uncomment `intentMap.set('your intent name here', googleAssistantHandler);`
  // // below to get this function to be run when a Dialogflow intent is matched
  // function googleAssistantHandler(agent) {
  //   let conv = agent.conv(); // Get Actions on Google library conv instance
  //   conv.ask('Hello from the Actions on Google client library!') // Use Actions on Google library
  //   agent.add(conv); // Add Actions on Google library responses to your agent's response
  // }
  // // See https://github.com/dialogflow/fulfillment-actions-library-nodejs
  // // for a complete Dialogflow fulfillment library Actions on Google client library v2 integration sample

  // Run the proper function handler based on the matched Dialogflow intent name
  let intentMap = new Map();
  //intentMap.set('Default Welcome Intent', welcome);
  //intentMap.set('Default Fallback Intent', fallback);
  // intentMap.set('your intent name here', yourFunctionHandler);
  // intentMap.set('your intent name here', googleAssistantHandler);

  // Dummies
  intentMap.set('02.01 > fizična oseba - no - telefon (D99.01.01)', D_99_01_01);
  intentMap.set('02.01 > fizična oseba - no - email (D99.01.02)', D_99_01_02);
  intentMap.set('02.01 > fizična oseba - no - klepet (D99.01.03)', D_99_01_03);
  intentMap.set('02.01 > fizična oseba - omrežje - posvet - telefon (D99.01.01)', D_99_01_01);
  intentMap.set('02.01 > fizična oseba - omrežje - posvet - email (D99.01.02)', D_99_01_02);
  intentMap.set('02.01 > fizična oseba - omrežje - posvet - klepet (D99.01.03)', D_99_01_03);
  intentMap.set('02.01 > ne zdaj - telefon (D99.01.01)', D_99_01_01);
  intentMap.set('02.01 > ne zdaj - email (D99.01.02)', D_99_01_02);
  intentMap.set('02.01 > ne zdaj - klepet (D99.01.03)', D_99_01_03);
  intentMap.set('02.02.01 > no - yes - klub (D02.02.03)', D_02_02_03);
  intentMap.set('02.02.01 > no - yes - razmerje (D02.02.01)', D_02_02_01);
  intentMap.set('02.02.01 > no - yes - podporne storitve (D02.02.02)', D_02_02_02);
  intentMap.set('02.02.01 > no - no (D99.02.01)', D_99_02_01);
  intentMap.set('02.02.01 > yes - klepet (D99.01.03)', D_99_01_03);
  intentMap.set('02.02.01 > yes - email (D99.01.02)', D_99_01_02);
  intentMap.set('02.02.01 > yes - telefon (D99.01.01)', D_99_01_01);
  intentMap.set('02.02.02 Podpora - no - splošno (D02.02)', D_02_02);
  intentMap.set('02.02.02 Podpora - no - zaključek (D99.03.01)', D_99_03_01);
  intentMap.set('02.02.02 Podpora - tv2go - no (D02.02.02_SERVICE_NO)', D_02_02_02_SERVICE_NO);
  intentMap.set('02.02.02 Podpora - tv2go - yes - email (D99.01.02)', D_99_01_02);
  intentMap.set('02.02.02 Podpora - tv2go - yes - klepet (D99.01.03)', D_99_01_03);
  intentMap.set('02.02.02 Podpora - tv2go - yes - other - obstoječ naročnik (D02.02.02_SERVICE_NO_CLIENT)', D_02_02_02_SERVICE_NO_CLIENT);
  intentMap.set('02.02.02 Podpora - tv2go - yes - other - podpora (D02.02.02_SERVICE_NO_SUPPORT)', D_02_02_02_SERVICE_NO_SUPPORT);
  intentMap.set('02.02.02 Podpora - tv2go - yes - other - zaključek (D02.02.02_SERVICE_NO_FINISH)', D_02_02_02_SERVICE_NO_FINISH);
  intentMap.set('02.02.02 Podpora - tv2go - yes - telefon (D99.01.01)', D_99_01_01);
  intentMap.set('02.02.02 Podpora - eračun - no (D02.02.02_SERVICE_NO)', D_02_02_02_SERVICE_NO);
  intentMap.set('02.02.02 Podpora - eposlovanje - no (D02.02.02_SERVICE_NO)', D_02_02_02_SERVICE_NO);
  intentMap.set('02.02.02 Podpora - trajnik - yes - telefon (D99.01.01)', D_99_01_01);
  intentMap.set('02.02.02 Podpora - trajnik - yes - no service - obstoječ naročnik (D02.02.02_SERVICE_NO_CLIENT)', D_02_02_02_SERVICE_NO_CLIENT);
  intentMap.set('02.02.02 Podpora - trajnik - yes - no service - zaključek (D02.02.02_SERVICE_NO_FINISH)', D_02_02_02_SERVICE_NO_FINISH);
  intentMap.set('02.02.02 Podpora - trajnik - yes - no service - podpora (D02.02.02_SERVICE_NO_SUPPORT)', D_02_02_02_SERVICE_NO_SUPPORT);
  intentMap.set('02.02.02 Podpora - trajnik - yes - email (D99.01.02)', D_99_01_02);
  intentMap.set('02.02.02 Podpora - trajnik - yes - klepet (D99.01.03)', D_99_01_03);
  intentMap.set('02.02.02 Podpora - trajnik - no (D02.02.02_SERVICE_NO)', D_02_02_02_SERVICE_NO);
  intentMap.set('02.02.02 Podpora - horizont - no (D02.02.02_SERVICE_NO)', D_02_02_02_SERVICE_NO);
  intentMap.set('02.02.02 Podpora - horizont - yes - telefon (D99.01.01)', D_99_01_01);
  intentMap.set('02.02.02 Podpora - horizont - yes - other - podpora (D02.02.02_SERVICE_NO_SUPPORT)', D_02_02_02_SERVICE_NO_SUPPORT);
  intentMap.set('02.02.02 Podpora - horizont - yes - other - zaključek (D02.02.02_SERVICE_NO_FINISH)', D_02_02_02_SERVICE_NO_FINISH);
  intentMap.set('02.02.02 Podpora - horizont - yes - other - obstoječ naročnik (D02.02.02_SERVICE_NO_CLIENT)', D_02_02_02_SERVICE_NO_CLIENT);
  intentMap.set('02.02.02 Podpora - horizont - yes - email (D99.01.02)', D_99_01_02);
  intentMap.set('02.02.02 Podpora - horizont - yes - klepet (D99.01.03)', D_99_01_03);
  intentMap.set('02.02.02 Podpora - STORITEV - no - zaključek (D99.03.01)', D_99_03_01);
  intentMap.set('02.02.02 Podpora - STORITEV - no - obstoječ naročnik (D02.02)', D_02_02);
  intentMap.set('02.02.02 Podpora - STORITEV - no - podpora (D02.02.02)', D_02_02_02);
  intentMap.set('02.02.03 > yes - klepet (D99.01.03)', D_99_01_03);
  intentMap.set('02.02.03 > yes - nič - splošno (D02.02)', D_02_02);
  intentMap.set('02.02.03 > yes - nič - zaključek (D99.03.01)', D_99_03_01);
  intentMap.set('02.02.03 > yes - nič - klub (D02.02.03_NO)', D_02_02_03_NO);
  intentMap.set('02.02.03 > yes - email (D99.01.02)', D_99_01_02);
  intentMap.set('02.02.03 > yes - telefon (D99.01.01)', D_99_01_01);
  intentMap.set('02.02.03 > no - naročilo storitev - yes (D02.02)', D_02_02);
  intentMap.set('02.02.03 > no - naročilo storitev - no (D99.02.01)', D_99_02_01);
  intentMap.set('02.02.03 > no - naročilo izdelka - pomoč - nič - zaključek (D99.03.01)', D_99_03_01);
  intentMap.set('02.02.03 > no - naročilo izdelka - pomoč - nič - klub (D02.02.03_NO)', D_02_02_03_NO);
  intentMap.set('02.02.03 > no - naročilo izdelka - pomoč - nič - splošno (D02.02)', D_02_02);
  intentMap.set('02.02.03 > no - naročilo izdelka - pomoč - email (D99.01.02)', D_99_01_02);
  intentMap.set('02.02.03 > no - naročilo izdelka - pomoč - telefon (D99.01.01)', D_99_01_01);
  intentMap.set('02.02.03 > no - naročilo izdelka - pomoč - klepet (D99.01.03)', D_99_01_03);
  intentMap.set('02.02.03 > no - naročilo izdelka - brez naročila - klub (D02.02.03_NO)', D_02_02_03_NO);
  intentMap.set('02.02.03 > no - naročilo izdelka - brez naročila - zaključek (D99.03.01)', D_99_03_01);
  intentMap.set('02.02.03 > no - naročilo izdelka - brez naročila - splošno (D02.02)', D_02_02);
  intentMap.set('02.02.03 > no - splošno - yes (D02.02)', D_02_02);
  intentMap.set('02.02.03 > no - splošno - no (D99.02.01)', D_99_02_01);
  intentMap.set('DFI - telefon (D99.01.01)', D_99_01_01);
  intentMap.set('DFI - email (D99.01.02)', D_99_01_02);
  intentMap.set('DFI - klepet (D99.01.03)', D_99_01_03);
  intentMap.set('DFI - osnovni meni (D01)', D_01);
  intentMap.set('DFI - zaključek (D99.03.01)', D_99_03_01);

  agent.handleRequest(intentMap);
});

Solution

  • According to the documentation, the Dialogflow fulfillment does not supports agent versioning:

    The Dialogflow fulfillment library does not support versioning. If you need to use versioning, choose one of the following options:

    In addition, it's recommended to move your code from the inline editor to a webhook service (eg, creating your own custom handlers in Cloud Functions) when deploying to production.

    The inline editor is intended for simple fulfillment testing and prototyping. Once you are ready to build a production application, you should create a webhook service.


    On the other hand, the Dialogflow fulfillment library is not longer maintained by Google, and since you've enabled beta features and API, this might be running into compatibility issues, as the first library is not up to date. I would recommend you to disable it, unless you have a real good reason to do use it, such as using Knowledge connectors, which is a beta feature (November, 2021).