AI
AI that works with your model. The grid assistant turns a sentence — “orders over 500 from İzmir this month, newest first” — into a sort, filter, grouping, search or column change, checks every field, operator and value the model returns against the grid's columns, and applies it after you press Apply. Undo steps back.
There is no hosted service and no AI surcharge: requests go to the transport you configure — your own backend, any OpenAI-compatible server (OpenAI, Azure, Ollama, vLLM) or Anthropic. The model is told the column names, types and lookup values; rows never leave the page. The demos below run on an offline demo model, so they work without a key — connect a real one in the last section. Full API reference →
Getting started
Pick a suggestion or type your own request, then Apply or Discard the proposal. The status line under the bar is a polite live region; Escape stops a request or discards a proposal.
import { ChangeDetectionStrategy, Component, signal } from '@angular/core';
import { OgeGridAssistant } from '@oge-ui/ai';
import { OgeColumn, OgeGrid } from '@oge-ui/grid';
interface Order {
id: number;
customer: string;
city: string;
status: number;
amount: number;
ordered: Date;
}
@Component({
selector: 'demo-root',
imports: [OgeGridAssistant, OgeColumn, OgeGrid],
changeDetection: ChangeDetectionStrategy.OnPush,
template: `
<!-- The assistant drives the grid through its public state API. The model
sees the column names, types and lookup values — never a row. -->
<oge-grid-assistant
[target]="grid"
[suggestions]="['Orders over 500 this month', 'Group by status', 'Newest first']"
/>
<oge-grid #grid [data]="orders" keyField="id" [groupPanel]="true" [paging]="{ pageSize: 10 }">
<oge-column field="id" caption="Order" dataType="number" [width]="90" />
<oge-column field="customer" caption="Customer" />
<oge-column field="city" caption="City" />
<oge-column
field="status"
caption="Status"
dataType="number"
[lookup]="{ dataSource: statuses, valueExpr: 'id', displayExpr: 'label' }"
/>
<oge-column field="amount" caption="Amount" dataType="number" [format]="{ type: 'currency', currency: 'EUR' }" />
<oge-column field="ordered" caption="Ordered" dataType="date" [format]="{ type: 'date' }" />
</oge-grid>
`,
})
export class Demo {
protected readonly statuses = [
{ id: 1, label: 'Open' },
{ id: 2, label: 'Shipped' },
{ id: 3, label: 'Delivered' },
{ id: 4, label: 'Cancelled' },
];
protected readonly orders: Order[] = [
{ id: 1001, customer: 'Acme Corp', city: 'İzmir', status: 2, amount: 1240.5, ordered: new Date(2026, 9, 3) },
{ id: 1002, customer: 'Ege Textiles', city: 'İstanbul', status: 1, amount: 310, ordered: new Date(2026, 9, 11) },
{ id: 1003, customer: 'Fjord Media', city: 'Berlin', status: 4, amount: 980, ordered: new Date(2026, 8, 21) },
];
}
// app.config.ts — requests go to YOUR endpoint; the API key stays on the server.
// Any object with complete(request, { signal }) is a transport too.
//
// import { createOgeAiHttpTransport, provideOgeAiConfig } from '@oge-ui/ai';
// providers: [
// provideOgeAiConfig({ transport: createOgeAiHttpTransport({ url: '/api/ai' }) }),
// ]Apply at once and undo
In apply mode the change lands immediately; Undo steps back through every applied change. Follow-ups build on the current state — try “newest first”, then “and only İzmir”.
import { ChangeDetectionStrategy, Component } from '@angular/core';
import { OgeGridAssistant } from '@oge-ui/ai';
import { OgeColumn, OgeGrid } from '@oge-ui/grid';
interface Order {
id: number;
customer: string;
city: string;
status: number;
amount: number;
ordered: Date;
}
@Component({
selector: 'demo-root',
imports: [OgeGridAssistant, OgeColumn, OgeGrid],
changeDetection: ChangeDetectionStrategy.OnPush,
template: `
<!-- mode="apply" skips the proposal: the change lands at once and Undo
steps back through every applied change. Follow-ups build on the
current state ("and only İzmir", "now group it by city"). -->
<oge-grid-assistant #assistant [target]="grid" mode="apply" />
<button type="button" (click)="assistant.submit('Newest first')">Ask from code</button>
<oge-grid #grid [data]="orders" keyField="id" [groupPanel]="true">
<oge-column field="id" caption="Order" dataType="number" [width]="90" />
<oge-column field="customer" caption="Customer" />
<oge-column field="city" caption="City" />
<oge-column
field="status"
caption="Status"
dataType="number"
[lookup]="{ dataSource: statuses, valueExpr: 'id', displayExpr: 'label' }"
/>
<oge-column field="amount" caption="Amount" dataType="number" [format]="{ type: 'currency', currency: 'EUR' }" />
<oge-column field="ordered" caption="Ordered" dataType="date" [format]="{ type: 'date' }" />
</oge-grid>
`,
})
export class Demo {
protected readonly statuses = [
{ id: 1, label: 'Open' },
{ id: 2, label: 'Shipped' },
{ id: 3, label: 'Delivered' },
{ id: 4, label: 'Cancelled' },
];
protected readonly orders: Order[] = [
{ id: 1001, customer: 'Acme Corp', city: 'İzmir', status: 2, amount: 1240.5, ordered: new Date(2026, 9, 3) },
{ id: 1002, customer: 'Ege Textiles', city: 'İstanbul', status: 1, amount: 310, ordered: new Date(2026, 9, 11) },
{ id: 1003, customer: 'Fjord Media', city: 'Berlin', status: 4, amount: 980, ordered: new Date(2026, 8, 21) },
];
}What the model sees
The panel shows the exact request the last prompt sent. Customer is left out of [columns], so the model never hears of it; column changes are switched off with [capabilities]. No row is ever part of a request.
Send a prompt to see the request the model receives.
import { ChangeDetectionStrategy, Component } from '@angular/core';
import { OgeGridAssistant } from '@oge-ui/ai';
import { OgeColumn, OgeGrid } from '@oge-ui/grid';
import type { OgeAiGridColumn, OgeGridAiCapabilities } from '@oge-ui/ai';
interface Order {
id: number;
customer: string;
city: string;
status: number;
amount: number;
ordered: Date;
}
@Component({
selector: 'demo-root',
imports: [OgeGridAssistant, OgeColumn, OgeGrid],
changeDetection: ChangeDetectionStrategy.OnPush,
template: `
<!-- [columns] replaces what the model is told: add descriptions, or leave a
column out entirely ("customer" never reaches the model below).
[capabilities] switches whole parts of the state off. -->
<oge-grid-assistant
[target]="grid"
[columns]="described"
[capabilities]="{ columns: false }"
instructions="Amounts are net, in EUR. 'Open' orders are not shipped yet."
/>
<oge-grid #grid [data]="orders" keyField="id">
<oge-column field="id" caption="Order" dataType="number" [width]="90" />
<oge-column field="customer" caption="Customer" />
<oge-column field="city" caption="City" />
<oge-column
field="status"
caption="Status"
dataType="number"
[lookup]="{ dataSource: statuses, valueExpr: 'id', displayExpr: 'label' }"
/>
<oge-column field="amount" caption="Amount" dataType="number" [format]="{ type: 'currency', currency: 'EUR' }" />
<oge-column field="ordered" caption="Ordered" dataType="date" [format]="{ type: 'date' }" />
</oge-grid>
`,
})
export class Demo {
protected readonly statuses = [
{ id: 1, label: 'Open' },
{ id: 2, label: 'Shipped' },
{ id: 3, label: 'Delivered' },
{ id: 4, label: 'Cancelled' },
];
protected readonly orders: Order[] = [
{ id: 1001, customer: 'Acme Corp', city: 'İzmir', status: 2, amount: 1240.5, ordered: new Date(2026, 9, 3) },
{ id: 1002, customer: 'Ege Textiles', city: 'İstanbul', status: 1, amount: 310, ordered: new Date(2026, 9, 11) },
{ id: 1003, customer: 'Fjord Media', city: 'Berlin', status: 4, amount: 980, ordered: new Date(2026, 8, 21) },
];
protected readonly described: readonly OgeAiGridColumn[] = [
{ field: 'city', caption: 'City', dataType: 'string', sortable: true, filterable: true, groupable: true, visible: true },
{
field: 'status', caption: 'Status', dataType: 'number', sortable: true, filterable: true, groupable: true, visible: true,
values: this.statuses.map((s) => ({ value: s.id, text: s.label })),
},
{
field: 'amount', caption: 'Amount', dataType: 'number', sortable: true, filterable: true, groupable: true, visible: true,
description: 'Net order value in EUR',
},
{ field: 'ordered', caption: 'Ordered', dataType: 'date', sortable: true, filterable: true, groupable: true, visible: true },
];
}Events
(applying) is cancelable — tick the box to veto changes that touch column visibility. (applied) carries the complete snapshot, ready to be saved as a view.
import { ChangeDetectionStrategy, Component, signal } from '@angular/core';
import { OgeGridAssistant } from '@oge-ui/ai';
import { OgeColumn, OgeGrid } from '@oge-ui/grid';
import type { OgeGridAssistantAppliedEvent, OgeGridAssistantApplyingEvent } from '@oge-ui/ai';
interface Order {
id: number;
customer: string;
city: string;
status: number;
amount: number;
ordered: Date;
}
@Component({
selector: 'demo-root',
imports: [OgeGridAssistant, OgeColumn, OgeGrid],
changeDetection: ChangeDetectionStrategy.OnPush,
template: `
<!-- (applying) is cancelable — here, a change that would hide columns needs
a confirmation. (applied) carries the full snapshot: save it as a view. -->
<oge-grid-assistant
[target]="grid"
mode="apply"
(applying)="guard($event)"
(applied)="log.update((l) => [event($event), ...l])"
/>
<oge-grid #grid [data]="orders" keyField="id">
<oge-column field="id" caption="Order" dataType="number" [width]="90" />
<oge-column field="customer" caption="Customer" />
<oge-column field="city" caption="City" />
<oge-column
field="status"
caption="Status"
dataType="number"
[lookup]="{ dataSource: statuses, valueExpr: 'id', displayExpr: 'label' }"
/>
<oge-column field="amount" caption="Amount" dataType="number" [format]="{ type: 'currency', currency: 'EUR' }" />
<oge-column field="ordered" caption="Ordered" dataType="date" [format]="{ type: 'date' }" />
</oge-grid>
<ol>
@for (line of log(); track $index) {
<li>{{ line }}</li>
}
</ol>
`,
})
export class Demo {
protected readonly statuses = [
{ id: 1, label: 'Open' },
{ id: 2, label: 'Shipped' },
{ id: 3, label: 'Delivered' },
{ id: 4, label: 'Cancelled' },
];
protected readonly orders: Order[] = [
{ id: 1001, customer: 'Acme Corp', city: 'İzmir', status: 2, amount: 1240.5, ordered: new Date(2026, 9, 3) },
{ id: 1002, customer: 'Ege Textiles', city: 'İstanbul', status: 1, amount: 310, ordered: new Date(2026, 9, 11) },
{ id: 1003, customer: 'Fjord Media', city: 'Berlin', status: 4, amount: 980, ordered: new Date(2026, 8, 21) },
];
protected readonly log = signal<string[]>([]);
protected guard(event: OgeGridAssistantApplyingEvent): void {
if (event.slices.includes('columns')) {
event.cancel = !confirm(`Apply "${event.summary}"?`);
}
}
protected event(event: OgeGridAssistantAppliedEvent): string {
return `${event.prompt} → ${event.slices.join(', ')}`;
}
}Transports and configuration
Point every demo on this page at a real OpenAI-compatible endpoint — a local Ollama needs no key. The key is held in this tab’s memory only and sent straight to the URL you enter; in production use your own backend so the key never reaches the browser.
import { ChangeDetectionStrategy, Component } from '@angular/core';
import { OgeGridAssistant, createOgeAiHttpTransport, createOgeAnthropicTransport, createOgeOpenAiTransport, provideOgeAiConfig } from '@oge-ui/ai';
import type { OgeAiTransport } from '@oge-ui/ai';
@Component({
selector: 'demo-root',
imports: [OgeGridAssistant],
changeDetection: ChangeDetectionStrategy.OnPush,
template: `
<!-- Pick the transport per environment; nothing else changes. -->
<oge-grid-assistant [transport]="local" />
`,
})
export class Demo {
/** A local model through Ollama's OpenAI-compatible API — nothing leaves the machine. */
protected readonly local: OgeAiTransport = createOgeOpenAiTransport({
baseUrl: 'http://localhost:11434/v1',
model: 'llama3.1',
});
}
// app.config.ts
export const productionAi = provideOgeAiConfig({
// your backend calls the model; the browser never holds a key
transport: createOgeAiHttpTransport({ url: '/api/ai' }),
gridAssistant: { mode: 'preview', undoLimit: 50 },
messages: { gridAssistant: { placeholder: 'Ask about your orders…' } },
});
// Claude through your own proxy route
export const claude = createOgeAnthropicTransport({
baseUrl: '/api/anthropic',
model: 'claude-haiku-5-5',
});
// Your endpoint, e.g. in Node: forward the request, return { text }.
//
// app.post('/api/ai', async (req, res) => {
// const { messages, responseSchema, temperature } = req.body; // OgeAiRequest
// const answer = await openai.chat.completions.create({
// model: 'gpt-4.1-mini',
// messages,
// temperature,
// response_format: { type: 'json_schema', json_schema: { name: 'oge', schema: responseSchema } },
// });
// res.json({ text: answer.choices[0].message.content });
// });