Skip to content

Flows

A flow is a special YAML file that represents a graphical conversation flow, as a graph consisting of nodes and links. Flows can be edited using a graphical flow editor.

Introduction

For simple bots and IVRs, having users use plain Bubblescript is often a bridge too far; the learning curve is simply too high for end users who just want to configure and "click together" a simple bot.

To address this, in the past a Flows skill has been implemented, as a content-manageable way to create linear flows. The skill defines flows inside a single YAML file, where each flow consists of a linear sequence of steps, each of which can be guarded with a tag expression to check whether or not it executes. From this YAML file, Bubblescript is generated which executes the variuos steps in the flow.

The flows skill has a few shortcomings however:

  • Lack of extensibility: the flows skill needs to know everything about other skills in order to expose all step types
  • Flow steps cannot be made specific on various channels
  • The CMS user interface is suboptimal

To overcome this, work has begun to create a node-based conversation flow system, something that is quite common in other platforms. However, unlike other platforms, Bubblescript is still the backend of this flow system, giving it advantages in terms of extensibility and non-linearity.

Some design goals have been stated:

  • Each flow is managed in its own file
  • Flows have top-level properties (on which channel it executes, etc)
  • Each flow is flowchart that consists of typed nodes and links between nodes
  • Nodes and links have common properties, but also type-specific ones
  • Nodes can have variants that are more specific, e.g. 'show image', 'show audio', 'ask phone'
  • Nodes and links are aware of their capabilities and are only applicable on channels that support these capabilities
  • The types and variants of node is extensible so skills can add new node types and variants, in order to provide more specific functionality.
  • All configuration UI ("property panels") for flow/node/link properties is supported by React JSONSchema Form

The initial flow UI is a "flat" click-through UI; which shows a single click path through a tree.

The text/yaml+flow YAML schema defines a schema for a node-based dialog builder.

Documentation around flows is not yet complete

A flow consists of nodes and links.

Nodes have a type and a variant. Each type needs to have at least a single variant. Usually, the 'main' variant is called default.

Links have currently two types: next and expecting. Links can currently not be customized as the Bubblescript generator and the flow UI builder has predefined behaviour for links.

Flow extensibility

The flow_schema YAML file can be used to add additional node types and variants to the flow editor.

# adding new node types
types: []

# adding new node variants
variants: []

# removing platform-defined defaults
omit_types: []
omit_variants: []

For each bot, the flow system looks in all folders for script files called flow_schema, and merges these files together into a single definition file.

This way, you can add new variants and types, overwrite predefined variants or types, or disable certain variant/type combinations (by using the omit_ fields).

flow_schema files are merged in script order, so the 'highest' flow definition wins.

Platform defaults

The following is an overview of all base types and variants in the system.

