Quickstart: Change model using Java

In this quickstart, add example utterances to a Travel Agent app and train the app. Example utterances are conversational user text mapped to an intent. By providing example utterances for intents, you teach LUIS what kinds of user-supplied text belongs to which intent.

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

For this article, you need a free LUIS account.

Prerequisites

  • Your LUIS authoring key.
  • Import the TravelAgent app from the cognitive-services-language-understanding GitHub repository.
  • The LUIS application ID for the imported TravelAgent app. The application ID is shown in the application dashboard.
  • The utterances.json file containing the example utterances to import.
  • The version ID within the application that receives the utterances. The default ID is "0.1".

Note

The complete solution including an example utterances.json file are available from the cognitive-services-language-understanding GitHub repository.

Example utterances JSON file

The example utterances file, utterances.json, follows a specific format.

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

If the entityLabels array is not empty, the startCharIndex and endCharIndex need to mark the entity referred to in the entityName field. The index is zero-based, meaning 6 in the top example refers to the "S" of Seattle and not the space before the capital S. If you begin or end the label at a space in the text, the API call to add the utterances fails.

[
  {
    "text": "go to Seattle today",
    "intentName": "BookFlight",
    "entityLabels": [
      {
        "entityName": "Location::LocationTo",
        "startCharIndex": 6,
        "endCharIndex": 12
      }
    ]
  },
  {
    "text": "purple dogs are difficult to work with",
    "intentName": "BookFlight",
    "entityLabels": []
  }
]

