.lg file format

APPLIES TO: SDK v4

The .lg file describes language generation templates with entity references and their composition. This article covers the various concepts expressed with the .lg file format.

Special Characters

Comments

Use > to create a comment. All lines that have this prefix will be skipped by the parser.

> This is a comment.

Escape character

Use \ as an escape character.

# TemplateName
- You can say cheese and tomato \[toppings are optional\]

Arrays and objects

Create an array

To create an array, use the ${[object1, object2, ...]} syntax. For example, this expression:

${['a', 'b', 'c']}

Returns the array ['a', 'b', 'c'].

Create an object

To create an object, use the ${{key1:value1, key2:value2, ...}} syntax. For example, this expression:

${{user: {name: "Wilson", age: 27}}}

Returns the following JSON object:

{
  "user": {
    "name": "Wilson",
    "age": 27
  }
}

Templates

Templates are the core concept of the language generation system. Each template has a name and one of the following:

  • a list of one-of variation text values
  • a structured content definition
  • a collection of conditions, each with:

Template names

Template names are case-sensitive and can only contain letters, underscores, and numbers. The following is an example of a template named TemplateName.

# TemplateName

Templates can't start with a number, and any part of a template name split by . can't start with a number.

Template response variations

Variations are expressed as a Markdown list. You can prefix each variation using the -, ', or + character.

# Template1
- text variation 1
- text variation 2
- one
- two

# Template2
* text variation 1
* text variation 2

# Template3
+ one
+ two

Simple response template

A simple response template includes one or more variations of text that are used for composition and expansion. One of the variations provided will be selected at random by the LG library.

Here is an example of a simple template that includes two variations.

> Greeting template with two variations.
# GreetingPrefix
- Hi
- Hello

Conditional response template

Conditional response templates let you author content that's selected based on a condition. All conditions are expressed using adaptive expressions.

Important

Conditional templates can't be nested in a single conditional response template. Use composition in a structured response template to nest conditionals.

If-else template

The if-else template lets you build a template that picks a collection based on a cascading order of conditions. Evaluation is top-down and stops when a condition evaluates to true or the ELSE block is hit.

Conditional expressions are enclosed in braces ${}. Here's an example that shows a simple IF ELSE conditional response template definition.

> time of day greeting reply template with conditions.
# timeOfDayGreeting
- IF: ${timeOfDay == 'morning'}
    - good morning
- ELSE:
    - good evening

Here's another example that shows an if-else conditional response template definition. Note that you can include references to other simple or conditional response templates in the variation for any of the conditions.

# timeOfDayGreeting
- IF: ${timeOfDay == 'morning'}
    - ${morningTemplate()}
- ELSEIF: ${timeOfDay == 'afternoon'}
    - ${afternoonTemplate()}
- ELSE:
    - I love the evenings! Just saying. ${eveningTemplate()}

Switch template

The switch template lets you design a template that matches an expression's value to a CASE clause and produces output based on that case. Condition expressions are enclosed in braces ${}.

Here's how you can specify a SWITCH CASE DEFAULT block in LG.

# TestTemplate
- SWITCH: ${condition}
- CASE: ${case-expression-1}
    - output1
- CASE: ${case-expression-2}
    - output2
- DEFAULT:
   - final output

Here's a more complicated SWITCH CASE DEFAULT example:

> Note: Any of the cases can include reference to one or more templates.
# greetInAWeek
- SWITCH: ${dayOfWeek(utcNow())}
- CASE: ${0}
    - Happy Sunday!
-CASE: ${6}
    - Happy Saturday!
-DEFAULT:
    - ${apology-phrase()}, ${defaultResponseTemplate()}

Note

Like conditional templates, switch templates can't be nested.

Structured response template

Structured response templates let you define a complex structure that supports major LG functionality, like templating, composition, and substitution, while leaving the interpretation of the structured response up to the caller of the LG library.

For bot applications, we natively support:

  • activity definition
  • card definition

Read about structure response templates for more information.

Template composition and expansion

References to templates

Variation text can include references to another named template to aid with composition and resolution of sophisticated responses. References to other named templates are denoted using braces, such as ${<TemplateName>()}.

> Example of a template that includes composition reference to another template.
# GreetingReply
- ${GreetingPrefix()}, ${timeOfDayGreeting()}

