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
    requires_features: []
    schema:
      properties: {}
      type: object
    type: next
    ui_schema:
  - caption: Labelled choice
    kind: link
    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
    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
    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
    requires_features: []
    schema:
    type: entry
    ui_schema:
  - caption: Say
    kind: node
    requires_features: []
    schema:
      additionalProperties: false
      properties:
        text:
          $ref: '#/definitions/__i18nstring'
          title: Text
      required:
        - text
      type: object
    type: say
    ui_schema:
      text:
        ui:field: i18n
        ui:widget: autosize_textarea
  - caption: Ask
    kind: node
    requires_features: []
    schema:
      additionalProperties: false
      properties:
        assign:
          pattern: ^[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
    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
      url:
        ui:widget: file
  - caption: Create note
    kind: node
    requires_features: []
    schema:
      properties:
        message:
          title: Message
          type: string
        subject:
          title: Subject
          type: string
      type: object
    type: note
    ui_schema:
      message:
        ui:widget: autosize_textarea
  - caption: Close conversation
    kind: node
    requires_features: []
    schema:
      properties: {}
      type: object
    type: close
    ui_schema:
  - caption: Pause
    kind: node
    requires_features: []
    schema:
      additionalProperties: false
      properties:
        timeout:
          title: Wait time (seconds)
          type: number
      type: object
    type: pause
    ui_schema:
  - caption: Escalate
    kind: node
    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
    requires_features: []
    schema:
      additionalProperties: false
      properties: {}
      type: object
    type: control_flow
    ui_schema:
variants:
  - caption:
    generator_template: |
      {{ node | goto_next }}
    kind: node
    links:
      - next
    omit: []
    requires_features: []
    schema:
    type: entry
    ui_schema:
    ui_template: ''
    variant: default
  - caption:
    generator_template: |
      say {{ say.text | as_string -}}

      {{ node | goto_next }}
    kind: node
    links:
      - next
    omit: []
    requires_features: []
    schema:
    type: say
    ui_schema:
    ui_template: ''
    variant: default
  - caption:
    generator_template: |
      ask {{ ask.text | as_string -}}
      {%- if node.out.labelled -%}
      , expecting: [
      {%- for link in node.out.labelled -%}
      {{ link.labelled.label | as_string }},
      {%- endfor -%}
      ]
      {% 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: []
    requires_features: []
    schema:
    type: ask
    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
    requires_features: []
    schema:
    type: ask
    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
    requires_features: []
    schema:
    type: ask
    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: []
    requires_features: []
    schema:
    type: show
    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: []
    requires_features: []
    schema:
    type: show
    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: []
    requires_features: []
    schema:
    type: show
    ui_schema:
    ui_template: ''
    variant: audio
  - 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: []
    requires_features: []
    schema:
    type: show
    ui_schema:
    ui_template: ''
    variant: video
  - caption:
    generator_template: |
      create_conversation_note("lalala")

      {{ node | goto_next }}
    kind: node
    links:
      - next
    omit: []
    requires_features: []
    schema:
    type: note
    ui_schema:
    ui_template: ''
    variant: default
  - caption: Send E-mail
    generator_template: |
      mail({{ note.email.email | as_string }}, {{ note.subject | as_string }}, {{ note.message | as_string }})

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

      {{ node | goto_next }}
    kind: node
    links:
      - next
    omit: []
    requires_features: []
    schema:
      additionalProperties: false
      properties:
        phone:
          title: Phone number
          type: string
      type: object
    type: note
    ui_schema:
      phone:
        ui:widget: phone_number
    ui_template: 'Send SMS to {{ note.sms.phone }} with subject: {{ note.subject }}'
    variant: sms
  - caption:
    generator_template: |
      close
    kind: node
    links: []
    omit: []
    requires_features: []
    schema:
    type: close
    ui_schema:
    ui_template: ''
    variant: default
  - caption:
    generator_template: |
      pause {{ pause.timeout }}
      {{ node | goto_next }}
    kind: node
    links:
      - next
    omit: []
    requires_features: []
    schema:
    type: pause
    ui_schema:
    ui_template: ''
    variant: default
  - caption: Stop
    generator_template: |
      stop
      {{ node | goto_next }}
    kind: node
    links:
      - next
    omit:
      - timeout
    requires_features: []
    schema:
    type: pause
    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: []
    requires_features: []
    schema:
    type: escalate
    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: []
    requires_features: []
    schema:
    type: control_flow
    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: []
    requires_features: []
    schema:
      additionalProperties: false
      properties:
        flow:
          title: Flow
          type: string
      type: object
    type: control_flow
    ui_schema:
      flow:
        ui:widget: flow_picker
    ui_template: ''
    variant: goto

Generated from platform version: 2.35.0

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 }}