Go Code Generation
Generate high-performance Go code from your visual workflows with goroutines, channels, and comprehensive helper libraries.
Installation
Requirements
- Go 1.21+
- Node.js installed and on PATH (only needed if your workflow has Code nodes)
- Standard library only — no external Go packages
Project Layout
The downloaded bundle is a ready-to-run Go module:
my-workflow/
go.mod # module github.com/circuitry/runtime
workflow.go # your generated workflow (module root)
helpers/ # helper library
ui/ # console UI helpers
Run it from the module root:
go run .
# or build a binary:
go build -o workflow .
Environment Variables
export OPENAI_API_KEY=your_openai_api_key_here
# For Claude models:
export ANTHROPIC_API_KEY=your_anthropic_api_key_here
Usage Example
package main
import (
"encoding/json"
"fmt"
"log"
"sync"
"github.com/circuitry/runtime/helpers"
"github.com/circuitry/runtime/ui"
)
// Generated function with parallel execution (example)
func myWorkflow(input map[string]interface{}) (map[string]interface{}, error) {
data := input
if data == nil {
data = make(map[string]interface{})
}
// Initial HTTP request (JSON responses are parsed automatically)
result, err := helpers.HTTPRequest("https://api.example.com/data", nil)
if err != nil {
return nil, err
}
if m, ok := result.(map[string]interface{}); ok {
data = m
}
// Fork node - parallel execution
var wg sync.WaitGroup
var branch1Result, branch2Result map[string]interface{}
var err1, err2 error
wg.Add(2)
// Branch 1: AI analysis
go func() {
defer wg.Done()
analysis, err := helpers.CallAI("Analyze items", &helpers.AIOptions{Model: "gpt-4o-mini"})
if err != nil {
err1 = err
return
}
branch1Result = map[string]interface{}{"analysis": analysis}
}()
// Branch 2: Data processing with JavaScript
go func() {
defer wg.Done()
jsCode := `
data.processed = data.items.map(item => ({
...item,
processed: true,
timestamp: new Date().toISOString()
}));
`
result, err := helpers.ExecuteJavaScript(jsCode, data)
if err != nil {
err2 = err
return
}
if m, ok := result.(map[string]interface{}); ok {
branch2Result = m
}
}()
wg.Wait()
// Check for errors
if err1 != nil {
return nil, err1
}
if err2 != nil {
return nil, err2
}
// Join results
for key, value := range branch1Result {
data[key] = value
}
for key, value := range branch2Result {
data[key] = value
}
// Show completion
ui.ShowMessage("Workflow completed successfully", "success")
return data, nil
}
func main() {
input := map[string]interface{}{
"items": []interface{}{
map[string]interface{}{"id": "1", "name": "Item 1"},
map[string]interface{}{"id": "2", "name": "Item 2"},
},
}
result, err := myWorkflow(input)
if err != nil {
log.Fatal(err)
}
// Pretty print result
jsonResult, _ := json.MarshalIndent(result, "", " ")
fmt.Printf("Result:\n%s\n", jsonResult)
}
Code Nodes
Go runs Code-node JavaScript with Node.js via ExecuteJavaScript() — Node.js must be installed and on PATH:
// JavaScript code execution in Go
jsCode := `
data.processed = data.items.map(item => ({
...item,
processed: true,
timestamp: new Date().toISOString()
}));
`
result, err := helpers.ExecuteJavaScript(jsCode, data)
if err != nil {
return nil, fmt.Errorf("JavaScript execution failed: %w", err)
}
// If the code returns a value, that value is the result;
// otherwise the (possibly mutated) data object is returned.
Parallel Execution
Fork/join nodes use goroutines and WaitGroups:
// Fork node - parallel execution
var wg sync.WaitGroup
results := make([]map[string]interface{}, 2)
errors := make([]error, 2)
wg.Add(2)
// Branch 1
go func() {
defer wg.Done()
result, err := helpers.HTTPRequest("https://api1.example.com/data", nil)
if m, ok := result.(map[string]interface{}); ok {
results[0] = m
}
errors[0] = err
}()
// Branch 2
go func() {
defer wg.Done()
result, err := helpers.CallAI("Process data", &helpers.AIOptions{Model: "gpt-4o-mini"})
results[1] = map[string]interface{}{"analysis": result}
errors[1] = err
}()
wg.Wait()
// Check for errors
for i, err := range errors {
if err != nil {
return nil, fmt.Errorf("branch %d failed: %w", i+1, err)
}
}
// Merge results
mergedData := make(map[string]interface{})
for _, result := range results {
for key, value := range result {
mergedData[key] = value
}
}
Helper Functions
HTTP Requests
// GET request — JSON responses are parsed automatically,
// other content types come back as a string
data, err := helpers.HTTPRequest("https://api.example.com/users", nil)
if err != nil {
return nil, err
}
users, _ := data.([]interface{}) // assert to the shape you expect
// POST with data
options := &helpers.HTTPOptions{
Method: "POST",
Headers: map[string]string{
"Content-Type": "application/json",
"Authorization": "Bearer token",
},
Body: map[string]interface{}{
"name": "John",
"email": "john@example.com",
},
}
result, err := helpers.HTTPRequest("https://api.example.com/users", options)
AI Calls
// Simple AI call (uses OPENAI_API_KEY from the environment)
response, err := helpers.CallAI("Summarize this text", &helpers.AIOptions{Model: "gpt-4o-mini"})
if err != nil {
return nil, err
}
// Claude models route to the Anthropic API automatically (uses ANTHROPIC_API_KEY)
analysis, err := helpers.CallAI("Analyze sentiment", &helpers.AIOptions{
Model: "claude-sonnet-4-20250514",
SystemPrompt: "You are a precise sentiment analyst",
Temperature: 0.7,
MaxTokens: 500,
})
// Local or custom OpenAI-compatible endpoint
local, err := helpers.CallAI("Hello", &helpers.AIOptions{
Model: "llama3",
Endpoint: "http://localhost:11434/v1/chat/completions",
})
Template Variables
text := "Hello {{name}}, your order {{order.id}} is ready"
context := helpers.WorkflowData{
"name": "John",
"order": map[string]interface{}{"id": "12345"},
}
result := helpers.ReplaceTemplateVariables(text, context)
// Result: "Hello John, your order 12345 is ready"
// {{input}} and {{previousOutput}} expand to the whole data object as JSON;
// dotted paths like {{input.order.id}} also work.
// Unknown placeholders resolve to an empty string.
Deployment Options
Standalone Binary
# Build for current platform (run from the module root)
go build -o workflow .
# Cross-compile for Linux
GOOS=linux GOARCH=amd64 go build -o workflow-linux .
# Cross-compile for Windows
GOOS=windows GOARCH=amd64 go build -o workflow.exe .
Docker Container
# Multi-stage build
FROM golang:1.22-alpine AS builder
WORKDIR /app
COPY . .
RUN go build -o workflow .
FROM alpine:latest
# nodejs is only needed if the workflow contains Code nodes
RUN apk --no-cache add ca-certificates nodejs
WORKDIR /root/
COPY --from=builder /app/workflow ./
CMD ["./workflow"]
HTTP Server
package main
import (
"encoding/json"
"log"
"net/http"
)
func workflowHandler(w http.ResponseWriter, r *http.Request) {
if r.Method != http.MethodPost {
http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
return
}
var input map[string]interface{}
if err := json.NewDecoder(r.Body).Decode(&input); err != nil {
http.Error(w, "Invalid JSON", http.StatusBadRequest)
return
}
result, err := myWorkflow(input)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(result)
}
func main() {
http.HandleFunc("/workflow", workflowHandler)
log.Println("Server starting on :8080")
log.Fatal(http.ListenAndServe(":8080", nil))
}
Testing
package main
import (
"testing"
"reflect"
)
func TestMyWorkflow(t *testing.T) {
input := map[string]interface{}{
"items": []interface{}{
map[string]interface{}{"id": "1", "name": "Test"},
},
}
result, err := myWorkflow(input)
if err != nil {
t.Fatalf("Workflow failed: %v", err)
}
if result["processed"] == nil {
t.Error("Expected processed field")
}
}
func TestEmptyInput(t *testing.T) {
result, err := myWorkflow(nil)
if err != nil {
t.Fatalf("Workflow failed with empty input: %v", err)
}
if result == nil {
t.Error("Expected non-nil result")
}
}
Error Handling
import "errors"
// Custom error types
var (
ErrInvalidInput = errors.New("invalid input data")
ErrNetworkError = errors.New("network request failed")
ErrProcessingError = errors.New("data processing failed")
)
func myWorkflow(input map[string]interface{}) (map[string]interface{}, error) {
if input == nil {
return nil, ErrInvalidInput
}
result, err := helpers.HTTPRequest(url, options)
if err != nil {
return nil, fmt.Errorf("%w: %v", ErrNetworkError, err)
}
data, ok := result.(map[string]interface{})
if !ok {
return nil, ErrProcessingError
}
// Continue processing...
return data, nil
}
Performance Tips
- Goroutine Pools: For high-throughput scenarios
- Context Cancellation: Use context.Context for timeout handling
- Memory Management: Reuse slices and maps where possible
- Profiling: Use pprof for performance analysis
import "context"
func myWorkflowWithContext(ctx context.Context, input map[string]interface{}) (map[string]interface{}, error) {
// Check context cancellation
select {
case <-ctx.Done():
return nil, ctx.Err()
default:
}
// Continue with workflow...
}