Write Custom Javascript Functions For CSML
When we designed CSML, we imagined a programming language focused on making chatbot developers' lives easier and started by developing a bunch of native conversational-oriented functions for these very developers.
We also knew that in some cases, developers would need to use Javascript or other mainstream languages in some situations, such as:
- I want to use a library that does not exist in CSML
- I want to use a program that I already coded and don't want to code it again
- I need to perform an action that CSML does not allow (very specific)
So we added support in CSML for external code execution in other languages by adding custom functions with the built-in keyword Fn()
.
In this blog post, I will explain how to add a custom Javascript (Nodejs) function to a CSML chatbot with CSML Studio.
The use case
In this tutorial, we will code a custom function to find out if a given word or sentence is close to another word or sentence using the Levenshtein distance algorithm.
This function can be useful to find the closest word to an input and ignore typos. For instance, if a chatbot asks a question to a user and display two buttons "Yes" and "No", it may be useful to match the input "Yep" with the "Yes" buttons as they are very close.
The Nodejs code
A. The function
CSML custom functions are very similar to Lambda functions, they have the following characteristics:
- They get their parameters from the
event
object that is injected automatically. - They can return values such as
Object
,Array
,String
,Integer
,Float
,Boolean
,null
- The handler name must be specified (
handler
as default) - The handler must be an async function
You can find CSML custom function boilerplates on Github.
This is for example what a function that returns Hello, {{name}}
looks like:
B. The algorithm
This algorithm is well known and has been implemented many times before. Let's use one of these implementations: fastest-levenshtein.
This is an npm dependency so it is very simple to download and use.
$ npm init -y
$ npm i fastest-levenshtein
Now that the dependency is setup, let's import it in index.js
and add the code to compare the user's input with a given set of strings.
const { distance, closest } = require('fastest-levenshtein');
async function getClosestString(event) {
const { input, list_cmp } = event;
// We get the closest string
const closest_string = closest(input, list_cmp);
return {
input,
closest_string,
distance: distance(input, closest_string), // We return the distance to the closest string
};
}
exports.handler = async function handler(event) {
// We wrap the results in an object stating if the everything went as expected
if (!event.input || !event.list_cmp) {
return {
success: false,
message: 'You must provide `input` and `list_cmp` parameters',
};
}
return {
success: true,
data: await getClosestString(event),
};
};
As you can see, the handler
calls an async function that finds the closest string and returns it along with the distance.
To test this code, you can use the code below, which will execute the async function:
// The code below allows you to check what your function returns
(async () => {
console.log(await getClosestString({
input: 'Yep',
list_cmp: ['Yes', 'No'],
}));
})();
C. Let's package it
Once we have the function, we want to package it and upload it to the CSML Studio.
Packaging the function is straightforward: you simply need to zip the Nodejs file(s) along with node_modules
.
$ zip -r9 closematch.zip index.js node_modules
That's it!
Custom functions in CSML Studio
Now that we have the packaged function, let's set it up in CSML Studio.
Go to Functions
> Add Custom Functions
> Quick mode
First, we upload the packaged function closematch.zip
, and fill the form:
- We give it a name:
closematch
- We set the handler as
index.handler
: the function calledhandler
is in theindex.js
file. - We set the arguments to the required values:
input
andlist_cmp
- We don't need any environment variables, so let's leave this field blank
Click on OK. You are all set!
Now let's use this function in a flow.
start:
say Question("Hello, do you like parrots?",
buttons=[Button("Yes"), Button("No")],
button_type="quick_reply"
)
hold
// We calculate the closest word from the user's input
do closematch = Fn("closematch", input=event, list_cmp=["Yes", "No"])
// If the distance is over 3, it means that the input is really far from "Yes" (and even further from "No")
if (closematch.data.closest_string == "Yes" && closematch.data.distance < 3) {
say "I love parrots too!"
} else {
say "You should love parrots !"
}
goto end
That's it, we've done it! Now we can use a function anywhere in our chatbot!
You can find the entire source code of the function over here: https://github.com/CSML-by-Clevy/fastest-algorithm-for-CSML