Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Available in CSML v1.5+
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
CSML (Conversational Standard Meta Language) is an Open-Source, Domain-Specific Language designed for developing rich conversational experiences easily. It makes creating powerful chatbots extremely easy.
Written itself in Rust, the purpose of this language is to simplify the creation and maintenance of rich conversational interactions between humans and machines. With a very expressive and text-only syntax, CSML flows are easy to write and understand, making it easy to deploy and maintain conversational agents.
CSML natively handles short and long-term memory slots, metadata injection, and connecting to any third party API or injecting arbitrary code in any programming language thanks to its powerful runtime APIs.
By using the CSML language, any developer can integrate arbitrarily complex conversational agents on any channel (Facebook Messenger, Slack, Facebook Workplace, Microsoft Teams, custom webapp, ...) and make any bot available to any end user. In addition to the language itself, CSML Studio, an online, full-featured development and deployment platform, comes with a large number of channel integrations that work out of the box, but developers are free to add new custom integrations by using the CSML interfaces.
This is a simple CSML flow example, which describes a conversation between a user and a machine.
A CSML flow
contains several steps
, which are defined by a name (which must be unique within a flow) followed by a colon. Each step contains instructions on how to handle the interactions within the step, what to say, what to remember, where to go. The syntax is very descriptive and contains very few keywords.
This goal of the example flow on the right is to retrieve the name of the user.
To do so, we will first check if we already have it in memory. If so, we can use it to greet our user, and close this conversation. Otherwise, we can go to a different step and simply ask for it. When the user responds, we will be able to remember
the name for future use, and close the conversation.
This simple flow shows a few of the features of the language. There are many more, which we will go into in detail now!
CSML Studio includes everything you need to create, develop and maintain your chatbots. You get a code editor and test chatbox, built-in services to author functions and install custom or managed integrations, deploy to many different communication channels including Messenger, Workplace Chat, Microsoft Teams, Slack, Whatsapp... and monitor the usage with custom analytics.
As an open-source programming language, there are several ways you can get started with CSML, either by compiling the sources yourself, using Docker, or using , a free online development environment provided by .
The easiest way to create CSML Chatbots is by using the free online development playground available at . No dependencies, no installation, no signup required!
➡ Try CSML Playground now on !
You can also create a free account on CSML Studio, a professional online development and deployment tool for all your CSML chatbots. Read more , and head over to the !
➡ Try CSML Studio now on !
To run CSML on your own machine, you will need a way to run the engine (CSML comes with bindings for nodejs or directly in rust), as well as a database. Optionally, you will also need a custom App
runtime ().
explains how to run the CSML engine on your own server. It may be a useful starting point for a custom installation of CSML!
We provide an easy-to-use docker image to run CSML on your own computer. To get started, follow the instructions .
We provide builds for macOS and linux (arm64) for every new version of the CSML engine. You can find them on the releases page of the . For the latest available version, visit .
CSML is written in Rust. You can find the source code of the CSML interpreter and engine on , and compile it from source as per the instructions.
Local or temporary variables are only usable within the current step. It is rather a way to write more readable, expressive code. But they are really powerful, because they allow you to do everything that a regular memory does, but temporarily. For more information about local variables, see the as
keyword.
Local variables are useful for temporary calculations. You do not want to memorize your internal temporary steps, rather save useful information about the user.
Long-term memories on the other hand are at the core of the purpose of CSML. When two persons converse, they constantly memorize information, which they can reuse in other discussions. For example, if I gathered that my friend likes Iron Man, I might propose later that we go see Captain America together. I do not have to ask them again about their favorite film, because I already learned this information before, even if it was in a different conversation, even if the topic of that conversation might have been completely unrelated.
The memory API is very powerful: by default a bot never forgets anything about the current user. For more information, see the remember
keyword.
CSML provides a _memory
global, read-only variable, which contains all variables saved until now for the current user. This especially useful if you need to debug the state of a user's memory at any given step:
Variables and memories (of any type) can not be larger than 50KB. Using data larger than 50KB in variables (for example as the return value of a function) may result in undefined behavior. Also, please note that messages larger than 30KB in size can not be displayed in the test webapp.
To output the value of any variable in a string, you can use the string interpolation helper: double curly braces {{ }}
.
This makes it extremely easy to concatenate multiple values of any type into a single string. Objects, arrays and numbers will be stringified automatically and missing properties will be evaluated as NULL
.
To concatenate two strings, you can also use the +
operator (starting in CSML v1.8.0):
As in any turing-complete programming language, CSML is able to handle any type of logic, in the form of loops and if/else statements.
As a CSML developer, you can easily implement any logic based on any variable or event, and very expressively decide how your conversation should be handled. Own of the main advantages of CSML is its descriptive textual interface, making it easy for anyone to understand the logic. It just makes sense and hides all the complexity of decision trees behind the simple concepts that every developer knows and uses.
A large part of developing CSML flows is about finding out whether an event matches a value in order to redirect the user to one step or another. The language comes with a number of helpers and operators:
comparison operators ==
, !=
, <
, >
, <=
, >=
match
keyword
&&
and ||
operators
if
, else if
, else
keywords
The CSML engine is able to automatically handle the execution of any payload, any code, in any language, thanks to the built-in App()
macro. With CSML Apps, you can integrate your own business logic, on your own servers, in your language of choice.
When used together with custom serverless function runtimes (cloud-based such as AWS Lambda, Azure Functions, Google Cloud Functions), or on-premise with FnProject, OpenFaas or OpenWhisk), CSML Functions are a good way to execute custom business logic outside of the context of the conversation.
Deprecation notice: the original Fn()
notation for calling Apps (formerly called Functions) in your CSML code has been replaced by the newer App()
built-in as of CSML v1.5. Both notations will continue to work until CSML v2 is released, but this documentation will only reference the new App()
usage from now on.
App
on CSML StudioWhen using the CSML Studio, the heavy setup of creating an App
runtime is already done for you, which means that you can easily import functions in java, python, nodejs, go... without any additional setup, simply by uploading your code and calling the function in your CSML script.
CSML Studio also comes with many ready-to-use integrations that are installable in one-click.
App
callsTo execute external functions in any programming language when provided a fn_endpoint
.
For example, when a App
is called, a HTTP POST request will be performed to the provided fn_endpoint
with the following payload:
The endpoint should return a JSON payload, formatted as follows:
If the function fails, or returns an invalid payload, CSML will convert it as a Null
value.
The following repository provides an easy way to run your own nodejs runtime: https://github.com/CSML-by-Clevy/csml-fn-endpoint-node
Follow the instructions to install and add your own apps!
CSML is a dynamically-typed language, even though it is written in Rust, a strong-typed, memory-safe, low-level language. It can handle a lot of different types but some magic happens behind the scenes to make it as easy for the developers to use the language.
In this section, let's learn more about how CSML handles the various quirks a chatbot needs to solve!
CSML is able to natively understand literal types (int
, float
, string
, ...).
integer
and float
are separate types, but most of the time you should not have to worry about it and consider it as a more generic and virtual number
type.
You can also express booleans with true
or false
.
Since CSML v1.1, you can also use \n
, \t
, \r
, \
and "
characters in strings, with proper escaping (\
and "
must be preceded by a \
while a single \
will be ignored). For example:
NULL
is its own type. Missing values evaluate to NULL
. The CSML interpreter will automatically parse the object with the usual dot notation x.y.z
and detect the type of the resulting property. If one of the properties in the chain does not exist or is not an object itself, it will evaluate to NULL
.
You can create an object by returning a JSON-like object from any function, or directly in the CSML code with the Object()
helper function or by using a shorthand notation similar to JSON format.
You can also iterate over an array (represented by items inside square brackets: ["a", "b", "c"]
) with the foreach
keyword, and access any of its items by using its index in square brackets notation: items[2]
.
Note that foreach creates a copy of each item as it iterates over the array. So for example, in this next example, the array is not modified:
You can modify the contents of an array either by assigning a new value to any of the items in the array, adding new items at the end with array.push(elem)
or removing the last element with array.pop()
.
You can also use Array methods to apply changes or get data from arrays.
To print any value as a string, simply use the Text(value)
component or use the curly-brace template syntax "{{value}}"
CSML functions are a simple way to define simple functional code that can be reused across all of your flows. They are defined in any flow file and use the same CSML syntax that is available in any other steps.
Conceptually, they can be viewed as simple placeholders that you can insert at any place in any flow, to perform repetitive tasks.
Declaring a function is similar to declaring a step with some minor differences:
the name of the function is preceded by the keyword fn
,
there is a list of parameters between parentheses after the name of the function,
but also, like a step declaration, it ends with a semicolon :
or is surrounded by braces {...}
(new syntax introduced in v1.10.0)
Here is an example of a simple my_double_add()
function, that takes 2 parameters, multiplies each of them by 2, then adds those numbers together, and returns the result:
There is no limit on how many functions a flow can have, and of course you can call a function from within another function. The above example could be rewritten as:
CSML native functions are isolated from the bot itself and can't interact with the memory or display messages. They have only access to non-state-binding keywords: do, if, foreach and return.
However, other builtins can be used!
Functions are by default attached to the flow where they are written. However, you can import functions from other flows to reuse them elsewhere!
You can also use a global import by not defining the flow from which a function can be imported.
Warning! If there is more than one flow defining a function with the same name, it will just import one of them randomly.
Don't use the same name on several functions, or specify which flow you want to import from
You can import multiple functions at once, and even rename them:
introduced in CSML v1.10.0
CSML Modules are an easy way to reuse CSML Functions across multiple chatbots or to provide access to your own services to other chatbot developers. You can create functions in separate flows that you can host on a repository to ease access.
A module at its core is simply a valid CSML flow, that exposes one or several functions, which can be imported into other flows without needing to copy the flow multiple times in multiple bots. An other benefit of using modules is that it makes it easier to replicate changes across multiple changes.
To use a module, you must first declare it as a dependancy to your chatbot. This is done in the modules section of the body when creating a new version of a chatbot or chatting with an unversioned chatbot.
Some example modules are given in this repository: https://github.com/CSML-by-Clevy/csml-modules
Let's import the buttons
module into a flow to access the YesNoButtons
function and make it easier to display Yes/No types of buttons.
The module specification is as follows:
name: buttons url: https://raw.githubusercontent.com/CSML-by-Clevy/csml-modules/master/modules/buttons.csml auth: none
Then you can simply import it in your CSML script as you would any other function, the only difference being the modules/
prefix that tells CSML that it should look for this function in the buttons
module and not in the bot's other flows.
CSML includes a native SMTP client, allowing you to easily send emails from your chatbot.
The JWT module lets you encode, decode and verify Json Web Tokens easily!
exemple: JWT(data).sign(algorithm, secret)
data
: json object to sign and convert to a JWT token
algorithm
: see below
secret
: a secret string used to sign the JWT
This method returns a properly encoded JWT as a string.
exemple: JWT(token).decode(algorithm, secret)
token
: the token to decode
Note: decode
does not try to verify that the token is valid. It simply decodes its payload back to the original form.
This method returns the full JWT data, in the form:{ "payload": { ... your data }, "headers": { "alg": ALG, "typ": "JWT" }}
exemple: JWT(token).verify(claims, algorithm, secret)
claims
: set of claims (in JSON form) to verify the JWT against
Note: only valid claims are verified. See the list of official claims in the JWT specs here.
This method returns the full JWT data, in the form:{ "payload": { ... your data }, "headers": { "alg": ALG, "typ": "JWT" }}
JWT supports the following algorithms: HS256
, HS384
, HS512
(corresponding to HMAC using SHA-256, SHA-384 and SHA-512 respectively).
The secret key must be a valid, url-safe string.
When a sign, decode or verify operation fails, Null
is returned and a say Error(error_message)
is emitted.
CSML includes a native HTTP client. The following verbs are accepted:
GET
POST
PUT
PATCH
DELETE
HTTP will automatically add the following headers to any request:
Content-Type: application/json;
Accept: application/json
In other words, HTTP requests only allow you to send and query json-formatted data.
To build your request, you can chain explicit methods to the HTTP function. No request is actually performed until .send()
is added, allowing you to gradually build your request.
Here is a simple example:
You can also create more complex requests:
The available methods are:
.get()
/ .post(body)
/ .put(body)
/ .patch(body)
/ .delete()
: set the request verb, and add an optional JSON-formatted body
.set(data)
: set the request headers, where {"x-api-key":"somevalue"}
is added as x-api-key:somevalue
headers
.auth(username, password)
set basic auth headers
.query(data)
: set the query strings, where {"key":"value"}
is automatically translated to ?key=value
.disable_ssl_verify()
: for situations where you know that the endpoint comes with an invalid SSL certificate, you can disable SSL verification to bypass this protection. This option should only be used if you know what you are doing!
You can check whether the call returned a response by using the .is_error()
method on the response object.
The .get_info()
method lets you inspect the response of HTTP calls, even when the call returns an error (in which case .get_info()
will also contain the body):
.is_error()
and .get_info()
are very useful methods to correctly handle HTTP responses!
You can find more info about the particular regex syntax used in the *_regex
methods on this link.
Return the same string in all uppercase characters.
Return the same string in all lowercase characters.
Return the same string with the first letter in uppercase. The rest of the string remains unchanged.
Returns a new string with both leading and trailing whitespace removed. .trim_left()
and .trim_right()
only trim the leading and trainling whitespace respectively.
Return the length of the target string.
Return whether the string contains another string or expression.
Replace the first, all or any occurrence matching the predicate:
Return whether a string starts with another string or expression.
Return whether a string ends with another string or expression.
Return all the matches of the string or expression in the target string, or Null if none are found.
About _regex methods:
The \
(backslash) character has a special meaning. For technical reasons, in all strings, it must be properly escaped, by convention by adding another \
in front of itself, to avoid being interpreted as a special character. For example, if you mean to write the exact string "\n"
you must in fact write \\n
, otherwise \n
will be interpreted as a line break.
This Python documentation explains why it especially matters in Regex syntax to escape backslashes: https://docs.python.org/2/howto/regex.html#the-backslash-plague
We follow this nomenclature for CSML Regex handling, so a single Regex backslash must be written as a "\\"
string, and an escaped backslash (that behaves as a literal "\"
string character) must in fact be escaped twice, once for being in a string, and once for being in a Regex: you have to write "\\\\"
to result in the Regex syntax \\
which in turn matches the literal "\"
string.
In a future release of CSML we might introduce a "raw string" method to bypass this limitation.
Return whether the given string represents a numerical value, an int, a float.
Split a string by a given separator and return an array containing all elements in order. The separator can be a single or multiple characters. If the separator can not be found in the string, the returned array will only contain the original string.
Cut a string between the start
and end
characters. Some rules apply:
If end
is not specified, all the characters after start
are returned.
When specified, end
must be ≥ start
.
If any of the parameters is < 0, the count is made from the end of the string.
Convert a string representing a number to a value cast as an integer or float:
Convert yaml to json and back
Encode and decode URI/URIComponent (see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/encodeURI#encodeuri_vs_encodeuricomponent)
Encode and decode HTML entities
Encode data using hash and HMAC methods:
The data
parameter is the data to encrypt or encode, and it must be a string.
The algorithm must be one of the following values:
Unlike the create_hash
method, create_hmac
requires a key to function. The list of accepted algorithms is the same.
The encoding must be either "base64"
or "hex"
.
When any operation fails, Null
is returned and a say Error(error_message)
is emitted.