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.
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:options:
acceptFromContextVariable: showNodeAccept
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
ui:order:
- subject
- message
- 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 ask.repeat_text -%}, {{ask.repeat_text | as_string-}}{%- endif -%}
]
{%- if node.out.labelled -%}, expecting: [
{%- for link in node.out.labelled -%}
{{ 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: []
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: Conversation note
generator_template: |
create_conversation_note({{ note.subject | as_string }} + "
" + {{ note.message | as_string }}
{%- if note.default.tag -%}, [{{ note.default.tag | as_string }}]{%- endif -%})
{{ node | goto_next }}
kind: node
links:
- next
omit: []
requires_features: []
schema:
additionalProperties: false
properties:
tag:
title: Note tag
type: string
type: object
type: note
ui_schema:
tag:
ui:field: tag_picker
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.38.0-develop.30ddce
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 }}