JSON Transform
Reshape arrays, objects, and strings in memory using a chain of declarative operations
Introduction to JSON Transform
The circuit.core.transform.json primitive evaluates a source selector against memory and runs an ordered list of operations over the resulting value. Each operation consumes the output of the previous one, so you can compose array, object, and string transformations into a single pipeline without writing custom code.
Common uses include preparing message arrays for LLM calls, normalising fetched API payloads, and projecting objects down to the fields a downstream step needs.
Step Definition
"steps": [
{
"name": "prepare_messages",
"description": "Inject or override the system prompt in messages array",
"function": "circuit.core.transform.json",
"input": {
"source": "${{ messages }}",
"operations": [
{ "type": "filter", "options": { "expression": "role != `system`" } },
{
"type": "unshift",
"options": {
"items": [{ "role": "system", "content": "${{ prompts.system_prompt }}" }]
}
}
]
},
"outputs": [
{
"name": "prepared_messages",
"description": "Messages array with system prompt injected",
"value": "${{ prepare_messages.output.transformed_result }}"
}
]
}
]input Properties
| Field | Type | Required | Description |
|---|---|---|---|
source | string | Yes | Selector expression (${{ ... }}) for the value to transform. |
operations | array<object> | Yes | Ordered list of operations. Each entry is { "type": "<op>", "options": { ... } }. |
conditions (optional)
Like other primitives, this step accepts a top-level conditions array. If any condition evaluates to false, the step is skipped and transformed_result is set to null.
"conditions": [
{ "expression": "${{ messages != `null` }}" }
]Output Shape
After execution the step exposes the following on its output object:
| Selector | Description |
|---|---|
${{ <step>.output.transformed_result }} | Final value after all operations have been applied. Use this. |
${{ <step>.output.input }} | Resolved source value before any operations ran. |
${{ <step>.output.operations_original }} | Operations as authored, with selectors unresolved. |
${{ <step>.output.operations_evaluated }} | Operations with all ${{ ... }} selectors resolved against memory. |
Selector Resolution Inside Options
Only specific fields within an operation's options are selector-evaluated before that operation runs. These fields are: push.items, unshift.items, insertAt.items, concat.arrays, merge.objects, and replaceMatching.with. All other fields are used as literal values.
Both of these forms are valid and produce the same result:
{ "type": "unshift", "options": { "items": "${{ new_system_messages }}" } }{
"type": "unshift",
"options": {
"items": [{ "role": "system", "content": "${{ prompts.system_prompt }}" }]
}
}Operation Reference
Operations are grouped by the kind of value they expect as input. Mismatched input types (e.g., running pick on an array) cause the step to record an error and leave transformed_result empty.
Array Operations
| Operation | Description | Options |
|---|---|---|
slice | Extract a portion of an array | start, end (optional) |
pop | Remove elements from the end | n (default: 1) |
push | Add elements to the end | items |
shift | Remove elements from the beginning | n (default: 1) |
unshift | Add elements to the beginning | items |
replaceAt | Replace element at index | index, value |
insertAt | Insert elements at index | index, items |
removeAt | Remove elements at indices | indices |
reverse | Reverse the array order | none |
sort | Sort the array | key (optional), order (default asc) |
unique | Remove duplicate elements | key (optional) |
flatten | Flatten nested arrays | depth (default: 1) |
filter | Keep elements matching a condition | expression |
map | Transform each element | expression (JMESPath) |
find | Return first matching element | expression |
findIndex | Return index of first match (or -1) | expression |
concat | Concatenate additional arrays | arrays |
groupBy | Group into an object keyed by field | key |
chunk | Split into fixed-size chunks | size |
replaceMatching | Replace elements matching a condition | condition, with, mode (default all) |
Object Operations
| Operation | Description | Options |
|---|---|---|
pick | Keep only the given keys | keys |
omit | Remove the given keys | keys |
merge | Deep-merge objects into the current | objects |
keys | Return the object's keys as an array | none |
values | Return the object's values as an array | none |
entries | Return [key, value] pairs | none |
fromEntries | Convert [key, value] pairs to an object | none |
set | Set a value at a dot-path | path, value |
unset | Remove values at dot-paths | paths |
renameKey | Rename a single top-level key | from, to |
defaults | Fill missing keys from a defaults object | defaults |
String Operations
| Operation | Description | Options |
|---|---|---|
split | Split a string into an array | separator, limit (optional) |
join | Join an array into a string | separator |
trim | Strip whitespace on both ends | none |
trimStart | Strip leading whitespace | none |
trimEnd | Strip trailing whitespace | none |
substring | Extract a substring | start, end (optional) |
toLowerCase | Lowercase the string | none |
toUpperCase | Uppercase the string | none |
replace | Replace the first match | pattern, replacement |
replaceAll | Replace all matches | pattern, replacement |
padStart | Pad on the left | length, pad_char (default: space) |
padEnd | Pad on the right | length, pad_char (default: space) |
repeat | Repeat the string n times | count |
Where to next
- Expressions — the
filtermini-syntax (with backticks) and the JMESPath context (item,@) used bymap,find,findIndex, andreplaceMatching. - Examples — full pipelines: system-prompt injection, top-N filter→sort→slice→pick, and config normalisation.