AI-assisted analysis
Give an optional adapter bounded context, validate its proposed view changes, and apply them only after review.
Review before applying
The example below always proposes the same yield sort. It is a deterministic local rule, makes no network request and does not interpret language. Submit, inspect the proposed action and context, then Apply to change the table.
Review a proposed sort
Deterministic planner · no modelFixed local sort rule · 18 material samples · revision 1
Inspect context sent to the adapter
4 fields · 0 active filters · 18 of 18 rows profiled. No raw records are included.
{
"version": 1,
"source": {
"id": "documentation-samples",
"version": "1",
"label": "18 material samples"
},
"fields": [
{
"name": "id",
"label": "Sample",
"type": "nominal",
"format": "text"
},
{
"name": "material",
"label": "Material",
"type": "nominal",
"format": "text"
},
{
"name": "yield",
"label": "Yield",
"type": "quantitative",
"format": "percent"
},
{
"name": "accepted",
"label": "Accepted",
"type": "boolean",
"format": "text"
}
],
"selection": [],
"profile": {
"totalRows": 18,
"scannedRows": 18,
"complete": true,
"fields": [
{
"field": "id",
"type": "nominal",
"valid": 18,
"invalid": 0,
"missing": 0,
"distinct": 18
},
{
"field": "material",
"type": "nominal",
"valid": 18,
"invalid": 0,
"missing": 0,
"distinct": 3
},
{
"field": "yield",
"type": "quantitative",
"valid": 15,
"invalid": 0,
"missing": 3,
"distinct": 15,
"min": 0.69,
"max": 0.95,
"mean": 0.8380000000000001
},
{
"field": "accepted",
"type": "boolean",
"valid": 18,
"invalid": 0,
"missing": 0,
"distinct": 2
}
]
},
"provenance": [
{
"label": "Source",
"detail": "Original deterministic documentation fixture; no external records."
}
]
}Proposals change the view only when you apply them.
The assistant workspace shows a broader local rule adapter and an optional HTTP endpoint. The library provides the inspectable interface and protocol; the application supplies model access, data authorization and action execution.
Build a bounded context
Import assistance from @quartile/react/ai. This entry has no provider SDK or database dependency. createAnalysisContext includes an allowlisted field catalog, source ID/revision, normalized active selection and optional profile/provenance. Change the source version when underlying data changes.
import { createAnalysisContext, profileDataset } from '@quartile/react/ai';
const context = createAnalysisContext({
source: { id: 'samples', version: 'revision-12' },
schema: samples.schema,
fields: ['material', 'yield'],
profile: profileDataset(samples.rows, {
schema: samples.schema, fields: ['material', 'yield'], maxRows: 1000,
}),
provenance: [{ label: 'Source', detail: 'Authorized experiment snapshot' }],
});Raw rows, nominal example values and formatter functions are not included. Field names, selected values, numeric summaries and provenance can still be sensitive; the application chooses what may leave the browser. Inspect the serialized context before connecting a service.
profileDataset reports valid, missing, invalid and distinct counts; numeric extrema/mean and temporal ranges. The default scan is the first 1,000 rows, with a 10,000-row maximum. Distinct counts are exact within that prefix. Show scannedRows, totalRows and complete; this is not a random sample, anomaly detector or imputation service.
Validate actions, not arbitrary code
A version-one AnalysisPlan contains title, concise summary and one to twelve actions. Supported actions are typed equality/inclusion/range filters, clear-filter, one sort, one chart choice and table fields/limit. Charts are histogram, scatter, bar or line. Plans contain no executable code, SQL, URL fetch or database write action.
Nominal fields accept strings, finite numbers and booleans in profiles and equality/inclusion proposals. Numeric 1, text "1" and true remain distinct. Nominal strings are bounded to 500 characters in filters; numeric identifiers do not become measures just because they use numbers. Downstream chart/selection comparison rules still apply.
validateAnalysisPlan(input, context) rejects malformed structures, unknown fields, incompatible types, contradictory duplicate actions and exceeded bounds. reduceAnalysisPlan(plan, state, context) validates the whole plan before returning a new view state. It does not query data, mutate Selection or render a chart by itself.
| Bound | Maximum |
|---|---|
| Context fields / table fields | 64 / 24 |
| Actions / filter values | 12 / 100 |
| Proposed table rows | 1,000 |
| Prompt / ordinary text | 8,000 / 2,000 characters |
| JSON payload | 64 KiB |
Connect a React surface
'use client';
import { useMemo, useState } from 'react';
import { AssistantPanel, createAnalysisContext, createHttpAssistantAdapter,
reduceAnalysisPlan, useAnalysisAssistant,
type AnalysisViewState } from '@quartile/react/ai';
import type { Schema } from '@quartile/react';
export function ProposalInspector({ schema, revision }: { schema: Schema; revision: string }) {
const adapter = useMemo(() => createHttpAssistantAdapter({
endpoint: '/api/analysis', label: 'Application analysis service',
}), []);
const context = useMemo(() => createAnalysisContext({
schema, source: { id: 'observations', version: revision },
fields: ['material', 'yield'],
}), [schema, revision]);
const [view, setView] = useState<AnalysisViewState>({ filters: [] });
const assistant = useAnalysisAssistant({ adapter, context,
onApply: (plan) => setView((current) => reduceAnalysisPlan(plan, current, context)),
});
return <>
<AssistantPanel assistant={assistant} />
<pre>{JSON.stringify(view, null, 2)}</pre>
</>;
}This example displays the reduced state. Map its approved filters, sort, chart and table projection to your own views; the gallery demonstrates the complete mapping. Keep the adapter identity stable. onApply must make a synchronous local commit; persist separately with explicit application error handling.
The hook makes no request on mount and never auto-applies. New requests, context changes, source revisions, adapter replacement and unmount invalidate old results. Apply revalidates and consumes a proposal once. Cancel aborts the adapter signal and rejects stale results even when transport ignores cancellation; it does not guarantee provider computation stops. Standalone validation/reduction does not perform revision tracking.
Also accepts the props of Omit<HTMLAttributes<HTMLElement>, 'onSubmit'>, spread onto the root element.
Connect your model backend
createHttpAssistantAdapter posts { requestId, prompt, context } as JSON to your HTTP(S) or root-relative endpoint. Return the AnalysisPlan object directly. Non-success HTTP responses, malformed JSON, oversized output and invalid plans surface as errors; there is no automatic mock fallback.
The runnable Node server example connects the OpenAI Responses REST API with a server-only key and model setting. It binds to loopback on port 8787, checks an exact configured browser origin, validates bounded request/output, and applies disconnect cancellation and timeouts. The core Quartile adapter remains provider agnostic.
pnpm build:lib
cp examples/ai-server/.env.example examples/ai-server/.env
# Edit the server-only key, model and exact allowed browser origin.
node --env-file=examples/ai-server/.env examples/ai-server/server.mjsUse http://127.0.0.1:8787/api/analysis from a local gallery matching that origin configuration, or host an equivalent authenticated endpoint in your own application. Origin checks are not authentication. The example is local development infrastructure, with no hosted account, tenant authorization, billing, persistent audit store or production rate policy.
Generated dashboard specs are a separate complete-layout format. An AnalysisPlan proposes bounded changes to an existing view. See the public AI guide for protocol and integration details.