Documentation

Grid state for AI models

"Show me the pending orders in the Aegean, biggest first, and hide the SKU." That is a filter, a sort and a hidden column, and the grid already supports all three. A language model can produce that state if it knows exactly what the state looks like for this grid. The grid gives you three calls for that:

CallWhat it does
getAiSchema(options?)A JSON Schema of the grid's state, limited to what its columns allow
getAiState(options?)The current state in that shape, for the model to start from
applyAiState(answer, options?)Checks the model's answer and applies the valid part in one step

There is no model inside the grid, and the grid sends nothing anywhere. You use your own model (any model that supports structured output, or any that can return JSON) and call it from your own code, usually your server. The calls are on the controller, so they work the same in React (onReady) and in plain JavaScript (grid.controller). Enterprise only: in Community they throw an error that names the feature.

The PivotGrid has the same for its layout of fields, measures and totals: see Pivot layout for AI models.

A round trip

// In the browser: describe the grid and its current state.
const schema = controller.getAiSchema();
const current = controller.getAiState();

// On your server: ask your own model, with the schema as its output format.
const answer = await askModel({
  instructions:
    'You change the state of a data grid. Start from the current state and ' +
    'change only what the user asks for.',
  input: `Current state: ${JSON.stringify(current)}\nRequest: ${userText}`,
  outputSchema: schema,
});

// Back in the browser: apply what came back.
const { applied, dropped } = controller.applyAiState(answer);
if (dropped.length > 0) showNotice(`Could not apply: ${dropped.join('; ')}`);

askModel stands for your own call to your model's API. Most APIs that support structured output accept the schema as it is. answer can be the parsed object or the JSON text the API returns.

What the schema covers

PartPropertyNotes
filterfilterOne condition or a tree of and / or groups. null clears it
sortsortOutermost first; [] for unsorted
groupinggroupByOutermost first; [] for none. Not offered on a tree grid
columnVisibilitycolumns[].visibleOnly the columns listed change
columnSizingcolumns[].widthPixels, or null to leave as is
columnPinningcolumns[].pinned'left', 'right' or null
searchsearchTextThe toolbar's free-text search

The schema only offers what the grid would accept. A column with sortable: false is not in the sort list, a column with groupable: false is not in the grouping list, and each data type gets the operators the filter builder offers for it. Text columns get contains / starts with / ends with. Numbers and dates get comparisons and between. Dates also get period, which keeps "this month" meaning this month tomorrow. Yes/no columns get =, and every column gets is blank / is not blank.

The schema's description lists every column with its id, header, type and meaning, so a model can map "biggest" to the right number column.

Strict structured output

Every object in the schema lists all of its properties as required and allows no others. "Leave this alone" is written as an explicit null or []. That is the subset the strictest structured-output modes accept, and any other JSON Schema consumer accepts it too. The schema is draft 2020-12. The filter is recursive through $defs.

Telling the model more

const options = {
  // Parts the model may not change. They are also left out of the schema.
  exclude: ['columnSizing', 'columnPinning'],
  columns: {
    revenue: { description: 'Order total in USD, VAT included' },
    // List this column's values, so "orders from Izmir" matches what is stored.
    city: { includeValues: true },
    // Never mention this column to the model.
    customerEmail: { exclude: true },
  },
  maxValues: 50, // most values listed per column (default 100)
};

controller.getAiSchema(options);
controller.applyAiState(answer, options); // same options on the way back

Data values are listed only when you ask. includeValues sends the column's distinct values to whatever model you use, so turn it on only for columns whose values you are allowed to share. A column with a static lookup list is the exception: that list is your own code → label table (say izm shown as "Izmir"), and a model cannot match a coded value without it. It is listed by default. Set includeValues: false to keep it out.

What applyAiState does with an answer

  • Keeps the valid part. A condition on a column that does not exist, an operator that does not suit the column's type, a sort on a column that cannot be sorted: each one is dropped, and the rest is applied.
  • Says what it dropped. dropped lists each problem in words, such as filter on country (=): no such column. Show it to the user, log it, or send it back to the model for a second try. With debugMode the console prints it as well.
  • Never throws on a bad answer. Text that is not JSON, or JSON that is not an object, returns applied: [] with the reason.
  • Applies everything in one step. The filter, sort, grouping and columns change in one view change, so the grid recomputes once.
  • Puts single-column conditions in that column's own filter. There the filter row and the header popup show them, and the user can clear them the usual way. Only conditions that span columns (an or across two of them) go to the filter builder's tree.
  • Will not hide every column. An answer that would leave no visible column keeps the visibility as it was.
  • Ignores parts it was told to leave alone. If exclude names a part, that part is ignored even when the model sends it.

What it does not do

  • No model, prompt or chat box. The schema and the checks are the grid's part. The conversation, the model and the API key belong to your application.
  • Not saved views. A saved view (getViewState) holds more than the model should touch: pages, open groups, chart state. Use saved views to remember a layout and this API to change one from words.
  • No row data in the schema. The model sees columns and, only when you allow it, the distinct values of a column. It never sees rows. Questions about the data ("which region sold most?") need the data, which is a different feature.