Lesson 6 -- Form Items, Properties, and Getting Caller Confirmation

This lesson covers some important subjects that you need to know about. The following sections are included:

As you have seen, forms are the key component of VoiceXML documents. When a VoiceXML application is executed, they are processed by a "form interpretation algorithm" (FIA), which loops through the elements in the form in order of their appearance.

Important note Important

Understanding how the FIA loop works is critical to your ability to write the code for forms. It is somewhat complicated, and you should read Appendix C -- The Form Interpretation Algorithm.

Also see section 2.1, Forms, of the VoiceXML 2.0 specification (http://www.w3.org/TR/2004/REC-voicexml20-20040316/#dml2.1) for a comprehensive presentation of forms, form items, form item variables, and the form interpretation algorithm.

What can be in directed forms?

In this lesson, and in the tutorial in general (except for Lesson 10) we are concerned only with directed forms. A directed form can contain:

  • A set of "form items," which are elements that are visited (are executed) in the loop of the FIA.

  • Declarations of variables.

  • Event handlers (to be discussed in Lesson 8).

Form items are subdivided into "input items" that can be "filled" by user input and "control items" that cannot be filled. Input items include the <field>, <object>, <record>, <subdialog>, and <transfer> elements. Control items include the <block> and <initial> elements.

In this lesson, we will only discuss these form items:

  1. The <field> element, used to collect user input. This is the workhorse form input item.

  2. The <block> element, a form control item, which is a sequence of procedural statements used for prompting and computation. We encountered this element briefly in Lesson 1 and will use it in several future lessons.

Note Note

The <subdialog> input item is covered in Lesson 7. The <initial> control item is covered in Lesson 10. The <object>, <record>, and <transfer> input items are covered in the VoiceXML Tutorial Supplement, as discussed in the Summary.

Each form item has an associated "form item variable." A form item variable can be given a name using the name attribute, or left nameless, in which case the FIA generates an internal name. Typically, input items are given names, but control items are not. As an example, in <field name="main_selection">, main_selection is the name of the form item variable for the field. We normally use the <block> element without a name attribute.

The FIA processes form items in a loop

Here are the key principles:

  1. Each form item has a "guard condition" which determines whether or not the FIA will "visit" (that is, will execute) that item.

  2. You, the programmer, can turn on the guard condition of an item so that the FIA will not visit the item (although you will not normally do this).

  3. The FIA begins looping through the form items "in document order" (that is, in sequence) and visits (executes) each item that does not have its guard condition turned on. The FIA skips any items that have their guard conditions turned on.

  4. When an item is executed successfully by the FIA, the FIA itself turns on the item's guard condition. This means that the FIA will not visit this item again in its next pass through the loop. For example:

    1. If a grammar match is made in a <field> element, the FIA turns on the field's guard condition by setting the field's name variable (more on this below).

    2. The FIA sets the guard condition of a <block> element when it begins to execute the statements in the element.

  5. The FIA continues looping through the form items until such time as it encounters a transfer of control command to leave the form (a <goto>, <submit>, or <transfer> element, for example) or there are no longer any form items whose guard conditions are turned off. If there are no further form items that can be visited and no transfer of control has been specified, the FIA causes the application to exit.

  6. If you want the FIA to revisit a form item, you, the programmer, can turn off the item's guard condition:

    1. For a field, you turn off the guard condition by resetting its name variable to undefined with the <clear/> element. You often do this when you want to reprompt a caller (more on this below).

    2. You can't normally turn off the guard condition for a block because you won't know its name.

The variables named by the name attributes of form items are set to undefined when the form is entered (unless you, the programmer, set a value in your code). These form item variables are waiting to contain the results of interpreting the form items. For example, an input item's form item variable will hold the value collected from the caller. We have already encountered this in our tutorial. For example, in our sample application, app-root.vxml, we have a main form that includes a field named main_selection. The <field> element is a form item and its form item variable is its name, main_selection.


<form id="main">
   <field name="main_selection">
      .............
      .............
   </field>
</form> 

If a match is made between the caller's utterance and the field's grammar, the match phrase is placed in the main_selection form item variable by the FIA.

Quoting from the VoiceXML 2.0 specification: "Fields are the major building blocks of forms. A field declares a variable and specifies the prompts, grammars, DTMF sequences, help messages, and other event handlers that are used to obtain it. Each field declares a VoiceXML form item variable in the form's dialog scope."

Important note Important

In the above quote, the specification states that the field name variables (their form item variables) are in the form's dialog scope. This means that a field variable in a form is visible to all the other fields in the form. This will be important when we add confirmation prompting later in this lesson.

All form items can have three optional attributes:

Attribute

Description

name

The name to be given to the form item variable. If omitted, an internal name is generated.

expr

The initial value of the form item variable. Should be omitted unless you want the FIA to ignore the form item during execution.

cond

A JavaScript expression to be evaluated by the FIA. If it evaluates to false, the FIA will ignore the form item during execution. If omitted, it defaults to true.

As noted, form items have "guard conditions," which determine whether or not the FIA ignores the form items. The FIA will ignore a form item during execution if either of two conditions is met:

  1. The form item variable has a value other than undefined. This can occur in two different ways: first, the programmer gives the form item variable a value using the expr attribute, or, second, the variable has a value from a prior execution of that form item and that value has not been cleared with the <clear/> element.

  2. The form item has a cond attribute that evaluates to false.

In Lesson 5, we included a short application to test grammars:


<?xml version="1.0"?>
<vxml version="2.1" revision="4"
xmlns="http://www.w3.org/2001/06/grammar"
xml:lang="en-US">
<link event="event.onquit">
    <grammar version="1.0" root="top">
      <rule id="top">
        <item>quit</item>
      </rule>
    </grammar>
  </link>
  <catch event="event.onquit">
    <exit/>
  </catch>
<form id="mainDialog">
<field name="result">
<prompt>please say something <break/> or say <break size="small"/>
              quit <break size="small"/> to exit.
      </prompt>
      <!-- your grammar goes here -->
      <filled>
   <log>raw data = <value expr="result"/></log>
         Thank you.
         <clear namelist="result"/>
      </filled>
      <noinput> I didnt hear you </noinput>
      <nomatch> I didnt get that </nomatch>
</field>
</form>
</vxml>

This test application was designed to allow the field (a form item) to be executed many successive times by the FIA so that a variety of utterances can be tested during one phone call. The key to allowing re-execution of the field in a subsequent loop of the FIA is the <clear namelist="result"/> statement. This statement resets result (the field item variable) to undefined. The FIA will then revisit the result field, which will prompt for input again. If the result variable had not been cleared, the FIA would not have revisited the result field.

Note Note

The mainDialog form has only one form item, the field with name="result". Since the code clears the form item variable, result, the FIA will keep looping (a loop with just one form item) forever until the caller says "quit" or hangs up.

VoiceXML properties specify speech parameters. For example,

<property name="inputmodes" value="dtmf voice"/>

Properties may be defined with application scope, document scope, or dialog scope:

  • Application scope properties—defined for the whole application, when the <property> element is a child of the <vxml> element of the application root document.

  • Document scope properties—defined for the whole document, when the <property> element is a child of the <vxml> element of the document.

  • Dialog scope properties—defined for a particular dialog when the <property> element is a child of a <form> or <menu> element.

  • Form item scope properties—defined for the form item when the <property> element is a child of the form item.

Properties apply to their parent element and all the descendants of the parent. A property at a lower level overrides a property at a higher level. When different values for a property are specified at the same level, the last one in document order applies.

Properties specified in the application root document provide default values for properties in every document in the application; properties specified in an individual document override property values specified in the application root document.

All properties have default values that are platform specific and apply to the entire application. The defaults can be overridden at any level by explicitly defining the property, for example:

<property name="confidencelevel" value="0.65"/>

A complete list of properties supported by the Tellme platform, along with their defaults (in bold text) can be found at http://msdn.microsoft.com/en-us/library/ff929002.aspx.

Tip Tip

Property default values can vary from platform to platform and, even in one platform, may be subject to change. If your voice application relies on a specific value, you should set the property explicitly in your application root document.

Here is a list of some properties that affect prompts and grammars, with the default values for the Tellme platform.

Property

Description

bargein

Controls whether or not a user can interrupt the system. The property applies to both DTMF and spoken input. The default value is true.

bargeintype

When bargein is enabled, this property controls the type of bargein that will be performed in response to voice or DTMF input. The default value is speech.

completetimeout

A time designation specifying the length of silence that is required before the speech recognizer finalizes a result (either accepting it or throwing a nomatch event), when the speech is a complete match of an active grammar. The valid range for this property is 0.1 seconds to 10.0 seconds. The completetimeout value should be less than the incompletetimeout value—otherwise unexpected behavior may occur. If the completetimeout value is out of range, the interpreter throws a generic error event. The default value is 0.25 seconds (value="0.25s").

confidencelevel

A confidence level from 0.0 to 1.0 inclusive. If the confidence score for an utterance is below the specified level, the utterance is rejected. The value must be specified as a floating point number (e.g., use 1.0 to specify the maximum). The default is 0.45 (value="0.45").

incompletetimeout

A time designation specifying the length of silence that is required to decide the end of speech when the last recognized word in the utterance is an incomplete match to an active grammar. The valid range for this property is 0.1 seconds to 10.0 seconds. The completetimeout value should be less than the incompletetimeout value—otherwise unexpected behavior may occur. If the incompletetimeout value is out of range, the interpreter throws a generic error event. The default value is 1 second (value="1s").

inputmodes

Controls the type of input the speech system should accept. Use this property in conjunction with the mode attribute of the grammar element to optimize application performance. Values are dtmf (only accept DTMF), voice (only accept speech), and dtmf voice (accept both).

maxspeechtimeout

A time designation representing the maximum duration of user speech. If this amount of time elapses before the user stops speaking, the maxspeechtimeout event is thrown. The default value is 60 seconds (value="60s").

sensitivity

The sensitivity to noise during input recognition. This affects whether bargein might be triggered by noise rather than actual user speech. The valid range for this property is 0.0 (least sensitive to noise) to 1.0 (highly sensitive to quiet input). Thus, if you set the property to a low value, the recognizer is less sensitive to noise, but the user must speak more loudly in order to be recognized. If the sensitivity value is out of range, the interpreter throws a generic error event. The default value is 0.5 (value="0.5").

speedvsaccuracy

The desired balance between speed and accuracy. The valid range for this property is 0.0 (fastest recognition) to 1.0 (best accuracy). If the speedvsaccuracy value is out of range, the interpreter throws a generic error event. The default value is 0.5 (value="0.5").

timeout

A time designation representing the interval of silence following the end of the last prompt that the interpreter allows while waiting for user input before throwing a noinput event. The default is 7 seconds (value="7s").

We have already used the bargein, bargeintype, and inputmodes properties in this tutorial.

In Lesson 4, we mentioned that it would be a good idea to ask the caller for confirmation that the application understood the caller's utterance. However, we did not add a confirmation prompt at that time because we did not yet know about the need to clear form item variables before re-entering a form item (in our sample application, this usually means a <field> element).

The grammar for our new_reservation form is a bit complicated and we would like to confirm the match result just to be sure it is correct before proceeding with the switch statement in the <filled> element. To accomplish this, we would like to do the following:

  1. Add a prompt that asks the caller if we understood correctly.

  2. Add a grammar that accepts "yes" or "no," as well as variations like "yeah" or "nope." The Tellme public grammar confirm.grxml does this, so we can add <grammar source = "http://grammar.svc.tellme.com/yesno/mss/v2/confirm.grxml"/> at the appropriate place.

  3. Add code of some sort that causes the FIA to revisit the new_reservation_type field and ask for input again if the caller says "no". If the caller says "yes", the code should proceed to a <filled> section containing the original switch statement.

There are various ways we can address confirming the caller's response. Two of these ways are:

  • Add a new field with a "confirmation" grammar to the new_reservation form. This is quite simple and it is what we will do in this lesson.

  • Use a subdialog. The subdialog mechanism allows us to suspend execution of the application, go and execute some other code, and then return to the main application and resume where we left off. The value of this is that we can create a single, stand-alone confirmation subdialog that we can reuse in many different documents in our application. We will include a confirmation subdialog in Lesson 7.

Adding a new field with a confirmation grammar

To accomplish the confirmation prompting, we:

  1. In the new_reservation_type field of the new_reservation form, remove the code in the <filled> element (a switch statement and a <goto> element) and save it.

  2. Delete the now-empty <filled> element of the new_reservation_type field.

  3. Add a new field (with name="confirm") in the new_reservation form right after the new_reservation_type field.

    Important note Important

    The new confirm field must be a child of the new_reservation form so that it has access to the form's input item variables (in particular, the new_reservation_type variable).

  4. In the confirm field, add a <prompt> element that tells the caller what the application thinks was a match in the new_reservation_type field and asks if that is correct.

  5. In the confirm field, add a grammar that references the Tellme confirmation grammar.

  6. In the <filled> element of the confirm field, add an <if> element that causes the FIA to revisit the new_reservation_type field of the new_reservation form if the caller says that the match interpretation was incorrect.

  7. In the <filled> element of the confirm field, add an <else/> element that includes the code that will be executed if the caller confirms the match interpretation. This should be the same code that was removed and saved from the original <filled> element of the new_reservation_type field.

The new version of app-root.vxml in the next section includes these changes (in bold font).

Note Note

Note: The new_reservation form now has two fields. If the caller says "no" to the confirmation prompt, we need to remove the guard condition on both of these input items. To do this, we add: <clear namelist="new_reservation_type confirm"/>.

Here is a version of app-root.vxml with a separate field for confirmation (in Lesson 7, we will abandon this version, though, and create a version with confirmation prompting that uses a subdialog).

The only new code is the confirm field (not shown in bold font).


<?xml version="1.0" encoding="UTF-8"?>
<vxml xmlns="http://www.w3.org/2001/vxml" version="2.1" xml:lang="en-US"
      xml:base="https://studio.tellme.com/vxml-tutorial/">

   <script><![CDATA[

      var doNewPlaneRes = false;

      var doNewHotelRes = false;
      var doNewCarRes = false;
      var doChangePlaneRes = false;
      var doChangeHotelRes = false;
      var doChangeCarRes = false;
      var doRestaurantRec = false;

   ]]></script>
   <form id="main">
      <field name="main_selection">
         <prompt bargein="true" bargeintype="speech">
            Welcome to Contoso Travel<break/>
            Say new reservation<break size="small"/> or press 1<break/>
            Say change reservation<break size="small"/> or press 2<break/>
            Say restaurant recommendation<break size="small"/> or press 3
         </prompt>

         <grammar version="1.0" root="top" tag-format="semantics/1.0">
            <rule id="top">
               <item><ruleref uri="#nonsense"/></item>
                  <one-of>
                     <item>new reservation
                     <tag>out="new reservation";</tag></item>

                     <item>change reservation
                     <tag>out="change reservation";</tag></item>

                     <item>restaurant recommendation
                     <tag>out="restaurant recommendation";</tag></item>
               </one-of>
               <item><ruleref uri="#nonsense"/></item>
            </rule>
            <rule id="nonsense">
               <one-of>
                  <item><ruleref special="GARBAGE"/></item>
                  <item><ruleref special="NULL"/></item>
               </one-of>
            </rule>
         </grammar>

         <grammar mode="dtmf" version="1.0" root="top">
            <rule id="top">
               <one-of>
                  <item>1</item>
                  <item>2</item>
                  <item>3</item>
               </one-of>
            </rule>
         </grammar>

         <catch event="noinput nomatch">
            Sorry. Didn't get that. Please try again.
            <reprompt/>
         </catch>

         <filled>
            <script><![CDATA[
               var next_destination = " ";
               switch (main_selection){

                  case "new reservation" :
                     next_destination = "#new_reservation";
                     break;

                  case "1" :
                     next_destination = "#new_reservation";
                     break;

                  case "change reservation" :
                     next_destination = "change-reservation.vxml";
                     break;

                  case "2" :
                     next_destination = "change-reservation.vxml";
                     break;

                  case "restaurant recommendation" :
                     next_destination = "restaurant.vxml";
                     break;

                  case "3" :
                     next_destination = "restaurant.vxml";
                     break;

                  default:
                     next_destination = "error.vxml";
                     break;
               }

            ]]></script>
            <goto expr="next_destination"/>
         </filled>
      </field>
   </form>
   <form id ="new_reservation">
      <field name="new_reservation_type">
         <prompt bargein="true" bargeintype="speech">
            What type of reservation do you want to make<break/>Say
            plane<break size="small"/>, hotel
            <break size="small"/>, or car<break/>
            You may choose more than one.
         </prompt>
         <grammar version="1.0" root="top" tag-format="semantics/1.0">
            <rule id="top">
               <one-of>
            <!--single choice -->
                  <item><ruleref uri="#nonsense"/>plane
                  <ruleref uri="#nonsense"/>
                  <tag>out="plane";</tag></item>

                  <item><ruleref uri="#nonsense"/>hotel
                  <ruleref uri="#nonsense"/>
                  <tag>out="hotel";</tag></item>

                  <item><ruleref uri="#nonsense"/>car
                  <ruleref uri="#nonsense"/>
                  <tag>out="car";</tag></item>

            <!--double choice -->
                  <item><ruleref uri="#nonsense"/>plane
                  <ruleref uri="#nonsense"/>hotel
                  <ruleref uri="#nonsense"/>
                  <tag>out="plane and hotel";</tag></item>

                  <item><ruleref uri="#nonsense"/>hotel
                  <ruleref uri="#nonsense"/>plane
                  <ruleref uri="#nonsense"/>
                  <tag>out="plane and hotel";</tag></item>

                  <item><ruleref uri="#nonsense"/>plane
                  <ruleref uri="#nonsense"/>car
                  <ruleref uri="#nonsense"/>
                  <tag>out="plane and car";</tag></item>

                  <item><ruleref uri="#nonsense"/>car
                  <ruleref uri="#nonsense"/>plane
                  <ruleref uri="#nonsense"/>
                  <tag>out="plane and car";</tag></item>

                  <item><ruleref uri="#nonsense"/>hotel
                  <ruleref uri="#nonsense"/>car
                  <ruleref uri="#nonsense"/>
                  <tag>out="hotel and car";</tag></item>

                  <item><ruleref uri="#nonsense"/>car
                  <ruleref uri="#nonsense"/>hotel
                  <ruleref uri="#nonsense"/>
                  <tag>out="hotel and car";</tag></item>

            <!--triple choice -->
                  <item><ruleref uri="#nonsense"/>plane
                  <ruleref uri="#nonsense"/>hotel
                  <ruleref uri="#nonsense"/>car
                  <ruleref uri="#nonsense"/>
                  <tag>out="plane and hotel and car";</tag></item>

                  <item><ruleref uri="#nonsense"/>plane
                  <ruleref uri="#nonsense"/>car
                  <ruleref uri="#nonsense"/>hotel
                  <ruleref uri="#nonsense"/>
                  <tag>out="plane and hotel and car";</tag></item>

                  <item><ruleref uri="#nonsense"/>hotel
                  <ruleref uri="#nonsense"/>plane
                  <ruleref uri="#nonsense"/>car
                  <ruleref uri="#nonsense"/>
                  <tag>out="plane and hotel and car";</tag></item>

                  <item><ruleref uri="#nonsense"/>hotel
                  <ruleref uri="#nonsense"/>car
                  <ruleref uri="#nonsense"/>plane
                  <ruleref uri="#nonsense"/>
                  <tag>out="plane and hotel and car";</tag></item>

                  <item><ruleref uri="#nonsense"/>car
                  <ruleref uri="#nonsense"/>plane
                  <ruleref uri="#nonsense"/>hotel
                  <ruleref uri="#nonsense"/>
                  <tag>out="plane and hotel and car";</tag></item>

                  <item><ruleref uri="#nonsense"/>car
                  <ruleref uri="#nonsense"/>hotel
                  <ruleref uri="#nonsense"/>plane
                  <ruleref uri="#nonsense"/>
                  <tag>out="plane and hotel and car";</tag></item>
               </one-of>
            </rule>
            <rule id="nonsense">
               <one-of>
                  <item><ruleref special="GARBAGE"/></item>
                  <item><ruleref special="NULL"/></item>
               </one-of>
            </rule>
         </grammar>

         <catch event="noinput nomatch">
            Sorry. Didn't get that. Please try again.
            <reprompt/>
         </catch>

         <!-- filled section was removed from here and put in the filled
              section of the confirm field -->
      </field>

      <field name="confirm">
         <prompt>
            I think I heard you say
            <value expr="new_reservation_type"/>.
            Is that correct?
         </prompt>
         <grammar version="1.0" root="top" tag-format="semantics/1.0">
            <rule id="top">
               <ruleref        
             uri="http://grammar.svc.tellme.com/yesno/mss/v2/confirm.grxml"/>
               <tag>out=rules.latest()</tag>
            </rule>
         </grammar>

         <catch event="noinput nomatch">
            Sorry. Didn't get that. Please try again.
            <reprompt/>
         </catch>

         <filled>
            <if cond="confirm == 'no'">
               <clear namelist="new_reservation_type confirm"/>
            <else/>
               <script><![CDATA[
                  var next_destination = "";

                  switch (new_reservation_type){

                     case "plane" :
                        next_destination = "new-plane.vxml";
                        break;

                     case "hotel" :
                        next_destination = "new-hotel.vxml";
                        break;

                     case "car" :
                        next_destination = "new-car.vxml";
                        break;

                     case "plane and hotel" :
                        next_destination = "new-plane.vxml";
                        doNewHotelRes = true;
                        break;

                     case "plane and car" :
                        next_destination = "new-plane.vxml";
                        doNewCarRes = true;
                        break;

                     case "hotel and car" :
                        next_destination = "new-hotel.vxml";
                        doNewCarRes = true;
                        break;

                     case "plane and hotel and car" :
                        next_destination = "new-plane.vxml";
                        doNewHotelRes = true;
                        doNewCarRes = true;
                        break;

                     default:
                        next_destination = "error.vxml";
                        break;
                  }
               ]]></script>
               <goto expr="next_destination"/>
            </if>
            
         </filled>
      </field>
   </form>
</vxml>

You should paste this code into the Tellme Studio Scratchpad and run it.

In this lesson we have learned about form items and the FIA, properties, and a method for doing confirmation prompting.

In the next lesson, Lesson 7, we will discuss VoiceXML's own scripting elements that can be used instead of JavaScript and we will use some of these elements to replace the switch statements in app-root.vxml's <filled> elements.

The next lesson will also cover subdialogs. A subdialog will be used to do confirmation prompting, replacing the method used in this lesson. Confirmation prompting is likely to be used at many different places in a VoiceXML application. Subdialogs are a mechanism that allows us to write reusable code—the same confirmation-prompting subdialog can be used throughout an application without rewriting it.

Community Additions

ADD
Show: