Module System Reference
The Kuetix Engine module system provides an extensible plugin architecture for custom functionality. This guide covers creating, registering, and using modules.
Overview
Modules extend the engine with custom transitions and services. They are organized under the modules/ directory and automatically registered via the dependency injection system.
Module Structure
Directory Layout
modules/
├── di.go # Generated DI configuration
├── meta.go # Generated function metadata
├── modules.go # Module registration
└── my_module/ # Your custom module
└── transitions/ # Transition handlers
└── my_transitions.go
Service Modules
modules/
└── services/
└── common/
└── transitions/
└── response.go # Common response handlers
Creating a Module
Step 1: Create Directory Structure
mkdir -p modules/my_module/transitions
Step 2: Implement Transition Handler
Create modules/my_module/transitions/my_transitions.go:
package transitions
import (
"github.com/kuetix/engine/engine/domain"
"github.com/kuetix/engine/engine/domain/interfaces"
"github.com/kuetix/engine/engine/workflow"
)
// MyTransitions handles custom transitions
type MyTransitions struct {
workflow.BaseServiceTransition
}
// NewMyTransitions creates a new instance
func NewMyTransitions() interfaces.ServiceTransitions {
return &MyTransitions{}
}
// DoSomething performs a custom action
// Can be called in WSL as: action my_module/my_transitions.DoSomething(...)
func (t *MyTransitions) DoSomething(p *workflow.WorkerSessionContext, input string) domain.FlowStepResult {
// Access session context
t.SetSession(p)
// Get property from workflow context
userId := t.Property("userId")
// Process
result := process(input, userId)
// Set response
t.SetResponse(map[string]interface{}{
"result": result,
}, 200)
return domain.FlowStepResult{
Success: true,
StatusCode: 200,
}
}
func process(input string, userId interface{}) string {
return "processed: " + input
}
Step 3: Regenerate Module Cache
After creating or modifying modules, regenerate the module cache:
# Using kue update
kue update --verbose
This generates:
modules/meta.go- Function metadata cachemodules/di.go- Dependency injection configurationmodules.json- Module metadata
Step 4: Use in WSL
module my_app
import my_module
workflow example {
start: Process
state Process {
action my_module/my_transitions.DoSomething(input: "hello") as Result
on success -> Finish(Result)
}
state Finish(Result) {
action services/common/response.ResponseValue(data: $Result)
end ok
}
}
BaseServiceTransition
The BaseServiceTransition struct provides common functionality for transition handlers.
Structure
type BaseServiceTransition struct {
Ctx *WorkerSessionContext
}
Methods
| Method | Description |
|---|---|
SetSession(ctx) | Set the worker session context |
GetSession() | Get the current session context |
GetContext() | Get the workflow context map |
S() | Shorthand for refresh + return context |
SetResponse(response, statusCode...) | Set worker response |
SetStatusCode(code) | Set HTTP status code |
SetValue(name, value) | Store value in context |
GetValue(name) | Retrieve value from context |
Property(key) | Get property from context |
SetError(issue, statusCode) | Set single error |
SetErrors(issues, statusCode) | Set multiple errors |
HandleError(err, statusCode) | Process and set error |
Example Usage
func (t *MyTransitions) MyMethod(p *workflow.WorkerSessionContext) domain.FlowStepResult {
// Set session context
t.SetSession(p)
// Access the context shorthand
ctx := t.S()
// Get properties
value := t.Property("myKey")
userId := t.Property("userId")
// Store values
t.SetValue("processed", true)
// Set response
t.SetResponse(map[string]interface{}{
"value": value,
}, 200)
return domain.FlowStepResult{
Success: true,
StatusCode: 200,
}
}
FlowStepResult
Transition methods return FlowStepResult:
type FlowStepResult struct {
Success bool // Execution success
Next string // Override next state (optional)
Error error // Error if any
Response interface{} // Response data
StatusCode int // HTTP status code
}
Success Response
return domain.FlowStepResult{
Success: true,
Response: map[string]interface{}{"data": result},
StatusCode: 200,
}
Error Response
return domain.FlowStepResult{
Success: false,
Error: errors.New("something went wrong"),
StatusCode: 500,
}
Override Next State
return domain.FlowStepResult{
Success: true,
Next: "SpecialState", // Go to SpecialState instead of normal transition
}
WorkerSessionContext
The session context provides access to workflow data:
Property Resolution
// Direct lookup
value := p.Value("key")
// String with default
name := p.String("name", "default")
// Integer with ok check
count, ok := p.Int("count", 0)
// Boolean
enabled := p.Bool("enabled")
Property Syntax
| Syntax | Description | Example |
|---|---|---|
key | Direct property lookup | username |
key.nested | Nested property access | user.profile.name |
key|modifier | Property with modifier | count|int |
key??default | Default value | name??"Unknown" |
Setting Values
// Set a value
p.SetValue("key", value)
// Set context
p.SetContext("key", value)
// Return value for capture
p.Return(result)
Dependency Injection
How It Works
The kueinit tool generates DI configuration in modules/di.go:
di.DependencyInjection["my_module"] = func(name string) {
di.ToResolve(defines.TransitionPrefix+"my_module"+"/"+"my_transitions", func() interface{} {
return workflow.ServiceTransitionMapping{
ServiceName: name,
Name: "my_transitions",
Impl: NewMyTransitions(),
}
})
}
ServiceTransitionMapping
type ServiceTransitionMapping struct {
ServiceName string // Module path
Name string // Transition file name
Impl interfaces.ServiceTransitions // Implementation instance
}
Function Metadata
The module cache generation tool creates metadata in modules/meta.go:
boot.AddMetaFunctionCache(map[string]map[string]map[string]interfaces.FunctionMetadata{
"my_module": {
"my_transitions": {
"DoSomething": {
Name: "DoSomething",
NumIn: 2,
NumOut: 1,
ArgTypes: []string{"*workflow.WorkerSessionContext", "string"},
ReturnTypes: []string{"domain.FlowStepResult"},
ArgNames: []string{"p", "input"},
},
},
},
})
This metadata enables reflection-based method invocation.
Built-in Modules
services/common
Common response handlers:
action services/common/response.ResponseValue(message: "Hello", statusCode: 200)
action services/common/response.ResponseError(message: "Error", code: 500)
converse
Conversation handlers:
action converse/speak.Say(on: message, v: {response: "hello"})
Best Practices
1. Keep Methods Focused
Each transition method should do one thing well:
// Good
func (t *MyTransitions) ValidateEmail(p *workflow.WorkerSessionContext, email string) domain.FlowStepResult
// Avoid
func (t *MyTransitions) ValidateAndProcessAndSave(p *workflow.WorkerSessionContext, data interface{}) domain.FlowStepResult
2. Use Proper Error Handling
func (t *MyTransitions) ProcessData(p *workflow.WorkerSessionContext, data string) domain.FlowStepResult {
result, err := doSomething(data)
if err != nil {
t.HandleError(err, 500)
return domain.FlowStepResult{
Success: false,
Error: err,
StatusCode: 500,
}
}
t.SetResponse(result, 200)
return domain.FlowStepResult{
Success: true,
StatusCode: 200,
}
}
3. Document Your Methods
// ValidateUser validates user credentials and returns validation result
//
// WSL usage:
// action auth/validation.ValidateUser(email: $input.email, password: $input.password)
//
// Returns:
// - Success: true if valid, false otherwise
// - Response: { valid: bool, message: string }
func (t *AuthTransitions) ValidateUser(p *workflow.WorkerSessionContext, email string, password string) domain.FlowStepResult
4. Regenerate After Changes
Always run kue update after modifying modules:
kue update --verbose
Related Topics
- Getting Started - Core concepts
- WSL Overview - Using modules in WSL
- Architecture - System architecture