Frame Tools
Reshape, query, and combine lists of records โ a columnar data operations layer for in-flight data.
Frame tools are pure deterministic operations on lists of record maps. There are no external calls, no AI generation, and no credit cost. Every response includes a deterministic receipt under _meta.datagrout. Frame tools compose naturally with each other and with the Math suite via flow.into, and accept cache_ref outputs from auto-paginated integration tools so you can operate on large datasets without re-transmitting them. All Frame tools are available at data-grout@1/frame.*@1.
frame.select@1
Keep, rename, or drop columns from a list of records.
Parameters
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
payload |
array | conditionally | โ |
List of record maps. Provide either payload or cache_ref, not both |
cache_ref |
string | conditionally | โ | Cache reference from a prior auto-paginated tool call |
columns |
array | object | no | โ |
Fields to keep. A list keeps only those fields; a { "old_name": "new_name" } map renames while selecting |
drop |
array | no | โ |
Field names to remove. Applied after columns if both are given |
Example: keep specific columns
{
"name": "data-grout@1/frame.select@1",
"arguments": {
"payload": [
{ "name": "Alice", "email": "alice@acme.com", "role": "admin", "internal_id": 1 },
{ "name": "Bob", "email": "bob@acme.com", "role": "user", "internal_id": 2 }
],
"columns": ["name", "email", "role"]
}
}
Response:
{
"records": [
{ "name": "Alice", "email": "alice@acme.com", "role": "admin" },
{ "name": "Bob", "email": "bob@acme.com", "role": "user" }
],
"count": 2
}
Example: rename columns
{
"name": "data-grout@1/frame.select@1",
"arguments": {
"payload": [ "..." ],
"columns": { "name": "customer_name", "email": "contact_email" }
}
}
frame.filter@1
Filter rows using declarative predicate conditions. All conditions in the where array must match (AND logic).
Parameters
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
payload |
array | conditionally | โ | List of record maps |
cache_ref |
string | conditionally | โ | Cache reference from a prior paginated call |
where |
array | yes | โ |
List of condition objects. Each has field, op, and (for most ops) value |
Filter operators
| Operator | Description |
|---|---|
eq / neq |
Equals / not equals |
gt / gte / lt / lte |
Numeric comparisons |
in / not_in |
Value is in (or not in) a list |
contains |
String contains substring |
starts_with / ends_with |
String prefix / suffix match |
is_null / not_null |
Null check (no value needed) |
Example
{
"name": "data-grout@1/frame.filter@1",
"arguments": {
"payload": [ "..." ],
"where": [
{ "field": "status", "op": "eq", "value": "active" },
{ "field": "amount", "op": "gte", "value": 1000 }
]
}
}
Response:
{
"records": [ "...matched rows..." ],
"count": 14,
"filtered": 38
}
frame.sort@1
Sort records by one or more fields with per-field direction control.
Parameters
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
payload |
array | conditionally | โ | List of record maps |
cache_ref |
string | conditionally | โ | Cache reference |
by |
array | yes | โ |
List of sort specs. Each has field (string) and optional dir ("asc" or "desc", default "asc") |
Example
{
"name": "data-grout@1/frame.sort@1",
"arguments": {
"payload": [ "..." ],
"by": [
{ "field": "revenue", "dir": "desc" },
{ "field": "name", "dir": "asc" }
]
}
}
Response:
{
"records": [ "...sorted rows..." ],
"count": 52
}
frame.group@1
Group rows by one or more fields and aggregate other fields. Returns one record per group.
Parameters
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
payload |
array | conditionally | โ | List of record maps |
cache_ref |
string | conditionally | โ | Cache reference |
by |
string | array | yes | โ | Field name or list of field names to group by |
agg |
array | no | โ |
List of aggregation specs. Each has field, op, and optional as (output column name) |
Aggregation operators
| Operator | Description |
|---|---|
sum |
Sum of numeric values |
mean |
Arithmetic mean |
count |
Row count per group |
count_distinct |
Count of distinct values |
min / max |
Minimum / maximum value |
first / last |
First or last value in group order |
list |
Collect all values into an array |
Example
{
"name": "data-grout@1/frame.group@1",
"arguments": {
"payload": [
{ "customer": "Acme", "amount": 500, "status": "paid" },
{ "customer": "Globex", "amount": 300, "status": "pending" },
{ "customer": "Acme", "amount": 200, "status": "paid" }
],
"by": "customer",
"agg": [
{ "field": "amount", "op": "sum", "as": "total" },
{ "field": "amount", "op": "count", "as": "invoice_count" }
]
}
}
Response:
{
"records": [
{ "customer": "Acme", "total": 700, "invoice_count": 2 },
{ "customer": "Globex", "total": 300, "invoice_count": 1 }
],
"count": 2,
"groups": 2
}
frame.pivot@1
Reshape a long dataset into a wide format. Distinct values of one column become new column headers.
Parameters
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
payload |
array | conditionally | โ | List of record maps in long format |
cache_ref |
string | conditionally | โ | Cache reference |
index |
string | yes | โ | Field whose values form the row identity |
columns |
string | yes | โ | Field whose distinct values become the new column headers |
values |
string | yes | โ | Field whose values fill the new columns |
agg |
string | no |
"first" |
Aggregation to apply when multiple rows map to the same cell: "first", "last", "sum", "mean", "count", "list" |
Example
{
"name": "data-grout@1/frame.pivot@1",
"arguments": {
"payload": [
{ "region": "North", "quarter": "Q1", "revenue": 42000 },
{ "region": "North", "quarter": "Q2", "revenue": 48000 },
{ "region": "South", "quarter": "Q1", "revenue": 31000 },
{ "region": "South", "quarter": "Q2", "revenue": 35000 }
],
"index": "region",
"columns": "quarter",
"values": "revenue"
}
}
Response:
{
"records": [
{ "region": "North", "Q1": 42000, "Q2": 48000 },
{ "region": "South", "Q1": 31000, "Q2": 35000 }
],
"count": 2,
"pivot_columns": ["Q1", "Q2"]
}
frame.slice@1
Paginate or take a top-N subset of records by offset and limit.
Parameters
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
payload |
array | conditionally | โ | List of record maps |
cache_ref |
string | conditionally | โ | Cache reference |
offset |
integer | no |
0 |
Number of records to skip |
limit |
integer | no |
25 |
Maximum number of records to return |
Example
{
"name": "data-grout@1/frame.slice@1",
"arguments": {
"cache_ref": "pg_abc123",
"offset": 50,
"limit": 25
}
}
Response:
{
"records": [ "...25 records..." ],
"count": 25,
"offset": 50,
"total": 842
}
frame.join@1
Join two lists of records on one or more shared key fields. For conflicting non-key fields, the left value wins and the right field is prefixed with right_.
Parameters
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
left |
array | conditionally | โ |
Left-side record list. Provide left or left_cache_ref |
right |
array | conditionally | โ |
Right-side record list. Provide right or right_cache_ref |
left_cache_ref |
string | conditionally | โ | Cache reference for left-side data |
right_cache_ref |
string | conditionally | โ | Cache reference for right-side data |
on |
string | array | yes | โ | Key field name or list of key field names to join on |
type |
string | no |
"inner" |
Join type: "inner", "left", "right", "outer" |
Example
{
"name": "data-grout@1/frame.join@1",
"arguments": {
"left": [
{ "id": "c1", "name": "Acme Corp", "tier": "enterprise" },
{ "id": "c2", "name": "Globex", "tier": "growth" }
],
"right": [
{ "id": "c1", "open_invoices": 3, "overdue_amount": 4500 },
{ "id": "c3", "open_invoices": 1, "overdue_amount": 800 }
],
"on": "id",
"type": "left"
}
}
Response:
{
"records": [
{ "id": "c1", "name": "Acme Corp", "tier": "enterprise", "open_invoices": 3, "overdue_amount": 4500 },
{ "id": "c2", "name": "Globex", "tier": "growth", "open_invoices": null, "overdue_amount": null }
],
"count": 2
}
Using cache_ref with frame tools
Every tool call result is now cached and assigned a cache_ref at _meta.datagrout.cache_ref. Frame tools can reference this ref to operate on cached data without re-transmitting it through the LLM context. See also the data.* tools for general JSON structure manipulation with cache_ref support.
{
"name": "data-grout@1/flow.into@1",
"arguments": {
"plan": [
{
"id": "fetch",
"tool": "quickbooks@1/get-all-invoices@1",
"args": { "limit": 10000 }
},
{
"id": "group",
"tool": "data-grout@1/frame.group@1",
"args": {
"cache_ref": "$fetch._cache_ref",
"by": "customer_name",
"agg": [
{ "field": "amount", "op": "sum", "as": "total_revenue" },
{ "field": "id", "op": "count", "as": "invoice_count" }
]
}
},
{
"id": "top10",
"tool": "data-grout@1/frame.sort@1",
"args": {
"payload": "$group.records",
"by": [{ "field": "total_revenue", "dir": "desc" }]
}
},
{
"id": "display",
"tool": "data-grout@1/frame.slice@1",
"args": { "payload": "$top10.records", "limit": 10 }
}
]
}
}