Add utterances to a LUIS app using C#

Programmatically add utterances to your Language Understanding (LUIS) app and train it using the command line. For more information, refer to the technical documentation for the add utterance, train, and training status APIs.

Prerequisites

  • Latest Visual Studio Community edition.
  • Your LUIS programmatic key. You can find this key under Account Settings in https://www.luis.ai.
  • 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.cs. The code in this article does not create the intents and entities. It adds 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.cs project in VSCode.

Note

The complete add-utterances.cs file and an example utterances.json file are available from the LUIS-Samples Github repository.

Write the C# code

Create AddUtterances namespace and .Net dependencies. Add the following code inside the namespace.

using System;
using System.IO;
using System.Net.Http;
using System.Text;
using System.Threading.Tasks;
using System.Xml.Linq;

namespace AddUtterances {

    class Program {

        // Add code here

    }
}

Add the LUIS IDs and strings.


// NOTE: Replace this example LUIS application ID with the ID of your LUIS application.
static string appID = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx";

// NOTE: Replace this example LUIS application version number with the version number of your LUIS application.
static string appVersion = "0.1";

// NOTE: Replace this example LUIS programmatic key with a valid key.
static string key = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx";

static string host = "https://westus.api.cognitive.microsoft.com";
static string path = "/luis/api/v2.0/apps/" + appID + "/versions/" + appVersion + "/";

static string usage = @"Usage:
add-utterances <input file>
add-utterances -train <input file>
add-utterances -status

The contents of <input file> must be in the format described at: https://aka.ms/add-utterance-json-format
";

Add the JsonPrettyPrint method.


static string JsonPrettyPrint(string json)
{
    if (string.IsNullOrEmpty(json))
        return string.Empty;

    json = json.Replace(Environment.NewLine, "").Replace("\t", "");

    StringBuilder sb = new StringBuilder();
    bool quote = false;
    bool ignore = false;
    int offset = 0;
    int indentLength = 3;

    foreach (char ch in json)
    {
        switch (ch)
        {
            case '"':
                if (!ignore) quote = !quote;
                break;
            case '\'':
                if (quote) ignore = !ignore;
                break;
        }

        if (quote)
            sb.Append(ch);
        else
        {
            switch (ch)
            {
                case '{':
                case '[':
                    sb.Append(ch);
                    sb.Append(Environment.NewLine);
                    sb.Append(new string(' ', ++offset * indentLength));
                    break;
                case '}':
                case ']':
                    sb.Append(Environment.NewLine);
                    sb.Append(new string(' ', --offset * indentLength));
                    sb.Append(ch);
                    break;
                case ',':
                    sb.Append(ch);
                    sb.Append(Environment.NewLine);
                    sb.Append(new string(' ', offset * indentLength));
                    break;
                case ':':
                    sb.Append(ch);
                    sb.Append(' ');
                    break;
                default:
                    if (ch != ' ') sb.Append(ch);
                    break;
            }
        }
    }

    return sb.ToString().Trim();
}

Add the GET request used to create utterances or start training.

async static Task<HttpResponseMessage> SendGet(string uri)
{
    using (var client = new HttpClient())
    using (var request = new HttpRequestMessage())
    {
        request.Method = HttpMethod.Get;
        request.RequestUri = new Uri(uri);
        request.Headers.Add("Ocp-Apim-Subscription-Key", key);
        return await client.SendAsync(request);
    }
}

Add the POST request used to create utterances or start training.

async static Task<HttpResponseMessage> SendPost(string uri, string requestBody)
{
    using (var client = new HttpClient())
    using (var request = new HttpRequestMessage())
    {
        request.Method = HttpMethod.Post;
        request.RequestUri = new Uri(uri);
        request.Content = new StringContent(requestBody, Encoding.UTF8, "text/json");
        request.Headers.Add("Ocp-Apim-Subscription-Key", key);
        return await client.SendAsync(request);
    }
}

Add the AddUtterances function.

async static Task AddUtterances(string input_file)
{
    string uri = host + path + "examples";
    string requestBody = File.ReadAllText(input_file);

    var response = await SendPost(uri, requestBody);
    var result = await response.Content.ReadAsStringAsync();
    Console.WriteLine("Added utterances.");
    Console.WriteLine(JsonPrettyPrint(result));
}

Add the Train function.

async static Task Train(string input_file)
{
    string uri = host + path + "train";
    string requestBody = File.ReadAllText(input_file);

    var response = await SendPost(uri, requestBody);
    var result = await response.Content.ReadAsStringAsync();
    Console.WriteLine("Sent training request.");
    Console.WriteLine(JsonPrettyPrint(result));
    await Status();
}

