1. Two parts the code and the backend
Aiflo is composed of two essential elements: the code, which is written in JSON format, and the backend infrastructure. The backend necessitates a Go-based runtime environment to function correctly. This runtime environment can be established and executed through multiple methods, providing flexibility in implementation.
1.1 Using Aiflo from a Go program
It is a very simple thing to use Aiflo from Go. You start by importing the Aiflo module:
import "github.com/cloudcamelopard/aiflo"
then we need a compiler:
compiler := aiflo.NewCompiler()
This is used to compile our programs and test for errors. Now, let's create the simplest possible Aiflo code file.
[
{ "name":"greeting", "type":"response", "value":"Hello World!" }
]
We prepare this. Note you can store the 'prepared' code if you want or do it in advance.
prepared,err := compiler.Prepare("first-program", "first-program.json")
if err != nil {
panic(err)
}
After this we run the code by using a runner.
runner := aiflo.NewRunner()
response, err := runner.Run(prepared)
if err != nil {
panic(err)
}
And we can output it as:
fmt.Println(response["greeting"])
The full code is for the program running the aiflo code:
package main
import (
"fmt"
"github.com/cloudcamelopard/aiflo"
)
func main() {
compiler := aiflo.NewCompiler()
prepared, err := compiler.Prepare("first-program", "first-program.json")
if err != nil {
panic(err)
}
runner := aiflo.NewRunner()
response, err := runner.Run(prepared)
if err != nil {
panic(err)
}
fmt.Println(response["greeting"])
}
and the aiflo code:
[
{ "name":"greeting", "type":"response", "value":"Hello World!" }
]
You see, it's not very hard.
1.2 Inputs
Most programs require inputs. It is easy to send inputs to a program. Lets modify our program taking two strings to and from.
[
{ "type":"inputs", "then":[
{ "value":"to", "target":"message_builder" },
{ "value":"from", "target":"message_builder" }
]},
{ "name":"message_builder", "type":"template", "value":"Greeting {{.to}} from {{.from}}!", "then":[
{ "target":"greeting" }
]},
{ "name":"greeting", "type":"response" }
]
Inserting the inputs is done in the run method it takes a variadic argument
runner.Run(prepared, map[string] { "to":"Joe", "from":"Ronny" })
If we run it we get:
panic: type template not handled
You have to specify for the compiler which types you support, this is done with:
compiler.AddType(types.TemplateTypeName, types.TemplateTypeHandler)
That's all. If we now run it it outputs:
Greeting Joe from Ronny!
The full code:
package main
import (
"fmt"
"github.com/cloudcamelopard/aiflo"
"github.com/cloudcamelopard/aiflo/types"
)
func main() {
compiler := aiflo.NewCompiler()
compiler.AddType(types.TemplateTypeName, types.TemplateTypeHandler)
prepared, err := compiler.Prepare("first-program", "first-program.json")
if err != nil {
panic(err)
}
runner := aiflo.NewRunner()
response, err := runner.Run(prepared, map[string]any{ "to":"Joe", "from": "Ronny"})
if err != nil {
panic(err)
}
fmt.Println(response["greeting"])
}
1.3 Making a custom type
One of the strengths with Aiflo is its extensibility. Let's make our own node uppercase. This will ... make an input uppercase.
We make a function.
func UppercaseTypeHandler(value any, inputs []any) any {
if len(inputs) > 1 {
return errors.New("uppercase type max 1 input of type string")
}
var str string
if val, ok := value.(string); ok {
str = strings.ToUpper(val)
}
if len(inputs) == 1 {
if val, ok := inputs[0].(string); ok {
str = strings.ToUpper(val)
} else {
return errors.New("uppercase type max 1 input of type string")
}
}
return str
}
And add if like before to the compiler:
compiler.AddType("uppercase", UppercaseTypeHandler)
And we modify our program to use it:
[
{ "type":"inputs", "then":[
{ "value":"to", "target":"message_builder", "name":"to"},
{ "value":"from", "target":"message_builder", "name":"from"}
]},
{ "name":"message_builder", "type":"template", "value":"Greeting {{"{{.to}}"}} from {{"{{.from}}"}}!", "then":[
{ "target":"uppercase" }
]},
{ "name":"uppercase", "type":"uppercase", "then":[
{ "target": "greeting"}
]},
{ "name":"greeting", "type":"response" }
]
And the full code for the program:
package main
import (
"errors"
"fmt"
"strings"
"github.com/cloudcamelopard/aiflo"
"github.com/cloudcamelopard/aiflo/types"
)
func UppercaseTypeHandler(value any, inputs []any) any {
if len(inputs) > 1 {
return errors.New("uppercase type max 1 input of type string")
}
var str string
if val, ok := value.(string); ok {
str = strings.ToUpper(val)
}
if len(inputs) == 1 {
if val, ok := inputs[0].(string); ok {
str = strings.ToUpper(val)
} else {
return errors.New("uppercase type max 1 input of type string")
}
}
return str
}
func main() {
compiler := aiflo.NewCompiler()
compiler.AddType(types.TemplateTypeName, types.TemplateTypeHandler)
compiler.AddType("uppercase", UppercaseTypeHandler)
prepared, err := compiler.Prepare("first-program", "first-program.json")
if err != nil {
panic(err)
}
runner := aiflo.NewRunner()
response, err := runner.Run(prepared, map[string]any{ "to":"Joe", "from": "Ronny"})
if err != nil {
panic(err)
}
fmt.Println(response["greeting"])
}