omit_types: []
omit_variants: []
types:
  - caption: Next node
    kind: link
    priority: 1000
    requires_features: []
    schema:
      properties: {}
      type: object
    type: next
    ui_schema:
  - caption: Labelled choice
    kind: link
    priority: 1100
    requires_features: []
    schema:
      additionalProperties: false
      properties:
        intent:
          pattern: ^[a-z][a-zA-Z0-9_]*$
          title: Intent
          type: string
        label:
          $ref: '#/definitions/__i18nstring'
          title: Label
      required:
        - label
      type: object
    type: labelled
    ui_schema:
      intent:
        ui:widget: intent_picker
      label:
        ui:field: i18n
      ui:order:
        - label
        - intent
  - caption: Dialog trigger
    kind: link
    priority: 1200
    requires_features: []
    schema:
      additionalProperties: false
      properties:
        intent:
          pattern: ^[a-z][a-zA-Z0-9_]*$
          title: Intent
          type: string
        system_dialog:
          enum:
            - unknown
          title: System dialog
          type: string
      type: object
    type: trigger
    ui_schema:
      intent:
        ui:widget: intent_picker
      ui:order:
        - intent
        - system_dialog
  - caption: Condition
    kind: link
    priority: 1300
    requires_features: []
    schema:
      additionalProperties: false
      properties:
        condition:
          title: Condition
          type: array
        label:
          title: Label
          type: string
      required:
        - condition
      type: object
    type: condition
    ui_schema:
      condition:
        ui:field: flow_condition
      ui:order:
        - condition
        - label
  - caption: Entrypoint
    kind: node
    priority: 1000
    requires_features: []
    schema:
      additionalProperties: false
      properties:
        frontends:
          items:
            type: string
          title: Channels
          type: array
        guard:
          title: When
          type: array
      type: object
    type: entry
    ui_schema:
      frontends:
        ui:field: frontend_picker
      guard:
        ui:field: flow_condition
      ui:order:
        - frontends
        - guard
  - caption: Say
    kind: node
    priority: 1100
    requires_features: []
    schema:
      additionalProperties: false
      properties:
        text:
          $ref: '#/definitions/__i18nstring'
          title: Text
      required:
        - text
      type: object
    type: say
    ui_schema:
      text:
        ui:field: speechmarkdown
        ui:widget: autosize_textarea
  - caption: Ask
    kind: node
    priority: 1200
    requires_features: []
    schema:
      additionalProperties: false
      properties:
        assign:
          pattern: (?!^(?:answer|message|bot|dialog|user|conversation|event)$)^[a-z][a-zA-Z0-9_]*(.[a-z][a-zA-Z0-9_]*)*$
          title: Assign to
          type: string
        remember:
          title: Remember
          type: boolean
        repeat_text:
          $ref: '#/definitions/__i18nstring'
          title: Try again
        text:
          $ref: '#/definitions/__i18nstring'
          title: Question
      required:
        - text
      type: object
    type: ask
    ui_schema:
      assign:
        ui:placeholder: answer
      repeat_text:
        ui:field: i18n
        ui:widget: autosize_textarea
      text:
        ui:field: i18n
        ui:widget: autosize_textarea
      ui:order:
        - text
        - repeat_text
        - assign
        - remember
  - caption: Show
    kind: node
    priority: 1300
    requires_features: []
    schema:
      properties:
        caption:
          $ref: '#/definitions/__i18nstring'
          title: Caption
        url:
          format: uri
          title: Media URL
          type: string
      required:
        - url
      type: object
    type: show
    ui_schema:
      caption:
        ui:field: i18n
  - caption: Send
    kind: node
    priority: 1400
    requires_features: []
    schema:
      properties:
        message:
          title: Message
          type: string
        subject:
          title: Subject
          type: string
      type: object
    type: send
    ui_schema:
      message:
        ui:widget: autosize_textarea
      ui:order:
        - subject
        - message
  - caption: Escalate
    kind: node
    priority: 1700
    requires_features: []
    schema:
      additionalProperties: false
      properties:
        message:
          title: Help message
          type: string
        tag:
          title: Tag
          type: string
      required:
        - message
      type: object
    type: escalate
    ui_schema:
      message:
        ui:widget: autosize_textarea
      ui:order:
        - tag
        - message
        - '*'
  - caption: Control flow
    kind: node
    priority: 1800
    requires_features: []
    schema:
      additionalProperties: false
      properties: {}
      type: object
    type: control_flow
    ui_schema:
  - caption: Dial
    kind: node
    priority: 1900
    requires_features: []
    schema:
      additionalProperties: false
      properties:
        number:
          title: Number
          type: string
        sip:
          default: invite
          enum:
            - invite
            - refer
          title: SIP
          type: string
      required:
        - number
        - sip
      type: object
    type: dial
    ui_schema:
      number:
        ui:widget: phone_number
      sip:
        ui:options:
          inline: true
        ui:widget: radio
      ui:order:
        - number
        - sip
        - '*'
