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