Modules
Module Initialization
The kue update command is responsible for generating the module metadata cache and dependency injection configuration. This tool must be run before building the engine or whenever modules are added/modified.
Purpose
The kue update tool scans all modules in the modules/ directory, parses their Go source files, and generates:
modules/meta.go: Function metadata cache for reflection-based method invocationmodules/di.go: Dependency injection configuration for all transition handlers
When to Run kue update
Run kue update in the following scenarios:
| Scenario | Command |
|---|---|
| Before building the engine | kue update -v |
| After adding a new module | kue update -v |
| After modifying transition methods | kue update -v |
| After changing method signatures | kue update -v |
How kue update Works
┌─────────────────────────────────────────────────────────────────────┐
│ kue update Execution Flow │
├─────────────────────────────────────────────────────────────────────┤
│ 1. Initialize environment (boot.InitializeEnvironment) │
│ 2. Call GenerateMetaCache(env) │
│ 3. Locate modules path from configuration │
│ 4. Walk directory tree looking for "transitions" folders │
│ 5. Parse each .go file in transitions folders │
│ 6. Extract function metadata (names, args, return types) │
│ 7. Generate meta.go with FunctionMetadata cache │
│ 8. Generate di.go with DI container registrations │
└─────────────────────────────────────────────────────────────────────┘
GenerateMetaCache (in the kue CLI)
The core function that generates the module cache:
func GenerateMetaCache(env *boot.Environment) {
// 1. Locate modules folder
modulesPath, err := helpers.ModulesPath(env.Config.Application.ModulesPath)
// 2. Walk directory tree
filepath.WalkDir(modulesPath, func(path string, d os.DirEntry, err error) error {
// 3. Look for "transitions" subfolders
transitionsPath := filepath.Join(path, "transitions")
if stat, err := os.Stat(transitionsPath); err == nil && stat.IsDir() {
// 4. Parse each .go file
filepath.Walk(transitionsPath, func(subPath string, subInfo os.FileInfo, err error) error {
if strings.HasSuffix(subPath, ".go") {
ParseGoFile(subPath, modulesPath)
}
return nil
})
}
return nil
})
// 5. Generate output files
GenerateMetaFiles(modulesPath, "/")
}
ParseGoFile - AST Parsing
The ParseGoFile function uses Go's AST parser to extract function metadata:
func ParseGoFile(path string, modulesPath string) {
// Parse Go source file into AST
fSet := token.NewFileSet()
node, err := parser.ParseFile(fSet, path, nil, 0)
// Extract function declarations
for _, decl := range node.Decls {
funcDecl, ok := decl.(*ast.FuncDecl)
// Extract inputs (parameter names and types)
for _, field := range funcDecl.Type.Params.List {
typeStr := helpers.ExprToString(field.Type)
for _, name := range field.Names {
inputs = append(inputs, typeStr)
argNames = append(argNames, name.Name)
}
}
// Extract outputs (return types)
for _, field := range funcDecl.Type.Results.List {
typeStr := helpers.ExprToString(field.Type)
outputs = append(outputs, typeStr)
}
// Store in FunctionCache
FunctionCache[service][transition][funcDecl.Name.Name] = domain.FunctionMetadata{
Name: funcDecl.Name.Name,
NumIn: len(inputs),
NumOut: len(outputs),
ArgTypes: inputs,
ReturnTypes: outputs,
ArgNames: argNames,
ReturnNames: returnNames,
}
}
}
Generated Files
meta.go - Function Metadata Cache
// Code generated by kueinit. DO NOT EDIT.
package modules
import (
"github.com/kuetix/engine/boot"
"github.com/kuetix/engine/engine/domain/interfaces"
)
func init() {
boot.AddMetaFunctionCache(map[string]map[string]map[string]interfaces.FunctionMetadata{
"services/common": {
"response": {
"ResponseValue": {
Name: "ResponseValue",
NumIn: 3,
NumOut: 1,
ArgTypes: []string{"*workflow.WorkerSessionContext", "string", "int"},
ReturnTypes: []string{"domain.FlowStepResult"},
ArgNames: []string{"p", "message", "statusCode"},
ReturnNames: []string{"out0"},
},
},
},
})
}
di.go - Dependency Injection Configuration
// Code generated by kueinit. DO NOT EDIT.
package modules
import (
di "github.com/kuetix/container"
transitionsServicesCommon "github.com/kuetix/engine/modules/services/common/transitions"
"github.com/kuetix/engine/engine/defines"
"github.com/kuetix/engine/engine/workflow"
)
func init() {
di.Boot()
di.DependencyInjection["services/common"] = func(name string) {
di.ToResolve(defines.TransitionPrefix+"services/common"+"/"+"response", func() interface{} {
return workflow.ServiceTransitionMapping{
ServiceName: name,
Name: "response",
Impl: transitionsServicesCommon.NewResponseTransitions(),
}
})
}
}
FunctionMetadata Structure
The metadata cache stores comprehensive information about each transition method:
type FunctionMetadata struct {
Name string // Function name (e.g., "ResponseValue")
NumIn int // Number of input parameters
NumOut int // Number of return values
ArgTypes []string // Input parameter types
ReturnTypes []string // Return value types
ArgNames []string // Input parameter names
ReturnNames []string // Return value names
}
Integration with Workflow Engine
The generated cache is used by CallTransitionByName for reflection-based method invocation:
┌─────────────────┐ ┌─────────────────┐ ┌───────────────────┐
│ kueinit │────▶│ meta.go │────▶│ boot.MetaFunction │
│ (build time) │ │ di.go │ │ Cache │
└─────────────────┘ └─────────────────┘ └───────────────────┘
│
▼
┌─────────────────┐ ┌─────────────────┐ ┌───────────────────┐
│ Workflow │────▶│ CallTransition │────▶│ Lookup metadata │
│ Execution │ │ ByName │ │ from cache │
└─────────────────┘ └─────────────────┘ └───────────────────┘
│
▼
┌───────────────────┐
│ Invoke method │
│ via reflection │
└───────────────────┘
Build Process
The recommended build process includes running the cache generation:
# 1. Generate module cache
kue update -v
# 2. Build the engine
go build ./...
# Or use the Makefile
make build
Dependency Injection
The engine uses github.com/kuetix/container for dependency injection.
Registration
// Register a service
di.ToFetch("serviceName", serviceInstance)
// Register a factory
di.ToResolve("serviceName", func() interface{} {
return NewService()
})
// Register transitions
di.DependencyInjection["module/name"] = func(name string) {
di.ToResolve(defines.TransitionPrefix+"module/name"+"/"+"transition", func() interface{} {
return workflow.ServiceTransitionMapping{
ServiceName: name,
Name: "transition",
Impl: NewTransition(),
}
})
}
Resolution
// Fetch a service
service := di.Fetch("serviceName")
// Resolve a factory
instance := di.Resolve("serviceName")
Module System
Modules extend the engine with custom transitions and services.
Module Structure
modules/
└── my_module/
└── transitions/
└── my_transitions.go
Transition Implementation
type MyTransitions struct {
workflow.BaseServiceTransitions
}
func NewMyTransitions() *MyTransitions {
return &MyTransitions{}
}
func (t *MyTransitions) MyMethod(ctx *workflow.WorkerSessionContext) domain.FlowStepResult {
// Implementation
return domain.FlowStepResult{
Success: true,
Response: result,
StatusCode: 200,
}
}
Registration 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(),
}
})
}
Base Service Transitions
The BaseServiceTransition struct (engine/workflow/base_service_transitions.go) provides common functionality for custom transition implementations.
Structure
type BaseServiceTransition struct {
Ctx *WorkerSessionContext
}
Helper 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 |
Creating Custom Transitions
type MyTransitions struct {
workflow.BaseServiceTransition
}
func NewMyTransitions() interfaces.ServiceTransitions {
return &MyTransitions{}
}
func (t *MyTransitions) MyMethod(p *workflow.WorkerSessionContext) domain.FlowStepResult {
// Access base methods
ctx := t.S()
// Get property
value := t.Property("myKey")
// Set response
t.SetResponse(map[string]interface{}{"result": value}, 200)
return domain.FlowStepResult{
Success: true,
StatusCode: 200,
}
}