variants:
  - caption: Main
    generator_template: ''
    kind: node
    links:
      - next
    omit: []
    priority: 1000
    requires_features: []
    schema:
    type: entry
    type_ui_schema:
    ui_schema:
    ui_template: ''
    variant: default
  - caption: Intent
    generator_template: ''
    kind: node
    links:
      - next
    omit: []
    priority: 1300
    requires_features: []
    schema:
      additionalProperties: false
      properties:
        intent:
          pattern: ^[a-z][a-zA-Z0-9_]*$
          title: Intent
          type: string
      required:
        - intent
      type: object
    type: entry
    type_ui_schema:
    ui_schema:
      intent:
        ui:widget: intent_picker
      ui:order:
        - intent
    ui_template: ''
    variant: trigger
  - caption: Unknown
    generator_template: ''
    kind: node
    links:
      - next
    omit: []
    priority: 1100
    requires_features: []
    schema:
    type: entry
    type_ui_schema:
    ui_schema:
    ui_template: ''
    variant: unknown
  - caption: Root
    generator_template: ''
    kind: node
    links:
      - next
    omit: []
    priority: 1200
    requires_features: []
    schema:
    type: entry
    type_ui_schema:
    ui_schema:
    ui_template: ''
    variant: root
  - caption:
    generator_template: |
      say {{ say.text | as_string -}}

      {{ node | goto_next }}
    kind: node
    links:
      - next
    omit: []
    priority: 1000
    requires_features: []
    schema:
    type: say
    type_ui_schema:
    ui_schema:
    ui_template: ''
    variant: default
  - caption: Choice
    generator_template: |
      ask [{{ ask.text | as_string -}}
        {%- if ask.repeat_text -%}, {{ask.repeat_text | as_string-}}{%- endif -%}
      ]

      {%- if node.out.labelled -%}, expecting: [
        {%- for link in node.out.labelled -%}
        entity(return: {{ link.labelled.label | as_string }}, label: {{ link.labelled.label | as_string }}, match: json_build({{ link.labelled.label | as_string }})),
        {%- endfor -%}
      ]
      {% endif %}

      {% if ask.assign %}
        {{ ask.assign }} = answer.text
        {% if ask.remember %}remember {{ ask.assign }}{% endif %}
      {% endif %}

      {% if node.out.labelled %}
      branch answer do
        {% for link in node.out.labelled %}
          {{ link.labelled | intent_or_label }} ->
          {{ link | goto }}
        {% endfor %}
      end
      {% endif %}
    kind: node
    links:
      - labelled
      - trigger
    omit: []
    priority: 1000
    requires_features: []
    schema:
    type: ask
    type_ui_schema:
    ui_schema:
    ui_template: ''
    variant: default
  - caption: Open question
    generator_template: |
       {% if ask.assign %}{{ ask.assign }} = {% endif -%}
       ask {{ ask.text | as_string }}
       {% if ask.assign and ask.remember %}
       remember {{ ask.assign }}
       {% endif %}
       {{ node | goto_next}}
    kind: node
    links:
      - next
    omit:
      - repeat_text
    priority: 1100
    requires_features: []
    schema:
    type: ask
    type_ui_schema:
    ui_schema:
    ui_template: ''
    variant: open
  - caption: Prompt
    generator_template: |
      prompt {{ ask.text | as_string }}

      {% for link in node.out.labelled %}
      dialog trigger: {{ link.labelled | intent_or_label }}, label: {{ link.labelled.label | as_string}} do
        {{ link | goto }}
      end
      {% endfor %}

      {% for link in node.out.trigger %}
      {% assign t = link.trigger %}
      dialog {% if t.system_dialog %}__{{ t.system_dialog }}__{% elsif t.intent %}trigger: @{{t.intent}}{% elsif t.bml %}trigger: {{ t.bml | as_string }}{% endif %} do
        {{ link | goto }}
      end
      {% endfor %}
    kind: node
    links:
      - labelled
      - trigger
    omit:
      - assign
    priority: 1200
    requires_features: []
    schema:
    type: ask
    type_ui_schema:
    ui_schema:
    ui_template: ''
    variant: prompt
  - caption: Image
    generator_template: |
         show {{ show.variant }}({{ show.url | as_string }}){% if show.caption %}, caption: {{ show.caption | as_string }}{% endif %}

         {{ node | goto_next }}
    kind: node
    links:
      - next
    omit: []
    priority: 1000
    requires_features: []
    schema:
    type: show
    type_ui_schema:
      url:
        ui:options:
          giphy: true
        ui:widget: image
    ui_schema:
    ui_template: ''
    variant: image
  - caption: Video
    generator_template: |
         show {{ show.variant }}({{ show.url | as_string }}){% if show.caption %}, caption: {{ show.caption | as_string }}{% endif %}

         {{ node | goto_next }}
    kind: node
    links:
      - next
    omit: []
    priority: 1100
    requires_features: []
    schema:
    type: show
    type_ui_schema:
      url:
        ui:options:
          accept: video/*
        ui:widget: file
    ui_schema:
    ui_template: ''
    variant: video
  - caption: Audio
    generator_template: |
         show {{ show.variant }}({{ show.url | as_string }}){% if show.caption %}, caption: {{ show.caption | as_string }}{% endif %}

         {{ node | goto_next }}
    kind: node
    links:
      - next
    omit: []
    priority: 1200
    requires_features: []
    schema:
    type: show
    type_ui_schema:
      url:
        ui:options:
          accept: audio/*
        ui:widget: file
    ui_schema:
    ui_template: ''
    variant: audio
  - caption: File
    generator_template: |
         show {{ show.variant }}({{ show.url | as_string }}){% if show.caption %}, caption: {{ show.caption | as_string }}{% endif %}

         {{ node | goto_next }}
    kind: node
    links:
      - next
    omit: []
    priority: 1300
    requires_features: []
    schema:
    type: show
    type_ui_schema:
      url:
        ui:widget: file
    ui_schema:
    ui_template: ''
    variant: file
  - caption: Note
    generator_template: |
      create_conversation_note({{ send.subject | as_string }} + "
      " + {{ send.message | as_string }}
      {%- if send.note.tag -%}, [{{ send.note.tag | as_string }}]{%- endif -%})

      {{ node | goto_next }}
    kind: node
    links:
      - next
    omit: []
    priority: 1000
    requires_features: []
    schema:
      additionalProperties: false
      properties:
        tag:
          title: Note tag
          type: string
      type: object
    type: send
    type_ui_schema:
    ui_schema:
      tag:
        ui:field: tag_picker
    ui_template: ''
    variant: note
  - caption: E-mail
    generator_template: |
      mail({{ send.email.email | as_string }}, {{ send.subject | as_string }}, {{ send.message | as_string }})

      {{ node | goto_next }}
    kind: node
    links:
      - next
    omit: []
    priority: 1100
    requires_features: []
    schema:
      additionalProperties: false
      properties:
        email:
          title: E-mail address
          type: string
      type: object
    type: send
    type_ui_schema:
    ui_schema:
    ui_template: 'Send email with subject: {{ send.subject }}'
    variant: email
  - caption: SMS
    generator_template: |
      sms_notify({{ send.sms.phone | as_string }}, {{ send.subject | as_string }} + " " + {{ send.message | as_string }})

      {{ node | goto_next }}
    kind: node
    links:
      - next
    omit: []
    priority: 1200
    requires_features: []
    schema:
      additionalProperties: false
      properties:
        phone:
          title: Phone number
          type: string
      type: object
    type: send
    type_ui_schema:
    ui_schema:
      phone:
        ui:widget: phone_number
    ui_template: 'Send SMS to {{ send.sms.phone }} with subject: {{ send.subject }}'
    variant: sms
  - caption:
    generator_template: |
      close
    kind: node
    links: []
    omit: []
    priority: 1000
    requires_features: []
    schema:
    type: close
    type_ui_schema:
    ui_schema:
    ui_template: ''
    variant: default
  - caption: Pause
    generator_template: |
      pause {{ pause.timeout }}
      {{ node | goto_next }}
    kind: node
    links:
      - next
    omit: []
    priority: 1000
    requires_features: []
    schema:
    type: pause
    type_ui_schema:
    ui_schema:
    ui_template: ''
    variant: default
  - caption: Stop
    generator_template: |
      stop
      {{ node | goto_next }}
    kind: node
    links:
      - next
    omit:
      - timeout
    priority: 1100
    requires_features: []
    schema:
    type: pause
    type_ui_schema:
    ui_schema:
    ui_template: ''
    variant: stop
  - caption:
    generator_template: |
      tag "workflow::unassigned"
      tag "escalated"

      {% if escalate.tag %}
      tag {{ escalate.tag | as_string }}
      {% endif %}
      escalate({{ escalate.message | as_string }})

      {{ node | goto_next }}
    kind: node
    links:
      - next
    omit: []
    priority: 1000
    requires_features: []
    schema:
    type: escalate
    type_ui_schema:
    ui_schema:
    ui_template: |
      Escalate: <b>{{ escalate.message }}</b>
      {% if escalate.tag %} to: <b>{{ escalate.tag }}</b>{% endif %}
    variant: default
  - caption: Branch
    generator_template: |
      branch do
      {% for link in node.out.condition %}
      {{ link.condition | branch_condition }} ->
        {{ link | goto }}
      {% endfor %}
      end
    kind: node
    links:
      - condition
    omit: []
    priority: 1000
    requires_features: []
    schema:
    type: control_flow
    type_ui_schema:
    ui_schema:
    ui_template: ''
    variant: branch
  - caption: Go to flow
    generator_template: |
      {% if node.control_flow.goto.flow %}
      goto flow_{{ node.control_flow.goto.flow }}
      {% endif %}
    kind: node
    links: []
    omit: []
    priority: 1100
    requires_features: []
    schema:
      additionalProperties: false
      properties:
        flow:
          title: Flow
          type: string
      type: object
    type: control_flow
    type_ui_schema:
    ui_schema:
      flow:
        ui:widget: flow_picker
    ui_template: ''
    variant: goto
  - caption: Close
    generator_template: |
      close
    kind: node
    links: []
    omit: []
    priority: 1200
    requires_features: []
    schema:
    type: control_flow
    type_ui_schema:
    ui_schema:
    ui_template: ''
    variant: close
  - caption: Pause
    generator_template: |
      pause {{ node.control_flow.pause.timeout }}
      {{ node | goto_next }}
    kind: node
    links:
      - next
    omit: []
    priority: 1300
    requires_features: []
    schema:
      additionalProperties: false
      properties:
        timeout:
          title: Wait time (seconds)
          type: number
      required:
        - timeout
      type: object
    type: control_flow
    type_ui_schema:
    ui_schema:
    ui_template: ''
    variant: pause
  - caption: Number
    generator_template: |
      tag "dial:{{ dial.number }}"

      {% if dial.sip == 'invite' %}
      dial "{{ dial.number }}"
      {% else %}
      refer "{{ dial.number }}"
      {% endif %}

      tag "dial:failed"

      {{ node | goto_next }}
    kind: node
    links:
      - next
    omit: []
    priority: 1000
    requires_features: []
    schema:
    type: dial
    type_ui_schema:
    ui_schema:
    ui_template: 'Dial: {{ number }}'
    variant: dial

Generated from platform version: 2.50.0-story-719-nlp-pipeline.285dbf

CMS definitions

A definition for a single flow:

type: flow
title: "Single flow"

storage:
  type: script
  script_title: single_flow

generators:
  - type: flow
    script_title: generated/single_flow

A definition for a collection of flows:

title: "My flows"
type: flow
storage:
  type: script
  collection: flows/
  collection_editable: true
generators:
  - type: flow
    script_title: generated/{{ data_script_title }}