User-defined slots (static)

In Alan, you can create custom, or user-defined slots. User-defined slots list expected possibilities – words or phrases – that the user can say when giving a command.

To define a slot, use the $ character followed by brackets (). In brackets, define the slot name followed by its possible value. In the example below, we define the D slot with the today value.

Voice script
 intent('What is the weather $(D today)', p => {
     p.play('It is great!');
 });

When providing possible slot values, you can use the same rules as with patterns. You can:

Voice script
 intent('What (is|was|would be) the weather $(D yesterday|today|tomorrow|)', p => {
     p.play('It is great!');
 });

The list of values in user-defined slots is static: it remains intact for the whole dialog session. If you need to update the slot values during the dialog, for example, with the data obtained from an external source, use dynamic slots in patterns:

Voice script
 onCreateUser(p => {
     p.userData.contacts = {en:"Anna|Emma|John|Mark"};
 });

 intent('Show details for $(CONTACT u:contacts)', p => {
     p.play(`Getting contact details for ${p.CONTACT.value}`);
 });

Slot values and labels

A slot is an object that has a set of predefined fields and methods. All slot types have the following fields:

  • .value

  • .label

The .value field is filled automatically when an intent with the slot is matched. This field contains the captured user’s input.

In the example below, the user’s input is displayed in Alan logs and pronounced by Alan as a response.

Voice script
 intent('What (is|was|would be) the weather $(D yesterday|today|tomorrow|)?', p => {
     console.log(p.D.value);
     p.play(p.D.value);
     p.play('It is great!');
 });

The .label field can be used to provide any description, identify or classify the slot values. Unlike slot values, labels do not necessarily need to be unique.

To use a label, you need to define it first. Add the ~ character followed by the label value to the slot value.

Voice script
 intent('What (is|was|would be) the weather $(D yesterday~past|today~now|tomorrow~future|)', p => {
     console.log('Value:', p.D.value);
     console.log('Label:', p.D.label);
     p.play('It is great!');
 });

In the example below, labels are used to adjust the logic of intents and provide more accurate responses to the user. Now, if the user says: What was the weather yesterday?, the slot value will be yesterday and the label will be past. Alan will give a response defined for the past label.

Voice script
 intent('What (is|was|would be) the weather $(D yesterday~past|last day~past|today~now|now~now|tomorrow~future|next day~future|)', p => {
     console.log('Value:', p.D.value);
     console.log('Label:', p.D.label);
     switch (p.D.label) {
         case 'past':
             p.play('It was terrible!');
             break;
         case 'future':
             p.play('I hope it will be fine.');
             break;
         case 'now':
         default:
             p.play('It is great!');
     }
 });

Slot values in responses

You can use slots in patterns for response functions: play() and reply(). Use the standard JavaScript syntax for template strings, that is, offset in backticks.

You can also add slot values to the string line.

Voice script
 intent('What (is|was|would be) the weather $(D yesterday~past|last day~past|today~now|now~now|tomorrow~future|next day~future|)', p => {
     console.log('Value:', p.D.value);
     console.log('Label:', p.D.label);
     switch (p.D.label) {
         case 'past':
             p.play(`The weather ${p.D.value} was terrible!`);
             break;
         case 'future':
             p.play(`I hope that the weather ${p.D.value} will be fine.`);
             break;
         case 'now':
         default:
             p.play('The weather ' + p.D.value + ' is great!');
     }
 });

Slots with fuzzy matching

By default, to match an intent with a slot with the highest score, Alan requires that the user’s input contains the exact slot value. However, it is not always possible to specify all variants of values for a slot. For example, values can contain word groups with variable word order, some words in the input phrase may be replaced with synonyms or there can be values that are hard to pronounce.

To allow Alan to match intents with slots whose values may be less than perfect, you can use slots with fuzzy matching. In this case, Alan matches the intent based on the most similar alternative. Alan relies on the semantic meaning, even if it does not coincide exactly with what the user has said.