Create quickstart code

  1. Add the Java dependencies to the file named AddUtterances.java.

    import java.io.*;
    import java.net.*;
    import java.util.*;
    import com.google.gson.*;
    
  2. Create AddUtterances class. This class contains all code snippets that follow.

    public class AddUtterances {
        // Insert code here
    }
    
  3. Add the LUIS constants to the class. Copy the following code and change to your authoring key, application ID, and version ID.

       // Enter information about your LUIS application and key below
       static final String LUIS_APP_ID      = "YOUR-APP-ID";
       static final String LUIS_APP_VERSION = "0.1";
       static final String LUIS_AUTHORING_ID  = "YOUR-AUTHORING-KEY";
    
       // Update the host if your LUIS subscription is not in the West US region
       static final String LUIS_BASE        = "https://westus.api.cognitive.microsoft.com";
    
       // File names for utterance and result files
       static final String UTTERANCE_FILE   = "./utterances.json";
    
       static final String UTF8 = "UTF-8";
    
  4. Add the method to call into the LUIS API to the AddUtterances class.

    //
    // LUIS Client class
    // Contains the functionality for adding utterances to a LUIS application
    //
    static class LuisClient{
    
        private final String PATH = "/luis/api/v2.0/apps/{app_id}/versions/{app_version}";
    
        // endpoint method names
        private final String TRAIN    = "/train";
        private final String EXAMPLES = "/examples";
        private final String APP_INFO = "/";
    
        // HTTP verbs
        private final String GET  = "GET";
        private final String POST = "POST";
    
        // Null string value for use in resolving method calls
        private final String NO_DATA = null;
    
        // Member variables
        private final String key;
        private final String host;
        private final String path;
    
        LuisClient(String host, String app_id, String app_version, String key) throws Exception {
            this.path = PATH.replace("{app_id}", app_id).replace("{app_version}", app_version);
            this.host = host;
            this.key  = key;
    
            // Test configuration by getting the application info
            // {
            //    "version": "0.1",
            //    "createdDateTime": "2018-08-21T13:24:18Z",
            //    "lastModifiedDateTime": "2018-08-24T15:29:32Z",
            //    "intentsCount": 5,
            //    "entitiesCount": 8,
            //    "endpointHitsCount": 0,
            //    "trainingStatus": "InProgress"
            //}
            this.get(APP_INFO).raiseForStatus();
        }
    
        private LuisResponse call(String endpoint, String method, byte[] data) throws Exception {
    
            // initialize HTTP connection
            URL url = new URL(this.host + this.path + endpoint);
    
            HttpURLConnection conn = (HttpURLConnection)url.openConnection();
            conn.setRequestMethod(method);
            conn.setRequestProperty("Ocp-Apim-Subscription-Key", key);
    
            // handle POST request
            if (method.equals(POST)) {
                if (data == null)
                        data = new byte[]{};    // make zero-length body for POST w/o data
                conn.setDoOutput(true);
                conn.setRequestProperty("Content-Type", "application/json");
                conn.setRequestProperty("Content-Length", Integer.toString(data.length));
                try (OutputStream ostream = conn.getOutputStream()) {
                    ostream.write(data, 0, data.length);
                }
            }
    
            // Get response from API call.  If response is an HTTP error, the JSON
            // response is on the error string.  Otherwise, it's on the input string.
            InputStream stream;
            try {
                stream = conn.getInputStream();
            } catch (IOException ex) {
                stream = conn.getErrorStream();
            }
            String body = new Scanner(stream, UTF8).useDelimiter("\\A").next();
    
            return new LuisResponse(body, conn.getResponseCode(), conn.getResponseMessage());
    
        }
    
        // Overload of call() with String data paramater
        private LuisResponse call(String endpoint, String method, String data) throws Exception {
            byte[] bytes = null;
            if (data != null)
                bytes = data.getBytes(UTF8);
            return call(endpoint, method, bytes);
        }
    
        // Overload of call() with InputStream data paramater
        private LuisResponse call(String endpoint, String method, InputStream stream) throws Exception {
            String data = new Scanner(stream, UTF8).useDelimiter("\\A").next();
            return call(endpoint, method, data);
        }
    
        // Shortcut for GET requests
        private LuisResponse get(String endpoint) throws Exception {
            return call(endpoint, GET, NO_DATA);
        }
    
        // Shortcut for POST requests -- InputStream data
        private LuisResponse post(String endpoint, InputStream data) throws Exception {
            return call(endpoint, POST, data);
        }
    
        // Shortcut for POST requests -- no data
        private LuisResponse post(String endpoint) throws Exception {
            return call(endpoint, POST, NO_DATA);
        }
    
        // Call to add utterances
        public LuisResponse addUtterances(String filename) throws Exception {
            try (FileInputStream stream = new FileInputStream(filename)) {
                return post(EXAMPLES, stream);
            }
        }
    
        public LuisResponse train() throws Exception {
            return post(TRAIN);
        }
    
        public LuisResponse status() throws Exception {
            return get(TRAIN);
        }
    
    }
    
  5. Add the method for the HTTP response from the LUIS API to the AddUtterances class.

    //
    // LUIS Response class
    // Represents a response from the LUIS client.  All methods return
    // the instance so method calls can be chained.
    //
    static class LuisResponse {
    
        private final String    body;
        private final int       status;
        private final String    reason;
        private JsonElement     data;
    
        LuisResponse(String body, int status, String reason) {
            JsonParser parser = new JsonParser();
            try {
                this.data = parser.parse(body);
            }
            catch (JsonSyntaxException ex) {
                this.data = parser.parse("{ \"message\": \"Invalid JSON response\" }");
            }
            this.body   = new GsonBuilder().setPrettyPrinting().create().toJson(data);
            this.status = status;
            this.reason = reason;
            System.out.println(this.body);
        }
    
        LuisResponse raiseForStatus() throws StatusException {
            if (this.status < 200 || this.status > 299) {
                throw new StatusException(this);
            }
            return this;
        }
    }
    
  6. Add exception handling to the AddUtterances class.

    // LUIS Status Exception class
    // Represents an exception raised by the LUIS client for HTTP status errors
    // Includes details extracted from the JSON response and the HTTP status
    //
    static class StatusException extends Exception {
    
        private String details = "";
        private final int status;
    
        StatusException(LuisResponse response) {
            super(String.format("%d %s", response.status, response.reason));
            JsonObject jsonInfo = (JsonObject)response.data;
            if (jsonInfo.has("error"))
                jsonInfo = (JsonObject)jsonInfo.get("error");
            if (jsonInfo.has("message"))
                this.details = jsonInfo.get("message").getAsString();
            this.status = response.status;
        }
    
        String getDetails() {
            return this.details;
        }
    
        int getStatus() {
            return this.status;
        }
    
    }
    
    static void printExceptionMsg(Exception ex) {
        System.out.println(String.format("%s: %s",
                ex.getClass().getSimpleName(), ex.getMessage()));
    
        StackTraceElement caller = ex.getStackTrace()[1];
        System.out.println(String.format("    in %s (line %d?)",
                caller.getFileName(), caller.getLineNumber()));
        if (ex instanceof StatusException)
           System.out.println(((StatusException)ex).getDetails());
    }
    
  7. Add the main function to the AddUtterances class.

    // ------------------------------------------------------------------------
    //
    // Command-line entry point
    //
    public static void main(String[] args) {
    
        LuisClient luis = null;
    
        try {
            luis = new LuisClient(LUIS_BASE, LUIS_APP_ID,
                    LUIS_APP_VERSION,LUIS_AUTHORING_ID);
        } catch (Exception ex) {
            printExceptionMsg(ex);
            System.exit(0);
        }
    
        try {
    
            System.out.println("Adding utterance(s).");
            luis.addUtterances(UTTERANCE_FILE)
                    .raiseForStatus();
    
            System.out.println("Requesting training.");
            luis.train()
                    .raiseForStatus();
    
            System.out.println("Requested training. Requesting training status.");
            luis.status()
                    .raiseForStatus();
    
        } catch (Exception ex) {
            printExceptionMsg(ex);
        }
    }
    

