0

I'm trying to use the Amazon in-built functionality for out-of-bound queries on my Custom Alexa skill. Alexa could use the "AMAZON.FallbackIntent" for such, I've found the basic setup here: https://developer.amazon.com/it/blogs/alexa/post/f6bb7c54-de0d-4abe-98c3-cf3fc75c32f8/how-to-add-fallbackintent-handling-to-your-alexa-skill

This ability is now available to all "en" locales, anyhow after numerous attempts I've set up an "en-GB.json" and "en-US.json" with the same structure as follows:

"intents": [
            {
                "name": "AMAZON.FallbackIntent",
                "samples": []
            },
            {
                "name": "AMAZON.CancelIntent",
                "samples": []
            },
            {
                "name": "AMAZON.HelpIntent",
                "samples": [
                    "I don't know",
                    "What can you do",
                    "What are you capable of",
                    "What is this",
                    "help"
                ]
            },
            {
                "name": "AMAZON.StopIntent",
                "samples": []
            },

All in-built intents work just fine with the exception of the Fallback one. Here's a sample from the index.js snippets for the skill:

const Alexa = require('ask-sdk');
const dbHelper = require('./helpers/dbHelper');
const GENERAL_REPROMPT = "What would you like to do?";

const dynamoDBTableName = "movies";
const LaunchRequestHandler = 
{ 
    canHandle(handlerInput) {
        const request = handlerInput.requestEnvelope.request;
        return request.type === 'LaunchRequest';
    },
    handle(handlerInput) {
        const speechText = 'Welcome to the X ' + '&' + 'Y Customer Service, my name is Natalia, how may I help you today?';
        const repromptText = 'What would you like to do? You can say HELP to get available options';

        return handlerInput.responseBuilder
        .speak(speechText)
        .reprompt(repromptText)
        .getResponse();
    }
};
const InProgressAddMovieIntentHandler = {
canHandle(handlerInput) {
    const request = handlerInput.requestEnvelope.request;
    return request.type === 'IntentRequest' &&
    request.intent.name === 'AddMovieIntent' &&
    request.dialogState !== 'COMPLETED';
},
handle(handlerInput) {
    const currentIntent = handlerInput.requestEnvelope.request.intent;
    return handlerInput.responseBuilder
    .addDelegateDirective(currentIntent)
    .getResponse();
}
};
const AddMovieIntentHandler = {
canHandle(handlerInput) {
    return handlerInput.requestEnvelope.request.type === 'IntentRequest'
    && handlerInput.requestEnvelope.request.intent.name === 'AddMovieIntent';
},
async handle(handlerInput) {
    const {responseBuilder } = handlerInput;
    const userID = handlerInput.requestEnvelope.context.System.user.userId; 
    const slots = handlerInput.requestEnvelope.request.intent.slots;
    const movieName = slots.MovieName.value;
    return dbHelper.addMovie(movieName, userID)
    .then((data) => {
        const speechText = `You have added movie ${movieName}. You can say add to add another one or remove to remove movie`;
        return responseBuilder
        .speak(speechText)
        .reprompt(GENERAL_REPROMPT)
        .getResponse();
    })
    .catch((err) => {
        console.log("Error occured while saving movie", err);
        const speechText = "we cannot save your movie right now. Try again!"
        return responseBuilder
        .speak(speechText)
        .getResponse();
    })
},
};
const GetMoviesIntentHandler = {
canHandle(handlerInput) {
    return handlerInput.requestEnvelope.request.type === 'IntentRequest'
    && handlerInput.requestEnvelope.request.intent.name === 'GetMoviesIntent';
},
async handle(handlerInput) {
    const {responseBuilder } = handlerInput;
    const userID = handlerInput.requestEnvelope.context.System.user.userId; 
    return dbHelper.getMovies(userID)
    .then((data) => {
        var speechText = "Your movies are "
        if (data.length == 0) {
        speechText = "You do not have any favourite movie yet, add movie by saving add moviename "
        } else {
        speechText += data.map(e => e.movieTitle).join(", ")
        }
        return responseBuilder
        .speak(speechText)
        .reprompt(GENERAL_REPROMPT)
        .getResponse();
    })
    .catch((err) => {
        const speechText = "we cannot get your movie right now. Try again!"
        return responseBuilder
        .speak(speechText)
        .getResponse();
    })
}
};
const InProgressRemoveMovieIntentHandler = {
canHandle(handlerInput) {
    const request = handlerInput.requestEnvelope.request;
    return request.type === 'IntentRequest' &&
    request.intent.name === 'RemoveMovieIntent' &&
    request.dialogState !== 'COMPLETED';
},
handle(handlerInput) {
    const currentIntent = handlerInput.requestEnvelope.request.intent;
    return handlerInput.responseBuilder
    .addDelegateDirective(currentIntent)
    .getResponse();
}
};
const RemoveMovieIntentHandler = {
canHandle(handlerInput) {
    return handlerInput.requestEnvelope.request.type === 'IntentRequest'
    && handlerInput.requestEnvelope.request.intent.name === 'RemoveMovieIntent';
}, 
handle(handlerInput) {
    const {responseBuilder } = handlerInput;
    const userID = handlerInput.requestEnvelope.context.System.user.userId; 
    const slots = handlerInput.requestEnvelope.request.intent.slots;
    const movieName = slots.MovieName.value;
    return dbHelper.removeMovie(movieName, userID)
    .then((data) => {
        const speechText = `You have removed movie with name ${movieName}, you can add another one by saying add`
        return responseBuilder
        .speak(speechText)
        .reprompt(GENERAL_REPROMPT)
        .getResponse();
    })
    .catch((err) => {
        const speechText = `You do not have movie with name ${movieName}, you can add it by saying add`
        return responseBuilder
        .speak(speechText)
        .reprompt(GENERAL_REPROMPT)
        .getResponse();
    })
}
};
const HelpIntentHandler = {
canHandle(handlerInput) {
    return handlerInput.requestEnvelope.request.type === 'IntentRequest'
    && handlerInput.requestEnvelope.request.intent.name === 'AMAZON.HelpIntent';
},
handle(handlerInput) {
    const speechText = 'You can access information about products, place an order or raise and issue';
    const repromptText = 'What would you like to do? You can say HELP to hear the options again';

    return handlerInput.responseBuilder
    .speak(speechText)
    .reprompt(repromptText)
    .getResponse();
},
};
const CancelAndStopIntentHandler = {
canHandle(handlerInput) {
    return handlerInput.requestEnvelope.request.type === 'IntentRequest'
    && (handlerInput.requestEnvelope.request.intent.name === 'AMAZON.CancelIntent'
        || handlerInput.requestEnvelope.request.intent.name === 'AMAZON.StopIntent');
},
handle(handlerInput) {
    const speechText = 'Goodbye! See you later!';

    return handlerInput.responseBuilder
    .speak(speechText)
    .getResponse();
},
};
const FallbackHandler = {
canHandle(handlerInput) {
    const request = handlerInput.requestEnvelope.request;
    return request.type === 'IntentRequest' && request.intent.name === 'AMAZON.FallbackIntent';
},
handle(handlerInput) {
    const output = "I am sorry, I can't help with that";
    return handlerInput.responseBuilder
    .speak(output)
    .reprompt(output)
    .getResponse();
},
};
const SessionEndedRequestHandler = {
canHandle(handlerInput) {
    return handlerInput.requestEnvelope.request.type === 'SessionEndedRequest';
},
handle(handlerInput) {
    console.log(`Session ended with reason: ${handlerInput.requestEnvelope.request.reason}`);

    return handlerInput.responseBuilder.getResponse();
},
};
const ErrorHandler = {
canHandle() {
    return true;
},
handle(handlerInput, error) {
    console.log(`Error handled: ${error.message}`);

    return handlerInput.responseBuilder
    .speak('Sorry, I can\'t understand the command. Please say again.')
    .reprompt('Sorry, I can\'t understand the command. Please say again.')
    .getResponse();
},
};
const skillBuilder = Alexa.SkillBuilders.standard();
exports.handler = skillBuilder
.addRequestHandlers(
    LaunchRequestHandler,
    InProgressAddMovieIntentHandler,
    AddMovieIntentHandler,
    GetMoviesIntentHandler,
    InProgressRemoveMovieIntentHandler,
    RemoveMovieIntentHandler,
    HelpIntentHandler,
    CancelAndStopIntentHandler,
    SessionEndedRequestHandler,
    FallbackHandler
)
.addErrorHandlers(ErrorHandler)
.withTableName(dynamoDBTableName)
.withAutoCreateTable(true)
.lambda();

After launching the skill, the skill executes the:

speechText

for which I try saying what's the weather, the skill ignores that input and just executes the:

repromptText

and then if I ask for weather again it just closes the skill...

How could I make this work for either english language models?

Mikev
  • 2,012
  • 1
  • 15
  • 27
G Tuli
  • 3
  • 7

1 Answers1

1

Your session is getting ended right after you send your speechText in LanunchRequest handler. As the session is ended, Alexa doesn't know that you are trying to invoke your skill by saying "whats the weather ?" and hence doesn't call your fallback handler.

Use withShouldEndSession(false) and it will keep the session opened and any incorrect input will land up in your Fallback handler.

Below code should work.

const LaunchRequestHandler = 
{ 
    canHandle(handlerInput) {
        const request = handlerInput.requestEnvelope.request;
        return request.type === 'LaunchRequest';
    },
    handle(handlerInput) {
        const speechText = 'Welcome to the X ' + '&' + 'Y Customer Service, my name is Natalia, how may I help you today?';
        const repromptText = 'What would you like to do? You can say HELP to get available options';

        return handlerInput.responseBuilder
        .speak(speechText)
        .withShouldEndSession(false) 
        .reprompt(repromptText)
        .getResponse();
    }
};
Shailesh Pratapwar
  • 4,054
  • 3
  • 35
  • 46
  • Unfortunately, this does not work. The Device Log still says the intent is the Fallback one, but It does not trigger it on the lambda – G Tuli Feb 19 '19 at 08:30
  • Since, you are able to see the device log for that invocation, this means your fallback handler was detected and used. Can you try adding some console log statements and also try removing reprompt as well in fallback handler ? – Shailesh Pratapwar Feb 19 '19 at 08:40
  • Just added the policy for Clouwatch on my lambda, I've added console.logs for the launch request as well, but nothing inside the FallbackHandler gets triggered – G Tuli Feb 19 '19 at 10:03