0.0
No release in over 3 years
Template-based payload transformation between tasks with multiple engines, schema validation, and composition
2005
2006
2007
2008
2009
2010
2011
2012
2013
2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
2024
2025
2026
 Dependencies

Runtime

>= 1.3.11
>= 1.4.9
>= 1.4.17
>= 1.2.1
>= 2.3
 Project Readme

lex-transformer

Payload transformation engine for LegionIO. Transforms task payloads between services in a relationship chain using pluggable template engines. Maps data from one task's output into the format expected by the next task's input.

Installation

gem install lex-transformer

Or add to your Gemfile:

gem 'lex-transformer'

Standalone Client

Use the transformer without the full LegionIO framework:

require 'legion/extensions/transformer/client'

client = Legion::Extensions::Transformer::Client.new

# Single transform
result = client.transform(
  transformation: '{"greeting":"hello <%= name %>"}',
  payload: { name: 'world' }
)
result[:success] # => true
result[:result]  # => { greeting: "hello world" }

# With explicit engine
result = client.transform(
  transformation: '{"greeting":"hello {{ name }}"}',
  payload: { name: 'world' },
  engine: :liquid
)

# With engine options (passed through to the engine)
result = client.transform(
  transformation: 'Summarize this in one sentence',
  payload: { text: 'Long article...' },
  engine: :llm,
  engine_options: { model: 'claude-opus-4-6', temperature: 0.3 }
)

# With schema validation
result = client.transform(
  transformation: '{"name":"test"}',
  payload: {},
  schema: { required_keys: [:name, :email] }
)
result[:success] # => false
result[:status]  # => "transformer.validation_failed"
result[:errors]  # => ["missing required key: email"]

# By named definition (loaded from Legion::Settings)
result = client.transform(name: 'my_template', payload: { foo: 'bar' })

Transform Chains

Pipe data through sequential transformation steps:

result = client.transform_chain(
  steps: [
    { transformation: '{"user":"<%= login %>"}', schema: { required_keys: [:user] } },
    { transformation: '{"message":"Welcome, <%= user %>!"}' }
  ],
  payload: { login: 'alice' }
)
result[:success]        # => true
result[:result][:args]  # => { message: "Welcome, alice!" }

Each step's output merges into the running payload, so subsequent steps can reference keys from earlier steps.

Engines

Engine Name Detection Description
ERB :erb <% or %> in template Full ERB template rendering via tilt
Static :static Default (no ERB markers) Plain JSON passthrough
Liquid :liquid Explicit only Liquid template rendering ({{ var }})
JSONPath :jsonpath Explicit only Dot-notation value extraction from payload
LLM :llm Explicit only Natural language transformation via Legion::LLM

Auto-detection selects ERB when the template contains ERB markers, otherwise falls back to Static. Use the engine: parameter to force a specific engine.

LLM Engine

The LLM engine uses natural language instructions as the "template":

client.transform(
  transformation: "Summarize this webhook payload into a Slack notification with the PR title and author",
  payload: { action: "opened", pull_request: { title: "Fix auth", user: { login: "alice" } } },
  engine: :llm
)

Requires legion-llm to be started. Provider-agnostic — uses whatever LLM provider is configured (Ollama, Bedrock, Anthropic, OpenAI, Gemini).

Schema Validation

Validate transformed output against a declared schema:

schema = {
  required_keys: [:name, :email],                    # keys that must be present
  types: { name: String, email: String, age: Integer } # optional type checks
}

When validation fails, the transform returns { success: false, status: 'transformer.validation_failed', errors: [...] }.

Named Definitions

Transform definitions can be registered in settings under lex-transformer.definitions.<name> and referenced by name:

{
  "lex-transformer": {
    "definitions": {
      "slack_notify": {
        "transformation": "{\"text\":\"<%= title %> by <%= author %>\"}",
        "engine": "erb"
      }
    }
  }
}
result = client.transform(name: 'slack_notify', payload: { title: 'PR merged', author: 'alice' })

If a definition includes conditions:, the conditioner client is evaluated first and the transform is skipped on failure.

Runners

Transform

transform(transformation:, engine: nil, schema: nil, engine_options: {}, name: nil, **payload)

Renders the transformation template against the payload, optionally validates the result, then dispatches:

  • Hash result: routes to next task (task.queued)
  • Array result: fan-out via dispatch_multiplied (one task per element, marked task.multiplied)
  • Schema failure: transformer.validation_failed, no dispatch

transform_chain(steps:, **payload)

Sequential pipeline. Each step has :transformation, optional :engine, optional :schema. Output of step N feeds into step N+1. Stops on first schema failure.

Transport

  • Exchange: task (inherits from Legion::Transport::Exchanges::Task)
  • Queue: task.transform
  • Routing keys: task.subtask, task.subtask.transform

Requirements

  • Ruby >= 3.4
  • LegionIO framework (for AMQP actor mode)
  • tilt >= 2.3
  • Standalone Client works without the framework

License

MIT