Add the Status function.

async static Task Status()
{
    var response = await SendGet(host + path + "train");
    var result = await response.Content.ReadAsStringAsync();
    Console.WriteLine("Requested training status.");
    Console.WriteLine(JsonPrettyPrint(result));
}

To manage arguments, add the main code.

static void Main(string[] args)
{
    if (args.Length < 1)
    {
        Console.WriteLine(usage);
    }
    else
    {
        if (true == String.Equals(args[0], "-train", StringComparison.OrdinalIgnoreCase))
        {
            if (args.Length > 1)
            {
                Train(args[1]).Wait();
            }
            else
            {
                Console.WriteLine(usage);
            }
        }
        else if (true == String.Equals(args[0], "-status", StringComparison.OrdinalIgnoreCase))
        {
            Status().Wait();
        }
        else
        {
            AddUtterances(args[0]).Wait();
        }
    }
}

Specify utterances to add

Create and edit the file utterances.json to specify the entities 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.cs. The code in this article does not create the intents and entities. It only adds 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

In the Visual Studio project, add a reference to System.Web.

Build and run the application from a command line with C# from the /bin/Debug directory. Make sure the utterances.json file is also in this directory.

Calling add-utterances.cs with only the utterance.json as an argument adds but does not train LUIS on the new utterances.

ConsoleApp\bin\Debug> ConsoleApp1.exe utterances.json

This command-line displays the results of 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.

ConsoleApp\bin\Debug> ConsoleApp1.exe -train utterances.json

Note

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

The following JSON 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 app with the -status argument to check the training status and display status details.

ConsoleApp\bin\Debug> ConsoleApp1.exe -status
Requested training status.
[
   {
      "modelId": "eb2f117c-e10a-463e-90ea-1a0176660acc",
      "details": {
         "statusId": 0,
         "status": "Success",
         "exampleCount": 33,
         "trainingDateTime": "2017-11-20T18:09:11Z"
      }
   },
   {
      "modelId": "c1bdfbfc-e110-402e-b0cc-2af4112289fb",
      "details": {
         "statusId": 0,
         "status": "Success",
         "exampleCount": 33,
         "trainingDateTime": "2017-11-20T18:09:11Z"
      }
   },
   {
      "modelId": "863023ec-2c96-4d68-9c44-34c1cbde8bc9",
      "details": {
         "statusId": 0,
         "status": "Success",
         "exampleCount": 33,
         "trainingDateTime": "2017-11-20T18:09:11Z"
      }
   },
   {
      "modelId": "82702162-73ba-4ae9-a6f6-517b5244c555",
      "details": {
         "statusId": 0,
         "status": "Success",
         "exampleCount": 33,
         "trainingDateTime": "2017-11-20T18:09:11Z"
      }
   },
   {
      "modelId": "37121f4c-4853-467f-a9f3-6dfc8cad2763",
      "details": {
         "statusId": 0,
         "status": "Success",
         "exampleCount": 33,
         "trainingDateTime": "2017-11-20T18:09:11Z"
      }
   },
   {
      "modelId": "de421482-753e-42f5-a765-ad0a60f50d69",
      "details": {
         "statusId": 0,
         "status": "Success",
         "exampleCount": 33,
         "trainingDateTime": "2017-11-20T18:09:11Z"
      }
   },
   {
      "modelId": "80f58a45-86f2-4e18-be3d-b60a2c88312e",
      "details": {
         "statusId": 0,
         "status": "Success",
         "exampleCount": 33,
         "trainingDateTime": "2017-11-20T18:09:11Z"
      }
   },
   {
      "modelId": "c9eb9772-3b18-4d5f-a1e6-e0c31f91b390",
      "details": {
         "statusId": 0,
         "status": "Success",
         "exampleCount": 33,
         "trainingDateTime": "2017-11-20T18:09:11Z"
      }
   },
   {
      "modelId": "2afec2ff-7c01-4423-bb0e-e5f6935afae8",
      "details": {
         "statusId": 0,
         "status": "Success",
         "exampleCount": 33,
         "trainingDateTime": "2017-11-20T18:09:11Z"
      }
   },
   {
      "modelId": "95a81c87-0d7b-4251-8e07-f28d180886a1",
      "details": {
         "statusId": 0,
         "status": "Success",
         "exampleCount": 33,
         "trainingDateTime": "2017-11-20T18:09:11Z"
      }
   }
]

Next steps