Write Custom Javascript Functions For CSML

Discover how to use CSML External Functions to augment your code with custom javascript code!

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 ask 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:

exports.handler = async function handler(event) {
	if (!event.name) return 'Error: You must specify a name parameter';
	return `Hello, ${name}`;
};
index.js

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 a 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 straight forward: 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:

Custom function settings
  • We give it a name: closematch
  • We set the handler as index.handler: the function called handler is in the index.js file.
  • We set the arguments to the required values: input and list_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 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