Skip to content

DSUL.ai (v1)

Digital Service Univeral Language.
A language created by Prisme.ai to describe applications.
This language is universal, transparent and intuitive. Prisme.ai has the ambition to make DSUL a new standard of connected Virtual Assistant creation.

Sommaire

  1. Hello World
  2. Section Intents.when
  3. Section Intents.parameters
  4. Section Entities
  5. Sections Intents.outputContexts & Intents.inputContexts
  6. Section Intents.do
  7. Section Resources
  8. Translations

Hello World

Intents:
    HelloWorld:
        when:
            match phrases:
                - Salut
                - bonjour
        do:
            - say text: Bienvenue à toi !

Section Intents.when

It is this section that defines when an intent is triggered. Two mechanisms are available: match phrases et match events.

match phrases

match phrases allows to detect an intent with training sentences:

Intents:
    Welcome Intent:
        when:
            match phrases:
                - Salut
                - bonjour 

Each time a sentence similar to these is sent by the user, the intent associated workflow (Intent.do) will be executed.

match events

Rather than waiting for a user sentence, it is also possible to automatically trigger intents with event buttons :

Intents:
    - name: step.one
        ...
        do:
            - say event:
                text: Accéder à l'étape 2
                value: STEP_2

In order to be triggered as soon as this STEP_2 event is emitted, another intent can declare it in match events :

Intents:
    - name: step.one
        when:
            match phrases:
                - je veux accéder à l'intent secrète !
        do:
            - say text: "Voici ce que je peux te proposer :"
            - say event:
                text: Accéder à l'étape 2
                value: STEP_2

    - name: step.two
        when:
            match events:
                - STEP_2
        do:
            - Text: Bravo !

As explained in section Intents.do, it is also possible to emit events without any user intervention.

Intents.parameters

Within training phrases (when.match phrases section), it is possible to specify parameters that would be extracted from user messages in order to gather complementary details about their request.
Each of these parameters must be described in Intents.*.parameters section:

  • name : parameter name, the one training phrases & workflow responses will refer to
  • entity : indicates the entity's name, which must be detailed in Entities section if it's a custom entity. Differents builtin entities are automatically provided to extract generic kinds of information (numbers, dates, duration, ... exhaustive list within Entities section)
  • required : configures whether this is a required parameter (false by default)
  • questions : indicates a few alternative questions to ask if the parameter is missing although required is set to true
  • displayEntityValues : when set to true, displays every possible values as clickable buttons
Intents:
    I_LIKE_FRUIT:
        when:
            match phrases:
                - j'aime la [poire](fruit)
                - j'aime bien les [pommes](fruit)
                - j'aime les fruits
        parameters:
            - name: fruit
                entity: Fruit
                required: true
                questions:
                    - Quel fruit tu aimes ?
        do:
            - say text: je suis vraiment ravi d'apprendre que tu aimes les $fruit !

Each parameter expected by training phrases must be specified as follows :

  • Parameters' value must be enclosed in square brackets (poire, pommes)
  • Parameters' name must be enclosed in parenthesis right next to the closing bracket

In the example above, one training phrase does not contain any parameter : j'aime les fruits

If someone would send this sentence or an equivalent formulation without the expected parameter inside, and since required is true, the application would automatically ask one of the defined questions : Quel fruit tu aimes ?

As the entity name (Fruit) is not prefixed with ggwg/, we know that is a custom entity described in Entities section.

Entities

An entity corresponds to a category of information that might be extracted from user messages, and Prisme.ai provides some builtin entities available to all intentions :

  • ggwg/amount-money : Detects amounts of money : "20€", "15€", ...
  • ggwg/date-time : Detects date ("Friday, October 23th"), and more informal expressions as well :"Friday", "next week", ...
  • ggwg/duration : Detects durations : "1h", "during 20 minuts", "3 months", ...
  • ggwg/number : Detects numbers : "1", "525", "18", ...
  • ggwg/ordinal : Detects ordinal numbers : "premier", "troisième", "second"
  • ggwg/percentage : Detects percentages : "15%", "20 %", ...
  • ggwg/temperature : Detects temperatures : "15°C"
  • ggwg/any : Detects anything that fits right where the training phrase is expecting the parameter