# GreetingPrefix
- Hi
- Hello

# timeOfDayGreeting
- IF: ${timeOfDay == 'morning'}
    - good morning
- ELSEIF: ${timeOfDay == 'afternoon'}
    - good afternoon
- ELSE:
    - good evening

Calling the GreetingReply template can result in one of the following expansion resolutions:

Hi, good morning
Hi, good afternoon
Hi, good evening
Hello, good morning
Hello, good afternoon
Hello, good evening

Entities

When used directly within a one-of variation text, entity references are denoted by enclosing them in braces, such as ${entityName}, or without braces when used as a parameter.

Entities can be used as a parameter:

Using prebuilt functions in variations

Prebuilt functions supported by adaptive expressions can also be used inline in a one-of variation text to achieve even more powerful text composition. To use an expression inline, simply wrap it in braces.

# RecentTasks
- IF: ${count(recentTasks) == 1}
    - Your most recent task is ${recentTasks[0]}. You can let me know if you want to add or complete a task.
- ELSEIF: ${count(recentTasks) == 2}
    - Your most recent tasks are ${join(recentTasks, ', ', ' and ')}. You can let me know if you want to add or complete a task.
- ELSEIF: ${count(recentTasks) > 2}
    - Your most recent ${count(recentTasks)} tasks are ${join(recentTasks, ', ', ' and ')}. You can let me know if you want to add or complete a task.
- ELSE:
    - You don't have any tasks.

The example above uses the join prebuilt function to list all values in the recentTasks collection.

Given templates and prebuilt functions share the same invocation signature, a template name can't be the same as a prebuilt function name.

A template name shouldn't match a prebuilt function name. The prebuilt function takes precedence. To avoid such conflicts, you can prepend lg. when referencing your template name. For example:

> Custom length function with one parameter.
# length(a)
- This is use's customized length function

# myfunc1
> will call prebuilt function length, and return 2
- ${length('hi')}

# mufunc2
> this calls the lg template and output 'This is use's customized length function'
- ${lg.length('hi')}

Multiline text in variations

Each one-of variation can include multiline text enclosed in triple quotes.

# MultiLineExample
    - ```This is a multiline list
        - one
        - two
        ```
    - ```This is a multiline variation
        - three
        - four
    ```

Multiline variation can request template expansion and entity substitution by enclosing the requested operation in braces, ${}.

# MultiLineExample
    - ```
        Here is what I have for the order
        - Title: ${reservation.title}
        - Location: ${reservation.location}
        - Date/ time: ${reservation.dateTimeReadBack}
    ```