Build code

Compile AddUtterance with the dependencies

> javac -classpath gson-2.8.2.jar AddUtterances.java

Run code

Calling AddUtterance with no arguments adds the LUIS utterances to the app, without training it.

> java -classpath .;gson-2.8.2.jar AddUtterances

This command-line displays the results of calling the add utterances API.

The response array for adding the example utterances indicates success or failure for each example utterance with the hasError property. The following JSON response shows both utterances were added successfully.

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

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

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

The following JSON shows the result of a successful request for training status. Each modelID is an intent. Each intent has to be trained on all the utterances to correctly identify utterances to do belong to the intent as well as utterances that do not belong to the intent.

[
    {
        "modelId": "0c694cf9-8c32-44b8-9ea0-3d30a7d901ca",
        "details": {
            "statusId": 3,
            "status": "InProgress",
            "exampleCount": 48
        }
    },
    {
        "modelId": "10e53836-ade4-494e-9531-3bd6a944c510",
        "details": {
            "statusId": 3,
            "status": "InProgress",
            "exampleCount": 48
        }
    },
    {
        "modelId": "21e48732-a512-4c33-b5ed-8ea629465269",
        "details": {
            "statusId": 3,
            "status": "InProgress",
            "exampleCount": 48
        }
    },
    {
        "modelId": "edee15b1-9999-45c2-bbab-591d3a643033",
        "details": {
            "statusId": 3,
            "status": "InProgress",
            "exampleCount": 48
        }
    },
    {
        "modelId": "aa78e06e-df81-4bb2-b2d9-a2fbb2f81c54",
        "details": {
            "statusId": 3,
            "status": "InProgress",
            "exampleCount": 48
        }
    },
    {
        "modelId": "e39bb7bd-b417-41a9-a24f-caf4c47fc62c",
        "details": {
            "statusId": 3,
            "status": "InProgress",
            "exampleCount": 48
        }
    },
    {
        "modelId": "3782eac7-db84-4d66-ba00-0598dffb48ee",
        "details": {
            "statusId": 3,
            "status": "InProgress",
            "exampleCount": 48
        }
    },
    {
        "modelId": "a941d926-cb0f-47a8-ab7e-deba4378b96f",
        "details": {
            "statusId": 3,
            "status": "InProgress",
            "exampleCount": 48
        }
    },
    {
        "modelId": "8137f40e-ce6d-40a5-881f-dfd46a05f7e0",
        "details": {
            "statusId": 3,
            "status": "InProgress",
            "exampleCount": 48
        }
    },
    {
        "modelId": "dc08f95a-58b4-4064-a210-03fe34f75a3c",
        "details": {
            "statusId": 3,
            "status": "InProgress",
            "exampleCount": 48
        }
    },
    {
        "modelId": "4fabdbed-5697-4562-8c7d-36e174efff2e",
        "details": {
            "statusId": 3,
            "status": "InProgress",
            "exampleCount": 48
        }
    }
]

Clean up resources

When you are done with the quickstart, remove all the files created in this quickstart.

Next steps