DataGrid - AI Assistant: Implement messageTemplate#33265
DataGrid - AI Assistant: Implement messageTemplate#33265Alyar666 wants to merge 8 commits intoDevExpress:26_1from
Conversation
There was a problem hiding this comment.
Pull request overview
Implements richer AI Assistant chat message rendering in the DataGrid/TreeList AI Assistant UI, introducing status-aware message templates (pending/success/error) and moving message state management into a dedicated controller.
Changes:
- Add
messageTemplaterendering for AI Assistant messages (status icon/header/body, command result list, pending progress bar, regenerate action). - Introduce
AIAssistantControllerto manage message store/data source and AI request lifecycle. - Refactor/extend AI chat styles (new
aiChatlayout, theme-specific includes) and add new localization keys for message headers.
Reviewed changes
Copilot reviewed 57 out of 57 changed files in this pull request and generated 6 comments.
Show a summary per file
| File | Description |
|---|---|
| packages/devextreme/js/localization/messages/ar.json | Add AI chat pending/error header message keys |
| packages/devextreme/js/localization/messages/bg.json | Add AI chat pending/error header message keys |
| packages/devextreme/js/localization/messages/ca.json | Add AI chat pending/error header message keys |
| packages/devextreme/js/localization/messages/cs.json | Add AI chat pending/error header message keys |
| packages/devextreme/js/localization/messages/da.json | Add AI chat pending/error header message keys |
| packages/devextreme/js/localization/messages/de.json | Add AI chat pending/error header message keys |
| packages/devextreme/js/localization/messages/el.json | Add AI chat pending/error header message keys |
| packages/devextreme/js/localization/messages/en.json | Add AI chat pending/error header message keys |
| packages/devextreme/js/localization/messages/es.json | Add AI chat pending/error header message keys |
| packages/devextreme/js/localization/messages/fa.json | Add AI chat pending/error header message keys |
| packages/devextreme/js/localization/messages/fi.json | Add AI chat pending/error header message keys |
| packages/devextreme/js/localization/messages/fr.json | Add AI chat pending/error header message keys |
| packages/devextreme/js/localization/messages/hu.json | Add AI chat pending/error header message keys |
| packages/devextreme/js/localization/messages/it.json | Add AI chat pending/error header message keys |
| packages/devextreme/js/localization/messages/ja.json | Add AI chat pending/error header message keys |
| packages/devextreme/js/localization/messages/ko.json | Add AI chat pending/error header message keys |
| packages/devextreme/js/localization/messages/lt.json | Add AI chat pending/error header message keys |
| packages/devextreme/js/localization/messages/lv.json | Add AI chat pending/error header message keys |
| packages/devextreme/js/localization/messages/nb.json | Add AI chat pending/error header message keys |
| packages/devextreme/js/localization/messages/nl.json | Add AI chat pending/error header message keys |
| packages/devextreme/js/localization/messages/pl.json | Add AI chat pending/error header message keys |
| packages/devextreme/js/localization/messages/pt.json | Add AI chat pending/error header message keys |
| packages/devextreme/js/localization/messages/ro.json | Add AI chat pending/error header message keys |
| packages/devextreme/js/localization/messages/ru.json | Add AI chat pending/error header message keys |
| packages/devextreme/js/localization/messages/sl.json | Add AI chat pending/error header message keys |
| packages/devextreme/js/localization/messages/sv.json | Add AI chat pending/error header message keys |
| packages/devextreme/js/localization/messages/tr.json | Add AI chat pending/error header message keys |
| packages/devextreme/js/localization/messages/uk.json | Add AI chat pending/error header message keys |
| packages/devextreme/js/localization/messages/vi.json | Add AI chat pending/error header message keys |
| packages/devextreme/js/localization/messages/zh.json | Add AI chat pending/error header message keys |
| packages/devextreme/js/localization/messages/zh-tw.json | Add AI chat pending/error header message keys |
| packages/devextreme/js/__internal/grids/tree_list/module_not_extended/ai_assistant.ts | Register new AI assistant controller and keep view controller under a new key |
| packages/devextreme/js/__internal/grids/data_grid/module_not_extended/ai_assistant.ts | Register new AI assistant controller and keep view controller under a new key |
| packages/devextreme/js/__internal/grids/grid_core/m_types.ts | Update controller type map for new AI assistant controller naming |
| packages/devextreme/js/__internal/grids/grid_core/ai_chat/types.ts | Re-export command result types and add message status union type |
| packages/devextreme/js/__internal/grids/grid_core/ai_chat/const.ts | Expand AI chat CSS class constants and add regenerate/icon constants |
| packages/devextreme/js/__internal/grids/grid_core/ai_chat/ai_chat.ts | Implement custom messageTemplate rendering and status-specific UI |
| packages/devextreme/js/__internal/grids/grid_core/ai_chat/ai_chat.test.ts | Add Jest coverage for new message template behaviors |
| packages/devextreme/js/__internal/grids/grid_core/ai_assistant/types.ts | Introduce AI command/response/result types and callbacks contract |
| packages/devextreme/js/__internal/grids/grid_core/ai_assistant/grid_commands.ts | Add placeholder grid command validation/execution layer |
| packages/devextreme/js/__internal/grids/grid_core/ai_assistant/const.ts | Add AI assistant author constants and shared message status enum |
| packages/devextreme/js/__internal/grids/grid_core/ai_assistant/ai_assistant_view.ts | Move message data source + request sending into controller |
| packages/devextreme/js/__internal/grids/grid_core/ai_assistant/ai_assistant_controller.ts | New controller: message store lifecycle, AI integration callbacks, status transitions |
| packages/devextreme/js/__internal/grids/grid_core/ai_assistant/tests/ai_assistant_view.test.ts | Update tests to mock controller-driven data source and message sending |
| packages/devextreme/js/__internal/grids/grid_core/ai_assistant/tests/ai_assistant_controller.test.ts | Add unit tests for controller request lifecycle and message updates |
| packages/devextreme-scss/scss/widgets/material/gridBase/layout/aiChat/_index.scss | Theme include for new aiChat mixins |
| packages/devextreme-scss/scss/widgets/material/gridBase/_index.scss | Wire aiChat layout module and remove inline empty-view include |
| packages/devextreme-scss/scss/widgets/generic/gridBase/layout/aiChat/_index.scss | Theme include for new aiChat mixins |
| packages/devextreme-scss/scss/widgets/generic/gridBase/_index.scss | Wire aiChat layout module and remove inline empty-view include |
| packages/devextreme-scss/scss/widgets/fluent/gridBase/layout/aiChat/_index.scss | Theme include for new aiChat mixins |
| packages/devextreme-scss/scss/widgets/fluent/gridBase/_index.scss | Wire aiChat layout module and remove inline empty-view include |
| packages/devextreme-scss/scss/widgets/base/gridBase/layout/aiChat/_mixins.scss | New mixins for empty view and pending message styling |
| packages/devextreme-scss/scss/widgets/base/gridBase/layout/aiChat/_index.scss | New base aiChat layout styles (message template layout + progress bar positioning) |
| packages/devextreme-scss/scss/widgets/base/gridBase/layout/ai-chat/_mixins.scss | Remove legacy ai-chat mixins (superseded by aiChat) |
| packages/devextreme-scss/scss/widgets/base/gridBase/layout/ai-chat/_index.scss | Remove legacy ai-chat base styles (superseded by aiChat) |
| packages/devextreme-scss/scss/widgets/base/gridBase/_mixins.scss | Remove legacy forward for ai-chat mixins (path removed) |
| packages/devextreme-scss/scss/widgets/base/gridBase/_index.scss | Switch base gridBase to use new layout/aiChat module |
| container: this.element(), | ||
| createComponent: this._createComponent.bind(this), | ||
| onChatCleared: (): void => {}, | ||
| onRegenerate: (): void => {}, |
There was a problem hiding this comment.
AIAssistantView passes an empty onRegenerate callback into AIChat. Because AIChat uses the presence of onRegenerate to decide whether to render the regenerate button, this will display a regenerate icon that does nothing when clicked. Either wire this to real regenerate logic or omit the callback until it’s implemented.
| onRegenerate: (): void => {}, |
| data: { | ||
| id: aiMessageId, | ||
| timestamp: parsedTimestamp, | ||
| // TODO: need to localize author name and move it to constants or options |
There was a problem hiding this comment.
This TODO is partially outdated: the author is already centralized in AI_ASSISTANT_AUTHOR constants. Consider updating the comment to focus on the remaining work (e.g., localization/configurability), so it doesn’t imply the constant extraction is still pending.
| // TODO: need to localize author name and move it to constants or options | |
| // TODO: need to make author name configurable/localizable |
| if (this.isAIChatMessage(message)) { | ||
| this.renderAIMessage(message, container); | ||
| } else { | ||
| $(container).text(message?.text); |
There was a problem hiding this comment.
In the non-assistant branch, $(container).text(message?.text) will call the getter overload when message.text is undefined (because jQuery treats undefined as “no argument”), potentially leaving stale content if the container is reused. Use an explicit fallback (e.g., empty string) to ensure the template always writes deterministic content.
| $(container).text(message?.text); | |
| $(container).text(message.text ?? ''); |
|
|
||
| // TODO: need to implement real command execution logic | ||
| public executeCommands(commands: Command[]): Promise<CommandResults> { | ||
| return Promise.resolve([...commands] as unknown as CommandResults); |
There was a problem hiding this comment.
executeCommands currently returns the incoming Command[] array cast to CommandResults, but CommandResults items are expected to have { status, message }. This will make the chat template render undefined messages and misclassify statuses. Even as a stub, return properly shaped CommandResult objects (or adjust the types) so the UI can render consistently.
| return Promise.resolve([...commands] as unknown as CommandResults); | |
| const results: CommandResults = commands.map(() => ({ | |
| status: 'success' as CommandResults[number]['status'], | |
| message: 'Command execution is not implemented yet.', | |
| })); | |
| return Promise.resolve(results); |
| if (!response?.commands || response?.explanation) { | ||
| // TODO: need to localize default error message when there are no commands | ||
| return Promise.reject(new Error(response.explanation || 'Default error message')); | ||
| } | ||
|
|
||
| if (!this.gridCommands.validate(response.commands)) { | ||
| // TODO: need to localize error message on validation fail | ||
| return Promise.reject(new Error('Received invalid commands')); |
There was a problem hiding this comment.
processResponse rejects whenever response.explanation is truthy, even if valid commands are present. It also doesn't explicitly treat an empty commands array as an error when explanation is empty. Consider checking commands?.length to detect “no commands”, and only using explanation as an error message when commands are missing/invalid.
| if (!response?.commands || response?.explanation) { | |
| // TODO: need to localize default error message when there are no commands | |
| return Promise.reject(new Error(response.explanation || 'Default error message')); | |
| } | |
| if (!this.gridCommands.validate(response.commands)) { | |
| // TODO: need to localize error message on validation fail | |
| return Promise.reject(new Error('Received invalid commands')); | |
| if (!response?.commands?.length) { | |
| // TODO: need to localize default error message when there are no commands | |
| return Promise.reject(new Error(response.explanation || 'Default error message')); | |
| } | |
| if (!this.gridCommands.validate(response.commands)) { | |
| // TODO: need to localize error message on validation fail | |
| return Promise.reject(new Error(response.explanation || 'Received invalid commands')); |
| private renderPendingState($parent: dxElementWrapper): void { | ||
| $('<div>') | ||
| .addClass(CLASSES.messageStatus) | ||
| .text('Processing...') |
There was a problem hiding this comment.
The pending-state status text is hardcoded as "Processing...". Since other AI assistant strings are localized via messageLocalization, this should be localized as well (or derived from an existing localized string) to avoid showing English text in non-English locales.
| private renderPendingState($parent: dxElementWrapper): void { | |
| $('<div>') | |
| .addClass(CLASSES.messageStatus) | |
| .text('Processing...') | |
| private getPendingStateText(): string { | |
| return messageLocalization.format('dxDataGrid-aiAssistantProcessing'); | |
| } | |
| private renderPendingState($parent: dxElementWrapper): void { | |
| $('<div>') | |
| .addClass(CLASSES.messageStatus) | |
| .text(this.getPendingStateText()) |
No description provided.