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
Nodes and links¶
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.
types:
- priority: 1000
type: next
kind: link
caption: Next node
schema:
type: object
properties: {}
ui_schema:
- priority: 1100
type: labelled
kind: link
caption: Labelled choice
schema:
type: object
required:
- label
properties:
label:
title: Label
$ref: '#/definitions/__i18nstring'
dtmf:
type: string
enum:
- '1'
- '2'
- '3'
- '4'
- '5'
- '6'
- '7'
- '8'
- '9'
- '0'
- '*'
- '#'
title: DTMF Key
intent:
type: string
title: Intent
pattern: ^[a-z][a-zA-Z0-9_]*$
additionalProperties: false
ui_schema:
label:
ui:field: i18n
intent:
ui:widget: intent_picker
ui:order:
- label
- intent
- dtmf
- priority: 1200
type: trigger
kind: link
caption: Dialog trigger
schema:
type: object
properties:
intent:
type: string
title: Intent
pattern: ^[a-z][a-zA-Z0-9_]*$
system_dialog:
type: string
enum:
- unknown
title: System dialog
additionalProperties: false
ui_schema:
intent:
ui:widget: intent_picker
ui:order:
- intent
- system_dialog
- priority: 1300
type: condition
kind: link
caption: Condition
schema:
type: object
required:
- condition
properties:
label:
type: string
title: Label
condition:
type: array
title: Condition
additionalProperties: false
ui_schema:
ui:order:
- condition
- label
condition:
ui:field: flow_condition
- priority: 1000
type: entry
kind: node
caption: Entrypoint
schema:
type: object
properties:
guard:
type: array
title: When
frontends:
type: array
items:
type: string
title: Channels
additionalProperties: false
ui_schema:
guard:
ui:field: flow_condition
frontends:
ui:field: frontend_picker
ui:order:
- frontends
- guard
- priority: 1100
type: say
kind: node
caption: Say
schema:
type: object
required:
- text
properties:
text:
title: Text
$ref: '#/definitions/__i18nstring'
additionalProperties: false
ui_schema:
text:
ui:widget: autosize_textarea
ui:field: speechmarkdown
- priority: 1200
type: ask
kind: node
caption: Ask
schema:
type: object
required:
- text
properties:
text:
title: Question
$ref: '#/definitions/__i18nstring'
assign:
type: string
title: Assign to
pattern: (?!^(?:answer|message|bot|dialog|user|conversation|event)$)^[a-z][a-zA-Z0-9_]*(.[a-z][a-zA-Z0-9_]*)*$
remember:
type: boolean
title: Remember
repeat_text:
title: Try again
$ref: '#/definitions/__i18nstring'
additionalProperties: false
ui_schema:
text:
ui:widget: autosize_textarea
ui:field: i18n
assign:
ui:placeholder: answer
repeat_text:
ui:widget: autosize_textarea
ui:field: i18n
ui:order:
- text
- repeat_text
- assign
- remember
- priority: 1300
type: show
kind: node
caption: Show
schema:
type: object
required:
- url
properties:
url:
type: string
format: uri
title: Media URL
caption:
title: Caption
$ref: '#/definitions/__i18nstring'
ui_schema:
caption:
ui:field: i18n
- priority: 1400
type: send
kind: node
caption: Send
schema:
type: object
properties:
message:
type: string
title: Message
subject:
type: string
title: Subject
ui_schema:
message:
ui:widget: autosize_textarea
ui:order:
- subject
- message
- priority: 1700
type: escalate
kind: node
caption: Escalate
schema:
type: object
required:
- message
properties:
message:
type: string
title: Help message
tag:
type: string
title: Tag
additionalProperties: false
ui_schema:
message:
ui:widget: autosize_textarea
ui:order:
- tag
- message
- '*'
- priority: 1800
type: control_flow
kind: node
caption: Control flow
schema:
type: object
properties: {}
additionalProperties: false
ui_schema:
- priority: 1900
type: dial
kind: node
caption: Dial
schema:
type: object
required:
- number
- sip
properties:
number:
type: string
title: Number
sip:
default: invite
type: string
enum:
- invite
- refer
title: SIP
additionalProperties: false
ui_schema:
number:
ui:widget: phone_number
sip:
ui:widget: radio
ui:options:
inline: true
ui:order:
- number
- sip
- '*'
omit_types: []
omit_variants: []
variants:
- links:
- next
priority: 1000
type: entry
kind: node
caption: Main
schema:
ui_schema:
generator_template: ''
omit: []
type_ui_schema:
ui_template: ''
variant: default
- links:
- next
priority: 1100
type: entry
kind: node
caption: Unknown
schema:
ui_schema:
generator_template: ''
omit: []
type_ui_schema:
ui_template: ''
variant: unknown
- links:
- next
priority: 1200
type: entry
kind: node
caption: Root
schema:
ui_schema:
generator_template: ''
omit: []
type_ui_schema:
ui_template: ''
variant: root
- links:
- next
priority: 1300
type: entry
kind: node
caption: Intent
schema:
type: object
required:
- intent
properties:
intent:
type: string
title: Intent
pattern: ^[a-z][a-zA-Z0-9_]*$
additionalProperties: false
ui_schema:
intent:
ui:widget: intent_picker
ui:order:
- intent
generator_template: ''
omit: []
type_ui_schema:
ui_template: ''
variant: trigger
- links:
- next
priority: 1400
type: entry
kind: node
caption: DTMF
schema:
type: object
required:
- dtmf
properties:
dtmf:
type: string
enum:
- '1'
- '2'
- '3'
- '4'
- '5'
- '6'
- '7'
- '8'
- '9'
- '0'
- '*'
- '#'
title: DTMF Key
additionalProperties: false
ui_schema:
ui:order:
- dtmf
generator_template: ''
omit: []
type_ui_schema:
ui_template: ''
variant: dtmf
- links:
- next
priority: 1000
type: say
kind: node
caption:
schema:
ui_schema:
generator_template: |
say {{ say.text | as_string -}}
{{ node | goto_next }}
omit: []
type_ui_schema:
ui_template: ''
variant: default
- links:
- labelled
- trigger
priority: 1000
type: ask
kind: node
caption: Choice
schema:
ui_schema:
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 | as_trigger }} ->
{{ link | goto }}
{% endfor %}
end
{% endif %}
omit: []
type_ui_schema:
ui_template: ''
variant: default
- links:
- next
priority: 1100
type: ask
kind: node
caption: Open question
schema:
ui_schema:
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}}
omit:
- repeat_text
type_ui_schema:
ui_template: ''
variant: open
- links:
- labelled
- trigger
priority: 1200
type: ask
kind: node
caption: Prompt
schema:
ui_schema:
generator_template: |
prompt {{ ask.text | as_string }}
{% for link in node.out.labelled %}
dialog trigger: {{ link.labelled | as_trigger }}, 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 }}__{% else %}trigger: @{{ t | as_trigger }}{% endif %} do
{{ link | goto }}
end
{% endfor %}
omit:
- assign
type_ui_schema:
ui_template: ''
variant: prompt
- links:
- next
priority: 1000
type: show
kind: node
caption: Image
schema:
ui_schema:
generator_template: |
show {{ show.variant }}({{ show.url | as_string }}){% if show.caption %}, caption: {{ show.caption | as_string }}{% endif %}
{{ node | goto_next }}
omit: []
type_ui_schema:
url:
ui:widget: image
ui:options:
giphy: true
ui_template: ''
variant: image
- links:
- next
priority: 1100
type: show
kind: node
caption: Video
schema:
ui_schema:
generator_template: |
show {{ show.variant }}({{ show.url | as_string }}){% if show.caption %}, caption: {{ show.caption | as_string }}{% endif %}
{{ node | goto_next }}
omit: []
type_ui_schema:
url:
ui:widget: file
ui:options:
accept: video/*
ui_template: ''
variant: video
- links:
- next
priority: 1200
type: show
kind: node
caption: Audio
schema:
ui_schema:
generator_template: |
show {{ show.variant }}({{ show.url | as_string }}){% if show.caption %}, caption: {{ show.caption | as_string }}{% endif %}
{{ node | goto_next }}
omit: []
type_ui_schema:
url:
ui:widget: file
ui:options:
accept: audio/*
ui_template: ''
variant: audio
- links:
- next
priority: 1300
type: show
kind: node
caption: File
schema:
ui_schema:
generator_template: |
show {{ show.variant }}({{ show.url | as_string }}){% if show.caption %}, caption: {{ show.caption | as_string }}{% endif %}
{{ node | goto_next }}
omit: []
type_ui_schema:
url:
ui:widget: file
ui_template: ''
variant: file
- links:
- next
priority: 1000
type: send
kind: node
caption: Note
schema:
type: object
properties:
tag:
type: string
title: Note tag
additionalProperties: false
ui_schema:
tag:
ui:field: tag_picker
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 }}
omit: []
type_ui_schema:
ui_template: ''
variant: note
- links:
- next
priority: 1100
type: send
kind: node
caption: E-mail
schema:
type: object
properties:
email:
type: string
title: E-mail address
additionalProperties: false
ui_schema:
generator_template: |
mail({{ send.email.email | as_string }}, {{ send.subject | as_string }}, {{ send.message | as_string }})
{{ node | goto_next }}
omit: []
type_ui_schema:
ui_template: 'Send email with subject: {{ send.subject }}'
variant: email
- links:
- next
priority: 1200
type: send
kind: node
caption: SMS
schema:
type: object
properties:
phone:
type: string
title: Phone number
additionalProperties: false
ui_schema:
phone:
ui:widget: phone_number
generator_template: |
sms_notify({{ send.sms.phone | as_string }}, {{ send.subject | as_string }} + " " + {{ send.message | as_string }})
{{ node | goto_next }}
omit: []
type_ui_schema:
ui_template: 'Send SMS to {{ send.sms.phone }} with subject: {{ send.subject }}'
variant: sms
- links: []
priority: 1000
type: close
kind: node
caption:
schema:
ui_schema:
generator_template: |
close
omit: []
type_ui_schema:
ui_template: ''
variant: default
- links:
- next
priority: 1000
type: pause
kind: node
caption: Pause
schema:
ui_schema:
generator_template: |
pause {{ pause.timeout }}
{{ node | goto_next }}
omit: []
type_ui_schema:
ui_template: ''
variant: default
- links:
- next
priority: 1100
type: pause
kind: node
caption: Stop
schema:
ui_schema:
generator_template: |
stop
{{ node | goto_next }}
omit:
- timeout
type_ui_schema:
ui_template: ''
variant: stop
- links:
- next
priority: 1000
type: escalate
kind: node
caption:
schema:
ui_schema:
generator_template: |
tag "escalated"
{% if escalate.tag %}
tag {{ escalate.tag | as_string }}
{% endif %}
escalate({{ escalate.message | as_string }})
{{ node | goto_next }}
omit: []
type_ui_schema:
ui_template: |
Escalate: <b>{{ escalate.message }}</b>
{% if escalate.tag %} to: <b>{{ escalate.tag }}</b>{% endif %}
variant: default
- links:
- condition
priority: 1000
type: control_flow
kind: node
caption: Branch
schema:
ui_schema:
generator_template: |
branch do
{% for link in node.out.condition %}
{{ link.condition | branch_condition }} ->
{{ link | goto }}
{% endfor %}
end
omit: []
type_ui_schema:
ui_template: ''
variant: branch
- links: []
priority: 1100
type: control_flow
kind: node
caption: Go to flow
schema:
type: object
properties:
flow:
type: string
title: Flow
additionalProperties: false
ui_schema:
flow:
ui:widget: flow_picker
generator_template: |
{% if node.control_flow.goto.flow %}
goto flow_{{ node.control_flow.goto.flow }}
{% endif %}
omit: []
type_ui_schema:
ui_template: ''
variant: goto
- links: []
priority: 1200
type: control_flow
kind: node
caption: Close
schema:
ui_schema:
generator_template: |
close
omit: []
type_ui_schema:
ui_template: ''
variant: close
- links:
- next
priority: 1300
type: control_flow
kind: node
caption: Pause
schema:
type: object
required:
- timeout
properties:
timeout:
type: number
title: Wait time (seconds)
additionalProperties: false
ui_schema:
generator_template: |
pause {{ node.control_flow.pause.timeout }}
{{ node | goto_next }}
omit: []
type_ui_schema:
ui_template: ''
variant: pause
- links:
- next
priority: 1000
type: dial
kind: node
caption: Number
schema:
ui_schema:
generator_template: |
tag "dial:{{ dial.number }}"
{% if dial.sip == 'invite' %}
dial "{{ dial.number }}"
{% else %}
refer "{{ dial.number }}"
{% endif %}
tag "dial:failed"
{{ node | goto_next }}
omit: []
type_ui_schema:
ui_template: 'Dial: {{ number }}'
variant: dial
Generated from platform version: 2.57.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 }}