AI assistant - Pro

Bring a language model into your grid with four helpers that stay strictly model-agnostic: natural-language filter, smart fill, summarise, and classify. Ships in the paid sv-grid-pro add-on; the Community build does not include these features.

Run all four helpers live - the demo below is wired to the bundled deterministic mockAIProvider, so no keys required:

What it is

installPro(api) (the same call you use for export and print) augments your SvGridApi with an ai namespace:

api.ai.filter(query, opts?)        // NL query -> filter + sort plan
api.ai.smartFill(opts)             // examples -> proposed column values
api.ai.summarize(opts)             // row / selection / group / all -> text + bullets
api.ai.classify(opts)              // free-text cells -> bucketed labels

Every call routes through one AIProvider you register at app boot. The grid never bundles a model client - you keep full control of model choice, routing, and data handling.

When to use it

If you don't need natural-language anywhere, skip this module entirely - the rest of sv-grid-pro doesn't depend on it.

Setting up the provider

The grid talks to your model through a single async function. Wire it once at app startup:

import { setAIProvider, type AIProvider } from 'sv-grid-pro'

const myProvider: AIProvider = async ({ prompt, responseFormat, signal, task, maxOutputTokens }) => {
  const r = await fetch('/api/ai', {
    method: 'POST',
    headers: { 'content-type': 'application/json' },
    body: JSON.stringify({ prompt, responseFormat, task, maxOutputTokens }),
    signal,
  })
  if (!r.ok) throw new Error(`AI provider returned ${r.status}`)
  return r.text()
}

setAIProvider(myProvider)

A few design points worth knowing:

For testing or for demo purposes, the package ships a deterministic mockAIProvider that returns plausible canned shapes per task. Wire it in development:

import { setAIProvider, mockAIProvider } from 'sv-grid-pro'
setAIProvider(mockAIProvider)

1. Natural-language filter

Translate a sentence into a filter + sort plan against the current grid's columns. The grid embeds the column schema (names, types, sample values) in the prompt so the model picks real field names rather than hallucinating.

const plan = await api.ai.filter('accounts losing momentum in EMEA, by NPS')
// {
//   filters: [
//     { field: 'region', operator: 'equals', value: 'EMEA' },
//     { field: 'nps',    operator: 'lessThan', value: '30' },
//   ],
//   sort: [{ field: 'nps', desc: false }],
//   rationale: 'EMEA region, low NPS, sorted ascending.',
// }

By default aiFilter returns the plan but does not apply it to the grid - that lets you show a preview ("here's what I'd do, accept?") before committing. Pass { apply: true } to apply directly:

await api.ai.filter('big EMEA deals', { apply: true })

Hallucination guard

If the model invents a column name that doesn't exist in your data, the helper silently drops that clause rather than passing it to setFilter, which would otherwise throw. This is intentional: you'd rather lose a clause than crash the page.

2. Smart fill

User provides one or two worked examples for a column; the grid asks the model to propose values for the remaining empty cells.

const result = await api.ai.smartFill({
  field: 'tier',
  examples: [
    { input: { company: 'Northwind' }, output: 'enterprise' },
    { input: { company: 'Helios' },    output: 'growth' },
  ],
})
// result.predictions: [{ rowIndex, value, confidence }, ...]

You decide what to do with the predictions - accept-all, accept-per-cell, write them to a proposedTier field on the row data and render a "✓" button in the cell, etc. The example demo does per-cell accept with a confidence-coloured pill.

If targetRowIndices is omitted, the helper auto-selects rows whose current field value is null, undefined, or ''.

3. Summarise

Drop a slice of the grid into the model and ask for a one-paragraph + bulleted summary. Four target modes:

await api.ai.summarize({ target: { kind: 'all' } })
await api.ai.summarize({ target: { kind: 'row', rowIndex: 5 } })
await api.ai.summarize({ target: { kind: 'selection', rowIndices: [1,2,3] } })
await api.ai.summarize({ target: { kind: 'group', field: 'region', value: 'EMEA' } })

For large slices the grid samples rows uniformly so the prompt stays under a sensible token budget. The response shape:

{
  text: 'One paragraph...',
  bullets: ['punchy bullet 1', 'punchy bullet 2', ...],
  highlightedFields: ['arr', 'nps'],   // columns the summary leans on
}

The optional question parameter biases the summary toward the columns that answer the question.

4. Classify

Bucket free-text cells into one of a known set of labels:

const r = await api.ai.classify({
  inputField: 'notes',
  outputField: 'sentiment',
  classes: ['at-risk', 'expanding', 'steady'],
  classDescriptions: {
    'at-risk':   'churn signals, escalations, lost champion',
    'expanding': 'new modules, more seats, additional regions',
    'steady':    'no signals in either direction',
  },
})

The helper filters out any predictions whose value is not in classes, so downstream code can trust the output is a clean enum.

Subscribing the grid to predictions

Cell snippets cache their output by (row, column). If you store predictions in a separate state ref - e.g. a Map<rowIndex, value> that the snippet looks up at render time - the grid won't know to re-render when that map changes. Write the proposal onto the row itself instead:

async function runClassify() {
  const result = await api.ai.classify({ /* ... */ })
  const byIdx = new Map(result.predictions.map((p) => [p.rowIndex, p]))
  // Re-assign the data array - Svelte 5 reactivity picks this up and the
  // grid re-renders every visible cell.
  accounts = accounts.map((a, i) => {
    const p = byIdx.get(i)
    return p ? { ...a, proposedSentiment: p.value, sentimentConfidence: p.confidence } : a
  })
}

Then your cell snippet reads props.row.proposedSentiment and re-renders exactly when expected.

License gate

Every AI call routes through the same polite license gate as exportData and print:

See also

Frequently asked questions

What AI features does SvGrid have?

The sv-grid-pro AI assistant ships four model-agnostic helpers: natural-language filter, smart fill, summarise, and classify. They run through a bring-your-own-model adapter, so you wire in your own LLM endpoint.

Which LLM does SvGrid use?

None by default - it is model-agnostic. You supply an adapter for OpenAI, Anthropic Claude, a local model, or anything else. The demo ships a mock provider so you can evaluate the flow without an API key.

Is my grid data sent to an AI provider?

Only if you wire one up and invoke a helper. SvGrid itself makes no network calls; the AI helpers send exactly the prompt you construct to the adapter you configure, so you control what leaves the browser.