Executing a Speech Server Workflow via the API

July 10, 2008

In my previous post I outlined a basic framework for using the Core API for Speech Server 2007. Today I’ll outline how to mix both the API and Workflow models by calling out to a workflow using the API and returning control back when it is complete. If you are interested in the complete project code you may download it here. I’m starting with the same basic framework from my last post. To this I’m adding a simple Voice Response Workflow with a single Statement activity. Rather than calling the synthesizer from the API, I’m going to use the Statement activity inside the workflow.  We’re going to pass in the text we want it to play at runtime. 1) The first thing we need to do is add a new Voice Response Workflow to the project. Too this we’ll add a single Statement activity. Because we’ve established the call using the API there is no AnswerCall, MakeCall, or DisconnectCall activities in this workflow. image   2) Now that we have our really-big-and-complex workflow ready, we can start adding some code to set the prompt. The first thing we need to do is add a handler for the TurnStarting event. This is where we will assign the Main Prompt property for the activity. 3) Next we need to add a property to pass in our input parameters too. We’ll call this MyPrompt. The resulting code behind should look like the following: =”Courier New”>using System;
using System.ComponentModel;
using System.ComponentModel.Design;
using System.Collections;
using System.Diagnostics;
using System.Drawing;
using System.Workflow.ComponentModel.Compiler;
using System.Workflow.ComponentModel.Serialization;
using System.Workflow.ComponentModel;
using System.Workflow.ComponentModel.Design;
using System.Workflow.Runtime;
using System.Workflow.Activities;
using System.Workflow.Activities.Rules;
using Microsoft.SpeechServer.Dialog; </font> namespace VoiceResponseWorkflowApplication2
{
    public sealed partial class Workflow1: SpeechSequentialWorkflowActivity
    {
        public Workflow1()
        {
            InitializeComponent();           
        }         private string _myPrompt;
        public string MyPrompt
        {
            get { return _myPrompt; }
            set { _myPrompt = value; }
        }         private void statementActivity1_TurnStarting(object sender, TurnStartingEventArgs e)
        {
            statementActivity1.MainPrompt.SetText(MyPrompt);
        }
    }
} 4) Now we need to add some code to kick off the workflow. We’ll do this from within the OpenCompleted event handler in Class1.cs. This code establishes an input parameter Dictionary<>, instantiates the workflow object, and starts the workflow. We’ll add a handler for the WorkflowCompleted event so that we can cleanup the call once the workflow is done.  Dictionary<string, object> inputParam = new Dictionary<string, object>();
inputParam.Add(“MyPrompt”, “HelloWorld”);
myWorkflow = SpeechSequentialWorkflowActivity.CreateWorkflow(_host, typeof(Workflow1), inputParam);
myWorkflow.WorkflowRuntime.WorkflowCompleted += new EventHandler<WorkflowCompletedEventArgs>(WorkflowRuntime_WorkflowCompleted);
SpeechSequentialWorkflowActivity.Start(myWorkflow); One interesting item here is the inputParam object. The way this works is that you pass in parameters to the workflow and it assigns the values to the corresponding public properties of the workflow. If you pass an input parameter for which there is no property you will get an exception. The complete Class1.cs: using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.SpeechServer ;
using Microsoft.SpeechServer.Dialog;
using System.Workflow.Runtime; namespace VoiceResponseWorkflowApplication2
{
    public class Class1 : IHostedSpeechApplication
    {
        private IApplicationHost _host;
        private WorkflowInstance myWorkflow;         public void Start(IApplicationHost host)
        {
            if (host != null)
            {
                _host = host;
                _host.TelephonySession.CurrentUICulture = System.Globalization.CultureInfo.GetCultureInfo(“en-US”);                 // Dial and outbound call (make sure you change these numbers :-)
                _host.TelephonySession.OpenCompleted += new EventHandler<AsyncCompletedEventArgs>(TelephonySession_OpenCompleted);
                _host.TelephonySession.OpenAsync(“7813062200”, “8887006263”);
            }
            else
            {
                throw new ArgumentNullException(“host”);
            }
        }         void TelephonySession_OpenCompleted(object sender, AsyncCompletedEventArgs e)
        {
            if (e.Error != null)
            {
                _host.TelephonySession.Close();
            }
            else
            {
                Dictionary<string, object> inputParam = new Dictionary<string, object>();
                inputParam.Add(“MyPrompt”, “HelloWorld”);
                myWorkflow = SpeechSequentialWorkflowActivity.CreateWorkflow(_host, typeof(Workflow1), inputParam);
                myWorkflow.WorkflowRuntime.WorkflowCompleted += new EventHandler<WorkflowCompletedEventArgs>(WorkflowRuntime_WorkflowCompleted);
                SpeechSequentialWorkflowActivity.Start(myWorkflow);
            }
        }         void WorkflowRuntime_WorkflowCompleted(object sender, WorkflowCompletedEventArgs e)
        {
            _host.TelephonySession.Close();
        }         public void Stop(bool immediate)
        {         }         public void OnUnhandledException(Exception exception)
        {
            if (exception != null)
            {
                _host.TelephonySession.LoggingManager.LogApplicationError(100, “An unexpected exception occurred: {0}”, exception.Message);
            }
            else
            {
                _host.TelephonySession.LoggingManager.LogApplicationError(100, “An unknown exception occurred: {0}”, System.Environment.StackTrace);
            }             _host.OnCompleted();
        }
    }
}

(download the zipped project here)