Presentations
TypeScript JQL chart and table inputs and typed presentation-frame output.
judgeval-js JQL source on GitHub
.chart(options: Omit<ChartQuery, "op" | "query">): ChartQuery
.table(options: Omit<TableQuery, "op" | "query">): TableQuery.chart() and .table() are available on both the query builder and the pipeline
builder. They return a presentation query object that wraps a source query or
pipeline. Execute that object with client.present(...), not client.query(...).
Presentation input keys are snake_case in both runtimes. Unlike other
TypeScript options, these are passed straight through to the wire without
renaming, so the accepted keys are exactly chart_type, x_axis, y_axis, series_by, title, description, columns.
Presentation field
Every axis, series, or table column is an object — never a bare string — with:
| Field | Type/default | Constraint |
|---|---|---|
key | string, required | 1–80 chars; ^[A-Za-z_][A-Za-z0-9_]*$; must be produced by the wrapped query |
label | string or null | At most 120 chars |
format | "auto" | auto number compact percent currency_usd duration_ms datetime |
Chart
| Input key | Type | Constraint |
|---|---|---|
chart_type | bar | line | area | pie | Required |
title | string | 1–200 chars |
description | string or null | At most 500 chars |
x_axis | presentation field | Required |
y_axis | presentation field | Required |
series_by | presentation field or null | Forbidden for pie charts |
Axis/series keys must be unique.
Table
A table requires a title and 1–16 ordered presentation fields in columns.
Column keys must be unique. Title and description use the same length limits as a
chart.
Example
import { aggExpr, spans } from "judgeval/jql";
const presentation = spans()
.last("7d")
.pipe()
.summarize("model", { spend: aggExpr({ func: "sum", field: "cost" }) })
.chart({
chart_type: "bar",
title: "Spend by model",
x_axis: { key: "model", label: "Model" },
y_axis: { key: "spend", label: "Spend", format: "currency_usd" },
});
const result = await client.present(presentation);Exact response behavior
present returns normalized presentation metadata and a renderer-neutral frame:
{
"query_id": "q_123",
"presentation": {
"op": "chart",
"chart_type": "bar",
"title": "Spend by model",
"description": null,
"x_axis": { "key": "model", "label": "Model", "format": "auto" },
"y_axis": {
"key": "spend",
"label": "Spend",
"format": "currency_usd"
},
"series_by": null
},
"frame": {
"schema": [
{ "key": "model", "data_type": "string", "nullable": false },
{ "key": "spend", "data_type": "number", "nullable": true }
],
"rows": [{ "model": "gpt-4.1", "spend": 12.5 }]
},
"elapsed_ms": 18
}data_type is string, number, duration, or datetime. Frame rows
contain only the requested presentation keys, in schema order.
Rendering boundary
client.present(...) does not render HTML, an image, a React component, or a chart.
It validates the presentation, runs the query, and returns metadata plus typed rows.
Your application chooses a renderer and maps presentation and frame into it.
If a requested presentation key is not produced by the query, execution fails with
BAD_PRESENTATION and a hint listing available result columns.