@sinequa/assistant 3.3.0 → 3.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/chat/chat-message/chat-message.component.d.ts +15 -106
- package/chat/chat-reference/chat-reference.component.d.ts +1 -105
- package/chat/chat.component.d.ts +72 -22
- package/chat/chat.service.d.ts +40 -22
- package/chat/types.d.ts +1 -1
- package/esm2020/chat/chat-message/chat-message.component.mjs +23 -5
- package/esm2020/chat/chat-reference/chat-reference.component.mjs +3 -3
- package/esm2020/chat/chat.component.mjs +154 -32
- package/esm2020/chat/chat.service.mjs +52 -39
- package/esm2020/chat/rest-chat.service.mjs +23 -5
- package/esm2020/chat/saved-chats/saved-chats.component.mjs +5 -1
- package/esm2020/chat/types.mjs +1 -1
- package/esm2020/chat/websocket-chat.service.mjs +19 -3
- package/fesm2015/sinequa-assistant-chat.mjs +271 -81
- package/fesm2015/sinequa-assistant-chat.mjs.map +1 -1
- package/fesm2020/sinequa-assistant-chat.mjs +270 -80
- package/fesm2020/sinequa-assistant-chat.mjs.map +1 -1
- package/package.json +1 -1
|
@@ -271,25 +271,25 @@ const chatConfigSchema = z.object({
|
|
|
271
271
|
|
|
272
272
|
class ChatService {
|
|
273
273
|
constructor() {
|
|
274
|
-
/** Emit true once the initialization of the chat process is done*/
|
|
274
|
+
/** Emit true once the initialization of the chat process is done. */
|
|
275
275
|
this.initProcess$ = new BehaviorSubject(false);
|
|
276
|
-
/** Emit true once the initialization of the chat config is done*/
|
|
276
|
+
/** Emit true once the initialization of the chat config is done. */
|
|
277
277
|
this.initConfig$ = new BehaviorSubject(false);
|
|
278
|
-
/** Global configuration of the chat */
|
|
278
|
+
/** Global configuration of the chat. */
|
|
279
279
|
this.chatConfig$ = new BehaviorSubject(undefined);
|
|
280
|
-
/** Streaming status of the chat endpoint */
|
|
280
|
+
/** Streaming status of the chat endpoint. */
|
|
281
281
|
this.streaming$ = new BehaviorSubject(false);
|
|
282
|
-
/** List of saved chats */
|
|
282
|
+
/** List of saved chats. */
|
|
283
283
|
this.savedChats$ = new BehaviorSubject([]);
|
|
284
|
-
/** Emit the saved chat to load */
|
|
284
|
+
/** Emit the saved chat to load. */
|
|
285
285
|
this.loadSavedChat$ = new BehaviorSubject(undefined);
|
|
286
|
-
/** Emit the quota each time the chat is invoked */
|
|
286
|
+
/** Emit the quota each time the chat is invoked. */
|
|
287
287
|
this.quota$ = new BehaviorSubject(undefined);
|
|
288
|
-
/** Emit the calculated user's token consumption based on the quota */
|
|
288
|
+
/** Emit the calculated user's token consumption based on the quota. */
|
|
289
289
|
this.userTokenConsumption$ = new BehaviorSubject(undefined);
|
|
290
|
-
/** Emit the chat usage metrics each time the generation of the assistant response is completed */
|
|
290
|
+
/** Emit the chat usage metrics each time the generation of the assistant response is completed. */
|
|
291
291
|
this.chatUsageMetrics$ = new BehaviorSubject(undefined);
|
|
292
|
-
/** Emit the calculated chat's token consumption based on the chat usage metrics */
|
|
292
|
+
/** Emit the calculated chat's token consumption based on the chat usage metrics. */
|
|
293
293
|
this.chatTokenConsumption$ = new BehaviorSubject(undefined);
|
|
294
294
|
this.userSettingsService = inject(UserSettingsWebService);
|
|
295
295
|
this.notificationsService = inject(NotificationsService);
|
|
@@ -299,6 +299,7 @@ class ChatService {
|
|
|
299
299
|
this.appService = inject(AppService);
|
|
300
300
|
this.intlService = inject(IntlService);
|
|
301
301
|
this.modalService = inject(ModalService);
|
|
302
|
+
this.principalService = inject(PrincipalWebService);
|
|
302
303
|
}
|
|
303
304
|
get assistants() {
|
|
304
305
|
if (!this.userSettingsService.userSettings)
|
|
@@ -335,6 +336,21 @@ class ChatService {
|
|
|
335
336
|
setSavedChatId(savedChatId) {
|
|
336
337
|
this._savedChatId = savedChatId;
|
|
337
338
|
}
|
|
339
|
+
/**
|
|
340
|
+
* Get the ID of the current chat discussion which is used to identify audit events
|
|
341
|
+
* @returns The ID of the current chat discussion
|
|
342
|
+
*/
|
|
343
|
+
get chatId() {
|
|
344
|
+
return this._chatId;
|
|
345
|
+
}
|
|
346
|
+
/**
|
|
347
|
+
* Generate an GUID for the current chat discussion which is used to identify audit events
|
|
348
|
+
* If the discussion is saved, the savedChatId is initialized with the value of this chatId
|
|
349
|
+
* @param chatId if provided, it will be considered as the ID of the current chat discussion which is used to identify audit events
|
|
350
|
+
*/
|
|
351
|
+
generateChatId(chatId) {
|
|
352
|
+
this._chatId = chatId || Utils.guid();
|
|
353
|
+
}
|
|
338
354
|
/**
|
|
339
355
|
* Initialize the chat config by managing ONLY sub-object **defaultValues** configs of the standard app config (defined in the customization json tab ) and the user preferences.
|
|
340
356
|
* To do so, a tracking mechanism is implemented to notify the user about the available updates in the defaultValues object of the standard app config.
|
|
@@ -474,27 +490,30 @@ class ChatService {
|
|
|
474
490
|
}
|
|
475
491
|
return model;
|
|
476
492
|
}
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
493
|
+
/**
|
|
494
|
+
* Generate an audit event with the given type and details. The generated audit event is sent afterwards via the AuditWebService
|
|
495
|
+
* @param type Audit event type
|
|
496
|
+
* @param details Audit event details
|
|
497
|
+
* @param id Actions (savedChat delete/rename/...) may occur on a specific chat different than the current one stored in this service, so the chat id can be provided
|
|
498
|
+
*/
|
|
499
|
+
generateAuditEvent(type, details, id) {
|
|
500
|
+
const baseDetails = {
|
|
501
|
+
"url": decodeURIComponent(window.location.href),
|
|
502
|
+
"app": this.appService.appName,
|
|
503
|
+
"user-id": this.principalService.principal?.userId,
|
|
504
|
+
"instance-id": this.chatInstanceId,
|
|
505
|
+
"chat-id": id || this.chatId,
|
|
506
|
+
"service-id": this.chatConfig$.value.defaultValues.service_id,
|
|
507
|
+
"model-id": this.chatConfig$.value.defaultValues.model_id,
|
|
508
|
+
};
|
|
509
|
+
const audit = {
|
|
510
|
+
type,
|
|
511
|
+
detail: {
|
|
512
|
+
...baseDetails,
|
|
513
|
+
...details
|
|
514
|
+
}
|
|
515
|
+
};
|
|
516
|
+
this.auditService.notify(audit);
|
|
498
517
|
}
|
|
499
518
|
/**
|
|
500
519
|
* Format a date string in UTC to a local date string
|
|
@@ -513,12 +532,6 @@ class ChatService {
|
|
|
513
532
|
static formatPrompt(prompt, context) {
|
|
514
533
|
return prompt.replace(/{{(.*?)}}/g, (match, expr) => get(context, expr) ?? match);
|
|
515
534
|
}
|
|
516
|
-
/**
|
|
517
|
-
* @returns A Globally Unique Identifier
|
|
518
|
-
*/
|
|
519
|
-
static generateGUID() {
|
|
520
|
-
return Utils.guid();
|
|
521
|
-
}
|
|
522
535
|
}
|
|
523
536
|
ChatService.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.0.6", ngImport: i0, type: ChatService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
524
537
|
ChatService.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "14.0.6", ngImport: i0, type: ChatService });
|
|
@@ -661,7 +674,6 @@ class WebSocketChatService extends ChatService {
|
|
|
661
674
|
);
|
|
662
675
|
// Invoke the Chat method
|
|
663
676
|
this.connection.invoke('Chat', data)
|
|
664
|
-
.then(() => this.notifyAudit(this.chatHistory, this.chatConfig$.value.defaultValues.service_id)) // When the server indicates it has successfully finished invoking the method, notify the audit service with the recent chat history
|
|
665
677
|
.catch(error => {
|
|
666
678
|
console.error('Error invoking Chat:', error);
|
|
667
679
|
return Promise.resolve(); // Return a resolved promise to handle the error and prevent unhandled promise rejection
|
|
@@ -736,12 +748,13 @@ class WebSocketChatService extends ChatService {
|
|
|
736
748
|
const addSavedChatSubject = new Subject();
|
|
737
749
|
const data = {
|
|
738
750
|
instanceId: this.chatInstanceId,
|
|
739
|
-
savedChatId:
|
|
751
|
+
savedChatId: this.chatId,
|
|
740
752
|
history: messages,
|
|
741
753
|
debug: this.chatConfig$.value.defaultValues.debug
|
|
742
754
|
};
|
|
743
755
|
this.connection.on('SavedChatAdd', (res) => {
|
|
744
756
|
this.setSavedChatId(res.savedChat.id); // Persist the savedChatId
|
|
757
|
+
this.generateAuditEvent('saved-chat.add', {}, res.savedChat.id); // Generate audit event
|
|
745
758
|
addSavedChatSubject.next(res.savedChat);
|
|
746
759
|
addSavedChatSubject.complete();
|
|
747
760
|
});
|
|
@@ -838,6 +851,22 @@ class WebSocketChatService extends ChatService {
|
|
|
838
851
|
action.pipe(take(1)).subscribe();
|
|
839
852
|
}
|
|
840
853
|
this._executionTime = history.executionTime;
|
|
854
|
+
// Generate audit event
|
|
855
|
+
const details = {
|
|
856
|
+
'duration': this._executionTime,
|
|
857
|
+
'text': this.chatHistory.at(-1).content,
|
|
858
|
+
'role': this.chatHistory.at(-1).role,
|
|
859
|
+
'rank': this.chatHistory.length - 1,
|
|
860
|
+
'generation-tokencount': this.chatHistory.at(-1).additionalProperties.usageMetrics.completionTokenCount,
|
|
861
|
+
'prompt-tokencount': this.chatHistory.at(-1).additionalProperties.usageMetrics.promptTokenCount,
|
|
862
|
+
'attachments': this._attachments.map(({ recordId, contextId, parts, type }) => ({
|
|
863
|
+
recordId,
|
|
864
|
+
contextId,
|
|
865
|
+
parts: parts.map(({ partId, text }) => ({ partId, text })),
|
|
866
|
+
type
|
|
867
|
+
}))
|
|
868
|
+
};
|
|
869
|
+
this.generateAuditEvent('message', details);
|
|
841
870
|
},
|
|
842
871
|
isGlobalHandler: false
|
|
843
872
|
});
|
|
@@ -1243,10 +1272,10 @@ class ChatReferenceComponent {
|
|
|
1243
1272
|
}
|
|
1244
1273
|
}
|
|
1245
1274
|
ChatReferenceComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.0.6", ngImport: i0, type: ChatReferenceComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
1246
|
-
ChatReferenceComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "14.0.6", type: ChatReferenceComponent, isStandalone: true, selector: "sq-chat-reference", inputs: { reference: "reference", attachment: "attachment", partId: "partId" }, outputs: { openDocument: "openDocument", openPreview: "openPreview" }, ngImport: i0, template: "<div [class.reference-tooltip]=\"!!partId\">\n <div class=\"reference-data\" [class.expanded]=\"attachment['$expanded'] || !!partId\">\n <span class=\"reference me-1\">{{reference}}</span>\n <sq-format-icon [extension]=\"attachment.record.fileext\"></sq-format-icon>\n <a [id]=\"'attachment-'+attachment.recordId\" (click)=\"expandAttachment()\">\n {{attachment.record.title}}\n </a>\n <i class=\"fas fa-eye\" (click)=\"openPreview.emit(attachment)\" [sqTooltip]=\"!partId ? 'Preview document' : ''\"></i>\n <i class=\"fas fa-arrow-up-right-from-square\" *ngIf=\"attachment.record.url1 || attachment.record.originalUrl\"\n (click)=\"openDocument.emit(attachment
|
|
1275
|
+
ChatReferenceComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "14.0.6", type: ChatReferenceComponent, isStandalone: true, selector: "sq-chat-reference", inputs: { reference: "reference", attachment: "attachment", partId: "partId" }, outputs: { openDocument: "openDocument", openPreview: "openPreview" }, ngImport: i0, template: "<div [class.reference-tooltip]=\"!!partId\">\n <div class=\"reference-data\" [class.expanded]=\"attachment['$expanded'] || !!partId\">\n <span class=\"reference me-1\">{{reference}}</span>\n <sq-format-icon [extension]=\"attachment.record.fileext\"></sq-format-icon>\n <a [id]=\"'attachment-'+attachment.recordId\" (click)=\"expandAttachment()\">\n {{attachment.record.title}}\n </a>\n <i class=\"fas fa-eye\" (click)=\"openPreview.emit(attachment)\" [sqTooltip]=\"!partId ? 'Preview document' : ''\"></i>\n <i class=\"fas fa-arrow-up-right-from-square\" *ngIf=\"attachment.record.url1 || attachment.record.originalUrl\"\n (click)=\"openDocument.emit(attachment)\" [sqTooltip]=\"!partId ? 'Open document' : ''\">\n </i>\n </div>\n <div class=\"reference-passages\" *ngIf=\"!!partId || (attachment['$expanded']) && parts.length\">\n <div class=\"reference-passage\" *ngFor=\"let part of parts\">\n <span class=\"reference me-1\">{{reference}}.{{part.partId}}</span>\n <span [innerHTML]=\"part.text\"></span>\n </div>\n </div>\n</div>\n", styles: [".ast-primary{color:var(--ast-primary-color, #005DA7);background-color:var(--ast-primary-bg, #f2f8fe)}.ast-primary-hover{background-color:var(--ast-primary-bg, #f2f8fe)}.ast-primary-hover:hover{color:var(--ast-primary-color, #005DA7)}.ast-secondary{color:var(--ast-secondary-color, #FF732E);background-color:var(--ast-secondary-bg, #FFF8F1)}.ast-btn{border:0;text-align:left;padding-top:.5rem;padding-bottom:.5rem;display:flex;align-items:center}.dark{--ast-primary-bg: #0d0701;--ast-primary-color: #008cd1;--ast-secondary-bg: #00070e;--ast-secondary-color: #ffa258;--ast-input-bg: #070707;--ast-input-color: rgba(222, 218, 218, .75);--ast-muted-color: rgba(222, 218, 218, .75);--ast-saved-chat-hover-background: #262421;--ast-message-table-border-color: #333333;--ast-message-table-tr-bg: #070707;--ast-message-table-tr-border-color: #222222;--ast-reference-icon-color: white;--ast-reference-icon-active-color: black;--ast-reference-passages-color: white;--ast-reference-expanded-hover-bg: #262421;--ast-message-reference-color: black}:host ::ng-deep .reference,:host ::ng-deep .message-content .reference,:host ::ng-deep .attachment .reference{position:relative;bottom:var(--ast-reference-bottom, .3em);font-weight:var(--ast-reference-font-weight, bold);padding:var(--ast-reference-padding, 0 .2em);margin:var(--ast-reference-margin, 0 .1em);border-radius:var(--ast-reference-border-radius, .2em);background-color:var(--ast-reference-background-color, lightblue);color:var(--ast-reference-color, black)}:host ::ng-deep .reference,:host ::ng-deep .message-content .reference{font-size:var(--ast-reference-message-font-size, .7em)}:host ::ng-deep .attachment .reference{font-size:var(--ast-reference-attachment-font-size, 13px)}:host{display:block}:host.expanded,:host:hover{background-color:var(--ast-reference-expanded-hover-bg, white)}.reference-data{display:flex;flex-direction:row;align-items:baseline;padding:var(--ast-size-1, .25rem)}.reference-data a{color:var(--ast-secondary-color, #FF732E);flex-grow:1;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;font-weight:var(--font-weight-bold, 500);cursor:pointer}.reference-data i{padding:var(--ast-size-1, .25rem);margin-left:var(--ast-size-1, .25rem);cursor:pointer;color:var(--ast-reference-icon-color, black)}.reference-data i.active{color:var(--ast-reference-icon-active-color, white);background-color:var(--ast-secondary-color, #FF732E)}.reference-data:not(.expanded) i{opacity:0}.reference-data:not(.expanded):hover i{opacity:1}.reference-passages{white-space:normal;font-style:italic;font-weight:400;padding:1rem 0;color:var(--ast-reference-passages-color, black)}.reference-passages .reference-passage{display:flex;align-items:baseline;padding-left:2.5rem;padding-right:1rem;word-wrap:break-word}.reference-passages .reference-passage+.reference-passage{padding-top:1rem}.reference-passages .reference-passage .reference{white-space:nowrap;margin-right:var(--ast-size-2, .5rem)}sq-format-icon{margin-left:var(--ast-size-1, .25rem);margin-right:var(--ast-size-2, .5rem);color:var(--ast-secondary-color, #FF732E)}.reference-tooltip{max-width:600px!important;box-shadow:0 .5rem 1rem #00000026;padding:.5rem;font-size:.875rem}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "ngmodule", type: UtilsModule }, { kind: "directive", type: i2$1.TooltipDirective, selector: "[sqTooltip]", inputs: ["sqTooltip", "sqTooltipData", "sqTooltipTemplate", "placement", "fallbackPlacements", "delay", "hoverableTooltip", "tooltipClass"] }, { kind: "component", type: FormatIconComponent, selector: "sq-format-icon", inputs: ["extension"] }] });
|
|
1247
1276
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.0.6", ngImport: i0, type: ChatReferenceComponent, decorators: [{
|
|
1248
1277
|
type: Component,
|
|
1249
|
-
args: [{ selector: 'sq-chat-reference', standalone: true, imports: [CommonModule, UtilsModule, FormatIconComponent], template: "<div [class.reference-tooltip]=\"!!partId\">\n <div class=\"reference-data\" [class.expanded]=\"attachment['$expanded'] || !!partId\">\n <span class=\"reference me-1\">{{reference}}</span>\n <sq-format-icon [extension]=\"attachment.record.fileext\"></sq-format-icon>\n <a [id]=\"'attachment-'+attachment.recordId\" (click)=\"expandAttachment()\">\n {{attachment.record.title}}\n </a>\n <i class=\"fas fa-eye\" (click)=\"openPreview.emit(attachment)\" [sqTooltip]=\"!partId ? 'Preview document' : ''\"></i>\n <i class=\"fas fa-arrow-up-right-from-square\" *ngIf=\"attachment.record.url1 || attachment.record.originalUrl\"\n (click)=\"openDocument.emit(attachment
|
|
1278
|
+
args: [{ selector: 'sq-chat-reference', standalone: true, imports: [CommonModule, UtilsModule, FormatIconComponent], template: "<div [class.reference-tooltip]=\"!!partId\">\n <div class=\"reference-data\" [class.expanded]=\"attachment['$expanded'] || !!partId\">\n <span class=\"reference me-1\">{{reference}}</span>\n <sq-format-icon [extension]=\"attachment.record.fileext\"></sq-format-icon>\n <a [id]=\"'attachment-'+attachment.recordId\" (click)=\"expandAttachment()\">\n {{attachment.record.title}}\n </a>\n <i class=\"fas fa-eye\" (click)=\"openPreview.emit(attachment)\" [sqTooltip]=\"!partId ? 'Preview document' : ''\"></i>\n <i class=\"fas fa-arrow-up-right-from-square\" *ngIf=\"attachment.record.url1 || attachment.record.originalUrl\"\n (click)=\"openDocument.emit(attachment)\" [sqTooltip]=\"!partId ? 'Open document' : ''\">\n </i>\n </div>\n <div class=\"reference-passages\" *ngIf=\"!!partId || (attachment['$expanded']) && parts.length\">\n <div class=\"reference-passage\" *ngFor=\"let part of parts\">\n <span class=\"reference me-1\">{{reference}}.{{part.partId}}</span>\n <span [innerHTML]=\"part.text\"></span>\n </div>\n </div>\n</div>\n", styles: [".ast-primary{color:var(--ast-primary-color, #005DA7);background-color:var(--ast-primary-bg, #f2f8fe)}.ast-primary-hover{background-color:var(--ast-primary-bg, #f2f8fe)}.ast-primary-hover:hover{color:var(--ast-primary-color, #005DA7)}.ast-secondary{color:var(--ast-secondary-color, #FF732E);background-color:var(--ast-secondary-bg, #FFF8F1)}.ast-btn{border:0;text-align:left;padding-top:.5rem;padding-bottom:.5rem;display:flex;align-items:center}.dark{--ast-primary-bg: #0d0701;--ast-primary-color: #008cd1;--ast-secondary-bg: #00070e;--ast-secondary-color: #ffa258;--ast-input-bg: #070707;--ast-input-color: rgba(222, 218, 218, .75);--ast-muted-color: rgba(222, 218, 218, .75);--ast-saved-chat-hover-background: #262421;--ast-message-table-border-color: #333333;--ast-message-table-tr-bg: #070707;--ast-message-table-tr-border-color: #222222;--ast-reference-icon-color: white;--ast-reference-icon-active-color: black;--ast-reference-passages-color: white;--ast-reference-expanded-hover-bg: #262421;--ast-message-reference-color: black}:host ::ng-deep .reference,:host ::ng-deep .message-content .reference,:host ::ng-deep .attachment .reference{position:relative;bottom:var(--ast-reference-bottom, .3em);font-weight:var(--ast-reference-font-weight, bold);padding:var(--ast-reference-padding, 0 .2em);margin:var(--ast-reference-margin, 0 .1em);border-radius:var(--ast-reference-border-radius, .2em);background-color:var(--ast-reference-background-color, lightblue);color:var(--ast-reference-color, black)}:host ::ng-deep .reference,:host ::ng-deep .message-content .reference{font-size:var(--ast-reference-message-font-size, .7em)}:host ::ng-deep .attachment .reference{font-size:var(--ast-reference-attachment-font-size, 13px)}:host{display:block}:host.expanded,:host:hover{background-color:var(--ast-reference-expanded-hover-bg, white)}.reference-data{display:flex;flex-direction:row;align-items:baseline;padding:var(--ast-size-1, .25rem)}.reference-data a{color:var(--ast-secondary-color, #FF732E);flex-grow:1;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;font-weight:var(--font-weight-bold, 500);cursor:pointer}.reference-data i{padding:var(--ast-size-1, .25rem);margin-left:var(--ast-size-1, .25rem);cursor:pointer;color:var(--ast-reference-icon-color, black)}.reference-data i.active{color:var(--ast-reference-icon-active-color, white);background-color:var(--ast-secondary-color, #FF732E)}.reference-data:not(.expanded) i{opacity:0}.reference-data:not(.expanded):hover i{opacity:1}.reference-passages{white-space:normal;font-style:italic;font-weight:400;padding:1rem 0;color:var(--ast-reference-passages-color, black)}.reference-passages .reference-passage{display:flex;align-items:baseline;padding-left:2.5rem;padding-right:1rem;word-wrap:break-word}.reference-passages .reference-passage+.reference-passage{padding-top:1rem}.reference-passages .reference-passage .reference{white-space:nowrap;margin-right:var(--ast-size-2, .5rem)}sq-format-icon{margin-left:var(--ast-size-1, .25rem);margin-right:var(--ast-size-2, .5rem);color:var(--ast-secondary-color, #FF732E)}.reference-tooltip{max-width:600px!important;box-shadow:0 .5rem 1rem #00000026;padding:.5rem;font-size:.875rem}\n"] }]
|
|
1250
1279
|
}], propDecorators: { reference: [{
|
|
1251
1280
|
type: Input
|
|
1252
1281
|
}], attachment: [{
|
|
@@ -1273,11 +1302,16 @@ class ChatMessageComponent {
|
|
|
1273
1302
|
this.openPreview = new EventEmitter();
|
|
1274
1303
|
this.suggestAction = new EventEmitter();
|
|
1275
1304
|
this.edit = new EventEmitter();
|
|
1305
|
+
this.copy = new EventEmitter();
|
|
1276
1306
|
this.regenerate = new EventEmitter();
|
|
1307
|
+
this.like = new EventEmitter();
|
|
1308
|
+
this.dislike = new EventEmitter();
|
|
1277
1309
|
this.references = [];
|
|
1278
1310
|
this.referenceMap = new Map();
|
|
1279
1311
|
this.showReferences = true;
|
|
1280
1312
|
this.iconSize = 24;
|
|
1313
|
+
this.liked = false;
|
|
1314
|
+
this.disliked = false;
|
|
1281
1315
|
/**
|
|
1282
1316
|
* This Unified plugin looks a text nodes and replaces any reference in the
|
|
1283
1317
|
* form [1], [2.3], etc. with custom nodes of type "chat-reference".
|
|
@@ -1402,16 +1436,23 @@ class ChatMessageComponent {
|
|
|
1402
1436
|
getReferenceMatches(content) {
|
|
1403
1437
|
return Array.from(content.matchAll(/\[(\s*\d+(?:\.\d+)?\s*)\]/g));
|
|
1404
1438
|
}
|
|
1405
|
-
copyToClipboard(
|
|
1406
|
-
this.ui.copyToClipboard(
|
|
1439
|
+
copyToClipboard(message) {
|
|
1440
|
+
this.ui.copyToClipboard(message.content);
|
|
1441
|
+
this.copy.emit(message);
|
|
1442
|
+
}
|
|
1443
|
+
openAttachmentPreview(attachment, partId) {
|
|
1444
|
+
this.openPreview.emit({ reference: attachment, partId });
|
|
1445
|
+
}
|
|
1446
|
+
openOriginalAttachment(attachment, partId) {
|
|
1447
|
+
this.openDocument.emit({ reference: attachment, partId });
|
|
1407
1448
|
}
|
|
1408
1449
|
}
|
|
1409
1450
|
ChatMessageComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.0.6", ngImport: i0, type: ChatMessageComponent, deps: [{ token: i1$1.SearchService }, { token: i2$1.UIService }, { token: i3.PrincipalWebService }, { token: i0.ChangeDetectorRef }, { token: i0.ElementRef }], target: i0.ɵɵFactoryTarget.Component });
|
|
1410
|
-
ChatMessageComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "14.0.6", type: ChatMessageComponent, isStandalone: true, selector: "sq-chat-message", inputs: { message: "message", conversation: "conversation", suggestedActions: "suggestedActions", assistantMessageIcon: "assistantMessageIcon", userMessageIcon: "userMessageIcon", streaming: "streaming", canEdit: "canEdit", canRegenerate: "canRegenerate", canCopy: "canCopy" }, outputs: { openDocument: "openDocument", openPreview: "openPreview", suggestAction: "suggestAction", edit: "edit", regenerate: "regenerate" }, usesOnChanges: true, ngImport: i0, template: "<!-- Message icon -->\n<span class=\"message-icon\" [title]=\"message?.role\">\n <i class=\"d-block\" [style.width.px]=\"iconSize\" *ngIf=\"!message\"></i>\n <i [ngClass]=\"assistantMessageIcon\" [style.--sq-size.px]=\"iconSize\" *ngIf=\"message.role === 'assistant'\"></i>\n <ng-container *ngIf=\"message?.role === 'user'\">\n <i [ngClass]=\"userMessageIcon\" [style.--sq-size.px]=\"iconSize\" *ngIf=\"!!userMessageIcon; else initialsAvatar\"></i>\n <ng-template #initialsAvatar>\n <sq-initials-avatar [fullName]=\"name\"></sq-initials-avatar>\n </ng-template>\n </ng-container>\n</span>\n\n<!-- Message body -->\n<div class=\"flex-grow-1\" style=\"min-width: 0;\" [ngClass]=\"'message-'+message.role\">\n\n <div *ngIf=\"message.additionalProperties.$progress as progress\" class=\"small ms-3 mb-2\">\n <a href=\"#\" (click)=\"collapseProgress = !collapseProgress; false\" class=\"text-muted\">\n View progress\n <i class=\"fas fa-chevron-{{collapseProgress? 'right' : 'down'}}\"></i>\n </a>\n <sq-collapse [collapsed]=\"collapseProgress\">\n <ng-template>\n <ul class=\"list-unstyled\">\n <li *ngFor=\"let step of progress\">\n <i class=\"fas fa-fw fa-check text-success\" *ngIf=\"step.done\"></i>\n <i class=\"fas fa-fw fa-spin fa-circle-notch\" *ngIf=\"!step.done\"></i>\n <span class=\"ms-2 fw-bold\">{{step.title}}</span>\n <span *ngIf=\"step.content\">: {{step.content}}</span>\n </li>\n </ul>\n </ng-template>\n </sq-collapse>\n </div>\n\n <div class=\"message-content\" *ngIf=\"message.content\">\n\n <remark *ngIf=\"message?.role === 'assistant'\" [markdown]=\"message.content\" [processor]=\"processor\">\n\n <ng-template remarkTemplate=\"chat-reference\" let-ref>\n\n <a *ngIf=\"referenceMap.get(ref.refId) as attachment; else staticRefTpl\"\n (click)=\"openDocument.emit(attachment.record)\"\n class=\"reference\"\n [sqTooltip]=\"attachment\"\n [sqTooltipTemplate]=\"tooltipTpl\"\n [hoverableTooltip]=\"true\"\n >{{ref.refId}}</a>\n\n <ng-template #staticRefTpl>\n <span class=\"reference\">{{ref.refId}}</span>\n </ng-template>\n\n </ng-template>\n\n <ng-template remarkTemplate=\"streaming-placeholder\">\n <span class=\"placeholder-glow\" *ngIf=\"streaming\">\n <span class=\"placeholder ms-1\"></span>\n </span>\n </ng-template>\n\n <ng-template remarkTemplate=\"code\" let-node>\n <div class=\"card mb-2\">\n <div class=\"card-header d-flex justify-content-between align-items-center\">\n <span>{{node.lang}}</span>\n <button class=\"btn btn-light btn-sm\" (click)=\"copyToClipboard(node.value)\"><i class=\"far fa-fw fa-clipboard\"></i> Copy code</button>\n </div>\n <pre class=\"language-{{node.lang}} my-0 rounded-0 rounded-bottom\"><code class=\"language-{{node.lang}}\">{{node.value}}</code></pre>\n </div>\n </ng-template>\n\n <ng-template remarkTemplate=\"link\" let-node>\n <a [href]=\"node.url\" target=\"_blank\" rel=\"noopener noreferrer\">{{getLinkText(node)}}</a>\n </ng-template>\n\n </remark>\n\n <p *ngIf=\"message?.role === 'user'\">{{message.content}}</p>\n\n <!-- List of reference, if any -->\n <div *ngIf=\"references?.length\" class=\"references\">\n <span class=\"references-title\">References</span> <i class=\"fas references-collapse\" [class.fa-chevron-down]=\"showReferences\" [class.fa-chevron-right]=\"!showReferences\" (click)=\"showReferences=!showReferences\"></i>\n <sq-collapse [collapsed]=\"!showReferences\">\n <ng-template>\n <ul>\n <ng-container *ngFor=\"let reference of references\">\n <li *ngIf=\"referenceMap.get(reference) as attachment\" class=\"text-truncate\">\n <sq-chat-reference [class.expanded]=\"attachment.$expanded\" [attachment]=\"attachment\" [reference]=\"reference\"\n (openPreview)=\"openPreview.emit($event)\" (openDocument)=\"openDocument.emit($event)\"></sq-chat-reference>\n </li>\n </ng-container>\n </ul>\n </ng-template>\n </sq-collapse>\n </div>\n\n </div>\n\n <!-- Edit / Regenerate floating actions -->\n <div class=\"sq-chat-message-action\" *ngIf=\"message\">\n <button class=\"btn btn-sm\" *ngIf=\"canEdit\" sqTooltip=\"Edit message\"\n (click)=\"edit.emit(message)\">\n <i class=\"fas fa-edit\"></i>\n </button>\n <button class=\"btn btn-sm\" *ngIf=\"canCopy\" sqTooltip=\"Copy to clipboard\"\n (click)=\"copyToClipboard(message.content)\">\n <i class=\"far fa-clipboard\"></i>\n </button>\n <button class=\"btn btn-sm\" *ngIf=\"canRegenerate\" sqTooltip=\"Regenerate message\"\n (click)=\"regenerate.emit(message)\">\n <i class=\"fas fa-sync-alt\"></i>\n </button>\n </div>\n\n <!-- List of suggested actions, if any -->\n <div *ngIf=\"suggestedActions\" class=\"mt-2 message-suggestion\">\n <div class=\"suggested-action\" *ngFor=\"let suggestedAction of suggestedActions\" (click)=\"suggestAction.emit(suggestedAction)\">\n <div class=\"message-content\">\n <p><i class=\"fas fa-clipboard-question\"></i> {{suggestedAction.content}}</p>\n </div>\n </div>\n </div>\n\n <ng-template #tooltipTpl let-ref>\n <sq-chat-reference class=\"expanded\"\n [attachment]=\"ref\"\n [reference]=\"ref.contextId\"\n [partId]=\"ref.$partId\"\n (openPreview)=\"openPreview.emit($event)\"\n (openDocument)=\"openDocument.emit($event)\">\n </sq-chat-reference>\n </ng-template>\n\n</div>\n", styles: [".ast-primary{color:var(--ast-primary-color, #005DA7);background-color:var(--ast-primary-bg, #f2f8fe)}.ast-primary-hover{background-color:var(--ast-primary-bg, #f2f8fe)}.ast-primary-hover:hover{color:var(--ast-primary-color, #005DA7)}.ast-secondary{color:var(--ast-secondary-color, #FF732E);background-color:var(--ast-secondary-bg, #FFF8F1)}.ast-btn{border:0;text-align:left;padding-top:.5rem;padding-bottom:.5rem;display:flex;align-items:center}.dark{--ast-primary-bg: #0d0701;--ast-primary-color: #008cd1;--ast-secondary-bg: #00070e;--ast-secondary-color: #ffa258;--ast-input-bg: #070707;--ast-input-color: rgba(222, 218, 218, .75);--ast-muted-color: rgba(222, 218, 218, .75);--ast-saved-chat-hover-background: #262421;--ast-message-table-border-color: #333333;--ast-message-table-tr-bg: #070707;--ast-message-table-tr-border-color: #222222;--ast-reference-icon-color: white;--ast-reference-icon-active-color: black;--ast-reference-passages-color: white;--ast-reference-expanded-hover-bg: #262421;--ast-message-reference-color: black}:host{display:flex}:host:not(:hover) .sq-chat-message-action{display:none}.message-content{padding:var(--ast-message-padding, var(--ast-size-3, .75rem));border-radius:var(--ast-message-border-radius, var(--ast-size-4, 1rem));display:inline-block;max-width:100%;overflow-wrap:break-word;word-wrap:break-word;word-break:break-word}.message-content .references{margin-top:var(--ast-size-5, 1.25rem)}.message-content .references ul{border-left:.2rem solid var(--ast-secondary-color, #FF732E);padding-left:var(--ast-size-5, 1.25rem);margin-top:var(--ast-size-2, .5rem)}.message-content .references .references-title{font-weight:var(--font-weight-bold, 500)}.message-content .references .references-collapse{cursor:pointer;margin-left:var(--ast-size-2, .5rem)}.message-content ::ng-deep p:last-child{margin-bottom:0}.message-content ::ng-deep .placeholder-glow .placeholder{animation-duration:.4s;width:12px;height:var(--ast-size-4, 1rem);vertical-align:text-bottom}.message-content ::ng-deep img{max-width:100%}.message-content ::ng-deep table{display:block;border:1px solid;border-color:var(--ast-message-table-border-color, #ccc);border-collapse:collapse;margin:0;padding:0;min-width:100%;overflow-x:auto;table-layout:fixed}.message-content ::ng-deep table tr{background-color:var(--ast-message-table-tr-bg, #f8f8f8);border:1px solid;border-color:var(--ast-message-table-tr-border-color, #ddd);padding:.35em}.message-content ::ng-deep table th,.message-content ::ng-deep table td{padding:.625em;text-align:center}.message-content ::ng-deep table th{font-size:.85em;letter-spacing:.1em;text-transform:uppercase}.message-content ::ng-deep .reference{color:var(--ast-message-reference-color, black)!important}.message-content ::ng-deep ul,.message-content ::ng-deep ol{display:flex;flex-direction:column;gap:.5rem;padding-right:2rem;margin-left:0;margin-right:0;padding-left:40px;unicode-bidi:-webkit-isolate;unicode-bidi:isolate;list-style:disc}.message-content ::ng-deep p:not(:first-child){margin-top:.5rem}.message-assistant .message-content{background:var(--ast-secondary-bg, #FFF8F1)}.message-user .message-content{background:var(--ast-primary-bg, #f2f8fe);font-weight:var(--ast-user-font-weight, var(--font-weight-bold, 500))}.message-user .message-content p{white-space:pre-line}.message-suggestion .message-content{background:var(--ast-input-bg, #F8F8F8);font-weight:var(--ast-user-font-weight, var(--font-weight-bold, 500));transition:background-color .5s ease,color .5s ease}.message-suggestion .message-content:hover{background:var(--ast-primary-bg, #f2f8fe);color:var(--ast-primary-color, #005DA7)}.message-suggestion .message-content p{white-space:pre-line}.message-suggestion .suggested-action{cursor:pointer}.message-suggestion .suggested-action+.suggested-action{margin-top:var(--ast-size-2, .5rem)}.sq-chat-message-action{position:absolute;right:calc(0rem - var(--ast-size-2, .5rem));top:0;display:flex;flex-direction:column}.message-icon{margin-top:var(--ast-size-3, .75rem);margin-right:var(--ast-size-4, 1rem)}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "ngmodule", type: UtilsModule }, { kind: "directive", type: i2$1.TooltipDirective, selector: "[sqTooltip]", inputs: ["sqTooltip", "sqTooltipData", "sqTooltipTemplate", "placement", "fallbackPlacements", "delay", "hoverableTooltip", "tooltipClass"] }, { kind: "ngmodule", type: CollapseModule }, { kind: "component", type: i5.Collapse, selector: "sq-collapse", inputs: ["collapsed"] }, { kind: "ngmodule", type: RemarkModule }, { kind: "component", type: i6.RemarkComponent, selector: "remark", inputs: ["markdown", "processor", "debug"] }, { kind: "directive", type: i6.RemarkTemplateDirective, selector: "[remarkTemplate]", inputs: ["remarkTemplate"] }, { kind: "component", type: InitialsAvatarComponent, selector: "sq-initials-avatar", inputs: ["fullName", "size"] }, { kind: "component", type: ChatReferenceComponent, selector: "sq-chat-reference", inputs: ["reference", "attachment", "partId"], outputs: ["openDocument", "openPreview"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
1451
|
+
ChatMessageComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "14.0.6", type: ChatMessageComponent, isStandalone: true, selector: "sq-chat-message", inputs: { message: "message", conversation: "conversation", suggestedActions: "suggestedActions", assistantMessageIcon: "assistantMessageIcon", userMessageIcon: "userMessageIcon", streaming: "streaming", canEdit: "canEdit", canRegenerate: "canRegenerate", canCopy: "canCopy" }, outputs: { openDocument: "openDocument", openPreview: "openPreview", suggestAction: "suggestAction", edit: "edit", copy: "copy", regenerate: "regenerate", like: "like", dislike: "dislike" }, usesOnChanges: true, ngImport: i0, template: "<!-- Message icon -->\n<span class=\"message-icon\" [title]=\"message?.role\">\n <i class=\"d-block\" [style.width.px]=\"iconSize\" *ngIf=\"!message\"></i>\n <i [ngClass]=\"assistantMessageIcon\" [style.--sq-size.px]=\"iconSize\" *ngIf=\"message.role === 'assistant'\"></i>\n <ng-container *ngIf=\"message?.role === 'user'\">\n <i [ngClass]=\"userMessageIcon\" [style.--sq-size.px]=\"iconSize\" *ngIf=\"!!userMessageIcon; else initialsAvatar\"></i>\n <ng-template #initialsAvatar>\n <sq-initials-avatar [fullName]=\"name\"></sq-initials-avatar>\n </ng-template>\n </ng-container>\n</span>\n\n<!-- Message body -->\n<div class=\"flex-grow-1\" style=\"min-width: 0;\" [ngClass]=\"'message-'+message.role\">\n\n <div *ngIf=\"message.additionalProperties.$progress as progress\" class=\"small ms-3 mb-2\">\n <a href=\"#\" (click)=\"collapseProgress = !collapseProgress; false\" class=\"text-muted\">\n View progress\n <i class=\"fas fa-chevron-{{collapseProgress? 'right' : 'down'}}\"></i>\n </a>\n <sq-collapse [collapsed]=\"collapseProgress\">\n <ng-template>\n <ul class=\"list-unstyled\">\n <li *ngFor=\"let step of progress\">\n <i class=\"fas fa-fw fa-check text-success\" *ngIf=\"step.done\"></i>\n <i class=\"fas fa-fw fa-spin fa-circle-notch\" *ngIf=\"!step.done\"></i>\n <span class=\"ms-2 fw-bold\">{{step.title}}</span>\n <span *ngIf=\"step.content\">: {{step.content}}</span>\n </li>\n </ul>\n </ng-template>\n </sq-collapse>\n </div>\n\n <div class=\"message-content\" *ngIf=\"message.content\">\n\n <remark *ngIf=\"message?.role === 'assistant'\" [markdown]=\"message.content\" [processor]=\"processor\">\n\n <ng-template remarkTemplate=\"chat-reference\" let-ref>\n\n <a *ngIf=\"referenceMap.get(ref.refId) as attachment; else staticRefTpl\"\n class=\"reference\"\n [sqTooltip]=\"attachment\"\n [sqTooltipTemplate]=\"tooltipTpl\"\n [hoverableTooltip]=\"true\"\n >{{ref.refId}}\n </a>\n\n <ng-template #staticRefTpl>\n <span class=\"reference\">{{ref.refId}}</span>\n </ng-template>\n\n </ng-template>\n\n <ng-template remarkTemplate=\"streaming-placeholder\">\n <span class=\"placeholder-glow\" *ngIf=\"streaming\">\n <span class=\"placeholder ms-1\"></span>\n </span>\n </ng-template>\n\n <ng-template remarkTemplate=\"code\" let-node>\n <div class=\"card mb-2\">\n <div class=\"card-header d-flex justify-content-between align-items-center\">\n <span>{{node.lang}}</span>\n <button class=\"btn btn-light btn-sm\" (click)=\"copyToClipboard(node.value)\"><i class=\"far fa-fw fa-clipboard\"></i> Copy code</button>\n </div>\n <pre class=\"language-{{node.lang}} my-0 rounded-0 rounded-bottom\"><code class=\"language-{{node.lang}}\">{{node.value}}</code></pre>\n </div>\n </ng-template>\n\n <ng-template remarkTemplate=\"link\" let-node>\n <a [href]=\"node.url\" target=\"_blank\" rel=\"noopener noreferrer\">{{getLinkText(node)}}</a>\n </ng-template>\n\n </remark>\n\n <p *ngIf=\"message?.role === 'user'\">{{message.content}}</p>\n\n <!-- List of reference, if any -->\n <div *ngIf=\"references?.length\" class=\"references\">\n <span class=\"references-title\">References</span> <i class=\"fas references-collapse\" [class.fa-chevron-down]=\"showReferences\" [class.fa-chevron-right]=\"!showReferences\" (click)=\"showReferences=!showReferences\"></i>\n <sq-collapse [collapsed]=\"!showReferences\">\n <ng-template>\n <ul>\n <ng-container *ngFor=\"let reference of references\">\n <li *ngIf=\"referenceMap.get(reference) as attachment\" class=\"text-truncate\">\n <sq-chat-reference\n [class.expanded]=\"attachment.$expanded\"\n [attachment]=\"attachment\"\n [reference]=\"reference\"\n (openPreview)=\"openAttachmentPreview($event)\"\n (openDocument)=\"openOriginalAttachment($event)\">\n </sq-chat-reference>\n </li>\n </ng-container>\n </ul>\n </ng-template>\n </sq-collapse>\n </div>\n\n </div>\n\n <!-- Edit / Regenerate floating actions -->\n <div class=\"sq-chat-message-actions\" *ngIf=\"message\">\n <button class=\"btn btn-sm\" *ngIf=\"canEdit\" sqTooltip=\"Edit message\"\n (click)=\"edit.emit(message)\">\n<i class=\"fas fa-edit\"></i>\n</button>\n <button class=\"btn btn-sm\" *ngIf=\"canCopy\" sqTooltip=\"Copy to clipboard\" (click)=\"copyToClipboard(message)\">\n <i class=\"far fa-clipboard\"></i>\n </button>\n <button class=\"btn btn-sm\" *ngIf=\"canRegenerate\" sqTooltip=\"Regenerate message\" (click)=\"regenerate.emit(message)\">\n <i class=\"fas fa-sync-alt\"></i>\n </button>\n<!-- <button class=\"btn btn-sm\" [class.bounce]=\"liked\" *ngIf=\"message.role === 'assistant' && !streaming\" sqTooltip=\"Like the answer\"\n (click)=\"like.emit(message); liked = true;\">\n <i class=\"far fa-thumbs-up\"></i>\n </button> -->\n<!-- <button class=\"btn btn-sm bounce\" [class.bounce]=\"disliked\" *ngIf=\"message.role === 'assistant' && !streaming\" sqTooltip=\"Report an issue\"\n (click)=\"dislike.emit(message); disliked = true;\">\n <i class=\"far fa-thumbs-down\"></i>\n </button> -->\n </div>\n\n <!-- List of suggested actions, if any -->\n <div *ngIf=\"suggestedActions\" class=\"mt-2 message-suggestion\">\n <div class=\"suggested-action\" *ngFor=\"let suggestedAction of suggestedActions\" (click)=\"suggestAction.emit(suggestedAction)\">\n <div class=\"message-content\">\n <p><i class=\"fas fa-clipboard-question\"></i> {{suggestedAction.content}}</p>\n </div>\n </div>\n </div>\n\n <ng-template #tooltipTpl let-ref>\n <sq-chat-reference\n class=\"expanded\"\n [attachment]=\"ref\"\n [reference]=\"ref.contextId\"\n [partId]=\"ref.$partId\"\n (openPreview)=\"openAttachmentPreview($event, ref.$partId)\"\n (openDocument)=\"openOriginalAttachment($event, ref.$partId)\">\n </sq-chat-reference>\n </ng-template>\n\n</div>\n", styles: [".ast-primary{color:var(--ast-primary-color, #005DA7);background-color:var(--ast-primary-bg, #f2f8fe)}.ast-primary-hover{background-color:var(--ast-primary-bg, #f2f8fe)}.ast-primary-hover:hover{color:var(--ast-primary-color, #005DA7)}.ast-secondary{color:var(--ast-secondary-color, #FF732E);background-color:var(--ast-secondary-bg, #FFF8F1)}.ast-btn{border:0;text-align:left;padding-top:.5rem;padding-bottom:.5rem;display:flex;align-items:center}.dark{--ast-primary-bg: #0d0701;--ast-primary-color: #008cd1;--ast-secondary-bg: #00070e;--ast-secondary-color: #ffa258;--ast-input-bg: #070707;--ast-input-color: rgba(222, 218, 218, .75);--ast-muted-color: rgba(222, 218, 218, .75);--ast-saved-chat-hover-background: #262421;--ast-message-table-border-color: #333333;--ast-message-table-tr-bg: #070707;--ast-message-table-tr-border-color: #222222;--ast-reference-icon-color: white;--ast-reference-icon-active-color: black;--ast-reference-passages-color: white;--ast-reference-expanded-hover-bg: #262421;--ast-message-reference-color: black}:host{display:flex}:host:not(:hover):not(.last-message) .sq-chat-message-actions{visibility:hidden}.message-content{padding:var(--ast-message-padding, var(--ast-size-3, .75rem));border-radius:var(--ast-message-border-radius, var(--ast-size-4, 1rem));display:inline-block;max-width:100%;overflow-wrap:break-word;word-wrap:break-word;word-break:break-word}.message-content .references{margin-top:var(--ast-size-5, 1.25rem)}.message-content .references ul{border-left:.2rem solid var(--ast-secondary-color, #FF732E);padding-left:var(--ast-size-5, 1.25rem);margin-top:var(--ast-size-2, .5rem)}.message-content .references .references-title{font-weight:var(--font-weight-bold, 500)}.message-content .references .references-collapse{cursor:pointer;margin-left:var(--ast-size-2, .5rem)}.message-content ::ng-deep p:last-child{margin-bottom:0}.message-content ::ng-deep .placeholder-glow .placeholder{animation-duration:.4s;width:12px;height:var(--ast-size-4, 1rem);vertical-align:text-bottom}.message-content ::ng-deep img{max-width:100%}.message-content ::ng-deep table{display:block;border:1px solid;border-color:var(--ast-message-table-border-color, #ccc);border-collapse:collapse;margin:0;padding:0;min-width:100%;overflow-x:auto;table-layout:fixed}.message-content ::ng-deep table tr{background-color:var(--ast-message-table-tr-bg, #f8f8f8);border:1px solid;border-color:var(--ast-message-table-tr-border-color, #ddd);padding:.35em}.message-content ::ng-deep table th,.message-content ::ng-deep table td{padding:.625em;text-align:center}.message-content ::ng-deep table th{font-size:.85em;letter-spacing:.1em;text-transform:uppercase}.message-content ::ng-deep .reference{color:var(--ast-message-reference-color, black)!important}.message-content ::ng-deep ul,.message-content ::ng-deep ol{display:flex;flex-direction:column;gap:.5rem;padding-right:2rem;margin-left:0;margin-right:0;padding-left:40px;unicode-bidi:-webkit-isolate;unicode-bidi:isolate;list-style:disc}.message-content ::ng-deep p:not(:first-child){margin-top:.5rem}.message-assistant .message-content{background:var(--ast-secondary-bg, #FFF8F1)}.message-user .message-content{background:var(--ast-primary-bg, #f2f8fe);font-weight:var(--ast-user-font-weight, var(--font-weight-bold, 500))}.message-user .message-content p{white-space:pre-line}.message-suggestion .message-content{background:var(--ast-input-bg, #F8F8F8);font-weight:var(--ast-user-font-weight, var(--font-weight-bold, 500));transition:background-color .5s ease,color .5s ease}.message-suggestion .message-content:hover{background:var(--ast-primary-bg, #f2f8fe);color:var(--ast-primary-color, #005DA7)}.message-suggestion .message-content p{white-space:pre-line}.message-suggestion .suggested-action{cursor:pointer}.message-suggestion .suggested-action+.suggested-action{margin-top:var(--ast-size-2, .5rem)}.sq-chat-message-actions{position:absolute;bottom:calc(0rem - var(--ast-size-3, .75rem));display:flex;z-index:999}.sq-chat-message-actions button{font-size:.75rem}.sq-chat-message-actions button:hover{color:var(--ast-primary-color, #005DA7)}.message-icon{margin-top:var(--ast-size-3, .75rem);margin-right:var(--ast-size-4, 1rem)}.bounce{animation:bounce 2s ease}@keyframes bounce{10%{transform:translateY(0)}20%{transform:translateY(-15%)}30%{transform:translateY(0)}35%{transform:translateY(-7%)}37%{transform:translateY(0)}39%{transform:translateY(-3%)}40%{transform:translateY(0)}}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "ngmodule", type: UtilsModule }, { kind: "directive", type: i2$1.TooltipDirective, selector: "[sqTooltip]", inputs: ["sqTooltip", "sqTooltipData", "sqTooltipTemplate", "placement", "fallbackPlacements", "delay", "hoverableTooltip", "tooltipClass"] }, { kind: "ngmodule", type: CollapseModule }, { kind: "component", type: i5.Collapse, selector: "sq-collapse", inputs: ["collapsed"] }, { kind: "ngmodule", type: RemarkModule }, { kind: "component", type: i6.RemarkComponent, selector: "remark", inputs: ["markdown", "processor", "debug"] }, { kind: "directive", type: i6.RemarkTemplateDirective, selector: "[remarkTemplate]", inputs: ["remarkTemplate"] }, { kind: "component", type: InitialsAvatarComponent, selector: "sq-initials-avatar", inputs: ["fullName", "size"] }, { kind: "component", type: ChatReferenceComponent, selector: "sq-chat-reference", inputs: ["reference", "attachment", "partId"], outputs: ["openDocument", "openPreview"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
1411
1452
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.0.6", ngImport: i0, type: ChatMessageComponent, decorators: [{
|
|
1412
1453
|
type: Component,
|
|
1413
1454
|
args: [{ selector: "sq-chat-message", changeDetection: ChangeDetectionStrategy.OnPush, standalone: true, imports: [CommonModule, UtilsModule, CollapseModule, RemarkModule,
|
|
1414
|
-
InitialsAvatarComponent, ChatReferenceComponent], template: "<!-- Message icon -->\n<span class=\"message-icon\" [title]=\"message?.role\">\n <i class=\"d-block\" [style.width.px]=\"iconSize\" *ngIf=\"!message\"></i>\n <i [ngClass]=\"assistantMessageIcon\" [style.--sq-size.px]=\"iconSize\" *ngIf=\"message.role === 'assistant'\"></i>\n <ng-container *ngIf=\"message?.role === 'user'\">\n <i [ngClass]=\"userMessageIcon\" [style.--sq-size.px]=\"iconSize\" *ngIf=\"!!userMessageIcon; else initialsAvatar\"></i>\n <ng-template #initialsAvatar>\n <sq-initials-avatar [fullName]=\"name\"></sq-initials-avatar>\n </ng-template>\n </ng-container>\n</span>\n\n<!-- Message body -->\n<div class=\"flex-grow-1\" style=\"min-width: 0;\" [ngClass]=\"'message-'+message.role\">\n\n <div *ngIf=\"message.additionalProperties.$progress as progress\" class=\"small ms-3 mb-2\">\n <a href=\"#\" (click)=\"collapseProgress = !collapseProgress; false\" class=\"text-muted\">\n View progress\n <i class=\"fas fa-chevron-{{collapseProgress? 'right' : 'down'}}\"></i>\n </a>\n <sq-collapse [collapsed]=\"collapseProgress\">\n <ng-template>\n <ul class=\"list-unstyled\">\n <li *ngFor=\"let step of progress\">\n <i class=\"fas fa-fw fa-check text-success\" *ngIf=\"step.done\"></i>\n <i class=\"fas fa-fw fa-spin fa-circle-notch\" *ngIf=\"!step.done\"></i>\n <span class=\"ms-2 fw-bold\">{{step.title}}</span>\n <span *ngIf=\"step.content\">: {{step.content}}</span>\n </li>\n </ul>\n </ng-template>\n </sq-collapse>\n </div>\n\n <div class=\"message-content\" *ngIf=\"message.content\">\n\n <remark *ngIf=\"message?.role === 'assistant'\" [markdown]=\"message.content\" [processor]=\"processor\">\n\n <ng-template remarkTemplate=\"chat-reference\" let-ref>\n\n <a *ngIf=\"referenceMap.get(ref.refId) as attachment; else staticRefTpl\"\n (click)=\"openDocument.emit(attachment.record)\"\n class=\"reference\"\n [sqTooltip]=\"attachment\"\n [sqTooltipTemplate]=\"tooltipTpl\"\n [hoverableTooltip]=\"true\"\n >{{ref.refId}}</a>\n\n <ng-template #staticRefTpl>\n <span class=\"reference\">{{ref.refId}}</span>\n </ng-template>\n\n </ng-template>\n\n <ng-template remarkTemplate=\"streaming-placeholder\">\n <span class=\"placeholder-glow\" *ngIf=\"streaming\">\n <span class=\"placeholder ms-1\"></span>\n </span>\n </ng-template>\n\n <ng-template remarkTemplate=\"code\" let-node>\n <div class=\"card mb-2\">\n <div class=\"card-header d-flex justify-content-between align-items-center\">\n <span>{{node.lang}}</span>\n <button class=\"btn btn-light btn-sm\" (click)=\"copyToClipboard(node.value)\"><i class=\"far fa-fw fa-clipboard\"></i> Copy code</button>\n </div>\n <pre class=\"language-{{node.lang}} my-0 rounded-0 rounded-bottom\"><code class=\"language-{{node.lang}}\">{{node.value}}</code></pre>\n </div>\n </ng-template>\n\n <ng-template remarkTemplate=\"link\" let-node>\n <a [href]=\"node.url\" target=\"_blank\" rel=\"noopener noreferrer\">{{getLinkText(node)}}</a>\n </ng-template>\n\n </remark>\n\n <p *ngIf=\"message?.role === 'user'\">{{message.content}}</p>\n\n <!-- List of reference, if any -->\n <div *ngIf=\"references?.length\" class=\"references\">\n <span class=\"references-title\">References</span> <i class=\"fas references-collapse\" [class.fa-chevron-down]=\"showReferences\" [class.fa-chevron-right]=\"!showReferences\" (click)=\"showReferences=!showReferences\"></i>\n <sq-collapse [collapsed]=\"!showReferences\">\n <ng-template>\n <ul>\n <ng-container *ngFor=\"let reference of references\">\n <li *ngIf=\"referenceMap.get(reference) as attachment\" class=\"text-truncate\">\n <sq-chat-reference [class.expanded]=\"attachment.$expanded\" [attachment]=\"attachment\" [reference]=\"reference\"\n (openPreview)=\"openPreview.emit($event)\" (openDocument)=\"openDocument.emit($event)\"></sq-chat-reference>\n </li>\n </ng-container>\n </ul>\n </ng-template>\n </sq-collapse>\n </div>\n\n </div>\n\n <!-- Edit / Regenerate floating actions -->\n <div class=\"sq-chat-message-action\" *ngIf=\"message\">\n <button class=\"btn btn-sm\" *ngIf=\"canEdit\" sqTooltip=\"Edit message\"\n (click)=\"edit.emit(message)\">\n <i class=\"fas fa-edit\"></i>\n </button>\n <button class=\"btn btn-sm\" *ngIf=\"canCopy\" sqTooltip=\"Copy to clipboard\"\n (click)=\"copyToClipboard(message.content)\">\n <i class=\"far fa-clipboard\"></i>\n </button>\n <button class=\"btn btn-sm\" *ngIf=\"canRegenerate\" sqTooltip=\"Regenerate message\"\n (click)=\"regenerate.emit(message)\">\n <i class=\"fas fa-sync-alt\"></i>\n </button>\n </div>\n\n <!-- List of suggested actions, if any -->\n <div *ngIf=\"suggestedActions\" class=\"mt-2 message-suggestion\">\n <div class=\"suggested-action\" *ngFor=\"let suggestedAction of suggestedActions\" (click)=\"suggestAction.emit(suggestedAction)\">\n <div class=\"message-content\">\n <p><i class=\"fas fa-clipboard-question\"></i> {{suggestedAction.content}}</p>\n </div>\n </div>\n </div>\n\n <ng-template #tooltipTpl let-ref>\n <sq-chat-reference class=\"expanded\"\n [attachment]=\"ref\"\n [reference]=\"ref.contextId\"\n [partId]=\"ref.$partId\"\n (openPreview)=\"openPreview.emit($event)\"\n (openDocument)=\"openDocument.emit($event)\">\n </sq-chat-reference>\n </ng-template>\n\n</div>\n", styles: [".ast-primary{color:var(--ast-primary-color, #005DA7);background-color:var(--ast-primary-bg, #f2f8fe)}.ast-primary-hover{background-color:var(--ast-primary-bg, #f2f8fe)}.ast-primary-hover:hover{color:var(--ast-primary-color, #005DA7)}.ast-secondary{color:var(--ast-secondary-color, #FF732E);background-color:var(--ast-secondary-bg, #FFF8F1)}.ast-btn{border:0;text-align:left;padding-top:.5rem;padding-bottom:.5rem;display:flex;align-items:center}.dark{--ast-primary-bg: #0d0701;--ast-primary-color: #008cd1;--ast-secondary-bg: #00070e;--ast-secondary-color: #ffa258;--ast-input-bg: #070707;--ast-input-color: rgba(222, 218, 218, .75);--ast-muted-color: rgba(222, 218, 218, .75);--ast-saved-chat-hover-background: #262421;--ast-message-table-border-color: #333333;--ast-message-table-tr-bg: #070707;--ast-message-table-tr-border-color: #222222;--ast-reference-icon-color: white;--ast-reference-icon-active-color: black;--ast-reference-passages-color: white;--ast-reference-expanded-hover-bg: #262421;--ast-message-reference-color: black}:host{display:flex}:host:not(:hover) .sq-chat-message-action{display:none}.message-content{padding:var(--ast-message-padding, var(--ast-size-3, .75rem));border-radius:var(--ast-message-border-radius, var(--ast-size-4, 1rem));display:inline-block;max-width:100%;overflow-wrap:break-word;word-wrap:break-word;word-break:break-word}.message-content .references{margin-top:var(--ast-size-5, 1.25rem)}.message-content .references ul{border-left:.2rem solid var(--ast-secondary-color, #FF732E);padding-left:var(--ast-size-5, 1.25rem);margin-top:var(--ast-size-2, .5rem)}.message-content .references .references-title{font-weight:var(--font-weight-bold, 500)}.message-content .references .references-collapse{cursor:pointer;margin-left:var(--ast-size-2, .5rem)}.message-content ::ng-deep p:last-child{margin-bottom:0}.message-content ::ng-deep .placeholder-glow .placeholder{animation-duration:.4s;width:12px;height:var(--ast-size-4, 1rem);vertical-align:text-bottom}.message-content ::ng-deep img{max-width:100%}.message-content ::ng-deep table{display:block;border:1px solid;border-color:var(--ast-message-table-border-color, #ccc);border-collapse:collapse;margin:0;padding:0;min-width:100%;overflow-x:auto;table-layout:fixed}.message-content ::ng-deep table tr{background-color:var(--ast-message-table-tr-bg, #f8f8f8);border:1px solid;border-color:var(--ast-message-table-tr-border-color, #ddd);padding:.35em}.message-content ::ng-deep table th,.message-content ::ng-deep table td{padding:.625em;text-align:center}.message-content ::ng-deep table th{font-size:.85em;letter-spacing:.1em;text-transform:uppercase}.message-content ::ng-deep .reference{color:var(--ast-message-reference-color, black)!important}.message-content ::ng-deep ul,.message-content ::ng-deep ol{display:flex;flex-direction:column;gap:.5rem;padding-right:2rem;margin-left:0;margin-right:0;padding-left:40px;unicode-bidi:-webkit-isolate;unicode-bidi:isolate;list-style:disc}.message-content ::ng-deep p:not(:first-child){margin-top:.5rem}.message-assistant .message-content{background:var(--ast-secondary-bg, #FFF8F1)}.message-user .message-content{background:var(--ast-primary-bg, #f2f8fe);font-weight:var(--ast-user-font-weight, var(--font-weight-bold, 500))}.message-user .message-content p{white-space:pre-line}.message-suggestion .message-content{background:var(--ast-input-bg, #F8F8F8);font-weight:var(--ast-user-font-weight, var(--font-weight-bold, 500));transition:background-color .5s ease,color .5s ease}.message-suggestion .message-content:hover{background:var(--ast-primary-bg, #f2f8fe);color:var(--ast-primary-color, #005DA7)}.message-suggestion .message-content p{white-space:pre-line}.message-suggestion .suggested-action{cursor:pointer}.message-suggestion .suggested-action+.suggested-action{margin-top:var(--ast-size-2, .5rem)}.sq-chat-message-action{position:absolute;right:calc(0rem - var(--ast-size-2, .5rem));top:0;display:flex;flex-direction:column}.message-icon{margin-top:var(--ast-size-3, .75rem);margin-right:var(--ast-size-4, 1rem)}\n"] }]
|
|
1455
|
+
InitialsAvatarComponent, ChatReferenceComponent], template: "<!-- Message icon -->\n<span class=\"message-icon\" [title]=\"message?.role\">\n <i class=\"d-block\" [style.width.px]=\"iconSize\" *ngIf=\"!message\"></i>\n <i [ngClass]=\"assistantMessageIcon\" [style.--sq-size.px]=\"iconSize\" *ngIf=\"message.role === 'assistant'\"></i>\n <ng-container *ngIf=\"message?.role === 'user'\">\n <i [ngClass]=\"userMessageIcon\" [style.--sq-size.px]=\"iconSize\" *ngIf=\"!!userMessageIcon; else initialsAvatar\"></i>\n <ng-template #initialsAvatar>\n <sq-initials-avatar [fullName]=\"name\"></sq-initials-avatar>\n </ng-template>\n </ng-container>\n</span>\n\n<!-- Message body -->\n<div class=\"flex-grow-1\" style=\"min-width: 0;\" [ngClass]=\"'message-'+message.role\">\n\n <div *ngIf=\"message.additionalProperties.$progress as progress\" class=\"small ms-3 mb-2\">\n <a href=\"#\" (click)=\"collapseProgress = !collapseProgress; false\" class=\"text-muted\">\n View progress\n <i class=\"fas fa-chevron-{{collapseProgress? 'right' : 'down'}}\"></i>\n </a>\n <sq-collapse [collapsed]=\"collapseProgress\">\n <ng-template>\n <ul class=\"list-unstyled\">\n <li *ngFor=\"let step of progress\">\n <i class=\"fas fa-fw fa-check text-success\" *ngIf=\"step.done\"></i>\n <i class=\"fas fa-fw fa-spin fa-circle-notch\" *ngIf=\"!step.done\"></i>\n <span class=\"ms-2 fw-bold\">{{step.title}}</span>\n <span *ngIf=\"step.content\">: {{step.content}}</span>\n </li>\n </ul>\n </ng-template>\n </sq-collapse>\n </div>\n\n <div class=\"message-content\" *ngIf=\"message.content\">\n\n <remark *ngIf=\"message?.role === 'assistant'\" [markdown]=\"message.content\" [processor]=\"processor\">\n\n <ng-template remarkTemplate=\"chat-reference\" let-ref>\n\n <a *ngIf=\"referenceMap.get(ref.refId) as attachment; else staticRefTpl\"\n class=\"reference\"\n [sqTooltip]=\"attachment\"\n [sqTooltipTemplate]=\"tooltipTpl\"\n [hoverableTooltip]=\"true\"\n >{{ref.refId}}\n </a>\n\n <ng-template #staticRefTpl>\n <span class=\"reference\">{{ref.refId}}</span>\n </ng-template>\n\n </ng-template>\n\n <ng-template remarkTemplate=\"streaming-placeholder\">\n <span class=\"placeholder-glow\" *ngIf=\"streaming\">\n <span class=\"placeholder ms-1\"></span>\n </span>\n </ng-template>\n\n <ng-template remarkTemplate=\"code\" let-node>\n <div class=\"card mb-2\">\n <div class=\"card-header d-flex justify-content-between align-items-center\">\n <span>{{node.lang}}</span>\n <button class=\"btn btn-light btn-sm\" (click)=\"copyToClipboard(node.value)\"><i class=\"far fa-fw fa-clipboard\"></i> Copy code</button>\n </div>\n <pre class=\"language-{{node.lang}} my-0 rounded-0 rounded-bottom\"><code class=\"language-{{node.lang}}\">{{node.value}}</code></pre>\n </div>\n </ng-template>\n\n <ng-template remarkTemplate=\"link\" let-node>\n <a [href]=\"node.url\" target=\"_blank\" rel=\"noopener noreferrer\">{{getLinkText(node)}}</a>\n </ng-template>\n\n </remark>\n\n <p *ngIf=\"message?.role === 'user'\">{{message.content}}</p>\n\n <!-- List of reference, if any -->\n <div *ngIf=\"references?.length\" class=\"references\">\n <span class=\"references-title\">References</span> <i class=\"fas references-collapse\" [class.fa-chevron-down]=\"showReferences\" [class.fa-chevron-right]=\"!showReferences\" (click)=\"showReferences=!showReferences\"></i>\n <sq-collapse [collapsed]=\"!showReferences\">\n <ng-template>\n <ul>\n <ng-container *ngFor=\"let reference of references\">\n <li *ngIf=\"referenceMap.get(reference) as attachment\" class=\"text-truncate\">\n <sq-chat-reference\n [class.expanded]=\"attachment.$expanded\"\n [attachment]=\"attachment\"\n [reference]=\"reference\"\n (openPreview)=\"openAttachmentPreview($event)\"\n (openDocument)=\"openOriginalAttachment($event)\">\n </sq-chat-reference>\n </li>\n </ng-container>\n </ul>\n </ng-template>\n </sq-collapse>\n </div>\n\n </div>\n\n <!-- Edit / Regenerate floating actions -->\n <div class=\"sq-chat-message-actions\" *ngIf=\"message\">\n <button class=\"btn btn-sm\" *ngIf=\"canEdit\" sqTooltip=\"Edit message\"\n (click)=\"edit.emit(message)\">\n<i class=\"fas fa-edit\"></i>\n</button>\n <button class=\"btn btn-sm\" *ngIf=\"canCopy\" sqTooltip=\"Copy to clipboard\" (click)=\"copyToClipboard(message)\">\n <i class=\"far fa-clipboard\"></i>\n </button>\n <button class=\"btn btn-sm\" *ngIf=\"canRegenerate\" sqTooltip=\"Regenerate message\" (click)=\"regenerate.emit(message)\">\n <i class=\"fas fa-sync-alt\"></i>\n </button>\n<!-- <button class=\"btn btn-sm\" [class.bounce]=\"liked\" *ngIf=\"message.role === 'assistant' && !streaming\" sqTooltip=\"Like the answer\"\n (click)=\"like.emit(message); liked = true;\">\n <i class=\"far fa-thumbs-up\"></i>\n </button> -->\n<!-- <button class=\"btn btn-sm bounce\" [class.bounce]=\"disliked\" *ngIf=\"message.role === 'assistant' && !streaming\" sqTooltip=\"Report an issue\"\n (click)=\"dislike.emit(message); disliked = true;\">\n <i class=\"far fa-thumbs-down\"></i>\n </button> -->\n </div>\n\n <!-- List of suggested actions, if any -->\n <div *ngIf=\"suggestedActions\" class=\"mt-2 message-suggestion\">\n <div class=\"suggested-action\" *ngFor=\"let suggestedAction of suggestedActions\" (click)=\"suggestAction.emit(suggestedAction)\">\n <div class=\"message-content\">\n <p><i class=\"fas fa-clipboard-question\"></i> {{suggestedAction.content}}</p>\n </div>\n </div>\n </div>\n\n <ng-template #tooltipTpl let-ref>\n <sq-chat-reference\n class=\"expanded\"\n [attachment]=\"ref\"\n [reference]=\"ref.contextId\"\n [partId]=\"ref.$partId\"\n (openPreview)=\"openAttachmentPreview($event, ref.$partId)\"\n (openDocument)=\"openOriginalAttachment($event, ref.$partId)\">\n </sq-chat-reference>\n </ng-template>\n\n</div>\n", styles: [".ast-primary{color:var(--ast-primary-color, #005DA7);background-color:var(--ast-primary-bg, #f2f8fe)}.ast-primary-hover{background-color:var(--ast-primary-bg, #f2f8fe)}.ast-primary-hover:hover{color:var(--ast-primary-color, #005DA7)}.ast-secondary{color:var(--ast-secondary-color, #FF732E);background-color:var(--ast-secondary-bg, #FFF8F1)}.ast-btn{border:0;text-align:left;padding-top:.5rem;padding-bottom:.5rem;display:flex;align-items:center}.dark{--ast-primary-bg: #0d0701;--ast-primary-color: #008cd1;--ast-secondary-bg: #00070e;--ast-secondary-color: #ffa258;--ast-input-bg: #070707;--ast-input-color: rgba(222, 218, 218, .75);--ast-muted-color: rgba(222, 218, 218, .75);--ast-saved-chat-hover-background: #262421;--ast-message-table-border-color: #333333;--ast-message-table-tr-bg: #070707;--ast-message-table-tr-border-color: #222222;--ast-reference-icon-color: white;--ast-reference-icon-active-color: black;--ast-reference-passages-color: white;--ast-reference-expanded-hover-bg: #262421;--ast-message-reference-color: black}:host{display:flex}:host:not(:hover):not(.last-message) .sq-chat-message-actions{visibility:hidden}.message-content{padding:var(--ast-message-padding, var(--ast-size-3, .75rem));border-radius:var(--ast-message-border-radius, var(--ast-size-4, 1rem));display:inline-block;max-width:100%;overflow-wrap:break-word;word-wrap:break-word;word-break:break-word}.message-content .references{margin-top:var(--ast-size-5, 1.25rem)}.message-content .references ul{border-left:.2rem solid var(--ast-secondary-color, #FF732E);padding-left:var(--ast-size-5, 1.25rem);margin-top:var(--ast-size-2, .5rem)}.message-content .references .references-title{font-weight:var(--font-weight-bold, 500)}.message-content .references .references-collapse{cursor:pointer;margin-left:var(--ast-size-2, .5rem)}.message-content ::ng-deep p:last-child{margin-bottom:0}.message-content ::ng-deep .placeholder-glow .placeholder{animation-duration:.4s;width:12px;height:var(--ast-size-4, 1rem);vertical-align:text-bottom}.message-content ::ng-deep img{max-width:100%}.message-content ::ng-deep table{display:block;border:1px solid;border-color:var(--ast-message-table-border-color, #ccc);border-collapse:collapse;margin:0;padding:0;min-width:100%;overflow-x:auto;table-layout:fixed}.message-content ::ng-deep table tr{background-color:var(--ast-message-table-tr-bg, #f8f8f8);border:1px solid;border-color:var(--ast-message-table-tr-border-color, #ddd);padding:.35em}.message-content ::ng-deep table th,.message-content ::ng-deep table td{padding:.625em;text-align:center}.message-content ::ng-deep table th{font-size:.85em;letter-spacing:.1em;text-transform:uppercase}.message-content ::ng-deep .reference{color:var(--ast-message-reference-color, black)!important}.message-content ::ng-deep ul,.message-content ::ng-deep ol{display:flex;flex-direction:column;gap:.5rem;padding-right:2rem;margin-left:0;margin-right:0;padding-left:40px;unicode-bidi:-webkit-isolate;unicode-bidi:isolate;list-style:disc}.message-content ::ng-deep p:not(:first-child){margin-top:.5rem}.message-assistant .message-content{background:var(--ast-secondary-bg, #FFF8F1)}.message-user .message-content{background:var(--ast-primary-bg, #f2f8fe);font-weight:var(--ast-user-font-weight, var(--font-weight-bold, 500))}.message-user .message-content p{white-space:pre-line}.message-suggestion .message-content{background:var(--ast-input-bg, #F8F8F8);font-weight:var(--ast-user-font-weight, var(--font-weight-bold, 500));transition:background-color .5s ease,color .5s ease}.message-suggestion .message-content:hover{background:var(--ast-primary-bg, #f2f8fe);color:var(--ast-primary-color, #005DA7)}.message-suggestion .message-content p{white-space:pre-line}.message-suggestion .suggested-action{cursor:pointer}.message-suggestion .suggested-action+.suggested-action{margin-top:var(--ast-size-2, .5rem)}.sq-chat-message-actions{position:absolute;bottom:calc(0rem - var(--ast-size-3, .75rem));display:flex;z-index:999}.sq-chat-message-actions button{font-size:.75rem}.sq-chat-message-actions button:hover{color:var(--ast-primary-color, #005DA7)}.message-icon{margin-top:var(--ast-size-3, .75rem);margin-right:var(--ast-size-4, 1rem)}.bounce{animation:bounce 2s ease}@keyframes bounce{10%{transform:translateY(0)}20%{transform:translateY(-15%)}30%{transform:translateY(0)}35%{transform:translateY(-7%)}37%{transform:translateY(0)}39%{transform:translateY(-3%)}40%{transform:translateY(0)}}\n"] }]
|
|
1415
1456
|
}], ctorParameters: function () { return [{ type: i1$1.SearchService }, { type: i2$1.UIService }, { type: i3.PrincipalWebService }, { type: i0.ChangeDetectorRef }, { type: i0.ElementRef }]; }, propDecorators: { message: [{
|
|
1416
1457
|
type: Input
|
|
1417
1458
|
}], conversation: [{
|
|
@@ -1438,8 +1479,14 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.0.6", ngImpor
|
|
|
1438
1479
|
type: Output
|
|
1439
1480
|
}], edit: [{
|
|
1440
1481
|
type: Output
|
|
1482
|
+
}], copy: [{
|
|
1483
|
+
type: Output
|
|
1441
1484
|
}], regenerate: [{
|
|
1442
1485
|
type: Output
|
|
1486
|
+
}], like: [{
|
|
1487
|
+
type: Output
|
|
1488
|
+
}], dislike: [{
|
|
1489
|
+
type: Output
|
|
1443
1490
|
}] } });
|
|
1444
1491
|
|
|
1445
1492
|
class RestChatService extends ChatService {
|
|
@@ -1564,9 +1611,25 @@ class RestChatService extends ChatService {
|
|
|
1564
1611
|
const action = !this.savedChatId ? this.addSavedChat(this.chatHistory) : this.updateSavedChat(this.savedChatId, undefined, this.chatHistory);
|
|
1565
1612
|
action.pipe(take(1)).subscribe();
|
|
1566
1613
|
}
|
|
1614
|
+
// Generate audit event
|
|
1615
|
+
const details = {
|
|
1616
|
+
'duration': res.executionTime,
|
|
1617
|
+
'text': response.content,
|
|
1618
|
+
'role': response.role,
|
|
1619
|
+
'rank': this.chatHistory.length - 1,
|
|
1620
|
+
'generation-tokencount': response.additionalProperties.usageMetrics.completionTokenCount,
|
|
1621
|
+
'prompt-tokencount': response.additionalProperties.usageMetrics.promptTokenCount,
|
|
1622
|
+
'attachments': response.additionalProperties.$attachment?.map(({ recordId, contextId, parts, type }) => ({
|
|
1623
|
+
recordId,
|
|
1624
|
+
contextId,
|
|
1625
|
+
parts: parts.map(({ partId, text }) => ({ partId, text })),
|
|
1626
|
+
type
|
|
1627
|
+
}))
|
|
1628
|
+
};
|
|
1629
|
+
this.generateAuditEvent('message', details);
|
|
1567
1630
|
// Return the result
|
|
1568
1631
|
return { history: [...messages, response], executionTime: res.executionTime };
|
|
1569
|
-
}),
|
|
1632
|
+
}), finalize(() => this.streaming$.next(false)));
|
|
1570
1633
|
}
|
|
1571
1634
|
listSavedChat() {
|
|
1572
1635
|
const data = {
|
|
@@ -1583,12 +1646,14 @@ class RestChatService extends ChatService {
|
|
|
1583
1646
|
const data = {
|
|
1584
1647
|
action: "SavedChatAdd",
|
|
1585
1648
|
instanceId: this.chatInstanceId,
|
|
1586
|
-
savedChatId:
|
|
1649
|
+
savedChatId: this.chatId,
|
|
1587
1650
|
history: messages,
|
|
1588
1651
|
debug: this.chatConfig$.value.defaultValues.debug
|
|
1589
1652
|
};
|
|
1590
|
-
return this.jsonMethodWebService.post(this.REQUEST_URL, data).pipe(map(res => res.savedChat), tap((savedChat) =>
|
|
1591
|
-
|
|
1653
|
+
return this.jsonMethodWebService.post(this.REQUEST_URL, data).pipe(map(res => res.savedChat), tap((savedChat) => {
|
|
1654
|
+
this.setSavedChatId(savedChat.id); // Persist the savedChatId
|
|
1655
|
+
this.generateAuditEvent('saved-chat.add', {}, savedChat.id); // Generate audit event
|
|
1656
|
+
}), catchError((error) => {
|
|
1592
1657
|
console.error('Error occurred while calling the SavedChatAdd API:', error.error.errorMessage);
|
|
1593
1658
|
this.notificationsService.error('Error occurred while calling the SavedChatAdd API:', error.error.errorMessage);
|
|
1594
1659
|
return throwError(() => error);
|
|
@@ -1699,6 +1764,7 @@ class ChatComponent extends AbstractFacet {
|
|
|
1699
1764
|
this.searchService = inject(SearchService);
|
|
1700
1765
|
this.principalService = inject(PrincipalWebService);
|
|
1701
1766
|
this.cdr = inject(ChangeDetectorRef);
|
|
1767
|
+
this.appService = inject(AppService);
|
|
1702
1768
|
/** Define the query to use to fetch answers */
|
|
1703
1769
|
this.query = this.searchService.query;
|
|
1704
1770
|
/** Define the protocol to be used for this chat instance*/
|
|
@@ -1730,10 +1796,21 @@ class ChatComponent extends AbstractFacet {
|
|
|
1730
1796
|
this.isAtBottom = true;
|
|
1731
1797
|
this.initializationError = false;
|
|
1732
1798
|
this.enabledUserInput = false;
|
|
1799
|
+
// Issue reporting
|
|
1800
|
+
this.issueTypes = [
|
|
1801
|
+
'User Interface bug',
|
|
1802
|
+
'Incorrect or misleading response',
|
|
1803
|
+
'Incomplete response',
|
|
1804
|
+
'Technical issue',
|
|
1805
|
+
'Privacy/data security issue',
|
|
1806
|
+
'Other'
|
|
1807
|
+
];
|
|
1808
|
+
this.issueType = '';
|
|
1809
|
+
this.showReportIssue = false;
|
|
1733
1810
|
this._actions.push(new Action({
|
|
1734
1811
|
icon: 'fas fa-sync',
|
|
1735
1812
|
title: 'Reset chat',
|
|
1736
|
-
action: () => this.
|
|
1813
|
+
action: () => this.newChat()
|
|
1737
1814
|
}));
|
|
1738
1815
|
}
|
|
1739
1816
|
ngOnInit() {
|
|
@@ -1768,7 +1845,7 @@ class ChatComponent extends AbstractFacet {
|
|
|
1768
1845
|
}
|
|
1769
1846
|
/**
|
|
1770
1847
|
* Instantiate the chat service based on the provided @input protocol
|
|
1771
|
-
*
|
|
1848
|
+
* This chat service instance will then be stored in the instanceManagerService with provided @input instanceId as a key
|
|
1772
1849
|
*/
|
|
1773
1850
|
instantiateChatService() {
|
|
1774
1851
|
switch (this.protocol) {
|
|
@@ -1802,7 +1879,15 @@ class ChatComponent extends AbstractFacet {
|
|
|
1802
1879
|
}
|
|
1803
1880
|
this.openChat(this.chat.messages);
|
|
1804
1881
|
};
|
|
1805
|
-
this.
|
|
1882
|
+
this.chatService.generateChatId();
|
|
1883
|
+
if (this.chat) {
|
|
1884
|
+
this.chatService.generateAuditEvent('new-chat', { 'chat-init': JSON.stringify(this.chat) });
|
|
1885
|
+
openChat();
|
|
1886
|
+
}
|
|
1887
|
+
else {
|
|
1888
|
+
this.chatService.generateAuditEvent('new-chat', {});
|
|
1889
|
+
this.loadDefaultChat();
|
|
1890
|
+
}
|
|
1806
1891
|
}
|
|
1807
1892
|
/**
|
|
1808
1893
|
* If the chat is initialized, the initialization event is "Query", the query changes and the queryChangeShouldTriggerReload function is provided,
|
|
@@ -1811,26 +1896,18 @@ class ChatComponent extends AbstractFacet {
|
|
|
1811
1896
|
*/
|
|
1812
1897
|
if (this.firstChangesHandled && changes?.query && this.config.modeSettings.initialization.event === 'Query') {
|
|
1813
1898
|
if (this.queryChangeShouldTriggerReload ? this.queryChangeShouldTriggerReload(this._previousQuery, this.query) : true) {
|
|
1814
|
-
this.
|
|
1899
|
+
this._triggerReloadAfterQueryChange();
|
|
1815
1900
|
}
|
|
1816
1901
|
this._previousQuery = JSON.parse(JSON.stringify(this.query)); // Update the previous query
|
|
1817
1902
|
}
|
|
1818
1903
|
}
|
|
1819
|
-
|
|
1904
|
+
_triggerReloadAfterQueryChange() {
|
|
1820
1905
|
const systemMsg = { role: 'system', content: this.config.defaultValues.systemPrompt, additionalProperties: { display: false } };
|
|
1821
1906
|
const userMsg = { role: 'user', content: ChatService.formatPrompt(this.config.defaultValues.userPrompt, { principal: this.principalService.principal }), additionalProperties: { display: this.config.modeSettings.displayUserPrompt } };
|
|
1822
|
-
|
|
1823
|
-
|
|
1824
|
-
|
|
1825
|
-
|
|
1826
|
-
if (!!this.query.text) {
|
|
1827
|
-
const userQueryMsg = { role: 'user', content: this.query.text, additionalProperties: { display: this.config.modeSettings.initialization.displayUserQuery, query: this.query, forcedWorkflow: this.config.modeSettings.initialization.forcedWorkflow, isUserInput: true } };
|
|
1828
|
-
this.openChat(this.config.modeSettings.sendUserPrompt ? [systemMsg, userMsg, userQueryMsg] : [systemMsg, userQueryMsg]);
|
|
1829
|
-
}
|
|
1830
|
-
else {
|
|
1831
|
-
const warningMsg = { role: 'assistant', content: "You must perform a full text search to retrieve some results", additionalProperties: { display: true } };
|
|
1832
|
-
this.openChat([warningMsg]);
|
|
1833
|
-
}
|
|
1907
|
+
this.chatService.setSavedChatId(undefined); // Reset the savedChatId
|
|
1908
|
+
this.chatService.generateChatId(); // Generate a new chatId
|
|
1909
|
+
this.chatService.generateAuditEvent('new-chat', {}); // Generate a new chat audit event
|
|
1910
|
+
this._handleQueryMode(systemMsg, userMsg);
|
|
1834
1911
|
}
|
|
1835
1912
|
addScrollListener() {
|
|
1836
1913
|
this.sub.add(merge(this.loading$, this.messages$, this.chatService.streaming$, fromEvent(this.messageList.nativeElement, 'scroll')).subscribe(() => {
|
|
@@ -1863,6 +1940,7 @@ class ChatComponent extends AbstractFacet {
|
|
|
1863
1940
|
const messages = [...conversation, userMsg];
|
|
1864
1941
|
this.messages$.next(messages);
|
|
1865
1942
|
this.fetch(messages);
|
|
1943
|
+
this.chatService.generateAuditEvent('message', { ...this._defineMessageAuditDetails(userMsg, messages.length - 1), 'query': JSON.stringify(this.query), 'is-user-input': true, 'enabled-functions': this.config.defaultValues.functions?.filter(func => func.enabled).map(func => func.name), });
|
|
1866
1944
|
}
|
|
1867
1945
|
/**
|
|
1868
1946
|
* Given a list of messages, the chat endpoint is invoked for a continuation and updates the list of messages accordingly.
|
|
@@ -1920,9 +1998,11 @@ class ChatComponent extends AbstractFacet {
|
|
|
1920
1998
|
*/
|
|
1921
1999
|
newChat() {
|
|
1922
2000
|
this.chatService.setSavedChatId(undefined); // Reset the savedChatId
|
|
2001
|
+
this.chatService.generateChatId(); // Generate a new chatId
|
|
1923
2002
|
if (this.config.savedChatSettings.enabled) {
|
|
1924
2003
|
this.chatService.listSavedChat(); // Refresh the list of saved chats
|
|
1925
2004
|
}
|
|
2005
|
+
this.chatService.generateAuditEvent('new-chat', {}); // Generate a new chat audit event
|
|
1926
2006
|
this.loadDefaultChat(); // Start a new chat
|
|
1927
2007
|
}
|
|
1928
2008
|
/**
|
|
@@ -1930,38 +2010,66 @@ class ChatComponent extends AbstractFacet {
|
|
|
1930
2010
|
* If the chat is meant to be initialized with event === "Query", the corresponding user query message will be added to the chat history
|
|
1931
2011
|
*/
|
|
1932
2012
|
loadDefaultChat() {
|
|
2013
|
+
// Define the default system prompt and user prompt messages
|
|
1933
2014
|
const systemMsg = { role: 'system', content: this.config.defaultValues.systemPrompt, additionalProperties: { display: false } };
|
|
1934
2015
|
const userMsg = { role: 'user', content: ChatService.formatPrompt(this.config.defaultValues.userPrompt, { principal: this.principalService.principal }), additionalProperties: { display: this.config.modeSettings.displayUserPrompt } };
|
|
1935
2016
|
if (this.config.modeSettings.initialization.event === 'Query') {
|
|
1936
|
-
|
|
1937
|
-
|
|
1938
|
-
|
|
1939
|
-
|
|
1940
|
-
|
|
2017
|
+
this._handleQueryMode(systemMsg, userMsg);
|
|
2018
|
+
}
|
|
2019
|
+
else { // If the chat is meant to be initialized with event === "Prompt"
|
|
2020
|
+
this.openChat([systemMsg, userMsg]);
|
|
2021
|
+
this.chatService.generateAuditEvent('message', this._defineMessageAuditDetails(systemMsg, 0));
|
|
2022
|
+
this.chatService.generateAuditEvent('message', this._defineMessageAuditDetails(userMsg, 1));
|
|
2023
|
+
}
|
|
2024
|
+
}
|
|
2025
|
+
/**
|
|
2026
|
+
* If the provided query text is not empty, then add the user query message to the chat history and invoke the assistant
|
|
2027
|
+
* Otherwise, just start a new chat with a warning message inviting the user to perform a full text search to retrieve some results
|
|
2028
|
+
*/
|
|
2029
|
+
_handleQueryMode(systemMsg, userMsg) {
|
|
2030
|
+
if (!!this.query.text) {
|
|
2031
|
+
const userQueryMsg = { role: 'user', content: this.query.text, additionalProperties: { display: this.config.modeSettings.initialization.displayUserQuery, query: this.query, forcedWorkflow: this.config.modeSettings.initialization.forcedWorkflow, isUserInput: true } };
|
|
2032
|
+
if (this.config.modeSettings.sendUserPrompt) {
|
|
2033
|
+
this.openChat([systemMsg, userMsg, userQueryMsg]);
|
|
2034
|
+
this.chatService.generateAuditEvent('message', this._defineMessageAuditDetails(systemMsg, 0));
|
|
2035
|
+
this.chatService.generateAuditEvent('message', this._defineMessageAuditDetails(userMsg, 1));
|
|
2036
|
+
this.chatService.generateAuditEvent('message', { ...this._defineMessageAuditDetails(userQueryMsg, 2), 'query': JSON.stringify(this.query), 'is-user-input': true, 'forced-workflow': this.config.modeSettings.initialization.forcedWorkflow, 'enabled-functions': this.config.defaultValues.functions?.filter(func => func.enabled).map(func => func.name), });
|
|
1941
2037
|
}
|
|
1942
2038
|
else {
|
|
1943
|
-
|
|
1944
|
-
this.
|
|
2039
|
+
this.openChat([systemMsg, userQueryMsg]);
|
|
2040
|
+
this.chatService.generateAuditEvent('message', this._defineMessageAuditDetails(systemMsg, 0));
|
|
2041
|
+
this.chatService.generateAuditEvent('message', { ...this._defineMessageAuditDetails(userQueryMsg, 1), 'query': JSON.stringify(this.query), 'is-user-input': true, 'forced-workflow': this.config.modeSettings.initialization.forcedWorkflow, 'enabled-functions': this.config.defaultValues.functions?.filter(func => func.enabled).map(func => func.name), });
|
|
1945
2042
|
}
|
|
1946
2043
|
}
|
|
1947
2044
|
else {
|
|
1948
|
-
|
|
2045
|
+
const warningMsg = { role: 'assistant', content: "You must perform a full text search to retrieve some results", additionalProperties: { display: true } };
|
|
2046
|
+
this.openChat([warningMsg]);
|
|
2047
|
+
this.chatService.generateAuditEvent('message', this._defineMessageAuditDetails(warningMsg, 0));
|
|
1949
2048
|
}
|
|
1950
2049
|
}
|
|
2050
|
+
_defineMessageAuditDetails(message, rank) {
|
|
2051
|
+
return {
|
|
2052
|
+
'duration': 0,
|
|
2053
|
+
'text': message.content,
|
|
2054
|
+
'role': message.role,
|
|
2055
|
+
'rank': rank
|
|
2056
|
+
};
|
|
2057
|
+
}
|
|
1951
2058
|
/**
|
|
1952
2059
|
* Start/open a new chat with the provided messages and chatId
|
|
1953
2060
|
* If the last message is from the user, a request to the assistant is made to get an answer
|
|
1954
2061
|
* If the last message is from the assistant, the conversation is loaded right away
|
|
1955
2062
|
* @param messages The list of messages of the chat
|
|
1956
|
-
* @param
|
|
2063
|
+
* @param savedChatId The id of the saved chat. If provided (ie. an existing discussion in the saved chat index), update the savedChatId in the chat service for the upcoming saved chat operations
|
|
1957
2064
|
*/
|
|
1958
|
-
openChat(messages,
|
|
2065
|
+
openChat(messages, savedChatId) {
|
|
1959
2066
|
if (!messages || !Array.isArray(messages)) {
|
|
1960
2067
|
console.error('Error occurs while trying to load the chat discussion. Invalid messages received :', messages);
|
|
1961
2068
|
return;
|
|
1962
2069
|
}
|
|
1963
|
-
if (
|
|
1964
|
-
this.chatService.setSavedChatId(
|
|
2070
|
+
if (savedChatId) {
|
|
2071
|
+
this.chatService.setSavedChatId(savedChatId);
|
|
2072
|
+
this.chatService.generateChatId(savedChatId);
|
|
1965
2073
|
}
|
|
1966
2074
|
this.resetChat();
|
|
1967
2075
|
this.messages$.next(messages);
|
|
@@ -2010,6 +2118,14 @@ class ChatComponent extends AbstractFacet {
|
|
|
2010
2118
|
editMessage(index) {
|
|
2011
2119
|
this.messageToEdit = index;
|
|
2012
2120
|
this.question = this.chatService.chatHistory[index].content;
|
|
2121
|
+
this.chatService.generateAuditEvent('edit.click', { 'rank': index });
|
|
2122
|
+
}
|
|
2123
|
+
/**
|
|
2124
|
+
* Copy a previous assistant message of the chat history to the clipboard.
|
|
2125
|
+
* @param index The index of the assistant's message to edit
|
|
2126
|
+
*/
|
|
2127
|
+
copyMessage(index) {
|
|
2128
|
+
this.chatService.generateAuditEvent('copy.click', { 'rank': index });
|
|
2013
2129
|
}
|
|
2014
2130
|
/**
|
|
2015
2131
|
* Starting from the provided index, remove all subsequent messages from the chat history and the UI accordingly.
|
|
@@ -2023,6 +2139,7 @@ class ChatComponent extends AbstractFacet {
|
|
|
2023
2139
|
this.messages$.next(slicedMessages);
|
|
2024
2140
|
// Fetch the answer
|
|
2025
2141
|
this.fetch(slicedMessages);
|
|
2142
|
+
this.chatService.generateAuditEvent('regenerate.click', { 'rank': index });
|
|
2026
2143
|
}
|
|
2027
2144
|
onKeyUp(event) {
|
|
2028
2145
|
switch (event.key) {
|
|
@@ -2048,18 +2165,84 @@ class ChatComponent extends AbstractFacet {
|
|
|
2048
2165
|
el.style.height = `${el.scrollHeight}px`;
|
|
2049
2166
|
el.style.overflowY = el.scrollHeight >= maxHeight ? 'scroll' : 'hidden';
|
|
2050
2167
|
}
|
|
2168
|
+
/**
|
|
2169
|
+
* Send a "like" event on clicking on the thumb-up icon of an assistant's message
|
|
2170
|
+
* @param rank The rank of the message to like
|
|
2171
|
+
*/
|
|
2172
|
+
onLike(rank) {
|
|
2173
|
+
this.chatService.generateAuditEvent('thumb-up.click', { rank });
|
|
2174
|
+
}
|
|
2175
|
+
/**
|
|
2176
|
+
* Send a "dislike" event on clicking on the thumb-down icon of an assistant's message.
|
|
2177
|
+
* It also opens the issue reporting dialog.
|
|
2178
|
+
* @param message The assistant message to dislike
|
|
2179
|
+
* @param rank The rank of the message to dislike
|
|
2180
|
+
*/
|
|
2181
|
+
onDislike(message, rank) {
|
|
2182
|
+
this.chatService.generateAuditEvent('thumb-down.click', { rank });
|
|
2183
|
+
this.messageRelatedIssue = message;
|
|
2184
|
+
this.issueType = '';
|
|
2185
|
+
this.issueComment = undefined;
|
|
2186
|
+
this.issueRank = rank;
|
|
2187
|
+
this.showReportIssue = true;
|
|
2188
|
+
}
|
|
2189
|
+
/**
|
|
2190
|
+
* Report an issue related to the assistant's message.
|
|
2191
|
+
*/
|
|
2192
|
+
sendIssue() {
|
|
2193
|
+
const details = {
|
|
2194
|
+
'report-type': this.issueType,
|
|
2195
|
+
'comment': this.issueComment,
|
|
2196
|
+
'text': this.messageRelatedIssue.content,
|
|
2197
|
+
'rank': this.issueRank,
|
|
2198
|
+
};
|
|
2199
|
+
this.chatService.generateAuditEvent('negative-report.send', details);
|
|
2200
|
+
this.showReportIssue = false;
|
|
2201
|
+
}
|
|
2202
|
+
/**
|
|
2203
|
+
* Close the issue reporting dialog.
|
|
2204
|
+
*/
|
|
2205
|
+
ignoreIssue() {
|
|
2206
|
+
this.showReportIssue = false;
|
|
2207
|
+
}
|
|
2208
|
+
openAttachmentPreview(data) {
|
|
2209
|
+
this.openPreview.emit(data.reference);
|
|
2210
|
+
const details = {
|
|
2211
|
+
'doc-id': data.reference.recordId,
|
|
2212
|
+
'title': data.reference.record.title,
|
|
2213
|
+
'source': data.reference.record.treepath,
|
|
2214
|
+
'collection': data.reference.record.collection,
|
|
2215
|
+
'index': data.reference.record.databasealias,
|
|
2216
|
+
};
|
|
2217
|
+
if (!!data.partId)
|
|
2218
|
+
details['part-id'] = data.partId;
|
|
2219
|
+
this.chatService.generateAuditEvent('attachment.preview.click', details);
|
|
2220
|
+
}
|
|
2221
|
+
openOriginalAttachment(data) {
|
|
2222
|
+
this.openDocument.emit(data.reference.record);
|
|
2223
|
+
const details = {
|
|
2224
|
+
'doc-id': data.reference.recordId,
|
|
2225
|
+
'title': data.reference.record.title,
|
|
2226
|
+
'source': data.reference.record.treepath,
|
|
2227
|
+
'collection': data.reference.record.collection,
|
|
2228
|
+
'index': data.reference.record.databasealias,
|
|
2229
|
+
};
|
|
2230
|
+
if (!!data.partId)
|
|
2231
|
+
details['part-id'] = data.partId;
|
|
2232
|
+
this.chatService.generateAuditEvent('attachment.link.click', details);
|
|
2233
|
+
}
|
|
2051
2234
|
}
|
|
2052
2235
|
ChatComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.0.6", ngImport: i0, type: ChatComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
2053
2236
|
ChatComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "14.0.6", type: ChatComponent, isStandalone: true, selector: "sq-chat-v3", inputs: { instanceId: "instanceId", query: "query", queryChangeShouldTriggerReload: "queryChangeShouldTriggerReload", protocol: "protocol", messageHandlers: "messageHandlers", automaticScrollToLastResponse: "automaticScrollToLastResponse", chat: "chat", assistantMessageIcon: "assistantMessageIcon", userMessageIcon: "userMessageIcon" }, outputs: { data: "data", openDocument: "openDocument", openPreview: "openPreview", suggestAction: "suggestAction", loading$: "loading", _config: "config" }, providers: [
|
|
2054
2237
|
RestChatService,
|
|
2055
2238
|
WebSocketChatService
|
|
2056
|
-
], queries: [{ propertyName: "loadingTpl", first: true, predicate: ["loadingTpl"], descendants: true }], viewQueries: [{ propertyName: "messageList", first: true, predicate: ["messageList"], descendants: true }, { propertyName: "questionInput", first: true, predicate: ["questionInput"], descendants: true }], usesInheritance: true, usesOnChanges: true, ngImport: i0, template: "\n<ng-container *ngIf=\"!initializationError\">\n <div *ngIf=\"messages$ | async as messages; else loadingTpl || loadingTplDefault\" class=\"h-100 d-flex flex-column\">\n <sq-token-progress-bar\n *ngIf=\"config?.globalSettings?.displayUserQuotaConsumption || config?.globalSettings?.displayChatTokensConsumption\"\n [instanceId]=\"instanceId\"\n class=\"ms-1\">\n </sq-token-progress-bar>\n\n <ul class=\"list-group list-group-flush overflow-auto pe-2 pb-2\" #messageList>\n <ng-container *ngFor=\"let message of messages; let index = index; let last = last\">\n <!-- Regular messages -->\n <li class=\"list-group-item\" *ngIf=\"message.additionalProperties.display\"\n [class.opacity-50]=\"messageToEdit && messageToEdit < index + 1\">\n <sq-chat-message\n [class.sq-user-message]=\"message.role === 'user'\"\n [message]=\"message\"\n [conversation]=\"messages\"\n [suggestedActions]=\"last ? message.additionalProperties.$suggestedAction : undefined\"\n [assistantMessageIcon]=\"assistantMessageIcon\"\n [userMessageIcon]=\"userMessageIcon\"\n [streaming]=\"last && (chatService.streaming$ | async)\"\n [canEdit]=\"(loading$ | async) === false && (chatService.streaming$ | async) === false && messageToEdit === undefined && message.role === 'user'\"\n [canRegenerate]=\"(loading$ | async) === false && (chatService.streaming$ | async) === false && messageToEdit === undefined && message.role === 'assistant' && last\"\n [canCopy]=\"!(last && (chatService.streaming$ | async)) && messageToEdit === undefined && message.role === 'assistant'\"\n (edit)=\"editMessage(index)\"\n (regenerate)=\"regenerateMessage(index)\"\n (openDocument)=\"openDocument.emit($event)\"\n (openPreview)=\"openPreview.emit($event)\"\n (suggestAction)=\"suggestAction.emit($event)\">\n </sq-chat-message>\n </li>\n </ng-container>\n\n <li class=\"list-group-item\" *ngIf=\"(loading$ | async) === true\">\n <ng-container *ngTemplateOutlet=\"loadingTpl || loadingTplDefault\"></ng-container>\n </li>\n </ul>\n\n <div class=\"user-input mt-auto\" *ngIf=\"enabledUserInput\">\n <div class=\"py-2\">\n <ng-container *ngTemplateOutlet=\"inputTpl\"></ng-container>\n <div class=\"text-end small text-muted px-3\" *ngIf=\"!!config?.globalSettings?.disclaimer\">\n {{ config?.globalSettings?.disclaimer }}\n </div>\n </div>\n </div>\n </div>\n</ng-container>\n\n<!-- NG TEMPLATES-->\n\n<ng-template #loadingTplDefault>\n <div class=\"spinner-grow text-primary d-block mx-auto my-5\" role=\"status\">\n <span class=\"visually-hidden\">Loading...</span>\n </div>\n</ng-template>\n\n<ng-template #inputTpl>\n <div class=\"px-3 py-1\">\n <div class=\"ast-input-container\">\n <button disabled class=\"btn btn-light\">\n <i class=\"fas fa-search\"></i>\n </button>\n <textarea #questionInput rows=\"1\"\n type=\"text\" class=\"form-control\"\n placeholder=\"Ask something\" autofocus\n [(ngModel)]=\"question\"\n (keyup)=\"onKeyUp($event)\"\n (keydown)=\"calculateHeight()\"\n [disabled]=\"(loading$ | async) || (chatService.streaming$ | async)\">\n </textarea>\n <button\n *ngIf=\"!(chatService.streaming$ | async) && !(loading$ | async)\"\n type=\"button\"\n class=\"btn btn-light ms-2\"\n title=\"Send message\"\n (click)=\"submitQuestion()\">\n <i class=\"fas fa-paper-plane\"></i>\n </button>\n <!--<button\n *ngIf=\"(chatService.streaming$ | async)\"\n type=\"button\"\n class=\"btn btn-light ms-2\"\n title=\"Stop generating\"\n (click)=\"terminateFetch()\">\n <i class=\"fas fa-stop\"></i>\n </button>-->\n <button\n *ngIf=\"messageToEdit\"\n type=\"button\"\n class=\"btn btn-light ms-2\"\n title=\"Cancel edition\"\n (click)=\"messageToEdit = undefined; question = ''\">\n <i class=\"fas fa-undo-alt\"></i>\n </button>\n <div class=\"sq-floating-scroll\" *ngIf=\"!isAtBottom\">\n <button class=\"btn shadow\" (click)=\"scrollDown()\">\n <i class=\"fas fa-angle-double-down\"></i>\n </button>\n </div>\n </div>\n </div>\n</ng-template>\n", styles: [".ast-primary{color:var(--ast-primary-color, #005DA7);background-color:var(--ast-primary-bg, #f2f8fe)}.ast-primary-hover{background-color:var(--ast-primary-bg, #f2f8fe)}.ast-primary-hover:hover{color:var(--ast-primary-color, #005DA7)}.ast-secondary{color:var(--ast-secondary-color, #FF732E);background-color:var(--ast-secondary-bg, #FFF8F1)}.ast-btn{border:0;text-align:left;padding-top:.5rem;padding-bottom:.5rem;display:flex;align-items:center}.dark{--ast-primary-bg: #0d0701;--ast-primary-color: #008cd1;--ast-secondary-bg: #00070e;--ast-secondary-color: #ffa258;--ast-input-bg: #070707;--ast-input-color: rgba(222, 218, 218, .75);--ast-muted-color: rgba(222, 218, 218, .75);--ast-saved-chat-hover-background: #262421;--ast-message-table-border-color: #333333;--ast-message-table-tr-bg: #070707;--ast-message-table-tr-border-color: #222222;--ast-reference-icon-color: white;--ast-reference-icon-active-color: black;--ast-reference-passages-color: white;--ast-reference-expanded-hover-bg: #262421;--ast-message-reference-color: black}:host ::ng-deep .reference,:host ::ng-deep .message-content .reference,:host ::ng-deep .attachment .reference{position:relative;bottom:var(--ast-reference-bottom, .3em);font-weight:var(--ast-reference-font-weight, bold);padding:var(--ast-reference-padding, 0 .2em);margin:var(--ast-reference-margin, 0 .1em);border-radius:var(--ast-reference-border-radius, .2em);background-color:var(--ast-reference-background-color, lightblue);color:var(--ast-reference-color, black)}:host ::ng-deep .reference,:host ::ng-deep .message-content .reference{font-size:var(--ast-reference-message-font-size, .7em)}:host ::ng-deep .attachment .reference{font-size:var(--ast-reference-attachment-font-size, 13px)}:host{font-size:.875rem}:host>div>.user-input>div:not(.progress),:host>div>ul>li{width:var(--ast-chat-container-width, 100%);max-width:100%;margin-left:auto;margin-right:auto}:host>div>ul{padding-top:var(--ast-chat-padding-top, 0);padding-bottom:var(--ast-chat-padding-bottom, 0)}li.attachment>p{display:-webkit-box;-webkit-box-orient:vertical;overflow:hidden;-webkit-line-clamp:3}li.attachment.expanded>p{display:block}.progress{--bs-progress-height: 3px}.progress.disabled{--bs-progress-height: 20px;--bs-progress-bar-bg: var(--bs-danger)}.user-input{z-index:1}.user-input ul.list-group{max-height:30vh}.form-control:disabled{background-color:#ededed}a.disabled{cursor:default;opacity:.5}.no-max-height{max-height:initial!important}.sq-floating-scroll{position:absolute;right:50%;bottom:75px;text-align:center}.sq-floating-scroll .btn{background-color:#fff}.sq-floating-scroll .btn:hover{background-color:#fff;opacity:.9}.ast-input-container{display:flex;align-items:end;background-color:var(--ast-input-bg, #F8F8F8);border-radius:var(--ast-size-3, .75rem)}.ast-input-container>i{padding-left:var(--ast-size-3, .75rem);color:var(--ast-muted-color, rgba(33, 37, 41, .75))}.ast-input-container textarea{padding-left:var(--ast-size-3, .75rem);padding-right:var(--ast-size-3, .75rem);resize:none}.ast-input-container textarea,.ast-input-container button,.ast-input-container button:hover{background-color:transparent;border:0}.ast-input-container button:hover{color:var(--ast-primary-color, #005DA7)}.ast-input-container button:not(:hover){color:var(--ast-muted-color, rgba(33, 37, 41, .75))}sq-chat-message.sq-user-message{float:var(--ast-user-message-float, none)}sq-token-progress-bar{z-index:10;position:absolute;top:0;right:0}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i1.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "pipe", type: i1.AsyncPipe, name: "async" }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i2.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "component", type: ChatMessageComponent, selector: "sq-chat-message", inputs: ["message", "conversation", "suggestedActions", "assistantMessageIcon", "userMessageIcon", "streaming", "canEdit", "canRegenerate", "canCopy"], outputs: ["openDocument", "openPreview", "suggestAction", "edit", "regenerate"] }, { kind: "component", type: TokenProgressBarComponent, selector: "sq-token-progress-bar", inputs: ["instanceId"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
2239
|
+
], queries: [{ propertyName: "loadingTpl", first: true, predicate: ["loadingTpl"], descendants: true }, { propertyName: "reportIssueTpl", first: true, predicate: ["reportIssueTpl"], descendants: true }], viewQueries: [{ propertyName: "messageList", first: true, predicate: ["messageList"], descendants: true }, { propertyName: "questionInput", first: true, predicate: ["questionInput"], descendants: true }], usesInheritance: true, usesOnChanges: true, ngImport: i0, template: "<ng-container *ngIf=\"!initializationError\">\n <div *ngIf=\"messages$ | async as messages; else loadingTpl || loadingTplDefault\" class=\"h-100 d-flex flex-column\">\n <sq-token-progress-bar\n *ngIf=\"config?.globalSettings?.displayUserQuotaConsumption || config?.globalSettings?.displayChatTokensConsumption\"\n [instanceId]=\"instanceId\"\n class=\"ms-1\">\n </sq-token-progress-bar>\n\n <ul class=\"list-group list-group-flush overflow-auto flex-grow-1 pe-2 pb-2\" #messageList>\n <ng-container *ngFor=\"let message of messages; let index = index; let last = last\">\n <!-- Regular messages -->\n <li class=\"list-group-item\" [style.--bs-list-group-item-padding-y.rem]=\"'0.6'\" *ngIf=\"message.additionalProperties.display\"\n [class.opacity-50]=\"messageToEdit && messageToEdit < index + 1\">\n <sq-chat-message\n [class.sq-user-message]=\"message.role === 'user'\"\n [class.last-message]=\"last\"\n [message]=\"message\"\n [conversation]=\"messages\"\n [suggestedActions]=\"last ? message.additionalProperties.$suggestedAction : undefined\"\n [assistantMessageIcon]=\"assistantMessageIcon\"\n [userMessageIcon]=\"userMessageIcon\"\n [streaming]=\"last && (chatService.streaming$ | async)\"\n [canEdit]=\"(loading$ | async) === false && (chatService.streaming$ | async) === false && messageToEdit === undefined && message.role === 'user'\"\n [canRegenerate]=\"(loading$ | async) === false && (chatService.streaming$ | async) === false && messageToEdit === undefined && message.role === 'assistant' && last\"\n [canCopy]=\"!(last && (chatService.streaming$ | async)) && messageToEdit === undefined && message.role === 'assistant'\"\n (edit)=\"editMessage(index)\"\n (copy)=\"copyMessage(index)\"\n (regenerate)=\"regenerateMessage(index)\"\n (openDocument)=\"openOriginalAttachment($event)\"\n (openPreview)=\"openAttachmentPreview($event)\"\n (suggestAction)=\"suggestAction.emit($event)\"\n (like)=\"onLike(index)\"\n (dislike)=\"onDislike($event, index)\">\n </sq-chat-message>\n </li>\n </ng-container>\n <!-- Loading spinner -->\n <li class=\"list-group-item\" *ngIf=\"(loading$ | async) === true\">\n <ng-container *ngTemplateOutlet=\"loadingTpl || loadingTplDefault\"></ng-container>\n </li>\n </ul>\n\n <!-- Reporting an issue form -->\n <div class=\"issue-report bg-light pt-3 pb-2\" *ngIf=\"showReportIssue\">\n <ng-container *ngTemplateOutlet=\"reportIssueTpl || reportIssueTplDefault\" ></ng-container>\n </div>\n <!-- User text input -->\n <div class=\"user-input mt-auto\" *ngIf=\"enabledUserInput && !showReportIssue\">\n <div class=\"py-2\">\n <ng-container *ngTemplateOutlet=\"inputTpl\"></ng-container>\n <div class=\"text-end small text-muted px-3\" *ngIf=\"!!config?.globalSettings?.disclaimer\">\n {{ config?.globalSettings?.disclaimer }}\n </div>\n </div>\n </div>\n <!-- Floating scroll button -->\n <div *ngIf=\"!isAtBottom && !showReportIssue\" class=\"sq-floating-scroll\" [ngClass]=\"enabledUserInput ? 'sq-floating-scroll--when-user-input' : 'sq-floating-scroll--without-user-input'\">\n <button class=\"btn shadow\" (click)=\"scrollDown()\">\n <i class=\"fas fa-angle-double-down\"></i>\n </button>\n </div>\n </div>\n</ng-container>\n\n<!-- NG TEMPLATES-->\n\n<ng-template #loadingTplDefault>\n <div class=\"spinner-grow text-primary d-block mx-auto my-5\" role=\"status\">\n <span class=\"visually-hidden\">Loading...</span>\n </div>\n</ng-template>\n\n<ng-template #inputTpl>\n <div class=\"px-3 py-1\">\n <div class=\"ast-input-container\">\n <button disabled class=\"btn btn-light\">\n <i class=\"fas fa-search\"></i>\n </button>\n <textarea #questionInput rows=\"1\"\n type=\"text\" class=\"form-control\"\n placeholder=\"Ask something\" autofocus\n [(ngModel)]=\"question\"\n (keyup)=\"onKeyUp($event)\"\n (keydown)=\"calculateHeight()\"\n [disabled]=\"(loading$ | async) || (chatService.streaming$ | async)\">\n </textarea>\n <button\n *ngIf=\"!(chatService.streaming$ | async) && !(loading$ | async)\"\n type=\"button\"\n class=\"btn btn-light ms-2\"\n title=\"Send message\"\n (click)=\"submitQuestion()\">\n <i class=\"fas fa-paper-plane\"></i>\n </button>\n <!--<button\n *ngIf=\"(chatService.streaming$ | async)\"\n type=\"button\"\n class=\"btn btn-light ms-2\"\n title=\"Stop generating\"\n (click)=\"terminateFetch()\">\n <i class=\"fas fa-stop\"></i>\n </button>-->\n <button\n *ngIf=\"messageToEdit\"\n type=\"button\"\n class=\"btn btn-light ms-2\"\n title=\"Cancel edition\"\n (click)=\"messageToEdit = undefined; question = ''\">\n <i class=\"fas fa-undo-alt\"></i>\n </button>\n </div>\n </div>\n</ng-template>\n\n<ng-template #reportIssueTplDefault>\n <div class=\"px-3\">\n <h5>Issue type</h5>\n <select class=\"form-select mb-4\" [(ngModel)]=\"issueType\">\n <option [value]=\"''\">Choose an issue type</option>\n <option *ngFor=\"let type of issueTypes\">{{type}}</option>\n </select>\n <h5>Comment (optional)</h5>\n <textarea class=\"form-control\" [(ngModel)]=\"issueComment\" placeholder=\"What was unsatisfying about this response?\"></textarea>\n <div class=\"d-flex flex-row-reverse mt-2\">\n <button class=\"btn btn-primary\" [disabled]=\"!issueType\" (click)=\"sendIssue()\">Send</button>\n <button class=\"btn btn-light\" (click)=\"ignoreIssue()\">Cancel</button>\n </div>\n </div>\n</ng-template>\n", styles: [".ast-primary{color:var(--ast-primary-color, #005DA7);background-color:var(--ast-primary-bg, #f2f8fe)}.ast-primary-hover{background-color:var(--ast-primary-bg, #f2f8fe)}.ast-primary-hover:hover{color:var(--ast-primary-color, #005DA7)}.ast-secondary{color:var(--ast-secondary-color, #FF732E);background-color:var(--ast-secondary-bg, #FFF8F1)}.ast-btn{border:0;text-align:left;padding-top:.5rem;padding-bottom:.5rem;display:flex;align-items:center}.dark{--ast-primary-bg: #0d0701;--ast-primary-color: #008cd1;--ast-secondary-bg: #00070e;--ast-secondary-color: #ffa258;--ast-input-bg: #070707;--ast-input-color: rgba(222, 218, 218, .75);--ast-muted-color: rgba(222, 218, 218, .75);--ast-saved-chat-hover-background: #262421;--ast-message-table-border-color: #333333;--ast-message-table-tr-bg: #070707;--ast-message-table-tr-border-color: #222222;--ast-reference-icon-color: white;--ast-reference-icon-active-color: black;--ast-reference-passages-color: white;--ast-reference-expanded-hover-bg: #262421;--ast-message-reference-color: black}:host ::ng-deep .reference,:host ::ng-deep .message-content .reference,:host ::ng-deep .attachment .reference{position:relative;bottom:var(--ast-reference-bottom, .3em);font-weight:var(--ast-reference-font-weight, bold);padding:var(--ast-reference-padding, 0 .2em);margin:var(--ast-reference-margin, 0 .1em);border-radius:var(--ast-reference-border-radius, .2em);background-color:var(--ast-reference-background-color, lightblue);color:var(--ast-reference-color, black)}:host ::ng-deep .reference,:host ::ng-deep .message-content .reference{font-size:var(--ast-reference-message-font-size, .7em)}:host ::ng-deep .attachment .reference{font-size:var(--ast-reference-attachment-font-size, 13px)}:host{font-size:.875rem}:host>div>.user-input>div:not(.progress),:host>div>.issue-report>div,:host>div>ul>li{width:var(--ast-chat-container-width, 100%);max-width:100%;margin-left:auto;margin-right:auto}:host>div>ul{padding-top:var(--ast-chat-padding-top, 0);padding-bottom:var(--ast-chat-padding-bottom, 0)}li.attachment>p{display:-webkit-box;-webkit-box-orient:vertical;overflow:hidden;-webkit-line-clamp:3}li.attachment.expanded>p{display:block}.progress{--bs-progress-height: 3px}.progress.disabled{--bs-progress-height: 20px;--bs-progress-bar-bg: var(--bs-danger)}.user-input{z-index:1}.user-input ul.list-group{max-height:30vh}.form-control:disabled{background-color:#ededed}a.disabled{cursor:default;opacity:.5}.no-max-height{max-height:initial!important}.sq-floating-scroll{position:absolute;right:50%;text-align:center}.sq-floating-scroll--when-user-input{bottom:75px}.sq-floating-scroll--without-user-input{bottom:15px}.sq-floating-scroll .btn{background-color:#fff}.sq-floating-scroll .btn:hover{background-color:#fff;opacity:.9}.ast-input-container{display:flex;align-items:end;background-color:var(--ast-input-bg, #F8F8F8);border-radius:var(--ast-size-3, .75rem)}.ast-input-container>i{padding-left:var(--ast-size-3, .75rem);color:var(--ast-muted-color, rgba(33, 37, 41, .75))}.ast-input-container textarea{padding-left:var(--ast-size-3, .75rem);padding-right:var(--ast-size-3, .75rem);resize:none}.ast-input-container textarea,.ast-input-container button,.ast-input-container button:hover{background-color:transparent;border:0}.ast-input-container button:hover{color:var(--ast-primary-color, #005DA7)}.ast-input-container button:not(:hover){color:var(--ast-muted-color, rgba(33, 37, 41, .75))}sq-chat-message.sq-user-message{float:var(--ast-user-message-float, none)}sq-token-progress-bar{z-index:10;position:absolute;top:0;right:0}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i1.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "pipe", type: i1.AsyncPipe, name: "async" }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i2.NgSelectOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i2.ɵNgSelectMultipleOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i2.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i2.SelectControlValueAccessor, selector: "select:not([multiple])[formControlName],select:not([multiple])[formControl],select:not([multiple])[ngModel]", inputs: ["compareWith"] }, { kind: "directive", type: i2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "component", type: ChatMessageComponent, selector: "sq-chat-message", inputs: ["message", "conversation", "suggestedActions", "assistantMessageIcon", "userMessageIcon", "streaming", "canEdit", "canRegenerate", "canCopy"], outputs: ["openDocument", "openPreview", "suggestAction", "edit", "copy", "regenerate", "like", "dislike"] }, { kind: "component", type: TokenProgressBarComponent, selector: "sq-token-progress-bar", inputs: ["instanceId"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
2057
2240
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.0.6", ngImport: i0, type: ChatComponent, decorators: [{
|
|
2058
2241
|
type: Component,
|
|
2059
2242
|
args: [{ selector: 'sq-chat-v3', providers: [
|
|
2060
2243
|
RestChatService,
|
|
2061
2244
|
WebSocketChatService
|
|
2062
|
-
], changeDetection: ChangeDetectionStrategy.OnPush, standalone: true, imports: [CommonModule, FormsModule, ChatMessageComponent, TokenProgressBarComponent], template: "
|
|
2245
|
+
], changeDetection: ChangeDetectionStrategy.OnPush, standalone: true, imports: [CommonModule, FormsModule, ChatMessageComponent, TokenProgressBarComponent], template: "<ng-container *ngIf=\"!initializationError\">\n <div *ngIf=\"messages$ | async as messages; else loadingTpl || loadingTplDefault\" class=\"h-100 d-flex flex-column\">\n <sq-token-progress-bar\n *ngIf=\"config?.globalSettings?.displayUserQuotaConsumption || config?.globalSettings?.displayChatTokensConsumption\"\n [instanceId]=\"instanceId\"\n class=\"ms-1\">\n </sq-token-progress-bar>\n\n <ul class=\"list-group list-group-flush overflow-auto flex-grow-1 pe-2 pb-2\" #messageList>\n <ng-container *ngFor=\"let message of messages; let index = index; let last = last\">\n <!-- Regular messages -->\n <li class=\"list-group-item\" [style.--bs-list-group-item-padding-y.rem]=\"'0.6'\" *ngIf=\"message.additionalProperties.display\"\n [class.opacity-50]=\"messageToEdit && messageToEdit < index + 1\">\n <sq-chat-message\n [class.sq-user-message]=\"message.role === 'user'\"\n [class.last-message]=\"last\"\n [message]=\"message\"\n [conversation]=\"messages\"\n [suggestedActions]=\"last ? message.additionalProperties.$suggestedAction : undefined\"\n [assistantMessageIcon]=\"assistantMessageIcon\"\n [userMessageIcon]=\"userMessageIcon\"\n [streaming]=\"last && (chatService.streaming$ | async)\"\n [canEdit]=\"(loading$ | async) === false && (chatService.streaming$ | async) === false && messageToEdit === undefined && message.role === 'user'\"\n [canRegenerate]=\"(loading$ | async) === false && (chatService.streaming$ | async) === false && messageToEdit === undefined && message.role === 'assistant' && last\"\n [canCopy]=\"!(last && (chatService.streaming$ | async)) && messageToEdit === undefined && message.role === 'assistant'\"\n (edit)=\"editMessage(index)\"\n (copy)=\"copyMessage(index)\"\n (regenerate)=\"regenerateMessage(index)\"\n (openDocument)=\"openOriginalAttachment($event)\"\n (openPreview)=\"openAttachmentPreview($event)\"\n (suggestAction)=\"suggestAction.emit($event)\"\n (like)=\"onLike(index)\"\n (dislike)=\"onDislike($event, index)\">\n </sq-chat-message>\n </li>\n </ng-container>\n <!-- Loading spinner -->\n <li class=\"list-group-item\" *ngIf=\"(loading$ | async) === true\">\n <ng-container *ngTemplateOutlet=\"loadingTpl || loadingTplDefault\"></ng-container>\n </li>\n </ul>\n\n <!-- Reporting an issue form -->\n <div class=\"issue-report bg-light pt-3 pb-2\" *ngIf=\"showReportIssue\">\n <ng-container *ngTemplateOutlet=\"reportIssueTpl || reportIssueTplDefault\" ></ng-container>\n </div>\n <!-- User text input -->\n <div class=\"user-input mt-auto\" *ngIf=\"enabledUserInput && !showReportIssue\">\n <div class=\"py-2\">\n <ng-container *ngTemplateOutlet=\"inputTpl\"></ng-container>\n <div class=\"text-end small text-muted px-3\" *ngIf=\"!!config?.globalSettings?.disclaimer\">\n {{ config?.globalSettings?.disclaimer }}\n </div>\n </div>\n </div>\n <!-- Floating scroll button -->\n <div *ngIf=\"!isAtBottom && !showReportIssue\" class=\"sq-floating-scroll\" [ngClass]=\"enabledUserInput ? 'sq-floating-scroll--when-user-input' : 'sq-floating-scroll--without-user-input'\">\n <button class=\"btn shadow\" (click)=\"scrollDown()\">\n <i class=\"fas fa-angle-double-down\"></i>\n </button>\n </div>\n </div>\n</ng-container>\n\n<!-- NG TEMPLATES-->\n\n<ng-template #loadingTplDefault>\n <div class=\"spinner-grow text-primary d-block mx-auto my-5\" role=\"status\">\n <span class=\"visually-hidden\">Loading...</span>\n </div>\n</ng-template>\n\n<ng-template #inputTpl>\n <div class=\"px-3 py-1\">\n <div class=\"ast-input-container\">\n <button disabled class=\"btn btn-light\">\n <i class=\"fas fa-search\"></i>\n </button>\n <textarea #questionInput rows=\"1\"\n type=\"text\" class=\"form-control\"\n placeholder=\"Ask something\" autofocus\n [(ngModel)]=\"question\"\n (keyup)=\"onKeyUp($event)\"\n (keydown)=\"calculateHeight()\"\n [disabled]=\"(loading$ | async) || (chatService.streaming$ | async)\">\n </textarea>\n <button\n *ngIf=\"!(chatService.streaming$ | async) && !(loading$ | async)\"\n type=\"button\"\n class=\"btn btn-light ms-2\"\n title=\"Send message\"\n (click)=\"submitQuestion()\">\n <i class=\"fas fa-paper-plane\"></i>\n </button>\n <!--<button\n *ngIf=\"(chatService.streaming$ | async)\"\n type=\"button\"\n class=\"btn btn-light ms-2\"\n title=\"Stop generating\"\n (click)=\"terminateFetch()\">\n <i class=\"fas fa-stop\"></i>\n </button>-->\n <button\n *ngIf=\"messageToEdit\"\n type=\"button\"\n class=\"btn btn-light ms-2\"\n title=\"Cancel edition\"\n (click)=\"messageToEdit = undefined; question = ''\">\n <i class=\"fas fa-undo-alt\"></i>\n </button>\n </div>\n </div>\n</ng-template>\n\n<ng-template #reportIssueTplDefault>\n <div class=\"px-3\">\n <h5>Issue type</h5>\n <select class=\"form-select mb-4\" [(ngModel)]=\"issueType\">\n <option [value]=\"''\">Choose an issue type</option>\n <option *ngFor=\"let type of issueTypes\">{{type}}</option>\n </select>\n <h5>Comment (optional)</h5>\n <textarea class=\"form-control\" [(ngModel)]=\"issueComment\" placeholder=\"What was unsatisfying about this response?\"></textarea>\n <div class=\"d-flex flex-row-reverse mt-2\">\n <button class=\"btn btn-primary\" [disabled]=\"!issueType\" (click)=\"sendIssue()\">Send</button>\n <button class=\"btn btn-light\" (click)=\"ignoreIssue()\">Cancel</button>\n </div>\n </div>\n</ng-template>\n", styles: [".ast-primary{color:var(--ast-primary-color, #005DA7);background-color:var(--ast-primary-bg, #f2f8fe)}.ast-primary-hover{background-color:var(--ast-primary-bg, #f2f8fe)}.ast-primary-hover:hover{color:var(--ast-primary-color, #005DA7)}.ast-secondary{color:var(--ast-secondary-color, #FF732E);background-color:var(--ast-secondary-bg, #FFF8F1)}.ast-btn{border:0;text-align:left;padding-top:.5rem;padding-bottom:.5rem;display:flex;align-items:center}.dark{--ast-primary-bg: #0d0701;--ast-primary-color: #008cd1;--ast-secondary-bg: #00070e;--ast-secondary-color: #ffa258;--ast-input-bg: #070707;--ast-input-color: rgba(222, 218, 218, .75);--ast-muted-color: rgba(222, 218, 218, .75);--ast-saved-chat-hover-background: #262421;--ast-message-table-border-color: #333333;--ast-message-table-tr-bg: #070707;--ast-message-table-tr-border-color: #222222;--ast-reference-icon-color: white;--ast-reference-icon-active-color: black;--ast-reference-passages-color: white;--ast-reference-expanded-hover-bg: #262421;--ast-message-reference-color: black}:host ::ng-deep .reference,:host ::ng-deep .message-content .reference,:host ::ng-deep .attachment .reference{position:relative;bottom:var(--ast-reference-bottom, .3em);font-weight:var(--ast-reference-font-weight, bold);padding:var(--ast-reference-padding, 0 .2em);margin:var(--ast-reference-margin, 0 .1em);border-radius:var(--ast-reference-border-radius, .2em);background-color:var(--ast-reference-background-color, lightblue);color:var(--ast-reference-color, black)}:host ::ng-deep .reference,:host ::ng-deep .message-content .reference{font-size:var(--ast-reference-message-font-size, .7em)}:host ::ng-deep .attachment .reference{font-size:var(--ast-reference-attachment-font-size, 13px)}:host{font-size:.875rem}:host>div>.user-input>div:not(.progress),:host>div>.issue-report>div,:host>div>ul>li{width:var(--ast-chat-container-width, 100%);max-width:100%;margin-left:auto;margin-right:auto}:host>div>ul{padding-top:var(--ast-chat-padding-top, 0);padding-bottom:var(--ast-chat-padding-bottom, 0)}li.attachment>p{display:-webkit-box;-webkit-box-orient:vertical;overflow:hidden;-webkit-line-clamp:3}li.attachment.expanded>p{display:block}.progress{--bs-progress-height: 3px}.progress.disabled{--bs-progress-height: 20px;--bs-progress-bar-bg: var(--bs-danger)}.user-input{z-index:1}.user-input ul.list-group{max-height:30vh}.form-control:disabled{background-color:#ededed}a.disabled{cursor:default;opacity:.5}.no-max-height{max-height:initial!important}.sq-floating-scroll{position:absolute;right:50%;text-align:center}.sq-floating-scroll--when-user-input{bottom:75px}.sq-floating-scroll--without-user-input{bottom:15px}.sq-floating-scroll .btn{background-color:#fff}.sq-floating-scroll .btn:hover{background-color:#fff;opacity:.9}.ast-input-container{display:flex;align-items:end;background-color:var(--ast-input-bg, #F8F8F8);border-radius:var(--ast-size-3, .75rem)}.ast-input-container>i{padding-left:var(--ast-size-3, .75rem);color:var(--ast-muted-color, rgba(33, 37, 41, .75))}.ast-input-container textarea{padding-left:var(--ast-size-3, .75rem);padding-right:var(--ast-size-3, .75rem);resize:none}.ast-input-container textarea,.ast-input-container button,.ast-input-container button:hover{background-color:transparent;border:0}.ast-input-container button:hover{color:var(--ast-primary-color, #005DA7)}.ast-input-container button:not(:hover){color:var(--ast-muted-color, rgba(33, 37, 41, .75))}sq-chat-message.sq-user-message{float:var(--ast-user-message-float, none)}sq-token-progress-bar{z-index:10;position:absolute;top:0;right:0}\n"] }]
|
|
2063
2246
|
}], ctorParameters: function () { return []; }, propDecorators: { instanceId: [{
|
|
2064
2247
|
type: Input
|
|
2065
2248
|
}], query: [{
|
|
@@ -2101,6 +2284,9 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.0.6", ngImpor
|
|
|
2101
2284
|
}], loadingTpl: [{
|
|
2102
2285
|
type: ContentChild,
|
|
2103
2286
|
args: ['loadingTpl']
|
|
2287
|
+
}], reportIssueTpl: [{
|
|
2288
|
+
type: ContentChild,
|
|
2289
|
+
args: ['reportIssueTpl']
|
|
2104
2290
|
}] } });
|
|
2105
2291
|
|
|
2106
2292
|
class SavedChatsComponent {
|
|
@@ -2133,7 +2319,9 @@ class SavedChatsComponent {
|
|
|
2133
2319
|
}
|
|
2134
2320
|
onLoad(savedChat) {
|
|
2135
2321
|
this.chatService.setSavedChatId(savedChat.id);
|
|
2322
|
+
this.chatService.generateChatId(savedChat.id);
|
|
2136
2323
|
this.chatService.loadSavedChat$.next(savedChat);
|
|
2324
|
+
this.chatService.generateAuditEvent('saved-chat.load', {}, savedChat.id);
|
|
2137
2325
|
this.chatService.listSavedChat();
|
|
2138
2326
|
this.load.emit(savedChat);
|
|
2139
2327
|
}
|
|
@@ -2159,6 +2347,7 @@ class SavedChatsComponent {
|
|
|
2159
2347
|
this.notificationsService.error(`Error occurred while updating the saved discussion "${savedChat.title}"`);
|
|
2160
2348
|
return throwError(() => error);
|
|
2161
2349
|
})).subscribe());
|
|
2350
|
+
this.chatService.generateAuditEvent('saved-chat.rename', { 'text': model.output }, savedChat.id);
|
|
2162
2351
|
}
|
|
2163
2352
|
});
|
|
2164
2353
|
}
|
|
@@ -2184,6 +2373,7 @@ class SavedChatsComponent {
|
|
|
2184
2373
|
this.notificationsService.error(`Error occurred while deleting the saved discussion "${savedChat.title}"`);
|
|
2185
2374
|
return throwError(() => error);
|
|
2186
2375
|
})).subscribe());
|
|
2376
|
+
this.chatService.generateAuditEvent('saved-chat.delete', {}, savedChat.id);
|
|
2187
2377
|
}
|
|
2188
2378
|
});
|
|
2189
2379
|
}
|