Skip to main content

Code Examples

This page provides practical examples of Kuetix Engine workflows. From simple "Hello World" to complex multi-state workflows with error handling.

Hello World

The simplest possible workflow that demonstrates the basic structure:

module example

import services/common

const {
event: "greet",
description: "A simple hello world workflow example",
version: "1.0.0"
}

workflow wsl_hello_world {
start: Hello

state Hello {
action converse/speak.Say(on: message, v: {response: $constants.event, statusCode: 202}) as ResponseAsValue
on success -> Response(ResponseAsValue)
}

state Response(ResponseAsValue) {
action services/common/response.ResponseValue(message: $ResponseAsValue.message, statusCode: $ResponseAsValue.message.statusCode|int)
end ok
}
}

Key concepts demonstrated:

  • Module and import declarations
  • Constants block with metadata
  • State with action and result capture (as ResponseAsValue)
  • State parameters for data passing
  • Built-in filter (|int)
  • Terminal state with end ok

Integer Assertion Test

A workflow that validates integer equality:

module tests.example

extends "resolvers"

workflow wsl_example_assert_int_test {
start: Integer

state Integer {
action assert.Integer(value: "<<b|int>>", eq: "<<a|int>>", op: "eq")
on success -> FinishTrue
on error -> FinishFalse
}

state FinishTrue {
action response/True(statusCode: 200)
on success -> LastResponse
on error -> LastResponse
}

state FinishFalse {
action response/False(statusCode: 400)
on success -> LastResponse
on error -> LastResponse
}

state LastResponse {
action response/ResponseLastSuccess()
end ok
}
}

Key concepts demonstrated:

  • extends for resolver inheritance
  • Template expressions with built-in filters ("<<b|int>>", "<<a|int>>")
  • Branching based on success/error
  • Multiple paths converging to a single terminal state

User Login Flow

A complete authentication workflow with multiple error handlers:

module auth.login
import services/common

workflow user_login {
start: Request

state Request {
action parameters/Request(type: "login") as Req
on success -> Lookup(Req)
}

state Lookup(Req) {
action workflow/repositorium/user/get(id: "<<helpers.Id(Req.Email)>>") as User
on success -> CheckPassword(Req, User)
on error -> Fail
}

state CheckPassword(Req, User) {
action login/CheckPassword(request: Req, user: User)
on success -> EncryptedId(User)
on error -> Invalid
}

state EncryptedId(User) {
action login/EncryptedId(user: User) as Enc
on success -> GenerateToken(User, Enc)
}

state GenerateToken(User, Enc) {
action login/GenerateToken(user: User) as Token
on success -> Persist(User)
on error -> TokenGenerationFailed
}

state Persist(User) {
action workflow/repositorium/user/set(id: $User.ID, entity: "user")
on success -> Respond(User, Token)
on error -> Respond(User, Token)
}

state Respond(User, Token) {
action response/ResponseEntity(entityName: "user", loginResponse: Token)
end ok
}

state Invalid {
action response/ResponseError(message: "Invalid credentials", code: 401)
end error
}

state TokenGenerationFailed {
action response/ResponseError(message: "Failed to generate access or refresh token", code: 500)
end error
}

state Fail {
action response/ResponseError(message: "Lookup failed", code: 404)
end error
}
}

Key concepts demonstrated:

  • Multi-state workflow with complex flow
  • Helper function calls in templates (helpers.Id(Req.Email))
  • Multiple error states with different status codes
  • State continuing regardless of success/error (Persist state)
  • Terminal states with end error for failure paths

CLI Usage Examples

Complete Setup and Run Workflow

Starting from scratch:

# 1. Create project and add engine module
mkdir myapp && cd myapp
go mod init myapp
go get github.com/kuetix/engine@latest

# 2. Install kue CLI (separate repository)
git clone https://github.com/kuetix/kue
cd kue && make install
cd ..

# 3. Create project
kue create --name myapp --app-type cli
cd myapp

# 4. Add components (optional)
kue add workflow hello_world

# 5. Update module cache
kue update -v

# 6. Build and run
kue run
# Or manually: make build-cli && ./runtime/bin/myapp-cli workflows/common/hello_world.wsl

# With verbose logging
./runtime/bin/myapp-cli -v workflows/common/hello_world.wsl

