Tutorial: Add utterances app using Node.js

In this tutorial, you write a program to add an utterance to an intent using the Authoring APIs in Node.js.

  • Create Visual Studio console project
  • Add method to call LUIS API to add utterance and train app
  • Add JSON file with example utterances for BookFlight intent
  • Run console and see training status for utterances

For more information, refer to the technical documentation for the add example utterance to intent, train, and training status APIs.

For this article, you need a free LUIS account in order to author your LUIS application.

Prerequisites

  • Latest Node.js with NPM.
  • NPM dependencies for this article: request, request-promise, fs-extra.
  • [Recommended] Visual Studio Code for IntelliSense and debugging.
  • Your LUIS authoring key. You can find this key under Account Settings in the LUIS website.
  • Your existing LUIS application ID. The application ID is shown in the application dashboard. The LUIS application with the intents and entities used in the utterances.json file must exist prior to running the code in add-utterances.js. The code in this article will not create the intents and entities. It will only add the utterances for existing intents and entities.
  • The version ID within the application that receives the utterances. The default ID is "0.1"
  • Create a new file named add-utterances.js project in VSCode.

Note

The complete add-utterances.js file is available from the LUIS-Samples Github repository.

Write the Node.js code

Add the NPM dependencies to the file.

// NPM Dependencies
var rp = require('request-promise');
var fse = require('fs-extra');
var path = require('path');

Add the LUIS constants to the file. Copy the following code and change to your authoring key, application ID, and version ID.

// To run this sample, change these constants.

// Authoring key, available in luis.ai under Account Settings
const LUIS_authoringKey = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx";
// ID of your LUIS app to which you want to add an utterance
const LUIS_appId = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx";
// The version number of your LUIS app
const LUIS_versionId = "0.1";

Add the name and location of the upload file containing your utterances.

// uploadFile is the file containing JSON for utterance(s) to add to the LUIS app.
// The contents of the file must be in this format described at: https://aka.ms/add-utterance-json-format
const uploadFile = "./utterances.json"

Add the variables that hold the command-line values.

// Variables holding command line arguments
var trainAfterAdd = false;
var requestTrainingStatus = false;


// Parse command line arguments:
// -train to train based on the utterances in uploadFile
// -status to get training status
if (process.argv.length >= 3) {
    if (process.argv[2] === "-train") {
        trainAfterAdd = true;
    } else if (process.argv[2] === "-status") {
        requestTrainingStatus = true;
    }
}

Add the function sendUtteranceToApi to send and receive HTTP calls.

// Send JSON as the body of the POST request to the API
var sendUtteranceToApi = async (options) => {
    try {

        var response; 
        if (options.method === 'POST') {
            response = await rp.post(options);
        } else if (options.method === 'GET') {
            response = await rp.get(options);
        }
        
        return { request: options.body, response: response };

    } catch (err) {
        throw err;
    }
}

Add the configuration JSON object used by the addUtterance function.

// upload configuration 
var configAddUtterance = {
    LUIS_authoringKey: LUIS_authoringKey,
    LUIS_appId: LUIS_appId,
    LUIS_versionId: LUIS_versionId,
    inFile: path.join(__dirname, uploadFile),
    uri: "https://westus.api.cognitive.microsoft.com/luis/api/v2.0/apps/{appId}/versions/{versionId}/examples".replace("{appId}", LUIS_appId).replace("{versionId}", LUIS_versionId)
};

Add the function addUtterance manage the API request and response used by SendUtteranceToApp.

// Call add-utterance
var addUtterance = async (config) => {

    try {

        // Extract the JSON for the request body
        // The contents of the file to upload need to be in this format described in the comments above.
        var jsonUtterance = await fse.readJson(config.inFile);

        // Add an utterance
        var utterancePromise = sendUtteranceToApi({
            uri: config.uri,
            method: 'POST',
            headers: {
                'Ocp-Apim-Subscription-Key': config.LUIS_authoringKey
            },
            json: true,
            body: jsonUtterance
        });

        let results = await utterancePromise;
        let response = await fse.writeJson(config.inFile.replace('.json', '.results.json'), results);

        console.log("Add utterance done");

    } catch (err) {
        console.log(`Error adding utterance:  ${err.message} `);
        //throw err;
    }

}

Add the configuration JSON object used by the train function.

// training configuration 
var configTrain = {
    LUIS_authoringKey: LUIS_authoringKey,
    LUIS_appId: LUIS_appId,
    LUIS_versionId: LUIS_versionId,
    uri: "https://westus.api.cognitive.microsoft.com/luis/api/v2.0/apps/{appId}/versions/{versionId}/train".replace("{appId}", LUIS_appId).replace("{versionId}", LUIS_versionId),
    method: 'POST', // POST to request training, GET to get training status
};

