Applicable when
- You need to implement a complex workflow to fulfill requests made to the chatbot.
Non-applicable when
- Your replies to the user are static and follow simple patterns.
Implementation
We're assuming you already have an AWS Lex chatbot containing the intents configured according to your needs. We also assume you have the fulfillment logic implemented and deployed in a step function. All we need to do is integrate these two components to offer a seamless experience to your end users.
AWS Lex can integrate with Lambda functions but not directly with step functions. Naturally, what we can do to bridge the gap is create a new Lambda function which will mediate between Lex and the step function. In other words, this new Lambda function will trigger the step function and will wait for the result to be returned to Lex. With this in mind, Lex can simply read out loud the result to your users after all the heavy-lifting has been made.
Let us consider the following boilerplate code for the trigger Lambda (feel free to change according to your specific use case):
import { StepFunctions } from 'aws-sdk';
export async function handler(event: any, context: any, callback: any): Promise<void> {
const stateMachineArn = process.env.stateMachineArn as string;
const stepFunctions = new StepFunctions();
const params = {
stateMachineArn,
input: eventString
};
try {
const response = await stepFunctions.startExecution(params).promise();
const oneSecInMillis = 1000;
const maxAttempts = 4;
for (let i = 0; i < maxAttempts; i++) {
await delay(oneSecInMillis);
const historyParams = {
executionArn: response.executionArn,
reverseOrder: true
};
const history = await stepFunctions.getExecutionHistory(historyParams).promise();
if (history?.events[0]?.type === 'ExecutionSucceeded') {
const speechToRead = history?.events[0]?.executionSucceededEventDetails?.output || '';
const response = {
dialogAction: {
type: 'Close',
fulfillmentState: 'Fulfilled',
message: {
contentType: 'PlainText',
content: speechToRead
}
}
};
callback(null, response);
break;
}
}
} catch (e) {
console.error(e);
throw new Error('State machine execution failed');
}
}
async function delay(ms: number): Promise<void> {
return await new Promise(resolve => setTimeout(resolve, ms));
}
In the Lambda function above we have the step function ARN in an environment variable and we are passing the required input to the step function where we have our fulfillment logic. In addition, we are starting it via the Amazon SDK.
As you can imagine, we need to -somehow- wait for the actual execution result since the startExecution call returns virtually right away. To achieve this, we inspect the execution history every second to see if it was successful. When that is the case, we take the speech to read back to the user and build an object Lex can understand (we are assuming the result we want to read to the user is given directly as the output of the step function).
Finally, we simply execute the trigger Lambda function callback to relay the result back to Lex which will take care of reading out loud the response to the user.
In the code above, we are making the process fail only on timeout. You can improve the implementation so that it looks for the type corresponding to a failed run ('ExecutionFailed') in the execution history to make the trigger Lambda function fail instantly.
Comments
0 comments
Article is closed for comments.