Warning

Slots with fuzzy matching should be used with caution, as they can lead to overly general matching.

To define a slot with fuzzy matching, add the ~ character after the slot name. As well as for other user-defined slots, you can accompany each value with a label. In this case:

  • The .value field of the slot will contain the user’s input exactly as user has said it, not the value you have defined in the script.

  • The .label field of the slot can have any value and can be used, for example, to give a proper response to the user.

In the example below, the intent will be matched even if the user skips a part of the item name or names a close variant instead. And to ensure proper verbiage, the slot label is played instead of the slot value in the confirmation phrase.

Voice script
 intent('I want a $(ORDER~ Double Cheese Burger~Cheese Burger|Spicy Mexican Burger~Mexican Burger|Grilled Hawaiian Burger~Hawaiian Burger)', p => {
     console.log(`Your choice is ${p.ORDER.value}`);
     p.play(`You have ordered ${p.ORDER.label}. Is it correct?`);
 });

Multiple values in slots

If you need to capture multiple slot values at once, you can add a slot to a pattern several times. When such an intent is matched, all pieces of data captured with the slot are collected to an array of values.

In such a case, p.SLOTNAME.value would contain only the value captured with the first slot instance in the pattern.

  • To access the list of all values in the array, use the slot name followed by the underscore character, for example: p.SLOTNAME_.

  • To access a specific element in the array, add the element index, for example: p.SLOTNAME_[0].value.

Each element in the array has its own .value and .label fields:

  • The value field is populated with the value obtained from the user’s input: p.SLOTNAME_[index].value.

  • The label field can contain a custom value and can be used to give more accurate responses to the user: p.SLOTNAME_[index].label.

Let’s assume we want to provide the user with the ability to ask about the price of different pizza sizes with one voice command. We can add the following intent:

Voice script
 const SHOW_REQ = 'How much is a $(D small|medium|large) pizza';
 const ALL_REQ = [
     `${SHOW_REQ}`,
     `${SHOW_REQ} and ${SHOW_REQ}`,
     `${SHOW_REQ} and ${SHOW_REQ} and ${SHOW_REQ}`,
 ]

 intent(ALL_REQ, p => {
     console.log(JSON.stringify(p.D));
     console.log(JSON.stringify(p.D_));
     let resultMessage = "";
     if(p.D_[0]) {
         resultMessage = playSizeMessage(p.D_[0].value);
         p.play(resultMessage);
     }
     if(p.D_[1]) {
         resultMessage = playSizeMessage(p.D_[1].value);
         p.play(resultMessage);
     }
     if(p.D_[2]) {
         resultMessage = playSizeMessage(p.D_[2].value);
         p.play(resultMessage);
     }
 })

 function playSizeMessage(theSize)  {
     if (theSize == "small") {
         return "A small pizza is 5 dollars";
     }
     if (theSize == "medium") {
         return "A medium pizza is 10 dollars"
     }
     if (theSize == "large") {
         return "A large pizza is 15 dollars"
     }
 }

Here, we use SHOW_REQ to define a question the user will ask: How much is a $(D small|medium|large) pizza? In ALL_REQ, we provide all possible combinations for this question. For example, the user can ask:

  • How much is a small pizza?

  • How much is a small pizza and how much is a large pizza?

  • How much is a small pizza and how much is a medium pizza and how much is a large pizza?

When the user asks one of the questions above, we collect all captured values for the D slot to an array and then play the pizza price for each element in the array with the playSizeMessage() function.

Slot name limitations

Mind the following limitations for user-defined slot names:

  • User-defined slot names must start with a letter.

  • You can use the following characters in user-defined slot names: A-Z, a-z, 0-9 and underscore (_).

  • Predefined slot names are reserved and cannot be used as user-defined slot names.

  • User-defined slot cannot start with one of the predefined slot names. For example, NAMECUSTOM will result in an error, while CUSTOMNAME will be a valid variant.