Add the function train to start the training process.

// Call train
var train = async (config) => {

    try {

        var trainingPromise = sendUtteranceToApi({
            uri: config.uri,
            method: config.method, // Use POST to request training, GET to get training status 
            headers: {
                'Ocp-Apim-Subscription-Key': config.LUIS_authoringKey
            },
            json: true,
            body: null      // The body can be empty for a training request
        });

        let results = await trainingPromise;
        
        if (config.method === 'POST') {
            let response = await fse.writeJson(path.join(__dirname, 'training-results.json'), results);        
            console.log(`Training request sent. The status of the training request is: ${results.response.status}.`);
        } else if (config.method === 'GET') {
            let response = await fse.writeJson(path.join(__dirname, 'training-status-results.json'), results);
            console.log(`Training status saved to file. `);
        }
        
    } catch (err) {
        console.log(`Error in Training:  ${err.message} `);
        // throw err;
    }

}

Add the code that chooses which action to take (add utterance or train) based on the command-line variables.

// MAIN
if (trainAfterAdd) {
    // Add the utterance to the LUIS app and train
    addUtterance(configAddUtterance)
        .then(() => {
            console.log("Add utterance complete. About to request training.");
            configTrain.method = 'POST';
            return train(configTrain, false);
        }).then(() => {
            console.log("Training process complete. Requesting training status.");
            configTrain.method = 'GET';
            return train(configTrain, true);
        }).then(() => {
            console.log("process done");
        });
} else if (requestTrainingStatus) {
    // Get the training status
    configTrain.method = 'GET';
    train(configTrain)
        .then(() => {
            console.log("Requested training status.");
        });
}
else {
    // Add the utterance to the LUIS app without training it afterwards
    addUtterance(configAddUtterance)
        .then(() => {
            console.log("Add utterance complete.");
        });

}

Specify utterances to add

Create and edit the file utterances.json to specify the array of utterances you want to add to the LUIS app. The intent and entities must already be in the LUIS app.

Note

The LUIS application with the intents and entities used in the utterances.json file must exist prior to running the code in add-utterances.js. The code in this article will not create the intents and entities. It will only add the utterances for existing intents and entities.

The text field contains the text of the utterance. The intentName field must correspond to the name of an intent in the LUIS app. The entityLabels field is required. If you don't want to label any entities, provide an empty list as shown in the following example.

If the entityLabels list is not empty, the startCharIndex and endCharIndex need to mark the entity referred to in the entityName field. Both indexes are zero-based counts meaning 6 in the top example refers to the "S" of Seattle and not the space before the capital S.

[
    {
        "text": "go to Seattle",
        "intentName": "BookFlight",
        "entityLabels": [
            {
                "entityName": "Location::LocationTo",
                "startCharIndex": 6,
                "endCharIndex": 12
            }
        ]
    },
    {
        "text": "book a flight",
        "intentName": "BookFlight",
        "entityLabels": []
    }
]

Add an utterance from the command-line

Run the application from a command-line with Node.js.

Calling add-utterance with no arguments adds an utterance to the app, without training it.

> node add-utterances.js

This sample creates a file with the results.json that contains the results from calling the add utterances API. The response field is in this format for utterances that was added. The hasError is false, indicating the utterance was added.

    "response": [
        {
            "value": {
                "UtteranceText": "go to seattle",
                "ExampleId": -5123383
            },
            "hasError": false
        },
        {
            "value": {
                "UtteranceText": "book a flight",
                "ExampleId": -169157
            },
            "hasError": false
        }
    ]

Add an utterance and train from the command-line

Call add-utterance with the -train argument to send a request to train, and subsequently request training status. The status is queued immediately after training begins. Status details are written to a file.

> node add-utterances.js -train

Note

Duplicate utterances aren't added again, but don't cause an error. The response contains the ID of the original utterance.

When you call the sample with the -train argument, it creates a training-results.json file indicating the request to train the LUIS app was successfully queued.

The following shows the result of a successful request to train:

{
    "request": null,
    "response": {
        "statusId": 9,
        "status": "Queued"
    }
}

After the request to train is queued, it can take a moment to complete training.

Get training status from the command line

Call the sample with the -status argument to check the training status and write status details to a file.

> node add-utterances.js -status

Clean up resources

When you are done with the tutorial, remove Visual Studio and the console application if you don't need them anymore.

Next steps