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"]) }