Judgment Labs Logo
PythonJQL

Presentations

Python JQL chart and table inputs and typed presentation-frame output.

judgeval JQL source on GitHub

A presentation is a data contract, not a rendered component. First-party React components for rendering these presentations are still in development and are not published yet — until they ship, map the returned presentation and frame into your own table or chart library. See Rendering boundary.

.chart(*, chart_type, title, x_axis, y_axis, series_by=None, description=None) -> dict
.table(*, title, columns, description=None) -> dict

.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 Python 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:

FieldType/defaultConstraint
keystring, required1–80 chars; ^[A-Za-z_][A-Za-z0-9_]*$; must be produced by the wrapped query
labelstring or nullAt most 120 chars
format"auto"auto number compact percent currency_usd duration_ms datetime

Chart

Input keyTypeConstraint
chart_typebar | line | area | pieRequired
titlestring1–200 chars
descriptionstring or nullAt most 500 chars
x_axispresentation fieldRequired
y_axispresentation fieldRequired
series_bypresentation field or nullForbidden 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

from judgeval.jql import agg_expr, spans

presentation = (
    spans()
    .last("7d")
    .pipe()
    .summarize({"spend": agg_expr("sum", "cost")}, by="model")
    .chart(
        chart_type="bar",
        title="Spend by model",
        x_axis={"key": "model", "label": "Model"},
        y_axis={"key": "spend", "label": "Spend", "format": "currency_usd"},
    )
)
result = 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.

The first-party React components are still in development. The response shape documented above is what they will consume, so an integration written against it now will carry over when they ship.

If a requested presentation key is not produced by the query, execution fails with BAD_PRESENTATION and a hint listing available result columns.