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, see the technical documentation for the add utterance, train, and training status APIs.

Prerequisites

  • Latest Visual Studio Community edition.
  • 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 this code. 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 C# solution including an example utterances.json file are available from the LUIS-Samples Github repository.

Create the project in Visual Studio

In Visual Studio, create a new Windows Classic Desktop Console app using the .Net Framework.

Visual Studio project type

Add the System.Web dependency

In the Visual Studio project, the project needs System.Web. In the Solution Explorer, right-click on References and select Add Reference.

Add System.web reference

Write the C# code

The Program.cs file should be:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace ConsoleApp3
{
    class Program
    {
        static void Main(string[] args)
        {
        }
    }
}

Add the System.IO and System.Net.Http dependencies.

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

Add the LUIS IDs and strings to the Program class.

// 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 authoring key with a valid key.
static string authoringKey = "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", authoringKey);
        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", authoringKey);
        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 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.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": []
    }
]

Mark the JSON file as content

In the Solution Explorer, right-click the utterances.json and select Properties. In the properties windows, mark the Build Action of Content, and the Copy to Output Directory of Copy Always.

Mark the JSON file as content

Add an utterance from the command line

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