If one needs an additional entity, it can be specified within Entities :

Entities:
    Fruit:
        automaticallyExtensible: false
        useSynonyms: true    
        values:
            fr:
                - value: poire
                  synonyms:
                    - poires
                - value: banane
                - value: pomme
                  synonyms:
                    - pome

Main options :

  • automaticallyExtensible : false by default, this allows any value to be considered as the entity even if it's not part of the configured values
  • useSynonyms : true by default, this allows to bind a list of synonyms to a value. Even if the user types one of the synonyms, the intent would always receive the standardized value (values.value)
  • values : Only mandatory setting, this is where you will list every possible values, categorized by language

In case of a multilingual assistant, automaticallyExtensible and useSynonyms options can be set differently depending on the language :

Entities:
    Fruit:
        automaticallyExtensible:
            fr: false
            en: true
        useSynonyms:
            fr: true    
            en: false

Structure of values :

  • First depth level of values indicate needed languages' code (fr, en, ...)
  • Each language structure contains a list of value, where each of them have optional synonyms
  • Important : synonyms option must be exactly aligned with the beginning of value, otherwise the file could not be parsed

Intents.outputContexts & Intents.inputContexts

Every intent maintains a context where all retrieved parameters are saved. By default, an intent's context expires as soon as we jump to another intent.

However, any intent might expose its context to the following ones for a specified lifetime (as a number of messages):

Intents:
    topup.ask:
        parameters:
            - name: amount
              entity: ggwg/amount-money    
        outputContexts:
            TopUp: 3

Any intent can now declare TopUp as an input context in order to access amout parameter, as long as they are triggered during the 3 first messages after topup.ask :

Intents:
    topup.confirm:
        inputContexts:
            - TopUp

Moreover, an intent specifying inputContexts cannot be triggered by training phrases if the input contexts have not been emitted.

Intents:
    topup.ask:
        outputContexts:
            TopUp: 3
        when:
            match phrases:
                - recharge mon forfait de [5 €](amount)
                - je veux recharger [10 €](amount) sur mon forfait
        parameters:
            - name: amount
              entity: ggwg/amount-money
              required: true
              questions:
                  - de combien voulez-vous recharger votre forfait ?                 

        do:
            - say text: Confirmez-vous la demande de rechargement de $amount € sur votre forfait ?
            - say button:
                text: Oui
                value: Je confirme

    topup.confirm:
        inputContexts:
            - TopUp
        when:
            match phrases:
                - Je confirme
        parameters:
            - name: amount
              entity: ggwg/amount-money
        do:
            - say text: Très bien, $amount € ont bien été rechargés sur votre forfait.

Important : An intent receiving a context must indicate again the parameters name & entity in order to use them.

User authentication

In order to authenticate webchat users, we can pass authenticating data to the webchat injection script's originalRequest parameter. Any intent can access these data by declaring originalContext in both inputContexts and parameters :

Intents:
    hello:
        inputContexts:
            - originalRequest
        when:
            match phrases:
                - Salut
                - Bonjour
        parameters:
            - name: originalRequest
              entity: ggwg/any
        do:
            - say text: Bonjour $originalRequest.firstName !

In the webchat injection script, these user data would be passed as follows :