# With parameters
./runtime/bin/myapp-cli workflows/common/hello_world.wsl message="Hello from CLI"

# Run multiple concurrent instances
./runtime/bin/myapp-cli --amount=5 workflows/common/batch_process.wsl

Using runner.sh Script

Generated projects include a runner.sh helper:

./runner.sh run workflows/common/hello_world.wsl
./runner.sh test
./runner.sh build

Build Commands

In projects created with kue create:

# Build and run with kue
kue run

# Build using Makefile
make build-cli # Build CLI application
make build-api # Build API server
make test # Run tests
make clean # Clean build artifacts

# Install built binaries
make install # Install all binaries
make install-cli # Install CLI only
make install-api # Install API only

# Using runner script
./runner.sh build
./runner.sh test

When developing the engine itself:

# Clone and enter the engine repo
git clone https://github.com/kuetix/engine
cd engine

# Build the MCP server binary
make build

# Run tests
make test

The kue CLI lives in a separate repository — see the kue repository.

Programmatic Execution

Using WorkflowManager

package main

import (
"github.com/kuetix/engine"
"github.com/kuetix/engine/engine/domain"
)

func main() {
options := &domain.Options{
EngineName: "kuetix",
Workflow: "hello_world",
Amount: 1,
Verbose: true,
}

responses := engine.RunWorkflow("production", options)

for _, response := range responses {
if response.Success {
fmt.Println("Workflow completed:", response.Response)
} else {
fmt.Println("Workflow failed:", response.Error)
}
}
}

Using HandleWorkflow Interface

package main

import (
"context"
"github.com/kuetix/engine/engine/workflow"
)

func main() {
ctx := context.Background()

workflowContext := &map[string]interface{}{
"input": "some value",
}

handler := workflow.NewHandleWorkflow(/* options */)
response := handler.ProcessWorkflow(ctx, "my_workflow", workflowContext)

if response.Success {
fmt.Println("Success:", response.Response)
}
}

Custom Transitions

Creating a Custom Transition Handler

package transitions

import (
"github.com/kuetix/engine/engine/domain"
"github.com/kuetix/engine/engine/domain/interfaces"
"github.com/kuetix/engine/engine/workflow"
)

type MyTransitions struct {
workflow.BaseServiceTransition
}

func NewMyTransitions() interfaces.ServiceTransitions {
return &MyTransitions{}
}

// ProcessData is a transition handler that can be called from WSL
// Example: action my_module/my_transitions.ProcessData(input: $data)
func (t *MyTransitions) ProcessData(p *workflow.WorkerSessionContext, input string) domain.FlowStepResult {
// Get additional context values
userId := t.Property("userId")

// Process the input
result := processData(input, userId)

// Set response for downstream states
t.SetResponse(map[string]interface{}{
"processed": result,
"input": input,
}, 200)

return domain.FlowStepResult{
Success: true,
StatusCode: 200,
}
}

func processData(input string, userId interface{}) string {
// Your business logic here
return "processed: " + input
}

Using Custom Transitions in WSL

module my_app

import my_module

workflow process_data {
start: Process

state Process {
action my_module/my_transitions.ProcessData(input: $request.data) as Result
on success -> Respond(Result)
on error -> HandleError
}

state Respond(Result) {
action services/common/response.ResponseValue(data: $Result, statusCode: 200)
end ok
}

state HandleError {
action services/common/response.ResponseError(message: "Processing failed", code: 500)
end error
}
}

Event Handling

Subscribing to Workflow Events

package main

import (
"log"
"github.com/kuetix/engine/event"
"github.com/kuetix/engine/engine/domain"
)

func init() {
// Subscribe to workflow completion
event.Bus.Subscribe("on:workflow:complete", func(wfConfig domain.WorkflowConfigItem, contexts []map[string]interface{}) {
log.Printf("Workflow %s completed successfully", wfConfig.Name)
})

// Subscribe to workflow start
event.Bus.Subscribe("on:workflow:before:run", func(wfConfig domain.WorkflowConfigItem, contexts []map[string]interface{}) {
log.Printf("Starting workflow %s", wfConfig.Name)
})

// Subscribe to workflow exit
event.Bus.Subscribe("on:workflow:exit", func(wfConfig domain.WorkflowConfigItem) {
log.Printf("Workflow %s exited", wfConfig.Name)
})
}

Additional Resources