Bonnard

Bonnard draws a table to show the rows themselves, with a label and a format per column.

The examples use Acme, a demo retailer that reports in pounds. Each variant is a few words in the question. A follow-up changes a table already drawn.

How an agent draws it

The chat above ran three tools, in this order:

  1. explore_schema finds the tables and their columns: here orders, one row per order, with order_date, region and revenue.
  2. sql_query runs a read-only query, so the agent can check the numbers first.
  3. render_chart takes the SQL and the chart options. It runs the query and returns the table the chat draws.

The render_chart call behind the table above:

render_chartjson

JSON

{
  "charts": [
    {
      "sql": "SELECT region, SUM(revenue) / 1000 AS revenue, COUNT(*) AS orders FROM orders WHERE order_date >= DATE '2026-07-01' AND order_date < DATE '2026-10-01' GROUP BY region ORDER BY revenue DESC",
      "chartType": "table",
      "title": "Revenue and orders by region, Q3 2026",
      "fields": [
        { "name": "region", "label": "Region" },
        { "name": "revenue", "label": "Revenue (£k)", "format": "number" },
        { "name": "orders", "label": "Orders", "format": "number" }
      ]
    }
  ]
}

A call takes up to 10 charts, shown as a carousel. Each example below shows its one entry. A connected agent reads the full option reference with explore_docs, topic charts/table.

Currency

Prompt

Revenue and average order value by region last quarter, in pounds.

render_chartjson

JSON

{
  "sql": "SELECT region, SUM(revenue) AS revenue, SUM(revenue) / COUNT(*) AS average_order FROM orders WHERE order_date >= DATE '2026-07-01' AND order_date < DATE '2026-10-01' GROUP BY region ORDER BY revenue DESC",
  "chartType": "table",
  "title": "Revenue and average order by region, Q3 2026",
  "fields": [
    { "name": "region", "label": "Region" },
    { "name": "revenue", "label": "Revenue", "format": "currency", "currency": "GBP" },
    { "name": "average_order", "label": "Average order", "format": "currency", "currency": "GBP" }
  ]
}

currency takes an ISO 4217 code, here GBP, and shows its symbol. A whole amount has no decimals, and an amount with pence has two.

Percentages

Prompt

September revenue by category, with each category's share of the total.

render_chartjson

JSON

{
  "sql": "SELECT category, SUM(revenue) / 1000 AS revenue, SUM(revenue) / SUM(SUM(revenue)) OVER () AS share FROM order_lines WHERE order_date >= DATE '2026-09-01' AND order_date < DATE '2026-10-01' GROUP BY category ORDER BY revenue DESC",
  "chartType": "table",
  "title": "Revenue by category, September 2026",
  "fields": [
    { "name": "category", "label": "Category" },
    { "name": "revenue", "label": "Revenue (£k)", "format": "number" },
    { "name": "share", "label": "Share", "format": "percent" }
  ]
}

The query returns each share as a fraction, and percent shows 0.2176 as 21.8%. A column of 0 to 100 values reads the same.

Short numbers

Prompt

Revenue by channel for each quarter this year, shortened to K and M.

render_chartjson

JSON

{
  "sql": "SELECT channel, SUM(revenue) FILTER (WHERE order_date < DATE '2026-04-01') AS q1, SUM(revenue) FILTER (WHERE order_date >= DATE '2026-04-01' AND order_date < DATE '2026-07-01') AS q2, SUM(revenue) FILTER (WHERE order_date >= DATE '2026-07-01') AS q3 FROM orders WHERE order_date >= DATE '2026-01-01' AND order_date < DATE '2026-10-01' GROUP BY channel ORDER BY q3 DESC",
  "chartType": "table",
  "title": "Revenue by channel and quarter, 2026 (£)",
  "fields": [
    { "name": "channel", "label": "Channel" },
    { "name": "q1", "label": "Q1", "format": "compact" },
    { "name": "q2", "label": "Q2", "format": "compact" },
    { "name": "q3", "label": "Q3", "format": "compact" }
  ]
}

compact writes 3,209,800 as 3.2M and 893,200 as 893.2K. Without it, a table shows every digit.

Every row

Prompt

Revenue, orders and conversion for every week of last quarter.

render_chartjson

JSON

{
  "sql": "SELECT week_start AS week, revenue / 1000 AS revenue, orders, conversion FROM weekly_trading WHERE week_start >= DATE '2026-06-29' AND week_start < DATE '2026-09-28' ORDER BY week_start",
  "chartType": "table",
  "title": "Weekly trading, Q3 2026",
  "fields": [
    { "name": "week", "label": "Week" },
    { "name": "revenue", "label": "Revenue (£k)", "format": "number" },
    { "name": "orders", "label": "Orders", "format": "number" },
    { "name": "conversion", "label": "Conversion", "format": "percent" }
  ]
}

The table holds every row the query returns. A long one scrolls inside the chat and pages on a phone. A tap on a header sorts by that column, descending first, and a third tap restores the query’s order.

Options and limits

What a table takes
Ask forWhat Bonnard draws
”as a table”Every column and row, in the query’s order
”in pounds” or “in euros”Amounts with the currency’s symbol
”as a percentage”Values with one decimal, from 0 to 1 fractions or 0 to 100
”shortened to K and M”Numbers such as 1.2M and 893.2K
”call the column Net revenue”A label in place of the column’s SQL name
”the top 10”The 10 rows the query returns
”sorted by orders”Rows in that order, which the reader can sort again
  • A table takes fields alone: a label and a format per column. encode, axes, stacking, reference lines and styling are refused, with the options a table takes.
  • Formats are never inferred from column names. Money and ratios show as plain numbers until a format is set.
  • Without a label, a snake_case name shows with spaces and a capital: average_order reads “Average order”.
  • Every row the query returns is shown. A chart query returns up to 5,000 rows by default.
  • When the rows run past what one reply can carry, the table keeps the first rows and tells the agent how many.
  • Dates show as 29 Jun 2026, and date-times as 29 Jun 26, 14:05.
  • There is no pivot table. A column per period comes from the SQL, as the quarters above do.
  • On a dashboard, a table is a chart tile with chartType: "table".