With multiline support, you can have the Language Generation sub-system fully resolve a complex JSON or XML (like SSML wrapped text to control bot's spoken reply).

Parametrization of templates

To aid with contextual reusability, templates can be parametrized. Different callers to the template can pass in different values for use in expansion resolution.

# timeOfDayGreetingTemplate (param1)
- IF: ${param1 == 'morning'}
    - good morning
- ELSEIF: ${param1 == 'afternoon'}
    - good afternoon
- ELSE:
    - good evening

# morningGreeting
- ${timeOfDayGreetingTemplate('morning')}

# timeOfDayGreeting
- ${timeOfDayGreetingTemplate(timeOfDay)}

Importing external references

You can split your language generation templates into separate files and reference a template from one file in another. You can use Markdown-style links to import templates defined in another file.

[Link description](filePathOrUri)

All templates defined in the target file will be pulled in. Ensure that your template names are unique (or namespaced with # \<namespace>.\<templatename>) across files being pulled in.

[Shared](../shared/common.lg)

Functions injected by LG

Adaptive expressions provide the ability to inject a custom set of functions. Read functions injected from the LG library for more information.

Options

Developers can set parser options to further customize how input is evaluated. Use the > !# notation to set parser options.

Important

The last setting found in the file trumps any prior setting found in the same document.

Strict option

Developers who don't want to allow a null result for a null evaluated result can implement the strict option. Below is an example of a simple strict option:

> !# @strict = true
# template
- hi

If the strict option is on, null errors will throw a friendly message.

# welcome
- hi ${name}

If name is null, the diagnostic would be 'name' evaluated to null. [welcome] Error occurred when evaluating '- hi ${name}'.. If strict is set to false or not set, a compatible result will be given. The above sample would produce hi null.

replaceNull option

Developers can create delegates to replace null values in evaluated expressions by using the replaceNull option:

> !# @replaceNull = ${path} is undefined

In the above example, the null input in the path variable would be replaced with ${path} is undefined. The following input, where user.name is null: :

hi ${user.name}

Would result in hi user.name is undefined.

lineBreakStyle option

Developers can set options for how the LG system renders line breaks using the lineBreakStyle option. Two modes are currently supported:

  • default: line breaks in multiline text create normal line breaks.
  • markdown: line breaks in multiline text will be automatically converted to two lines to create a newline

The example below shows how to set the lineBreakStyle option to markdown:

> !# @lineBreakStyle = markdown

Namespace option

You can register a namespace for the LG templates you want to export. If there is no namespace specified, the namespace will be set to the filename without an extension.

The example below shows how to set the namespace option to foo:

> !# @Namespace = foo

Exports option

You can specify a list of LG templates to export. The exported templates can be called like prebuilt functions.

The example below shows how to set the exports option to template1, template2:

> !# @Namespace = foo
> !# @Exports = template1, template2

# template1(a, b)
- ${a + b}

# template2(a, b)
- ${join(a, b)}

Use foo.template1(1,2), foo.template2(['a', 'b', 'c'], ',') to call these exported templates.

Cache scope

The cache scope options let you control when the LG evaluator reevaluates an expression it has seen before and when it stores and uses a cached result.

  • Global cache is effective in the life cycle of an evaluation. LG caches all evaluation results, and if the template name and parameters are the same, returns the result from the cache.
  • Local cache scope is the default. In the same layer, if the previous template has been called with the same template name and the same parameters, the cached result is directly returned.
  • None cache scope disables all cache scope, and each time returns the new result.

For examples, see the global and local cache scope examples.

> !# @cacheScope= global // global cache
> !# @cacheScope= local // local cache
> !# @cacheScope= none // none cache
> !# @cacheScope= xxx // fallback to local cache

Note that the cache scope option is case-insensitive.

> !# @cacheScope= global // ok
> !# @CACHESCOPE= global // ok
> !# @cachescope= global // ok

Note that cache scope follows the scope of the Microsoft Entrance .lg file.

Say you have two files: a.lg and b.lg, shown below:

a.lg

> !# @cacheScope= global
 [import](b.lg)

b.lg

> !# @cacheScope= none
# template1
- ${template2()} ${template2()}

# template2
- ${rand(1, 10000000)}

If you run the following code, you'll notice that template2 uses the cached result of the first evaluated result because of the global cache scope option in a.lg:

var templates = Templates.ParseFile("a.lg");
var result = templates.Evaluate("template1"); // the second "template2" would use the cache of the first evaluate result

Re-execute mark influence

If the template name ends with "!", the template forces re-execution. This result won't be added to the cache regardless of the cache scope.

Say you have following template:

# template2
- ${template1()} ${template1!()} ${template1()}

template1!() fires and the result is added to the cache. The second template1() clobbers the result from the first template1(). The final call uses the results stored in the cache.

Global cache scope example

Say you have the following templates:

# template1
- ${template2()} ${template3()}

# template2
- ${rand(1, 10)}
- abc
- hi

# template3
- ${template2()}

template2 would be evaluated once, and the second execution in template3 would apply the cache of the first one.

Another example is in the following code snippet:

var templates = Templates.ParseFile("xxx.lg");
var result1 = templates.Evaluate("template", null, new EvaluationOptions { CacheScope = LGCacheScope.Global});

// The second evaluation would drop all the results cached before.
var result2 = templates.Evaluate("template", null, new EvaluationOptions { CacheScope = LGCacheScope.Global});

A template is parsed using the Templates.ParseFile() function, and the template evaluation results are stored in result1. Note that the second evaluation results, result2, drops all results previously cached.

Local cache scope example

The following examples show when the local cache scope does and doesn't work. Assume that t() and subT() are templates that take a parameter:

>  Cache works, the second template call would re-use the first's result.
# template1
- ${t(param)} ${t(param)}

> Cache doesn't work because param1's value is different with param2's. value)
# template2
- ${t(param1)} ${t(param2)}

> Cache doesn't work because of different layers.
# template3
- ${subT(param1)} ${t(param2)}

# subT(param)
- ${t(param)}

Additional Resources