@progress/kendo-angular-grid
Version:
Kendo UI Grid for Angular - high performance data grid with paging, filtering, virtualization, CRUD, and more.
273 lines (272 loc) • 14 kB
JavaScript
/**-----------------------------------------------------------------------------------------
* Copyright © 2025 Progress Software Corporation. All rights reserved.
* Licensed under commercial license. See LICENSE.md in the project root for more information
*-------------------------------------------------------------------------------------------*/
import { Component, ViewChild, NgZone } from '@angular/core';
import { AIPromptComponent, OutputViewComponent, PromptViewComponent, AIPromptCustomMessagesComponent, AIPromptOutputTemplateDirective, AIPromptOutputBodyTemplateDirective } from '@progress/kendo-angular-conversational-ui';
import { HttpClient, HttpRequest } from '@angular/common/http';
import { ContextService } from './../../../../common/provider.service';
import { ColumnInfoService } from './../../../../common/column-info.service';
import { GridAIAssistantResponseSuccessEvent, GridAIAssistantResponseErrorEvent } from './models';
import { GridAIRequestResponseService } from './ai-request-response.service';
import * as i0 from "@angular/core";
import * as i1 from "@angular/common/http";
import * as i2 from "./../../../../common/provider.service";
import * as i3 from "./../../../../common/column-info.service";
import * as i4 from "./ai-request-response.service";
/**
* @hidden
*/
export class AiAssistantComponent {
http;
ctx;
columnInfoService;
zone;
aiRequestResponseService;
aiPrompt;
activeView = 0;
requestUrl;
requestOptions;
aiPromptSettings;
aiToolDirective;
streaming = false;
disabledGenerateButton = false;
lastMessage;
requestData;
currentRequestSubscription = null;
//Remove this when the AI Assistant has a built-in loading indicator
loadingOutput = { id: 'k-loading-item', output: '', prompt: '' };
// flat columns used for highlight utilities (expects { field })
columns = [];
leafColumns = [];
idCounter = 0;
constructor(http, ctx, columnInfoService, zone, aiRequestResponseService) {
this.http = http;
this.ctx = ctx;
this.columnInfoService = columnInfoService;
this.zone = zone;
this.aiRequestResponseService = aiRequestResponseService;
}
ngAfterViewInit() {
// Preserve a flat columns array (fields) for highlight utilities.
// Use leafNamedColumns as the canonical list of leaf columns with display titles.
this.leafColumns = this.columnInfoService.leafNamedColumns || [];
this.columns = this.leafColumns.map((col) => ({ field: col.field }));
}
ngOnDestroy() {
this.unsubscribeCurrentRequest();
}
message(message) {
return this.ctx.localization.get(message);
}
cancelRequest() {
this.aiToolDirective.cancelRequest.emit();
this.unsubscribeCurrentRequest();
this.streaming = false;
}
onPromptRequest(ev) {
if (this.aiToolDirective.promptOutputs.length === 0) {
this.aiToolDirective.promptOutputs.push(this.loadingOutput);
}
this.unsubscribeCurrentRequest();
this.streaming = true;
this.activeView = 1;
if (ev.prompt) {
this.lastMessage = ev.prompt;
}
this.requestData = {
columns: [], // Will be populated by service
promptMessage: ev.prompt,
url: this.requestUrl,
requestOptions: {
...this.requestOptions
}
};
if (!this.requestOptions.body) {
this.requestData.requestOptions.body = this.aiRequestResponseService.buildRequestBody(this.requestData.promptMessage, this.requestData.requestOptions.role);
}
this.aiToolDirective.promptRequest.emit({ requestData: this.requestData, isRetry: ev.isRetry });
if (!this.requestUrl) {
return;
}
this.currentRequestSubscription = this.sendPromptRequest().subscribe((res) => {
if (res.body) {
this.processResponse(res);
this.streaming = false;
}
this.currentRequestSubscription = null;
}, (error) => {
this.handleError(error);
this.streaming = false;
this.currentRequestSubscription = null;
});
}
sendPromptRequest() {
const request = new HttpRequest(this.requestData.requestOptions.method, this.requestData.url, this.requestData.requestOptions.body, this.requestData.requestOptions);
return this.http.request(request);
}
processResponse(response) {
if (this.aiToolDirective.autoClose) {
this.aiToolDirective.emitOpenClose = true;
this.aiToolDirective.toggleWindow();
}
const responseBody = response.body || { commands: [] };
const responseSuccessEvent = new GridAIAssistantResponseSuccessEvent(response);
this.aiToolDirective.responseSuccess.emit(responseSuccessEvent);
if (responseSuccessEvent.isDefaultPrevented()) {
this.deleteLoadingOutput();
return;
}
const messages = [];
if (responseBody.message) {
messages.push(responseBody.message);
}
const commandMessages = this.aiRequestResponseService.processCommands(responseBody.commands || [], this.columns, this.leafColumns);
messages.push(...commandMessages);
const responseContentStart = [`${this.ctx.localization.get('aiAssistantOutputCardBodyContent')} \n`];
const responseContentBody = messages
.map((output, idx) => `${idx + 1} ${output}`)
.join('\n');
const output = {
id: this.idCounter++,
title: this.ctx.localization.get('aiAssistantOutputCardTitle'),
prompt: this.lastMessage,
output: responseContentStart.concat(responseContentBody).join(''),
};
this.deleteLoadingOutput();
this.aiToolDirective.promptOutputs.unshift(output);
}
handleError(error) {
const responseErrorEvent = new GridAIAssistantResponseErrorEvent(error);
this.aiToolDirective.responseError.emit(responseErrorEvent);
if (responseErrorEvent.isDefaultPrevented()) {
this.deleteLoadingOutput();
return;
}
const output = {
id: this.idCounter++,
prompt: this.lastMessage,
output: error.message
};
this.deleteLoadingOutput();
this.aiToolDirective.promptOutputs.unshift(output);
}
deleteLoadingOutput() {
if (this.aiToolDirective.promptOutputs[0]?.id === this.loadingOutput.id) {
this.aiToolDirective.promptOutputs.splice(0, 1);
}
}
unsubscribeCurrentRequest() {
if (this.currentRequestSubscription) {
this.currentRequestSubscription.unsubscribe();
this.currentRequestSubscription = null;
}
}
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: AiAssistantComponent, deps: [{ token: i1.HttpClient }, { token: i2.ContextService }, { token: i3.ColumnInfoService }, { token: i0.NgZone }, { token: i4.GridAIRequestResponseService }], target: i0.ɵɵFactoryTarget.Component });
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.2.14", type: AiAssistantComponent, isStandalone: true, selector: "ng-component", viewQueries: [{ propertyName: "aiPrompt", first: true, predicate: AIPromptComponent, descendants: true }], ngImport: i0, template: `
<kendo-aiprompt
#aiPrompt
[promptSuggestions]="aiPromptSettings?.promptSuggestions"
[showOutputRating]="aiPromptSettings?.showOutputRating"
[streaming]="streaming"
[speechToTextButton]="aiPromptSettings?.speechToTextButton"
[(activeView)]="activeView"
[generateButtonSVGIcon]="aiPromptSettings?.generateButtonSVGIcon"
[generateButtonIcon]="aiPromptSettings?.generateButtonIcon"
[disabledGenerateButton]="disabledGenerateButton || promptView.textAreaValue?.length === 0"
[promptOutputs]="aiPromptSettings?.promptOutputs"
[textAreaSettings]="aiPromptSettings?.textAreaSettings"
(promptRequest)="onPromptRequest($event)"
(promptRequestCancel)="cancelRequest()"
>
<kendo-aiprompt-prompt-view #promptView></kendo-aiprompt-prompt-view>
<kendo-aiprompt-output-view></kendo-aiprompt-output-view>
(streaming && aiPrompt.streaming; as output) {
<ng-template kendoAIPromptOutputTemplate let-output>
<div class="k-card">
<div class="k-card-header">
<div class="k-card-title">
<span class="k-skeleton k-skeleton-text k-skeleton-pulse" [style.width.px]="200"></span>
</div>
<div class="k-card-subtitle">
<span class="k-skeleton k-skeleton-text k-skeleton-pulse" style="width: 100%;"></span>
</div>
</div>
<div class="k-card-body">
<span class="k-skeleton k-skeleton-rect k-skeleton-pulse" style="height: 80px;"></span>
</div>
<div class="k-card-actions">
<span class="k-skeleton k-skeleton-text k-skeleton-pulse" style="width: 100%;"></span>
</div>
</div>
</ng-template>
}
(!(streaming && aiPrompt.streaming); as output) {
<ng-template kendoAIPromptOutputBodyTemplate let-output>
<p>{{output.output}}</p>
</ng-template>
}
<kendo-aiprompt-messages
[generateOutput]="message('aiAssistantApplyButtonText')"
></kendo-aiprompt-messages>
</kendo-aiprompt>
`, isInline: true, dependencies: [{ kind: "component", type: AIPromptComponent, selector: "kendo-aiprompt", inputs: ["activeView", "promptCommands", "promptSuggestions", "promptOutputs", "showOutputRating", "streaming", "speechToTextButton", "textAreaSettings", "generateButtonSVGIcon", "generateButtonIcon", "disabledGenerateButton"], outputs: ["activeViewChange", "promptRequest", "commandExecute", "outputCopy", "outputRatingChange", "promptRequestCancel"], exportAs: ["kendoAIPrompt"] }, { kind: "component", type: AIPromptCustomMessagesComponent, selector: "kendo-aiprompt-messages" }, { kind: "component", type: PromptViewComponent, selector: "kendo-aiprompt-prompt-view" }, { kind: "component", type: OutputViewComponent, selector: "kendo-aiprompt-output-view" }, { kind: "directive", type: AIPromptOutputTemplateDirective, selector: "[kendoAIPromptOutputTemplate]" }, { kind: "directive", type: AIPromptOutputBodyTemplateDirective, selector: "[kendoAIPromptOutputBodyTemplate]" }] });
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: AiAssistantComponent, decorators: [{
type: Component,
args: [{
standalone: true,
imports: [AIPromptComponent, AIPromptCustomMessagesComponent, PromptViewComponent, OutputViewComponent, AIPromptOutputTemplateDirective, AIPromptOutputBodyTemplateDirective],
template: `
<kendo-aiprompt
#aiPrompt
[promptSuggestions]="aiPromptSettings?.promptSuggestions"
[showOutputRating]="aiPromptSettings?.showOutputRating"
[streaming]="streaming"
[speechToTextButton]="aiPromptSettings?.speechToTextButton"
[(activeView)]="activeView"
[generateButtonSVGIcon]="aiPromptSettings?.generateButtonSVGIcon"
[generateButtonIcon]="aiPromptSettings?.generateButtonIcon"
[disabledGenerateButton]="disabledGenerateButton || promptView.textAreaValue?.length === 0"
[promptOutputs]="aiPromptSettings?.promptOutputs"
[textAreaSettings]="aiPromptSettings?.textAreaSettings"
(promptRequest)="onPromptRequest($event)"
(promptRequestCancel)="cancelRequest()"
>
<kendo-aiprompt-prompt-view #promptView></kendo-aiprompt-prompt-view>
<kendo-aiprompt-output-view></kendo-aiprompt-output-view>
(streaming && aiPrompt.streaming; as output) {
<ng-template kendoAIPromptOutputTemplate let-output>
<div class="k-card">
<div class="k-card-header">
<div class="k-card-title">
<span class="k-skeleton k-skeleton-text k-skeleton-pulse" [style.width.px]="200"></span>
</div>
<div class="k-card-subtitle">
<span class="k-skeleton k-skeleton-text k-skeleton-pulse" style="width: 100%;"></span>
</div>
</div>
<div class="k-card-body">
<span class="k-skeleton k-skeleton-rect k-skeleton-pulse" style="height: 80px;"></span>
</div>
<div class="k-card-actions">
<span class="k-skeleton k-skeleton-text k-skeleton-pulse" style="width: 100%;"></span>
</div>
</div>
</ng-template>
}
(!(streaming && aiPrompt.streaming); as output) {
<ng-template kendoAIPromptOutputBodyTemplate let-output>
<p>{{output.output}}</p>
</ng-template>
}
<kendo-aiprompt-messages
[generateOutput]="message('aiAssistantApplyButtonText')"
></kendo-aiprompt-messages>
</kendo-aiprompt>
`
}]
}], ctorParameters: () => [{ type: i1.HttpClient }, { type: i2.ContextService }, { type: i3.ColumnInfoService }, { type: i0.NgZone }, { type: i4.GridAIRequestResponseService }], propDecorators: { aiPrompt: [{
type: ViewChild,
args: [AIPromptComponent]
}] } });