Introduction
flowdown is a minimal markup language for writing conversations, specifically for the voiceflow platform. It can be thought of a 'voiceflow programming language' of sorts.
The fundamental guiding principles of flowdown are as follows:
- writing a conversation should be 'text' first, meaning that any functionality should be built as syntax around the dialogue.
- a conversation should read as it flows - control flow should be obvious to follow and syntax should not be cryptic
- conversations should be fun to write and not at all tedious!
Also please note that flowdown is very much still a prototype. It lacks many essential features and also may be buggy. If you run into any issues or have feedback, please leave an issue in the github repo.
Getting Started
Installation
You can get the binary from the github release. Currently M1 macos and linux are supported. Simply unzip the files, make the binary executable and place it somewhere in your path.
Installation example for linux
$ wget https://github.com/MrPicklePinosaur/flowdown/releases/download/v0.1/fdc-linux-v0.1.zip
$ unzip fdc-linux-v0.1.zip
$ chmod +x fdc
$ mv fdc /usr/local/bin
Installation example for macos
$ wget https://github.com/MrPicklePinosaur/flowdown/releases/download/v0.1/fdc-macos-v0.1.zip
$ unzip fdc-linux-v0.1.zip
$ chmod +x fdc
$ mv fdc /usr/local/bin
Packaging for various platforms may or may not be coming in the future.
Hello World Conversation
Let's compile a basic hello world conversation. The most basic flowdown file is just a single utterance.
$ echo 'Hello world' > hello.fd
$ fdc -o hello.vf hello.fd
Import to Voiceflow
To be able to run the compiled voiceflow conversation, you will need an account on the voiceflow platform.
Once you are setup, navigate to your dashboard and locate the import button on the top right corner.
Select and upload the compiled hello.vf
file, and you should see the project
appear in your dashboard. Running our project, we get our first compiled
conversation!
Developer Tools
An important aspect of a language is not just the language itself, but also the ecosystem and set of tools around the language to make development fun and productive. Here are some developer tool (prototypes) that are included.
Vim
A vim plugin with syntax highlighting can be installed with the following (for vim-plug users)
Plug 'MrPicklePinosaur/flowdown', { 'rtp': 'tools/vim' }
Vscode
Vscode extension is coming soon...
Syntax
This section will highlight the syntax of flowdown.
Utterances
The most basic 'program' is just text:
hello world!
Curly braces represent special text actions, we will break down a couple. Firstly, variables can be incorporated by prefixing an identifier with a dollar sign
hello {$name}, nice to meet you!
We can introduce variations on the text by using the pipe |
character. This
will generate all the appropriate utterance variations for you.
{what can I get for|how can i help} you today?
Comments
Single line comments supported with (//
) and multi line comments with (/* */
)
Tones and Voices
You can control how the voice assistant outputs the utterance with tone indicators. For example, the voice that is used to say the utterance can be specified.
hello I am the default voice
hello I am a japanese voice # ja-JP-standard-A
Commands
Commands are actions that we wish to take that are external to the conversation. These should read similar to stage directions in a script. A good way to think of commands are as 'side effects'. Most (if not all) voiceflow blocks for voice assistants are implemented:
Audio
The audio file is provided as a url.
please wait a moment...
[audio https://audio.com/mysound.wav]
Image
Similar to the audio command, takes in an image url to display.
here's a cool picture
[image https://coolpic.com/image.png]
Code
A javascript file can be passed in. It's path is relative to the flowdown file.
got it! placing an order for you right now!
[code placeOrder.js]
Exit
Immediately terminate the program
goodbye!
[end]
Capture
Prompt user and capture entire result to variable
what's your name?
[capture $firstName]
Set
Update or define variable.
[set $name "daniel"]
Dialog and Bookmarks
A fundamental part of writing readable conversations is the ability to break apart conversations into reusable and contained pieces. In the canvas, this is accomplished by topics and flows.
Topics allow us to move intent triggers into it's own section (no functional difference other than organization), whereas flows resemble function calls, allowing us to package up reusable logic.
In flowdown, we have dialogs and bookmarks.
Dialogs
Dialogs are analogous to function calls, they allow us to define reusable blocks of dialog that we can jump to from anywhere.
this is the main dialog
@ welcome
hello welcome to my store!
@ about
my store sells a lot of things
@ contact
contact me!
The output is as follows
> this is the main dialog
Notice how the other dialogs aren't executed. A flowdown conversation has an implicit main
dialog before any
dialogs are declared. It's also the entry point into the conversation.
Dialogs can be jumped to by using the ->
operator like so:
this is the main dialog
-> @welcome
@ welcome
this is the welcome dialog
-> about
@ about
this is the about dialog
And our output will be:
> this is the main dialog
> this is the welcome dialog
> this is the about dialog
When a dialog ends, we entire terminate (if top level dialog), or we return control to the caller.
enter layer1
-> @layer2
exit layer1
@ layer2
enter layer2
-> @layer3
exit layer2
@ layer 3
enter layer3
exit layer3
This will output
> enter layer1
> enter layer2
> enter layer3
> exit layer3
> exit layer2
> exit layer1
Bookmarks
Bookmarks are quite like html header links. They give us an anchor to jump to,
but don't offer any containment, when the next bookmark starts, we will start
executing it. Bookmarks are specifed by using
an equal sign (=
) followed by a name for the bookmark. We can jump to
bookmarks by using the ->
operator again, but this time we do not prefix the
identifier with an @
sign.
@ self intro
my name is pinosaur
ok we don't actually care about backstory lol
-> present day
= backstory
i have been making stores for over 20 years
= present day
i am currently making a store
= future
i will continue making stores
output:
> my name is pinosaur
> ok we don't actually care about backstory lol
> i am currently making a store
> i will continue making stores
And of course, bookmarks are scoped, they are local to a dialog.
@ dialog 1
jump to:
-> bookmark 3 // this will error
= bookmark 1
= bookmark 2
@ dialog 2
= bookmark 2
= bookmark 3
Control Flow
Simple control flow can be modified using the choice construct. You can think
of each choice line as an if-statement. If the condition before the :
is
true, the statement will be executed. Note that only one statement is supported
after a condition. If you need a more involved branch, use a dialog and jump to
it, as shown in the 'red' case.
What's your favorite color?
[capture $color]
* $color == "red": -> @red
* $color == "blue": [audio blue_da_ba_dee.wav]
* $color == "green": i guess green is an ok color...
@ red
yay, my favorite color is red too!
Intents (WIP)
Currently not implemented.
Intents are almost to how topics work, we simply mark the topic with intent:
and that block can be jumped to from any [listen]
command (they behave like
normal topics otherwise).
hello, how can i help you today?
[listen]
@ intent: check balance
...
@ intent: deposit money
...
@ intent: withdraw money
...
Examples
This section will provide a couple of example programs to get you started.
Hello World
The most basic flowdown conversation is just plain text. Each line is one utterance that is spoken by the assistant.
In my younger and more vulnerable years my father gave me some advice that I've been turning over in my mind ever since.
'Whenever you feel like criticizing any one,' he told me,
'just remember that all the people in this world haven't had the advantages that you've had.'
Custom Code
You can provide code snippets for code blocks. Just ensure that the location of the source code is relative to the flowdown file. Note that you need to forward declare any variables if the script introduces them.
// dToF.fd
Input temperature
[capture $temperature]
[set $converted "0"]
[code convert.js]
temperature in fahrenheight is {$converted}
// convert.js
// all variables are strings (for now)
const _temp = parseInt(temperature);
converted = (9/5 * _temp) + 32;
Full Example
This example uses most of the supported features in flowdown. It walks through a simple pizzeria customer service experience.
Hello! Welcome to Flowdown Pizzaria, what can I do for you?
[capture $mode]
* $mode == "order pizza": -> @order
* $mode == "menu": [image https://flowdownpizza/menu.png]
@ order
What type of pizza would you like?
[capture $pizzaType]
What size of pizza?
[capture $pizzaSize]
How would you like to recieve your pizza?
[capture $pizzaMethod]
* $pizzaMethod == "delivery": -> @delivery
* $pizzaMethod == "take out": -> @take out
Thank you for choosing Flowdown Pizzaria!
-> @survey
@ delivery
Can I get an address
[capture $address]
[set $price "0"]
[set $deliveryTime "0"]
[code calculatePrice.js]
[code computeDeliveryRoute.js]
Your final price is {$price} and you will get your pizza in about {$deliveryTime}!
@ take out
When would you like to pick up your food?
[capture $pickupTime]
[set $price "0"]
[code calculatePrice.js]
Your final price is {$price}.
@ survey
Would you like to complete an optional survey?
[capture $survey]
* $survey == "yes": -> start survey
* $survey == "no": -> end survey
= start survey
How would you rate today's experience?
[capture $rating]
Is there any feedback you would like to give?
[capture $feedback]
= end survey
Thank you!