<script type="text/javascript" src="//cdn.gogowego.com/wegobot_inject.js" charset="UTF-8"></script>
<script type="text/javascript">
injectWegobot(
{
  "botId": "bVXMBVIjIV",
  "originalRequest": {
      "firstName": "Peter"
  }
  ...
}
</script>

Section Intents.Do

Visual responses

Every visual responses are prefixed with say :

Intents:
  show_responses:
    ...
    do:
      - say text: Montre moi plus d'actualités
      - say random text:
          - Premiere formulation
          - Deuxieme formulation
      - say richText:
          - |-
            <h3>Texte enrichi</h3>
            <p>Sur plusieurs lignes</p>
      - say random richText:
          - <h3>Premiere possibilité de texte enrichi</h3>
          - <h3>Deuxieme possibilité de texte enrichi</h3>
      - say button:
          text: Plus d'actu
          value: Montre moi plus d'actualités
      - say card:
          title: Titre card
          text: 'Texte en dessous'
          image: https://.../url_image.png
          buttons:
            - say button:
                text: Titre Bouton 1 card
                value: Valeur bouton 1 card
      - say link:
          text: Titre lien
          value: 'https://www.google.com'

Event buttons

The first manner to trigger an event is to send an event button to the user with say event :

Intents:
  My_Intent:
    ...
    do:    
      - say event:
          text: Plus d'actu
          value: MORE_NEWS

As soon as the user clicks this button, MORE_NEWS event will be emitted, trigerring any intent that declared MORE_NEWS in when.match events.

It is also possible to send custom data within event payload :

Intents:
  My_Intent:
    ...
    parameters:
      - name: category
        entity: ggwg/any
        required: true    
      - name: page
        entity: ggwg/number
        required: true
    do:    
      - say event:
          text: Plus d'actu
          value: MORE_NEWS
          payload:  
            - type: 'parameters'
              value:
                category: $category
                page: $page  

  ShowMoreNews:
    when:
      match events:
        - MORE_NEWS
    parameters:
      - name: category
        entity: ggwg/any
      - name: page
        entity: ggwg/number
    do:
      ...

Emit an event without user interaction

We can also automatically trigger an event as soon as a given intent is run, without manual intervention from the user :

Intents:
    My_Intent:
        ...
        do:
            - say text: Vous allez être redirigé vers une nouvelle intent !
            - jump: MORE_NEWS

    Intent_2:
        when:
            match events:
                - MORE_NEWS
        ...

As soon as My_Intent required parameters are fulfilled and the workflow executed, the jump would automatically trigger Intent_2 intent.

Forms

Forms let you ask the user various informations & send them to another intent through event buttons.

Forms are displayed using say form response, which have the following 2 required fields :

  • parameters : describes the informations to ask, identical to the Intents parameters field in its base version as shown below
  • submit : submit button, for now it can only be an event button
Intents:
  Soumettre:
      when:
        match phrases:
          - remplir le formulaire
      do: 
        - say form:
            parameters:
              - name: firstName
                question: Prénom
                entity: ggwg/any
              - name: age
                question: Age
                entity: ggwg/number
            submit:
              say event:
                text: Envoyer
                value: PROCESS_FORM 

  ValidationFormulaire:
    when:
      match events:
        - PROCESS_FORM
    parameters:
      - name: firstName
        required: true 
        entity: ggwg/any
      - name: age
        entity: ggwg/number
    do:
      - say text: Tu t'appelles donc $firstName et tu as $age ans 
      - call write dataflow: Ages

Parameters accept these other options :

  • validator : custom validations. May include a pattern sub structure configuring a regex
  • HTMLAttributes : html attributes directly injected in the input

validator.pattern structure :

  • value : the regex itself
  • message : error message to show on validation failure

About HTMLAttributes :
HTMLAttributes fields will be directly injected into the corresponding HTML input element. This implies that all HTML attributes supported by a <input /> are also supported by the parameter field.

For example, it is possible to give a type to your parameter (type: 'password' | 'checkbox' | 'email' ...) in order to customize the display and the behaviour of the field.

Example :

- say form:
          parameters:
            - name: mail
              question: Mail
              required: true
              entity: ggwg/any
              HTMLAttributes:
                type: email
              validator:
                pattern:
                value: >-
                    /^[a-zA-Z0-9.!#$%&’*+/=?^_`{|}~-]+@[a-zA-Z0-9-]+(?:.[a-zA-Z0-9-]+)*$/
                message: Please, enter a valid mail.
            - name: password
              question: Password
              required: true
              entity: ggwg/any
              HTMLAttributes:
                type: password
            - name: birthdate
              question: Birthdate (dd/mm/yyyy)
              required: true
              entity: ggwg/any
              validator:
                pattern:
                  value: '/[0-9]{2}/[0-9]{2}/[0-9]{4}/g'
                  message: 'Expected format : dd/mm/yyyy'
        submit:
            say event:
              text: Ok.
              value: ACCOUNT_FORM_COMPLETED

Channels customization

Intents.do can be adapted for each available channel :

Intents:
    Welcome Intent:
        description: un intent qui dit bonjour
        when:
          match phrases:
            - Hello
        do:
          - on default:
              - say text: default channel
          - on web:
              - say text: Depuis le web
          - on telephony:
              - say text: depuis un appel
          - on speaker:
              - say text: depuis un speaker (alexa & cie)
          - on messenger:
              - say text: depuis messenger
          - on whatsapp:
              - say text: depuis whatsapp
          - on slack:
              - say text: depuis slack

All response modules presented in this documentation can be used inside each customized channel.

Conditions

do:
  - if:
      condition: age < 18
      then:
        - say text: Tu es donc mineur !
  - else if:
      condition: age > 50
      then:
        - say text: En voilà un avec de la bouteille !
  - else:
        then:
            - say text: Bravo, te voilà majeur !  
  - say text: Ce texte sera affiché quelque soit la condition exécutée. Il doit toujours se situer après les conditions et jamais avant.

Every usual responses and actions might be used within these conditions.
We can also combine multiple sub conditions using and and or operators :

- if:
    condition: age > 18 and age < 50    

More comparison operators are available :

  • a > b : a greather than b
  • a < b : a lower than b
  • a == b : a equals to b
  • a != b : a not equals to b
  • a exists : parameter a is defined
  • a not-exists : parameter a is undefined

If willing to use both conditions & channels customization, conditions must be located inside custom channels : the inverse is not possible.

Actions & Ressources

Ressources are differents services which can be created and shared between bots from your Prisme.ai dashboard. Except for Cron, every resources provide one or more actions.

Each action name is prefixed by a call, and those that do not need any other parameter than the corresponding resource name can be written on a single line :

    ...
    - call myAction: myResourceName

And, in its customizable form :

    ...
    - call myAction:
        resource: myResourceName
        parameter1: value1

When saving a DSUL model containing actions, new resources are linked to their internal id inside an automatically created Resources section. This Resources section must not be manually edited, as it could break existing actions.

Actions result display & Repeat

Although most actions returning data provide a responses parameter configuring how these data are visually formatted, it is sometimes necessary to separate the action execution time from when its result is displayed.

In this regard, some actions have an output parameter specifying the name of a context variable in which to store their result, so that it can be displayed later on within the workflow.

When this result is a list (as it might be the case for webbook or dataflow actions), we need to loop on each entry of this list using a repeat block :

Intents:
    MyIntent:
        ...
        do:
            - call myAction:
                resource: myResourceName
                output: resultVariable
            - repeat:
                on: $resultVariable
                then:
                    - say text: $text

For more complex or mulit-step processes, these repeat blocks can themselves nest new actions and repeats.

Searchengine

The only searchengine action transmits user message to the searchengine and automatically formats matching web pages as cards.

Intents:
    Fallback Intent:
        isFallback: true
        do:
            - call search: MySearchengine

Here, we call search within Fallback Intent workflow so that the searchengine will take over only when the application cannot process the request itself.

An option onFailure let you configure a workflow to execute when no results are found :

Intents:
    Fallback Intent:
        isFallback: true
        do:
            - call search:
                resource: MySearchengine
                onFailure:
                    - say text: I could not find anything relevant to your question ...
                    - say link:
                        text: Go to website
                        value: http://...

API

Resource documentation

Action webhook

The only webhook action simply makes an HTTP call to the underlying route :

Intents:
    Call_API:
        when:
            match phrases:
                - Appelle mon webhook
                - Lance le webhook
        do:
            - call webhook: MyAPI

In its simplest form as above, the request configuration is entirely driven by its resource.
However, all API resource options can be overriden on the action level :

  • parameters : object of parameters to inject as GET parameters and inside the request body (unless body is specified)
  • method : GET, POST, PUT, DELETE. Defaults to POST
  • body : custom request body
  • headers : HTTP deaders
  • auth: sub object containing username and password to configure HTTP Basic Auth
  • JSONPathFilter : JSON path to extract from the returned response
  • output : name of the variable in which to store the extracted result

If method is not GET and body is not specified, the request body is automatically built as follows :

interface request {
  timestamp: string; // DateTime of the call
  sessionId: string; // Session identifier
  bot: {
    id: string; // Wizard identifier
  };
  user: { // Current user
    id: string; // User ID
    location: string; // Location of the user if authorized by the user
    platform: string; // User agent platform
    conversationSize: number; // Number of messages in the conversation
    conversationId: string; // ConversationId: string; // ConversationId identifier
  };
  userMessage: { // User message
    event: string; // If the user's message is an event, its name
    text: string; // If it's text, its content
    payload: {}; // If the message contains an arbitrary object, this one
  }
  originalRequest: {}; // Object of arbitrary parameters passed to the injection script
  lang: string; // User's browser language
  query: string; // Request sent by the user
  event: string; // Event sent by the user
  fulfillment: { // Responses generated by the workflow before arriving at the function
    stream: {
      type: string;
      resourceId: string;
    }[];
  intent: { // Intention corresponding to the user's request
    name: string;
    confidence: number; // Match index
    inputs: {}; // Value of intention parameters
  };
  contexts: { // Contexts
    lifespan: string; // Lifetime
    required: boolean; // If required
    name: string; // Name
    parameters: {}; // Associated parameters
    scope: string; // Perimeter
  }[];
  endConversation: boolean; // If the convesation is over
}

Tuning request inputs

With simple GET parameters (i.e http://.../?foo=contentOfMyVariable):

- call webhook:
    resource: MyAPI
    method: GET
    parameters:
        foo: $myVariable 

With a custom request body :

- call webhook:
    resource: MyAPI
    method: POST
    parameters:
        foo: $myVariable 
    body:
        params:
            foo: $myVariable

Output

If output is not specified, extracted result is considered as standard Prisme.ai objects in order to leave full control over the responses to be created to the underlying API.

However, if the API does not return Prisme.ai standards compliant responses, we can store its response with output and JSONPathFilter in order to manage visual responses with DSUL / Prisme.ai dashboard :

Intents:
    Call_API:
        when:
            match phrases:
                - Appelle mon webhook
                - Lance le webhook
        do:
            - call webhook:
                resource: MyAPI
                output: names
                JSONPathFilter: .result.data
            - repeat:
                on: $names
                then:
                    - say card:
                        title: $name
                        text: "$tel"

Instead of directly displaying result, one might need to process returned data further using a function.

Dataflow

Action read dataflow

Intents:
    recipes.ideas:
        description: ask for some recipes ideas
        when:
            match phrases:
                - Idée d'un plat à cuisiner ? 
                - Que pourrais-je cuisiner ?
                - Je ne sais pas quoi faire pour manger
        parameters:
            - name: ingredient
              required: true
              entity: Ingredient
              questions:
                - De quel ingrédient disposes-tu ?
        do:
            - call read dataflow:
                resource: MyIngredientsDataflow
                query:
                    ingredient: $ingredient
                responses:
                    - say card:
                        title: $recipe
                        text: Il faut $count $ingredient

As for webhook, it is also possible to save its result in a context variable and display it later on :

Intents:
    recipes.ideas:
        description: ask for some recipes ideas
        when:
            match phrases:
                - Idée d'un plat à cuisiner ? 
                - Que pourrais-je cuisiner ?
                - Je ne sais pas quoi faire pour manger
        parameters:
            - name: ingredient
              required: true
              entity: Ingredient
              questions:
                - De quel ingrédient disposes-tu ?
        do:
            - call read dataflow:
                resource: MyIngredientsDataflow
                query:
                    ingredient: $ingredient
                output: recipes
            - repeat:
                on: $recipes
                then:
                    - say card:
                        title: $recipe
                        text: Il faut $count $ingredient

An option onFailure let you configure a workflow to execute when no results are found :

- call read dataflow:
    resource: MyIngredientsDataflow
    query:
        ingredient: $ingredient
    output: recipes    
    onFailure:
        - say text: Sorry, I did not find anything matching $ingredient ...

Action write dataflow

Insert a new dataflow row, containing every declared parameters :

Intents:
    ingredients.new:
        description: Enregistre un nouvel ingrédient
        when:
            match phrases:
                - j'ai acheté un [oeuf](ingredient)
        parameters:
            - name: ingredient
              entity: Ingredient
            - name: count
              entity: ggwg/number
              defaultValue: 1
        do:
            - call write dataflow: MyIngredientsDataflow
            - say text: J'ai retenu que tu as maintenant un $ingredient !

If needed, you can also choose which parameters to write with data option :

- call write dataflow:
    resource: MyIngredientsDataflow
    data:
        MyIngredient: $ingredient
        count: $count  
        comment: "Some hardcoded value"

Action update dataflow

Updates a row from the dataflow with every declared parameters :

Intents:
    ingredients.delete:
        description: Supprime un ingrédient
        when:
            match phrases:
                - Ma quantité d'[oeuf](ingredient) a changé !
        parameters:
            - name: ingredient
              entity: Ingredient
            - name: count
              entity: ggwg/number
              required: true
              questions:
                - Combien en as-tu ?
        do:
            - call update dataflow:
                resource: MyIngredientsDataflow
                query:
                    ingredient: $ingredient
            - say text: J'ai retenu que tu as maintenant $count $ingredient !

As for write dataflow, the data option lets you choose which parameters to write :

- call update dataflow:
    resource: MyIngredientsDataflow
    query:
        ingredient: $ingredient    
    data:
        count: $count  

Action delete dataflow

Delete a row from the dataflow :

Intents:
    ingredients.delete:
        description: Supprime un ingrédient
        when:
            match phrases:
                - je n'ai plus d'[oeuf](ingredient)
        parameters:
            - name: ingredient
              entity: Ingredient
        do:
            - call delete dataflow:
                resource: MyIngredientsDataflow
                query:
                    ingredient: $ingredient
            - say text: J'ai retenu que tu n'as plus de $ingredient ! 

Function

Resource documentation

Action function

Allows you to execute arbitrary javascript code to generate a response or a value to be used in the workflow.

Intents:
  Welcome Intents:
    when:
      MatchPhrases:
        - Hi
    do:
      - say text: Execute a function
      - say event:
          text: Go !
          value: CALL_FUNCTION

  RunFunction:
    when:
      MatchEvents:
        - CALL_FUNCTION
    do:
      - call function:
            resource: MyFunction
            output: myVariable
      - say text: Function returns $myVariable

Translations

In an application, 4 differents elements might be translated :

  1. Training phrases
  2. Parameters
  3. Responses
  4. Entities

Nested translations

In order to translate these 4 elements, we only need to duplicate translated content in one substructure per language :

Settings:
    languages:
        - fr
        - en

Intents:
    MyCountry:
        when:
            match phrases:
                fr:
                    - Je viens du [Portugal](country)
                    - Je suis originaire de [France](country)
                    - Mon pays d'origine est le ...
                    - Mon pays d'origine est le [Maroc](country)
                en:
                    - I'm from [Portugal](country)
                    - Hello, I'm coming from [France](country)
                    - Hello, I'm coming from [Morocco](country)
        parameters:
            fr:
                - name: country
                  entity: Country
                  required: true
                  displayEntityValues: true
                  questions:
                    - D'où viens-tu ?
                    - De quel pays viens-tu ?
            en:
                - name: country
                  entity: Country
                  required: true
                  displayEntityValues: true
                  questions:            
                    - Where are you from ?
                    - Where do you come from ?
        do:
            fr:
                - say text: Et c'est beau le $country ?
            en:
                - say text: Is $country beautiful to see ?

Entities:
    Country:
        values:
            fr:
                - value: France
                - value: Portugal
                - value: Maroc
            en:
                - value: France
                - value: Portugal
                - value: Morocco

Settings.languages is important, indicating which languages are supported by the application.