@sinequa/assistant 3.8.0 → 3.9.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.
Files changed (151) hide show
  1. package/chat/charts/chart/chart.component.d.ts +13 -13
  2. package/chat/chat-message/chat-message.component.d.ts +85 -81
  3. package/chat/chat-message/i18n/en.json +11 -0
  4. package/chat/chat-message/i18n/fr.json +11 -0
  5. package/chat/chat-reference/chat-reference.component.d.ts +14 -14
  6. package/chat/chat-reference/i18n/en.json +4 -0
  7. package/chat/chat-reference/i18n/fr.json +4 -0
  8. package/chat/chat-settings-v3/chat-settings-v3.component.d.ts +48 -50
  9. package/chat/chat-settings-v3/i18n/en.json +14 -0
  10. package/chat/chat-settings-v3/i18n/fr.json +14 -0
  11. package/chat/chat.component.d.ts +388 -1405
  12. package/chat/chat.service.d.ts +247 -228
  13. package/chat/debug-message/debug-message.component.d.ts +17 -17
  14. package/chat/debug-message/i18n/en.json +3 -0
  15. package/chat/debug-message/i18n/fr.json +3 -0
  16. package/chat/dialogs/delete-saved-chat.component.d.ts +22 -0
  17. package/chat/dialogs/i18n/en.json +19 -0
  18. package/chat/dialogs/i18n/fr.json +19 -0
  19. package/chat/dialogs/rename-saved-chat.component.d.ts +21 -0
  20. package/chat/dialogs/updates.component.d.ts +15 -0
  21. package/chat/documents-upload/document-list/document-list.component.d.ts +68 -77
  22. package/chat/documents-upload/document-overview/document-overview.component.d.ts +31 -41
  23. package/chat/documents-upload/document-upload/document-upload.component.d.ts +96 -98
  24. package/chat/documents-upload/documents-upload.model.d.ts +66 -66
  25. package/chat/documents-upload/documents-upload.service.d.ts +170 -174
  26. package/chat/documents-upload/i18n/en.json +24 -0
  27. package/chat/documents-upload/i18n/fr.json +24 -0
  28. package/chat/format-icon/format-icon.component.d.ts +10 -10
  29. package/chat/format-icon/icons.d.ts +5 -5
  30. package/chat/i18n/en.json +42 -0
  31. package/chat/i18n/fr.json +42 -0
  32. package/chat/index.d.ts +5 -5
  33. package/chat/initials-avatar/initials-avatar.component.d.ts +35 -35
  34. package/chat/instance-manager.service.d.ts +28 -28
  35. package/chat/pipes/message-content.pipe.d.ts +16 -0
  36. package/chat/prompt.component.d.ts +25 -21
  37. package/chat/public-api.d.ts +17 -17
  38. package/chat/references/i18n/en.json +6 -0
  39. package/chat/references/i18n/fr.json +6 -0
  40. package/chat/references/inline-image-reference.d.ts +21 -0
  41. package/chat/references/inline-page-reference.d.ts +21 -0
  42. package/chat/rest-chat.service.d.ts +31 -33
  43. package/chat/saved-chats/i18n/en.json +4 -0
  44. package/chat/saved-chats/i18n/fr.json +4 -0
  45. package/chat/saved-chats/saved-chats.component.d.ts +30 -36
  46. package/chat/services/app.service.d.ts +8 -0
  47. package/chat/services/dialog.service.d.ts +12 -0
  48. package/chat/services/notification.service.d.ts +10 -0
  49. package/chat/services/principal.service.d.ts +7 -0
  50. package/chat/services/search.service.d.ts +7 -0
  51. package/chat/services/signalR.web.service.d.ts +45 -0
  52. package/chat/services/ui.service.d.ts +13 -0
  53. package/chat/services/user-settings.service.d.ts +7 -0
  54. package/chat/token-progress-bar/i18n/en.json +4 -0
  55. package/chat/token-progress-bar/i18n/fr.json +4 -0
  56. package/chat/token-progress-bar/token-progress-bar.component.d.ts +24 -27
  57. package/chat/tooltip/tooltip.component.d.ts +12 -0
  58. package/chat/tooltip/tooltip.directive.d.ts +81 -0
  59. package/chat/types/message-content.types.d.ts +54 -0
  60. package/chat/types/message-reference.types.d.ts +11 -0
  61. package/chat/types.d.ts +913 -873
  62. package/chat/unified-plugins/embedded-image-reference.plugin.d.ts +3 -0
  63. package/chat/unified-plugins/embedded-page-reference.plugin.d.ts +3 -0
  64. package/chat/utils/assistant-json.d.ts +2 -0
  65. package/chat/websocket-chat.service.d.ts +102 -103
  66. package/esm2022/chat/charts/chart/chart.component.mjs +40 -0
  67. package/esm2022/chat/chat-message/chat-message.component.mjs +351 -0
  68. package/esm2022/chat/chat-reference/chat-reference.component.mjs +40 -0
  69. package/esm2022/chat/chat-settings-v3/chat-settings-v3.component.mjs +118 -0
  70. package/esm2022/chat/chat.component.mjs +1090 -0
  71. package/esm2022/chat/chat.service.mjs +417 -0
  72. package/esm2022/chat/debug-message/debug-message.component.mjs +43 -0
  73. package/esm2022/chat/dialogs/delete-saved-chat.component.mjs +81 -0
  74. package/esm2022/chat/dialogs/rename-saved-chat.component.mjs +84 -0
  75. package/esm2022/chat/dialogs/updates.component.mjs +61 -0
  76. package/esm2022/chat/documents-upload/document-list/document-list.component.mjs +140 -0
  77. package/esm2022/chat/documents-upload/document-overview/document-overview.component.mjs +65 -0
  78. package/esm2022/chat/documents-upload/document-upload/document-upload.component.mjs +256 -0
  79. package/{esm2020 → esm2022}/chat/documents-upload/documents-upload.model.mjs +1 -1
  80. package/esm2022/chat/documents-upload/documents-upload.service.mjs +291 -0
  81. package/{esm2020 → esm2022}/chat/format-icon/format-icon.component.mjs +23 -23
  82. package/{esm2020 → esm2022}/chat/format-icon/icons.mjs +137 -137
  83. package/{esm2020 → esm2022}/chat/initials-avatar/initials-avatar.component.mjs +60 -60
  84. package/esm2022/chat/instance-manager.service.mjs +46 -0
  85. package/esm2022/chat/pipes/message-content.pipe.mjs +34 -0
  86. package/esm2022/chat/prompt.component.mjs +88 -0
  87. package/{esm2020 → esm2022}/chat/public-api.mjs +18 -18
  88. package/esm2022/chat/references/inline-image-reference.mjs +110 -0
  89. package/esm2022/chat/references/inline-page-reference.mjs +110 -0
  90. package/esm2022/chat/rest-chat.service.mjs +296 -0
  91. package/esm2022/chat/saved-chats/saved-chats.component.mjs +82 -0
  92. package/esm2022/chat/services/app.service.mjs +19 -0
  93. package/esm2022/chat/services/dialog.service.mjs +40 -0
  94. package/esm2022/chat/services/notification.service.mjs +25 -0
  95. package/esm2022/chat/services/principal.service.mjs +16 -0
  96. package/esm2022/chat/services/search.service.mjs +13 -0
  97. package/esm2022/chat/services/signalR.web.service.mjs +79 -0
  98. package/esm2022/chat/services/ui.service.mjs +61 -0
  99. package/esm2022/chat/services/user-settings.service.mjs +22 -0
  100. package/{esm2020 → esm2022}/chat/sinequa-assistant-chat.mjs +4 -4
  101. package/esm2022/chat/token-progress-bar/token-progress-bar.component.mjs +52 -0
  102. package/esm2022/chat/tooltip/tooltip.component.mjs +44 -0
  103. package/esm2022/chat/tooltip/tooltip.directive.mjs +203 -0
  104. package/esm2022/chat/types/message-content.types.mjs +2 -0
  105. package/esm2022/chat/types/message-reference.types.mjs +2 -0
  106. package/esm2022/chat/types.mjs +130 -0
  107. package/esm2022/chat/unified-plugins/embedded-image-reference.plugin.mjs +57 -0
  108. package/esm2022/chat/unified-plugins/embedded-page-reference.plugin.mjs +57 -0
  109. package/esm2022/chat/utils/assistant-json.mjs +12 -0
  110. package/esm2022/chat/websocket-chat.service.mjs +654 -0
  111. package/{esm2020 → esm2022}/public-api.mjs +2 -2
  112. package/{esm2020 → esm2022}/sinequa-assistant.mjs +4 -4
  113. package/fesm2022/sinequa-assistant-chat.mjs +5340 -0
  114. package/fesm2022/sinequa-assistant-chat.mjs.map +1 -0
  115. package/{fesm2015 → fesm2022}/sinequa-assistant.mjs +3 -3
  116. package/index.d.ts +5 -5
  117. package/package.json +52 -25
  118. package/public-api.d.ts +1 -1
  119. package/chat/messages/de.d.ts +0 -4
  120. package/chat/messages/en.d.ts +0 -4
  121. package/chat/messages/fr.d.ts +0 -4
  122. package/chat/messages/index.d.ts +0 -4
  123. package/esm2020/chat/charts/chart/chart.component.mjs +0 -40
  124. package/esm2020/chat/chat-message/chat-message.component.mjs +0 -263
  125. package/esm2020/chat/chat-reference/chat-reference.component.mjs +0 -40
  126. package/esm2020/chat/chat-settings-v3/chat-settings-v3.component.mjs +0 -117
  127. package/esm2020/chat/chat.component.mjs +0 -1069
  128. package/esm2020/chat/chat.service.mjs +0 -333
  129. package/esm2020/chat/debug-message/debug-message.component.mjs +0 -43
  130. package/esm2020/chat/documents-upload/document-list/document-list.component.mjs +0 -191
  131. package/esm2020/chat/documents-upload/document-overview/document-overview.component.mjs +0 -80
  132. package/esm2020/chat/documents-upload/document-upload/document-upload.component.mjs +0 -258
  133. package/esm2020/chat/documents-upload/documents-upload.service.mjs +0 -289
  134. package/esm2020/chat/instance-manager.service.mjs +0 -46
  135. package/esm2020/chat/messages/de.mjs +0 -4
  136. package/esm2020/chat/messages/en.mjs +0 -4
  137. package/esm2020/chat/messages/fr.mjs +0 -4
  138. package/esm2020/chat/messages/index.mjs +0 -9
  139. package/esm2020/chat/prompt.component.mjs +0 -88
  140. package/esm2020/chat/rest-chat.service.mjs +0 -241
  141. package/esm2020/chat/saved-chats/saved-chats.component.mjs +0 -175
  142. package/esm2020/chat/token-progress-bar/token-progress-bar.component.mjs +0 -54
  143. package/esm2020/chat/types.mjs +0 -112
  144. package/esm2020/chat/websocket-chat.service.mjs +0 -641
  145. package/fesm2015/sinequa-assistant-chat.mjs +0 -4200
  146. package/fesm2015/sinequa-assistant-chat.mjs.map +0 -1
  147. package/fesm2020/sinequa-assistant-chat.mjs +0 -4171
  148. package/fesm2020/sinequa-assistant-chat.mjs.map +0 -1
  149. package/fesm2020/sinequa-assistant.mjs +0 -9
  150. package/fesm2020/sinequa-assistant.mjs.map +0 -1
  151. /package/{fesm2015 → fesm2022}/sinequa-assistant.mjs.map +0 -0
@@ -1,4200 +0,0 @@
1
- import * as i1 from '@angular/common';
2
- import { CommonModule } from '@angular/common';
3
- import * as i0 from '@angular/core';
4
- import { Injectable, EventEmitter, inject, Component, Input, Output, ViewEncapsulation, ChangeDetectionStrategy, ChangeDetectorRef, ViewChild, ContentChild, Inject } from '@angular/core';
5
- import * as i2 from '@angular/forms';
6
- import { FormsModule, Validators, UntypedFormControl, ReactiveFormsModule } from '@angular/forms';
7
- import { Subscription, filter, tap, switchMap, BehaviorSubject, forkJoin, map, catchError, throwError, shareReplay, take, finalize, defer, from, Subject, fromEvent, mergeMap, of, merge, takeUntil, Observable, combineLatest, EMPTY, interval, startWith, takeWhile } from 'rxjs';
8
- import { AppService } from '@sinequa/core/app-utils';
9
- import { Utils } from '@sinequa/core/base';
10
- import { LoginService, AuthenticationService } from '@sinequa/core/login';
11
- import * as i3 from '@sinequa/core/web-services';
12
- import { PrincipalWebService, UserSettingsWebService, AuditWebService, JsonMethodPluginService, SignalRWebService } from '@sinequa/core/web-services';
13
- import { HttpTransportType, LogLevel, HubConnectionState } from '@microsoft/signalr';
14
- import { Action } from '@sinequa/components/action';
15
- import { AbstractFacet } from '@sinequa/components/facet';
16
- import * as i1$1 from '@sinequa/components/search';
17
- import { SearchService } from '@sinequa/components/search';
18
- import * as i2$1 from '@sinequa/components/utils';
19
- import { UtilsModule } from '@sinequa/components/utils';
20
- import { NotificationsService } from '@sinequa/core/notification';
21
- import { unified } from 'unified';
22
- import remarkParse from 'remark-parse';
23
- import { visit, CONTINUE, EXIT } from 'unist-util-visit';
24
- import remarkGfm from 'remark-gfm';
25
- import * as i5 from '@sinequa/components/collapse';
26
- import { CollapseModule } from '@sinequa/components/collapse';
27
- import * as i6 from 'ngx-remark';
28
- import { RemarkModule } from 'ngx-remark';
29
- import SafeColor from 'safecolor';
30
- import 'prismjs-components-importer/esm';
31
- import 'prismjs/plugins/autoloader/prism-autoloader';
32
- import { Chart, registerables } from 'chart.js';
33
- import { toDate, parseISO, isToday, isYesterday, isThisWeek, differenceInDays, endOfYesterday, isThisMonth, differenceInMonths, isThisQuarter, isThisYear, differenceInYears, format } from 'date-fns';
34
- import get from 'lodash/get';
35
- import { UserPreferences } from '@sinequa/components/user-settings';
36
- import * as i6$1 from '@sinequa/core/intl';
37
- import { IntlService, IntlModule } from '@sinequa/core/intl';
38
- import * as i1$2 from '@sinequa/core/modal';
39
- import { ModalService, ModalButton, ModalModule, MODAL_MODEL } from '@sinequa/core/modal';
40
- import { z } from 'zod';
41
- import * as Prism$1 from 'prismjs';
42
- import * as i4 from '@sinequa/components/modal';
43
- import { BsModalModule } from '@sinequa/components/modal';
44
- import * as i5$1 from '@sinequa/core/validation';
45
- import { ValidationModule } from '@sinequa/core/validation';
46
- import * as i2$2 from '@flowjs/ngx-flow';
47
- import { NgxFlowModule } from '@flowjs/ngx-flow';
48
- import { HttpClient, HttpHeaders, HttpEventType } from '@angular/common/http';
49
-
50
- /**
51
- * A service to create and manage instances of ChatService dynamically based on the provided component references and the implementation type (http or websocket)
52
- * All chat-related components should share the same instance of this InstanceManagerService, which in turn provides the appropriate instance of ChatService
53
- */
54
- class InstanceManagerService {
55
- constructor() {
56
- this._serviceInstances = new Map();
57
- }
58
- /**
59
- * Store the instance of ChatService in the map
60
- * @param key key differentiator between components used to store their corresponding ChatService instance
61
- * @param service The ChatService instance
62
- */
63
- storeInstance(key, service) {
64
- this._serviceInstances.set(key, service);
65
- }
66
- /**
67
- * @param key key differentiator between components based on which the corresponding ChatService instance is fetched
68
- * @returns The instance of the service corresponding to the instance of the component
69
- */
70
- getInstance(key) {
71
- if (!this.checkInstance(key)) {
72
- throw new Error(`No assistant instance found for the given key : '${key}'`);
73
- }
74
- return this._serviceInstances.get(key);
75
- }
76
- /**
77
- *
78
- * @param key key differentiator between components based on which the check for an existent ChatService instance is performed
79
- * @returns True if a ChatService instance has been already instantiated for the given key. Otherwise, false.
80
- */
81
- checkInstance(key) {
82
- return this._serviceInstances.has(key);
83
- }
84
- }
85
- InstanceManagerService.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.0.6", ngImport: i0, type: InstanceManagerService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
86
- InstanceManagerService.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "14.0.6", ngImport: i0, type: InstanceManagerService, providedIn: 'root' });
87
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.0.6", ngImport: i0, type: InstanceManagerService, decorators: [{
88
- type: Injectable,
89
- args: [{
90
- providedIn: 'root',
91
- }]
92
- }] });
93
-
94
- class ChatSettingsV3Component {
95
- constructor() {
96
- this._update = new EventEmitter();
97
- this._cancel = new EventEmitter();
98
- this.subscription = new Subscription();
99
- this.functions = [];
100
- this.isAdmin = false;
101
- this.showDebugToggle = false;
102
- this.loginService = inject(LoginService);
103
- this.instanceManagerService = inject(InstanceManagerService);
104
- this.principalService = inject(PrincipalWebService);
105
- this.appService = inject(AppService);
106
- }
107
- ngOnInit() {
108
- this.subscription.add(this.loginService.events.pipe(filter(e => e.type === 'login-complete'), tap(_ => this.instantiateChatService()), switchMap(() => this.chatService.assistantConfig$), filter(config => !!config)).subscribe(_ => {
109
- var _a;
110
- this.isAdmin = this.principalService.principal.isAdministrator;
111
- // Init config with a copy of the original chat config, so that it won't be modified by the user until he clicks on save
112
- this.config = JSON.parse(JSON.stringify(this.chatService.assistantConfig$.value));
113
- // Show debug toggle whether the user is an admin or a delegated admin with the debug mode enabled in the uiSettings
114
- this.showDebugToggle = (_a = (this.isAdmin || !!this.chatService.userOverride$.value || (this.principalService.principal.isDelegatedAdmin && this.config.uiSettings.debug))) !== null && _a !== void 0 ? _a : false;
115
- this.selectedModel = this.chatService.getModel(this.config.defaultValues.service_id, this.config.defaultValues.model_id);
116
- this.initFunctionsList();
117
- }));
118
- }
119
- ngOnDestroy() {
120
- this.subscription.unsubscribe();
121
- }
122
- get hasPrompts() {
123
- return this.isAdmin
124
- || !!this.config.uiSettings.displaySystemPrompt
125
- || !!this.config.uiSettings.displayUserPrompt;
126
- }
127
- get hasAdvancedParameters() {
128
- return this.isAdmin
129
- || !!this.config.uiSettings.temperature
130
- || !!this.config.uiSettings.top_p
131
- || !!this.config.uiSettings.max_tokens;
132
- }
133
- get hasModel() {
134
- return this.isAdmin
135
- || !!this.config.uiSettings.servicesModels
136
- || !!this.config.uiSettings.functions
137
- || !!this.config.uiSettings.debug
138
- || !!this.config.uiSettings.temperature
139
- || !!this.config.uiSettings.top_p
140
- || !!this.config.uiSettings.max_tokens;
141
- }
142
- instantiateChatService() {
143
- this.chatService = this.instanceManagerService.getInstance(this.instanceId);
144
- }
145
- onChatModelChange(selectedModel) {
146
- // Update properties based on the selected model
147
- this.config.defaultValues.service_id = selectedModel.serviceId;
148
- this.config.defaultValues.model_id = selectedModel.modelId;
149
- }
150
- getFunctionDescription(name) {
151
- var _a, _b;
152
- return ((_b = (_a = this.chatService.functions) === null || _a === void 0 ? void 0 : _a.find(fn => fn.functionName === name)) === null || _b === void 0 ? void 0 : _b.description) || "";
153
- }
154
- toggleFunctionsSelection(name) {
155
- // Update the enabled property of the function
156
- const index = this.config.defaultValues.functions.findIndex(func => func.name === name);
157
- this.config.defaultValues.functions[index].enabled = this.functions[index].enabled;
158
- }
159
- initFunctionsList() {
160
- this.functions = this.config.defaultValues.functions.filter(func => { var _a; return !!((_a = this.chatService.functions) === null || _a === void 0 ? void 0 : _a.find(fn => fn.functionName === func.name)); });
161
- }
162
- /**
163
- * Save the new chat config in the chat service and the user preferences
164
- * If the user has never modified the default values, we need to save the hash of the standard default values, as defined by the admin, in order to properly track changes afterwards.
165
- */
166
- save() {
167
- var _a, _b, _c;
168
- const userSettingsConfig = this.chatService.assistants[this.instanceId] || {};
169
- if (!userSettingsConfig.defaultValues) { // At this point, it is the very first time the user makes changes to the default values
170
- const standardChatConfig = (_c = (_b = (_a = this.appService.app) === null || _a === void 0 ? void 0 : _a.data) === null || _b === void 0 ? void 0 : _b.assistants) === null || _c === void 0 ? void 0 : _c[this.instanceId];
171
- const hashes = Object.assign(Object.assign({}, userSettingsConfig.hashes), { "applied-defaultValues-hash": Utils.sha512(JSON.stringify(standardChatConfig.defaultValues)) });
172
- this.chatService.updateChatConfig(this.config, hashes);
173
- }
174
- else {
175
- this.chatService.updateChatConfig(this.config);
176
- }
177
- this.chatService.generateAuditEvent("configuration.edit", { 'configuration': JSON.stringify(this.config) });
178
- this._update.emit(this.config);
179
- }
180
- /**
181
- * Cancel the current changes
182
- */
183
- cancel() {
184
- this._cancel.emit(this.chatService.assistantConfig$.value);
185
- }
186
- }
187
- ChatSettingsV3Component.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.0.6", ngImport: i0, type: ChatSettingsV3Component, deps: [], target: i0.ɵɵFactoryTarget.Component });
188
- ChatSettingsV3Component.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "14.0.6", type: ChatSettingsV3Component, isStandalone: true, selector: "sq-chat-settings-v3", inputs: { instanceId: "instanceId" }, outputs: { _update: "update", _cancel: "cancel" }, ngImport: i0, template: "<div class=\"sq-chat-settings\" *ngIf=\"isAdmin || config.uiSettings.display\">\n <div class=\"settings-panel card-body small\" *ngIf=\"config\">\n\n <h5 *ngIf=\"hasModel\">Model</h5>\n <div class=\"mb-2\" *ngIf=\"isAdmin || config.uiSettings.servicesModels\">\n <label for=\"gllmModel\" class=\"form-label\">Model</label>\n <select class=\"form-select\" id=\"gllmModel\" [(ngModel)]=\"selectedModel\" (ngModelChange)=\"onChatModelChange($event)\">\n <option *ngFor=\"let model of chatService.models\" [ngValue]=\"model\">{{model.name}}</option>\n </select>\n </div>\n\n <div class=\"mb-4\" *ngIf=\"isAdmin || config.uiSettings.functions\">\n <label for=\"gllmFunctions\" class=\"form-label\">Functions</label>\n <div id=\"gllmFunctions\" *ngFor=\"let func of functions\" class=\"multi-option form-check form-switch\">\n <input class=\"form-check-input\" type=\"checkbox\" role=\"switch\" [id]=\"func.name\" [(ngModel)]=\"func.enabled\"\n (ngModelChange)=\"toggleFunctionsSelection(func.name)\">\n <label class=\"form-check-label\" [for]=\"func.name\" [title]=\"getFunctionDescription(func.name)\">{{ func.name }}</label>\n </div>\n </div>\n\n <div class=\"form-check form-switch mb-2\" *ngIf=\"showDebugToggle\">\n <input class=\"form-check-input\" type=\"checkbox\" role=\"switch\" id=\"debug\" [(ngModel)]=\"config.defaultValues.debug\">\n <label class=\"form-check-label\" for=\"debug\">Debug</label>\n </div>\n\n <details *ngIf=\"hasAdvancedParameters\">\n <summary>Advanced parameters</summary>\n <div class=\"mb-2\" *ngIf=\"isAdmin || config.uiSettings.temperature\">\n <label for=\"temperature\" class=\"form-label\">Temperature: {{config.defaultValues.temperature}}</label>\n <input type=\"range\" class=\"form-range form-range-sm\" min=\"0\" max=\"2\" step=\"0.1\" id=\"temperature\"\n [(ngModel)]=\"config.defaultValues.temperature\">\n </div>\n <div class=\"mb-2\" *ngIf=\"isAdmin || config.uiSettings.top_p\">\n <label for=\"top-p\" class=\"form-label\">Top P: {{config.defaultValues.top_p}}</label>\n <input type=\"range\" class=\"form-range form-range-sm\" min=\"0\" max=\"1\" step=\"0.05\" id=\"top-p\"\n [(ngModel)]=\"config.defaultValues.top_p\">\n </div>\n <div class=\"mb-2\" *ngIf=\"isAdmin || config.uiSettings.max_tokens\">\n <label for=\"max-tokens\" class=\"form-label\">Max generated tokens per answer:\n {{config.defaultValues.max_tokens}}</label>\n <input type=\"range\" class=\"form-range form-range-sm\" min=\"1\" max=\"2048\" step=\"1\" id=\"max-tokens\"\n [(ngModel)]=\"config.defaultValues.max_tokens\">\n </div>\n </details>\n\n <hr>\n\n <h5 *ngIf=\"hasPrompts\">Prompts</h5>\n <div class=\"mb-2\" *ngIf=\"isAdmin || config.uiSettings.displaySystemPrompt\">\n <label for=\"initialSystemPrompt\" class=\"form-label\">System prompt (hidden)</label>\n <textarea class=\"form-control\" id=\"initialSystemPrompt\" [(ngModel)]=\"config.defaultValues.systemPrompt\"></textarea>\n </div>\n <div class=\"mb-2\" *ngIf=\"isAdmin || config.uiSettings.displayUserPrompt\">\n <label for=\"initialUserPrompt\" class=\"form-label\">Initial user prompt</label>\n <textarea class=\"form-control\" id=\"initialUserPrompt\" [(ngModel)]=\"config.defaultValues.userPrompt\"></textarea>\n </div>\n\n </div>\n\n <div class=\"buttons-panel d-flex justify-content-end\">\n <button class=\"btn btn-light me-1\" (click)=\"cancel()\">Cancel</button>\n <button class=\"btn btn-primary\" *ngIf=\"config\" (click)=\"save()\">Save</button>\n </div>\n\n</div>\n", styles: [":host{display:block;width:var(--ast-chat-settings-width, 100%);max-width:100%;height:100%;margin-left:auto;margin-right:auto;padding-top:var(--ast-chat-settings-padding-top, 0);padding-bottom:var(--ast-chat-settings-padding-bottom, 0)}.sq-chat-settings{display:flex;flex-direction:column;height:100%}.sq-chat-settings .settings-panel{flex-grow:1;overflow:auto}.sq-chat-settings .buttons-panel{padding-top:.5rem}\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: 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.RangeValueAccessor, selector: "input[type=range][formControlName],input[type=range][formControl],input[type=range][ngModel]" }, { kind: "directive", type: i2.CheckboxControlValueAccessor, selector: "input[type=checkbox][formControlName],input[type=checkbox][formControl],input[type=checkbox][ngModel]" }, { 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"] }] });
189
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.0.6", ngImport: i0, type: ChatSettingsV3Component, decorators: [{
190
- type: Component,
191
- args: [{ selector: 'sq-chat-settings-v3', standalone: true, imports: [CommonModule, FormsModule], template: "<div class=\"sq-chat-settings\" *ngIf=\"isAdmin || config.uiSettings.display\">\n <div class=\"settings-panel card-body small\" *ngIf=\"config\">\n\n <h5 *ngIf=\"hasModel\">Model</h5>\n <div class=\"mb-2\" *ngIf=\"isAdmin || config.uiSettings.servicesModels\">\n <label for=\"gllmModel\" class=\"form-label\">Model</label>\n <select class=\"form-select\" id=\"gllmModel\" [(ngModel)]=\"selectedModel\" (ngModelChange)=\"onChatModelChange($event)\">\n <option *ngFor=\"let model of chatService.models\" [ngValue]=\"model\">{{model.name}}</option>\n </select>\n </div>\n\n <div class=\"mb-4\" *ngIf=\"isAdmin || config.uiSettings.functions\">\n <label for=\"gllmFunctions\" class=\"form-label\">Functions</label>\n <div id=\"gllmFunctions\" *ngFor=\"let func of functions\" class=\"multi-option form-check form-switch\">\n <input class=\"form-check-input\" type=\"checkbox\" role=\"switch\" [id]=\"func.name\" [(ngModel)]=\"func.enabled\"\n (ngModelChange)=\"toggleFunctionsSelection(func.name)\">\n <label class=\"form-check-label\" [for]=\"func.name\" [title]=\"getFunctionDescription(func.name)\">{{ func.name }}</label>\n </div>\n </div>\n\n <div class=\"form-check form-switch mb-2\" *ngIf=\"showDebugToggle\">\n <input class=\"form-check-input\" type=\"checkbox\" role=\"switch\" id=\"debug\" [(ngModel)]=\"config.defaultValues.debug\">\n <label class=\"form-check-label\" for=\"debug\">Debug</label>\n </div>\n\n <details *ngIf=\"hasAdvancedParameters\">\n <summary>Advanced parameters</summary>\n <div class=\"mb-2\" *ngIf=\"isAdmin || config.uiSettings.temperature\">\n <label for=\"temperature\" class=\"form-label\">Temperature: {{config.defaultValues.temperature}}</label>\n <input type=\"range\" class=\"form-range form-range-sm\" min=\"0\" max=\"2\" step=\"0.1\" id=\"temperature\"\n [(ngModel)]=\"config.defaultValues.temperature\">\n </div>\n <div class=\"mb-2\" *ngIf=\"isAdmin || config.uiSettings.top_p\">\n <label for=\"top-p\" class=\"form-label\">Top P: {{config.defaultValues.top_p}}</label>\n <input type=\"range\" class=\"form-range form-range-sm\" min=\"0\" max=\"1\" step=\"0.05\" id=\"top-p\"\n [(ngModel)]=\"config.defaultValues.top_p\">\n </div>\n <div class=\"mb-2\" *ngIf=\"isAdmin || config.uiSettings.max_tokens\">\n <label for=\"max-tokens\" class=\"form-label\">Max generated tokens per answer:\n {{config.defaultValues.max_tokens}}</label>\n <input type=\"range\" class=\"form-range form-range-sm\" min=\"1\" max=\"2048\" step=\"1\" id=\"max-tokens\"\n [(ngModel)]=\"config.defaultValues.max_tokens\">\n </div>\n </details>\n\n <hr>\n\n <h5 *ngIf=\"hasPrompts\">Prompts</h5>\n <div class=\"mb-2\" *ngIf=\"isAdmin || config.uiSettings.displaySystemPrompt\">\n <label for=\"initialSystemPrompt\" class=\"form-label\">System prompt (hidden)</label>\n <textarea class=\"form-control\" id=\"initialSystemPrompt\" [(ngModel)]=\"config.defaultValues.systemPrompt\"></textarea>\n </div>\n <div class=\"mb-2\" *ngIf=\"isAdmin || config.uiSettings.displayUserPrompt\">\n <label for=\"initialUserPrompt\" class=\"form-label\">Initial user prompt</label>\n <textarea class=\"form-control\" id=\"initialUserPrompt\" [(ngModel)]=\"config.defaultValues.userPrompt\"></textarea>\n </div>\n\n </div>\n\n <div class=\"buttons-panel d-flex justify-content-end\">\n <button class=\"btn btn-light me-1\" (click)=\"cancel()\">Cancel</button>\n <button class=\"btn btn-primary\" *ngIf=\"config\" (click)=\"save()\">Save</button>\n </div>\n\n</div>\n", styles: [":host{display:block;width:var(--ast-chat-settings-width, 100%);max-width:100%;height:100%;margin-left:auto;margin-right:auto;padding-top:var(--ast-chat-settings-padding-top, 0);padding-bottom:var(--ast-chat-settings-padding-bottom, 0)}.sq-chat-settings{display:flex;flex-direction:column;height:100%}.sq-chat-settings .settings-panel{flex-grow:1;overflow:auto}.sq-chat-settings .buttons-panel{padding-top:.5rem}\n"] }]
192
- }], propDecorators: { instanceId: [{
193
- type: Input
194
- }], _update: [{
195
- type: Output,
196
- args: ["update"]
197
- }], _cancel: [{
198
- type: Output,
199
- args: ["cancel"]
200
- }] } });
201
-
202
- class InitialsAvatarComponent {
203
- constructor() {
204
- this.fullName = '';
205
- this.size = 1.5; // in rem
206
- }
207
- /**
208
- * Gives initials of a name and a safe color background to use,
209
- * assuming text color will be white
210
- * @param fullName full name to evaluate intials for
211
- * @param split string to use has splitter for `fullName`
212
- * @returns an `object` containing initials and color
213
- */
214
- getInitialsAndColorFromFullName(fullName, split = ' ') {
215
- return { initials: this.getInitialsFromFullName(fullName, split), color: this.getColorFromName(fullName) };
216
- }
217
- /**
218
- * Gives initials of a name, ie:
219
- * ```
220
- * getInitialForFullName('John Snow') => 'JS'
221
- * getInitialForFullName('Geralt of Rivia', ' of ') => 'GR'
222
- * ```
223
- * @param fullName full name to evaluate intial for
224
- * @param split string to use has splitter
225
- * @returns string containg the first letter of splitted name
226
- */
227
- getInitialsFromFullName(fullName, split = ' ') {
228
- var _a, _b;
229
- if (!fullName)
230
- return '';
231
- const names = fullName.split(split);
232
- return names[0][0] + ((_b = (_a = names[1]) === null || _a === void 0 ? void 0 : _a[0]) !== null && _b !== void 0 ? _b : '');
233
- }
234
- /**
235
- * Gets a random color using text as seed
236
- * @param text string to use for color generation
237
- * @returns string formatted like `rgb(xxx, xxx, xxx)`
238
- */
239
- getColorFromName(text) {
240
- const safeColor = new SafeColor({
241
- color: [255, 255, 255],
242
- contrast: 4.5
243
- });
244
- return safeColor.random(text);
245
- }
246
- }
247
- InitialsAvatarComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.0.6", ngImport: i0, type: InitialsAvatarComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
248
- InitialsAvatarComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "14.0.6", type: InitialsAvatarComponent, isStandalone: true, selector: "sq-initials-avatar", inputs: { fullName: "fullName", size: "size" }, ngImport: i0, template: "<span class=\"sq-initials-avatar\" *ngIf=\"getInitialsAndColorFromFullName(fullName) as meta\"\n [ngStyle]=\"{ 'background-color': meta.color }\" [style.height.rem]=\"size\" [style.width.rem]=\"size\"\n [style.line-height.rem]=\"size\" [style.font-size.rem]=\"size/2\">\n {{ meta.initials | uppercase }}\n</span>", styles: [".sq-initials-avatar{display:block;border-radius:50%;text-align:center;color:#fff}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i1.NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { kind: "pipe", type: i1.UpperCasePipe, name: "uppercase" }] });
249
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.0.6", ngImport: i0, type: InitialsAvatarComponent, decorators: [{
250
- type: Component,
251
- args: [{ selector: 'sq-initials-avatar', standalone: true, imports: [CommonModule], template: "<span class=\"sq-initials-avatar\" *ngIf=\"getInitialsAndColorFromFullName(fullName) as meta\"\n [ngStyle]=\"{ 'background-color': meta.color }\" [style.height.rem]=\"size\" [style.width.rem]=\"size\"\n [style.line-height.rem]=\"size\" [style.font-size.rem]=\"size/2\">\n {{ meta.initials | uppercase }}\n</span>", styles: [".sq-initials-avatar{display:block;border-radius:50%;text-align:center;color:#fff}\n"] }]
252
- }], propDecorators: { fullName: [{
253
- type: Input
254
- }], size: [{
255
- type: Input
256
- }] } });
257
-
258
- const defaultFormatIcons = {
259
- "extractslocations": { icon: "far fa-file-alt" },
260
- "matchlocations": { icon: "far fa-flag" },
261
- "geo": { icon: "fas fa-map-marker-alt" },
262
- "person": { icon: "fas fa-user" },
263
- "company": { icon: "fas fa-building" },
264
- "title": { icon: "fas fa-tag" },
265
- "modified": { icon: "far fa-calendar-alt" },
266
- "size": { icon: "fas fa-weight-hanging" },
267
- "treepath": { icon: "fas fa-folder-open" },
268
- "filename": { icon: "far fa-file-alt" },
269
- "authors": { icon: "fas fa-user-edit" },
270
- "accesslists": { icon: "fas fa-lock" },
271
- "doctype": { icon: "far fa-file" },
272
- "documentlanguages": { icon: "fas fa-globe-americas" },
273
- "globalrelevance": { icon: "far fa-star" },
274
- "indexationtime": { icon: "fas fa-search" },
275
- "concepts": { icon: "far fa-comment-dots" },
276
- "keywords": { icon: "fas fa-tags" },
277
- "matchingpartnames": { icon: "fas fa-align-left" },
278
- "msgfrom": { icon: "fas fa-envelope" },
279
- "msgto": { icon: "fas fa-envelope-open-text" },
280
- "file": { icon: "far fa-file" },
281
- "htm": { icon: "fas fa-globe-europe", color: '#4545bf' },
282
- "html": { icon: "fas fa-globe-europe", color: '#4545bf' },
283
- "xhtm": { icon: "fas fa-globe-europe", color: '#4545bf' },
284
- "xhtml": { icon: "fas fa-globe-europe", color: '#4545bf' },
285
- "mht": { icon: "fas fa-globe-europe", color: '#4545bf' },
286
- "doc": { icon: "far fa-file-word", color: '#3f3fca' },
287
- "docx": { icon: "far fa-file-word", color: '#3f3fca' },
288
- "docm": { icon: "far fa-file-word", color: '#3f3fca' },
289
- "dot": { icon: "far fa-file-word", color: '#3f3fca' },
290
- "dotx": { icon: "far fa-file-word", color: '#3f3fca' },
291
- "dotm": { icon: "far fa-file-word", color: '#3f3fca' },
292
- "rtf": { icon: "far fa-file-word", color: '#3f3fca' },
293
- "odt": { icon: "far fa-file-word", color: 'grey' },
294
- "ott": { icon: "far fa-file-word", color: 'grey' },
295
- "gdoc": { icon: "far fa-file-word", color: 'blue' },
296
- "xls": { icon: "far fa-file-excel", color: 'green' },
297
- "xlsx": { icon: "far fa-file-excel", color: 'green' },
298
- "xlt": { icon: "far fa-file-excel", color: 'green' },
299
- "xltx": { icon: "far fa-file-excel", color: 'green' },
300
- "xlsm": { icon: "far fa-file-excel", color: 'green' },
301
- "xltm": { icon: "far fa-file-excel", color: 'green' },
302
- "gsheet": { icon: "far fa-file-excel", color: 'darkgreen' },
303
- "ods": { icon: "far fa-file-excel", color: 'lightgreen' },
304
- "ots": { icon: "far fa-file-excel", color: 'lightgreen' },
305
- "ppt": { icon: "far fa-file-powerpoint", color: '#e64b30' },
306
- "pptx": { icon: "far fa-file-powerpoint", color: '#e64b30' },
307
- "pptm": { icon: "far fa-file-powerpoint", color: '#e64b30' },
308
- "pptm2": { icon: "far fa-file-powerpoint", color: '#e64b30' },
309
- "pps": { icon: "far fa-file-powerpoint", color: '#e64b30' },
310
- "ppsx": { icon: "far fa-file-powerpoint", color: '#e64b30' },
311
- "ppsm": { icon: "far fa-file-powerpoint", color: '#e64b30' },
312
- "pot": { icon: "far fa-file-powerpoint", color: '#e64b30' },
313
- "potx": { icon: "far fa-file-powerpoint", color: '#e64b30' },
314
- "potm": { icon: "far fa-file-powerpoint", color: '#e64b30' },
315
- "odp": { icon: "far fa-file-powerpoint", color: 'red' },
316
- "otp": { icon: "far fa-file-powerpoint", color: 'red' },
317
- "gslides": { icon: "far fa-file-powerpoint", color: 'orange' },
318
- "pdf": { icon: "far fa-file-pdf", color: '#ec2e2e' },
319
- "jpg": { icon: "far fa-file-image", color: '#4545bf' },
320
- "jpeg": { icon: "far fa-file-image", color: '#4545bf' },
321
- "bmp": { icon: "far fa-file-image", color: '#4545bf' },
322
- "tiff": { icon: "far fa-file-image", color: '#4545bf' },
323
- "tif": { icon: "far fa-file-image", color: '#4545bf' },
324
- "gif": { icon: "far fa-file-image", color: '#4545bf' },
325
- "png": { icon: "far fa-file-image", color: '#4545bf' },
326
- "mp4": { icon: "far fa-file-video", color: '#4545bf' },
327
- "flv": { icon: "far fa-file-video", color: '#4545bf' },
328
- "swf": { icon: "far fa-file-video", color: '#4545bf' },
329
- "mts": { icon: "far fa-file-video", color: '#4545bf' },
330
- "divx": { icon: "far fa-file-video", color: '#4545bf' },
331
- "wmv": { icon: "far fa-file-video", color: '#4545bf' },
332
- "avi": { icon: "far fa-file-video", color: '#4545bf' },
333
- "mov": { icon: "far fa-file-video", color: '#4545bf' },
334
- "mpg": { icon: "far fa-file-video", color: '#4545bf' },
335
- "mpeg": { icon: "far fa-file-video", color: '#4545bf' },
336
- "asf": { icon: "far fa-file-video", color: '#4545bf' },
337
- "rm": { icon: "far fa-file-video", color: '#4545bf' },
338
- "mp3": { icon: "far fa-file-audio", color: 'lightblue' },
339
- "wav": { icon: "far fa-file-audio", color: 'lightblue' },
340
- "ogg": { icon: "far fa-file-audio", color: 'lightblue' },
341
- "wma": { icon: "far fa-file-audio", color: 'lightblue' },
342
- "aac": { icon: "far fa-file-audio", color: 'lightblue' },
343
- "m3u": { icon: "far fa-file-audio", color: 'lightblue' },
344
- "txt": { icon: "far fa-file-alt", color: '#202020' },
345
- "text": { icon: "far fa-file-alt", color: '#202020' },
346
- "xml": { icon: "far fa-file-code", color: '#4545bf' },
347
- "cs": { icon: "far fa-file-code", color: '#4545bf' },
348
- "java": { icon: "far fa-file-code", color: '#4545bf' },
349
- "cpp": { icon: "far fa-file-code", color: '#4545bf' },
350
- "c": { icon: "far fa-file-code", color: '#4545bf' },
351
- "h": { icon: "far fa-file-code", color: '#4545bf' },
352
- "hpp": { icon: "far fa-file-code", color: '#4545bf' },
353
- "js": { icon: "far fa-file-code", color: '#4545bf' },
354
- "ts": { icon: "far fa-file-code", color: '#4545bf' },
355
- "zip": { icon: "far fa-file-archive", color: 'yellow' },
356
- "7zip": { icon: "far fa-file-archive", color: 'yellow' },
357
- "7z": { icon: "far fa-file-archive", color: 'yellow' },
358
- "rar": { icon: "far fa-file-archive", color: 'yellow' },
359
- "gz": { icon: "far fa-file-archive", color: 'yellow' },
360
- "notes": { icon: "fas fa-file-invoice", color: 'orange' },
361
- "quickr": { icon: "fas fa-file-invoice", color: 'orange' },
362
- "email": { icon: "far fa-envelope", color: 'black' },
363
- "mail": { icon: "far fa-envelope", color: 'black' },
364
- "msg": { icon: "far fa-envelope", color: 'black' },
365
- "mdb": { icon: "far fa-database", color: 'purple' },
366
- "odb": { icon: "far fa-database", color: 'darkred' },
367
- "otb": { icon: "far fa-database", color: 'darkred' },
368
- "xsn": { icon: "fas fa-file-excel", color: 'purple' },
369
- "gform": { icon: "fas fa-file-excel", color: 'purple' },
370
- "one": { icon: "far fa-book", color: 'purple' },
371
- "odf": { icon: "fas fa-file-medical-alt", color: 'grey' },
372
- "otf": { icon: "fas fa-file-medical-alt", color: 'grey' },
373
- "vsdx": { icon: "far fa-object-group", color: 'purple' },
374
- "vsx": { icon: "far fa-object-group", color: 'purple' },
375
- "vtx": { icon: "far fa-object-group", color: 'purple' },
376
- "vdx": { icon: "far fa-object-group", color: 'purple' },
377
- "vssx": { icon: "far fa-object-group", color: 'purple' },
378
- "vstx": { icon: "far fa-object-group", color: 'purple' },
379
- "vsdm": { icon: "far fa-object-group", color: 'purple' },
380
- "vssm": { icon: "far fa-object-group", color: 'purple' },
381
- "vstm": { icon: "far fa-object-group", color: 'purple' },
382
- "vdw": { icon: "far fa-object-group", color: 'purple' },
383
- "vsd": { icon: "far fa-object-group", color: 'purple' },
384
- "vss": { icon: "far fa-object-group", color: 'purple' },
385
- "vst": { icon: "far fa-object-group", color: 'purple' },
386
- "odg": { icon: "far fa-object-group", color: 'orange' },
387
- "otg": { icon: "far fa-object-group", color: 'orange' },
388
- "gdraw": { icon: "far fa-object-group", color: 'red' },
389
- "pub": { icon: "far fa-object-group", color: 'darkgreen' },
390
- "ldap": { icon: "far fa-users", color: 'brown' },
391
- "ad": { icon: "far fa-users", color: 'brown' },
392
- "mmp": { icon: "fas fa-file-medical", color: 'grey' },
393
- "mppx": { icon: "fas fa-file-medical", color: 'grey' },
394
- };
395
-
396
- class FormatIconComponent {
397
- constructor() {
398
- this._formatIcons = defaultFormatIcons;
399
- }
400
- ngOnChanges() {
401
- const icon = this.extension ? this._formatIcons[this.extension] : undefined;
402
- this.icon = (icon === null || icon === void 0 ? void 0 : icon.icon) || this._formatIcons.file.icon;
403
- }
404
- }
405
- FormatIconComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.0.6", ngImport: i0, type: FormatIconComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
406
- FormatIconComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "14.0.6", type: FormatIconComponent, isStandalone: true, selector: "sq-format-icon", inputs: { extension: "extension" }, usesOnChanges: true, ngImport: i0, template: "<span *ngIf=\"icon\" class=\"{{icon}}\"></span>", dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }], encapsulation: i0.ViewEncapsulation.None });
407
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.0.6", ngImport: i0, type: FormatIconComponent, decorators: [{
408
- type: Component,
409
- args: [{ selector: 'sq-format-icon', encapsulation: ViewEncapsulation.None, standalone: true, imports: [CommonModule], template: "<span *ngIf=\"icon\" class=\"{{icon}}\"></span>" }]
410
- }], propDecorators: { extension: [{
411
- type: Input
412
- }] } });
413
-
414
- class ChatReferenceComponent {
415
- constructor() {
416
- this.openDocument = new EventEmitter();
417
- this.openPreview = new EventEmitter();
418
- }
419
- get parts() {
420
- if (!this.attachment)
421
- return [];
422
- return this.attachment.parts.filter(part => (!this.partId || part.partId === this.partId) && !!part.text);
423
- }
424
- expandAttachment() {
425
- if (this.partId)
426
- return;
427
- this.attachment['$expanded'] = !this.attachment['$expanded'];
428
- }
429
- }
430
- ChatReferenceComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.0.6", ngImport: i0, type: ChatReferenceComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
431
- 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\" (click)=\"expandAttachment()\">\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\">\n {{attachment.record.title}}\n </a>\n <i class=\"fas fa-eye\" (click)=\"$event.stopPropagation(); 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)=\"$event.stopPropagation(); 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 class=\"w-100 pe-2\" [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-error{background-color:var(--ast-error-bg, rgba(249, 58, 55, .2));color:var(--ast-action-buttons-color, inherit)}.ast-error:hover{color:var(--ast-error-color, rgba(249, 58, 55, .7))}.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-uploaded-doc-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;--ast-action-buttons-color: white;--ast-action-buttons-hover-color: #6dbee6;--ast-report-bg: #070707}: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);cursor:pointer}.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"] }] });
432
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.0.6", ngImport: i0, type: ChatReferenceComponent, decorators: [{
433
- type: Component,
434
- 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\" (click)=\"expandAttachment()\">\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\">\n {{attachment.record.title}}\n </a>\n <i class=\"fas fa-eye\" (click)=\"$event.stopPropagation(); 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)=\"$event.stopPropagation(); 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 class=\"w-100 pe-2\" [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-error{background-color:var(--ast-error-bg, rgba(249, 58, 55, .2));color:var(--ast-action-buttons-color, inherit)}.ast-error:hover{color:var(--ast-error-color, rgba(249, 58, 55, .7))}.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-uploaded-doc-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;--ast-action-buttons-color: white;--ast-action-buttons-hover-color: #6dbee6;--ast-report-bg: #070707}: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);cursor:pointer}.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"] }]
435
- }], propDecorators: { reference: [{
436
- type: Input
437
- }], attachment: [{
438
- type: Input
439
- }], partId: [{
440
- type: Input
441
- }], openDocument: [{
442
- type: Output
443
- }], openPreview: [{
444
- type: Output
445
- }] } });
446
-
447
- Chart.register(...registerables);
448
- class ChartComponent {
449
- constructor() { }
450
- ngOnChanges(changes) {
451
- if (changes['rawChartData']) {
452
- clearTimeout(this.debounceTimer);
453
- this.debounceTimer = setTimeout(() => this.initializeChart(), 300);
454
- }
455
- }
456
- initializeChart() {
457
- var _a, _b, _c;
458
- (_a = Chart.getChart('chart-canvas')) === null || _a === void 0 ? void 0 : _a.destroy();
459
- const canvasElement = document.getElementById('chart-canvas');
460
- if (canvasElement) {
461
- try {
462
- let chartInput = (_c = (_b = this.rawChartData.match(/<chartjs>({[\s\S]*?)<\/chartjs>/)) === null || _b === void 0 ? void 0 : _b[1]) === null || _c === void 0 ? void 0 : _c.trim().replace(/'/g, '"');
463
- let chartConfig = JSON.parse(chartInput);
464
- this.chart = new Chart('chart-canvas', chartConfig);
465
- }
466
- catch (error) {
467
- console.error('Invalid chart configuration, check the assistant configuration: ', error);
468
- }
469
- }
470
- else {
471
- console.error('Chart Canvas is not found');
472
- }
473
- }
474
- }
475
- ChartComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.0.6", ngImport: i0, type: ChartComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
476
- ChartComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "14.0.6", type: ChartComponent, isStandalone: true, selector: "sq-assistant-chart", inputs: { rawChartData: "rawChartData" }, usesOnChanges: true, ngImport: i0, template: "<div class=\"chart-container\">\n <canvas id=\"chart-canvas\">{{ chart }}</canvas>\n</div>\n", styles: [""], dependencies: [{ kind: "ngmodule", type: CommonModule }] });
477
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.0.6", ngImport: i0, type: ChartComponent, decorators: [{
478
- type: Component,
479
- args: [{ selector: 'sq-assistant-chart', standalone: true, imports: [CommonModule], template: "<div class=\"chart-container\">\n <canvas id=\"chart-canvas\">{{ chart }}</canvas>\n</div>\n" }]
480
- }], ctorParameters: function () { return []; }, propDecorators: { rawChartData: [{
481
- type: Input
482
- }] } });
483
-
484
- class ChatMessageComponent {
485
- constructor(searchService, ui, principalService, cdr, el) {
486
- this.searchService = searchService;
487
- this.ui = ui;
488
- this.principalService = principalService;
489
- this.cdr = cdr;
490
- this.el = el;
491
- this.canEdit = false;
492
- this.canRegenerate = false;
493
- this.canCopy = false;
494
- this.canDebug = false;
495
- this.canLike = false;
496
- this.canDislike = false;
497
- this.openDocument = new EventEmitter();
498
- this.openPreview = new EventEmitter();
499
- this.suggestAction = new EventEmitter();
500
- this.edit = new EventEmitter();
501
- this.copy = new EventEmitter();
502
- this.regenerate = new EventEmitter();
503
- this.like = new EventEmitter();
504
- this.dislike = new EventEmitter();
505
- this.debug = new EventEmitter();
506
- this.references = [];
507
- this.referenceMap = new Map();
508
- this.showReferences = true;
509
- this.iconSize = 24;
510
- this.hiddenTooltip = false;
511
- /**
512
- * This Unified plugin looks a text nodes and replaces any reference in the
513
- * form [1], [2.3], etc. with custom nodes of type "chat-reference".
514
- */
515
- this.referencePlugin = (tree) => {
516
- const references = new Set();
517
- // Visit all text nodes
518
- visit(tree, "text", (node, index, parent) => {
519
- var _a;
520
- let text = node.value;
521
- text = this.reformatReferences(text);
522
- const matches = this.getReferenceMatches(text);
523
- // Quit if no references were found
524
- if (matches.length === 0) {
525
- return CONTINUE;
526
- }
527
- const nodes = [];
528
- for (let match of matches) {
529
- const refId = match[1].trim();
530
- const [ref] = refId.split(".");
531
- // We find a valid reference in the text
532
- if (!isNaN(+ref)) {
533
- references.add(+ref); // Add it to the set of used references
534
- // If needed, insert a text node before the reference
535
- const current = (_a = nodes.at(-1)) !== null && _a !== void 0 ? _a : { end: 0 };
536
- if (match.index > current.end) {
537
- nodes.push({ type: "text", value: text.substring(current.end, match.index), end: match.index });
538
- }
539
- // Add a custom reference node
540
- nodes.push({ type: "chat-reference", refId, end: match.index + match[0].length });
541
- }
542
- }
543
- // Quit if no references were found
544
- if (nodes.length === 0) {
545
- return CONTINUE;
546
- }
547
- if (nodes.at(-1).end < text.length) {
548
- nodes.push({ type: "text", value: text.substring(nodes.at(-1).end, text.length), end: text.length });
549
- }
550
- // Delete the current text node from the parent and replace it with the new nodes
551
- parent.children.splice(index, 1, ...nodes);
552
- return index + nodes.length; // Visit the next node after the inserted ones
553
- });
554
- if (references.size > 0) {
555
- this.references = Array.from(references.values())
556
- .sort((a, b) => a - b)
557
- .map(r => '' + r);
558
- this.cdr.detectChanges();
559
- }
560
- return tree;
561
- };
562
- this.placeholderPlugin = (tree) => {
563
- visit(tree, "text", (node, index, parent) => {
564
- parent.children.push({ type: "streaming-placeholder" });
565
- return EXIT;
566
- }, true);
567
- return tree;
568
- };
569
- }
570
- ngOnChanges(changes) {
571
- var _a, _b, _c, _d, _e;
572
- if (changes.streaming) {
573
- this.collapseProgress = !this.streaming;
574
- }
575
- if (((_a = this.message) === null || _a === void 0 ? void 0 : _a.role) === "assistant") {
576
- this.references = [];
577
- this.referenceMap.clear();
578
- for (let m of this.conversation) {
579
- if (m.additionalProperties.$attachment) {
580
- for (const attachment of m.additionalProperties.$attachment) {
581
- this.referenceMap.set('' + attachment.contextId, Object.assign(Object.assign({}, attachment), { $partId: (_c = (_b = attachment.parts) === null || _b === void 0 ? void 0 : _b[0]) === null || _c === void 0 ? void 0 : _c.partId }));
582
- if (!!attachment.parts && attachment.parts.length > 0) {
583
- for (let i = 0; i < attachment.parts.length; i++) {
584
- const refId = `${attachment.contextId}.${attachment.parts[i].partId}`;
585
- this.referenceMap.set(refId, Object.assign(Object.assign({}, attachment), { $partId: (_e = (_d = attachment.parts) === null || _d === void 0 ? void 0 : _d[i]) === null || _e === void 0 ? void 0 : _e.partId }));
586
- }
587
- }
588
- }
589
- }
590
- if (m.content.startsWith("<chartjs>")) {
591
- m.messageType = "CHART";
592
- }
593
- else {
594
- m.messageType = "MARKDOWN";
595
- }
596
- }
597
- this.processor = unified()
598
- .use(remarkParse)
599
- .use(remarkGfm)
600
- .use(() => this.referencePlugin);
601
- if (this.streaming) {
602
- this.processor = this.processor.use(() => this.placeholderPlugin);
603
- }
604
- }
605
- }
606
- ngAfterViewInit() {
607
- var _a;
608
- (_a = Prism === null || Prism === void 0 ? void 0 : Prism.highlightAllUnder) === null || _a === void 0 ? void 0 : _a.call(Prism, this.el.nativeElement);
609
- }
610
- get name() {
611
- return !this.principalService.principal ? ''
612
- : this.principalService.principal['fullName'] || this.principalService.principal.name;
613
- }
614
- get isAdmin() {
615
- var _a;
616
- return ((_a = this.principalService.principal) === null || _a === void 0 ? void 0 : _a.isAdministrator) || false;
617
- }
618
- getLinkText(node) {
619
- if (node.text) {
620
- return node.text; // Return directly if text is provided in node.text ([Example link](https://example.com))
621
- }
622
- else if (node.children && node.children.length > 0) {
623
- // Recursively search for text content in child nodes
624
- for (const child of node.children) {
625
- if (child.type === 'text' && child.value) {
626
- return child.value; // Return the value of the first text node found ([**Emphasized Link Text**](https://example.com))
627
- }
628
- else if (child.children && child.children.length > 0) {
629
- const textContent = this.getLinkText(child); // Recursively search child nodes ([![Example image](https://example.com/image.png)](https://example.com))
630
- if (textContent) {
631
- return textContent; // Return text content if found
632
- }
633
- }
634
- }
635
- }
636
- return 'link'; // Return empty string if no text content is found
637
- }
638
- /**
639
- * Reformat [ids: 12.2, 42.5] to [12.2][42.5]
640
- */
641
- reformatReferences(content) {
642
- return content.replace(/\[(?:ids?:?\s*)?(?:documents?:?\s*)?(\s*(?:,?\s*\d+(?:\.\d+)?(?:\.part)?\s*)+)\]/g, (str, match) => `[${match.replace(/\.part/g, "").split(',').join("] [")}]`);
643
- }
644
- /**
645
- * Match all references in a given message
646
- */
647
- getReferenceMatches(content) {
648
- return Array.from(content.matchAll(/\[(\s*\d+(?:\.\d+)?\s*)\]/g));
649
- }
650
- _copyToClipboard(content) {
651
- this.ui.copyToClipboard(content);
652
- }
653
- copyMessage(message) {
654
- const content = message.content.replaceAll(/\s+\[.+?\]/g, "");
655
- this._copyToClipboard(content);
656
- this.copy.emit(message);
657
- }
658
- copyCode(code) {
659
- this._copyToClipboard(code);
660
- }
661
- openAttachmentPreview(attachment, partId) {
662
- this.openPreview.emit({ reference: attachment, partId });
663
- this.hideTooltip();
664
- }
665
- openOriginalAttachment(attachment, partId) {
666
- this.openDocument.emit({ reference: attachment, partId });
667
- this.hideTooltip();
668
- }
669
- hideTooltip() {
670
- this.hiddenTooltip = true;
671
- setTimeout(() => {
672
- this.hiddenTooltip = false;
673
- });
674
- }
675
- }
676
- 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 });
677
- 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", connectionErrorMessageIcon: "connectionErrorMessageIcon", searchWarningMessageIcon: "searchWarningMessageIcon", streaming: "streaming", canEdit: "canEdit", canRegenerate: "canRegenerate", canCopy: "canCopy", canDebug: "canDebug", canLike: "canLike", canDislike: "canDislike" }, outputs: { openDocument: "openDocument", openPreview: "openPreview", suggestAction: "suggestAction", edit: "edit", copy: "copy", regenerate: "regenerate", like: "like", dislike: "dislike", debug: "debug" }, 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 <ng-container [ngSwitch]=\"message?.role\">\n <!-- For 'assistant' -->\n <i *ngSwitchCase=\"'assistant'\" [ngClass]=\"assistantMessageIcon\" [style.--sq-size.px]=\"iconSize\"></i>\n <!-- For 'user' -->\n <ng-container *ngSwitchCase=\"'user'\">\n <i *ngIf=\"!!userMessageIcon; else initialsAvatar\" [ngClass]=\"userMessageIcon\" [style.--sq-size.px]=\"iconSize\"></i>\n <ng-template #initialsAvatar>\n <sq-initials-avatar [fullName]=\"name\"></sq-initials-avatar>\n </ng-template>\n </ng-container>\n <!-- For 'connection-error' -->\n <ng-container *ngSwitchCase=\"'connection-error'\">\n <i *ngIf=\"!!connectionErrorMessageIcon; else defaultErrorIcon\" [ngClass]=\"connectionErrorMessageIcon\" [style.--sq-size.px]=\"iconSize\"></i>\n <ng-template #defaultErrorIcon>\n <svg [style.--sq-size.px]=\"iconSize\" class=\"connection-error\" xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 512 512\">\n <path fill=\"currentColor\" d=\"M17.1 292c-12.9-22.3-12.9-49.7 0-72L105.4 67.1c12.9-22.3 36.6-36 62.4-36l176.6 0c25.7 0 49.5 13.7 62.4 36L494.9 220c12.9 22.3 12.9 49.7 0 72L406.6 444.9c-12.9 22.3-36.6 36-62.4 36l-176.6 0c-25.7 0-49.5-13.7-62.4-36L17.1 292zM256 128c-13.3 0-24 10.7-24 24l0 112c0 13.3 10.7 24 24 24s24-10.7 24-24l0-112c0-13.3-10.7-24-24-24zm32 224a32 32 0 1 0 -64 0 32 32 0 1 0 64 0z\"/>\n </svg>\n </ng-template>\n </ng-container>\n <!-- For 'search-warning' -->\n <ng-container *ngSwitchCase=\"'search-warning'\">\n <i *ngIf=\"!!searchWarningMessageIcon; else defaultWarningIcon\" [ngClass]=\"searchWarningMessageIcon\" [style.--sq-size.px]=\"iconSize\"></i>\n <ng-template #defaultWarningIcon>\n <svg [style.--sq-size.px]=\"iconSize\" class=\"search-warning\" xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 384 512\">\n <path fill=\"currentColor\" d=\"M272 384c9.6-31.9 29.5-59.1 49.2-86.2c0 0 0 0 0 0c5.2-7.1 10.4-14.2 15.4-21.4c19.8-28.5 31.4-63 31.4-100.3C368 78.8 289.2 0 192 0S16 78.8 16 176c0 37.3 11.6 71.9 31.4 100.3c5 7.2 10.2 14.3 15.4 21.4c0 0 0 0 0 0c19.8 27.1 39.7 54.4 49.2 86.2l160 0zM192 512c44.2 0 80-35.8 80-80l0-16-160 0 0 16c0 44.2 35.8 80 80 80zm0-448c13.3 0 24 10.7 24 24l0 112c0 13.3-10.7 24-24 24s-24-10.7-24-24l0-112c0-13.3 10.7-24 24-24zM160 288a32 32 0 1 1 64 0 32 32 0 1 1 -64 0z\"/>\n </svg>\n </ng-template>\n </ng-container>\n </ng-container>\n\n</span>\n\n<!-- Message body -->\n<div class=\"flex-grow-1\" style=\"min-width: 0;\" [ngClass]=\"'message-'+message.role\">\n\n <!-- Progress steps -->\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-spinner fa-pulse step-ongoing\" *ngIf=\"!step.done && streaming\"></i>\n <i class=\"fas fa-fw fa-ban step-error\" *ngIf=\"!step.done && !streaming\"></i>\n <span class=\"ms-2 fw-bold\">{{step.title}}</span>\n <span *ngIf=\"step.content\" [innerHTML]=\"': ' + step.content\"></span>\n </li>\n </ul>\n </ng-template>\n </sq-collapse>\n </div>\n\n <!-- Message content -->\n <div class=\"message-content\" *ngIf=\"message.content\">\n\n <div *ngIf=\"message?.role === 'assistant' && message.messageType === 'CHART'\">\n <sq-assistant-chart [rawChartData]=\"message.content\"></sq-assistant-chart>\n </div>\n\n <!-- This section is responsible for customizing the template nodes used in the application.\n Template nodes are predefined structures that serve as blueprints for creating/customizing dynamic content -->\n <remark *ngIf=\"(message?.role === 'assistant' && message.messageType === 'MARKDOWN') || message?.role === 'connection-error' || message?.role === 'search-warning'\" [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)=\"copyCode(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 <details role=\"button\" class=\"select-none\" [open]=\"showReferences\">\n <summary class=\"references-title\">References</summary>\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 </details>\n </div>\n\n </div>\n\n <!-- Edit / Regenerate floating actions -->\n <div class=\"sq-chat-message-actions\" *ngIf=\"message\">\n <!-- Common action buttons for \"user\" & \"assistant\" message -->\n <button class=\"btn btn-sm\" *ngIf=\"canCopy\" sqTooltip=\"Copy text\" (click)=\"copyMessage(message)\">\n <i class=\"far fa-clipboard\"></i>\n </button>\n <!-- Action buttons for \"user\" message -->\n <button class=\"btn btn-sm\" *ngIf=\"canEdit\" sqTooltip=\"Edit message\" (click)=\"edit.emit(message)\">\n <i class=\"fas fa-edit\"></i>\n </button>\n <!-- Action buttons for \"assistant\" message -->\n <button class=\"btn btn-sm\" [class.bounce]=\"message.additionalProperties.$liked === true\" *ngIf=\"canLike\" sqTooltip=\"Like the answer\" (click)=\"like.emit()\">\n <i class=\"fa-thumbs-up {{message.additionalProperties.$liked === true ? 'fas' : 'far'}}\"></i>\n </button>\n <button class=\"btn btn-sm bounce\" [class.bounce]=\"message.additionalProperties.$disliked === true\" *ngIf=\"canDislike\" sqTooltip=\"Report an issue\" (click)=\"dislike.emit()\">\n <i class=\"fa-thumbs-down {{message.additionalProperties.$disliked === true ? 'fas' : 'far'}}\"></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\" *ngIf=\"canDebug\" sqTooltip=\"Show Log Information\" (click)=\"debug.emit(message);\">\n <i class=\"far fa-list-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-icon\" [style.width.px]=\"iconSize\"></div>\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 *ngIf=\"!hiddenTooltip\"\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-error{background-color:var(--ast-error-bg, rgba(249, 58, 55, .2));color:var(--ast-action-buttons-color, inherit)}.ast-error:hover{color:var(--ast-error-color, rgba(249, 58, 55, .7))}.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-uploaded-doc-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;--ast-action-buttons-color: white;--ast-action-buttons-hover-color: #6dbee6;--ast-report-bg: #070707}: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:table;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-connection-error .message-content{background:var(--ast-error-bg, rgba(249, 58, 55, .2))}.message-search-warning .message-content{background:var(--ast-warning-bg, #fff1b8);color:var(--ast-message-reference-color, inherit)}.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;color:var(--ast-action-buttons-color, #212529)}.sq-chat-message-actions button:hover{color:var(--ast-action-buttons-hover-color, var(--ast-primary-color, #005DA7))}.message-icon{margin-top:var(--ast-size-3, .75rem);margin-right:var(--ast-size-4, 1rem)}.connection-error{height:var(--sq-size);width:var(--sq-size);color:var(--ast-error-color, rgba(249, 58, 55, .7))}.search-warning{height:var(--sq-size);width:var(--sq-size);color:var(--ast-warning-color, #fed86f)}.step-success{color:var(--ast-primary-color, #005DA7)}.step-ongoing{color:var(--ast-secondary-color, #FF732E)}.step-error{color:var(--ast-error-color, rgba(249, 58, 55, .7))}.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: "directive", type: i1.NgSwitch, selector: "[ngSwitch]", inputs: ["ngSwitch"] }, { kind: "directive", type: i1.NgSwitchCase, selector: "[ngSwitchCase]", inputs: ["ngSwitchCase"] }, { 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"] }, { kind: "component", type: ChartComponent, selector: "sq-assistant-chart", inputs: ["rawChartData"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
678
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.0.6", ngImport: i0, type: ChatMessageComponent, decorators: [{
679
- type: Component,
680
- args: [{ selector: "sq-chat-message", changeDetection: ChangeDetectionStrategy.OnPush, standalone: true, imports: [CommonModule, UtilsModule, CollapseModule, RemarkModule,
681
- InitialsAvatarComponent, ChatReferenceComponent, ChartComponent], template: "<!-- Message icon -->\n<span class=\"message-icon\" [title]=\"message?.role\">\n <i class=\"d-block\" [style.width.px]=\"iconSize\" *ngIf=\"!message\"></i>\n <ng-container [ngSwitch]=\"message?.role\">\n <!-- For 'assistant' -->\n <i *ngSwitchCase=\"'assistant'\" [ngClass]=\"assistantMessageIcon\" [style.--sq-size.px]=\"iconSize\"></i>\n <!-- For 'user' -->\n <ng-container *ngSwitchCase=\"'user'\">\n <i *ngIf=\"!!userMessageIcon; else initialsAvatar\" [ngClass]=\"userMessageIcon\" [style.--sq-size.px]=\"iconSize\"></i>\n <ng-template #initialsAvatar>\n <sq-initials-avatar [fullName]=\"name\"></sq-initials-avatar>\n </ng-template>\n </ng-container>\n <!-- For 'connection-error' -->\n <ng-container *ngSwitchCase=\"'connection-error'\">\n <i *ngIf=\"!!connectionErrorMessageIcon; else defaultErrorIcon\" [ngClass]=\"connectionErrorMessageIcon\" [style.--sq-size.px]=\"iconSize\"></i>\n <ng-template #defaultErrorIcon>\n <svg [style.--sq-size.px]=\"iconSize\" class=\"connection-error\" xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 512 512\">\n <path fill=\"currentColor\" d=\"M17.1 292c-12.9-22.3-12.9-49.7 0-72L105.4 67.1c12.9-22.3 36.6-36 62.4-36l176.6 0c25.7 0 49.5 13.7 62.4 36L494.9 220c12.9 22.3 12.9 49.7 0 72L406.6 444.9c-12.9 22.3-36.6 36-62.4 36l-176.6 0c-25.7 0-49.5-13.7-62.4-36L17.1 292zM256 128c-13.3 0-24 10.7-24 24l0 112c0 13.3 10.7 24 24 24s24-10.7 24-24l0-112c0-13.3-10.7-24-24-24zm32 224a32 32 0 1 0 -64 0 32 32 0 1 0 64 0z\"/>\n </svg>\n </ng-template>\n </ng-container>\n <!-- For 'search-warning' -->\n <ng-container *ngSwitchCase=\"'search-warning'\">\n <i *ngIf=\"!!searchWarningMessageIcon; else defaultWarningIcon\" [ngClass]=\"searchWarningMessageIcon\" [style.--sq-size.px]=\"iconSize\"></i>\n <ng-template #defaultWarningIcon>\n <svg [style.--sq-size.px]=\"iconSize\" class=\"search-warning\" xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 384 512\">\n <path fill=\"currentColor\" d=\"M272 384c9.6-31.9 29.5-59.1 49.2-86.2c0 0 0 0 0 0c5.2-7.1 10.4-14.2 15.4-21.4c19.8-28.5 31.4-63 31.4-100.3C368 78.8 289.2 0 192 0S16 78.8 16 176c0 37.3 11.6 71.9 31.4 100.3c5 7.2 10.2 14.3 15.4 21.4c0 0 0 0 0 0c19.8 27.1 39.7 54.4 49.2 86.2l160 0zM192 512c44.2 0 80-35.8 80-80l0-16-160 0 0 16c0 44.2 35.8 80 80 80zm0-448c13.3 0 24 10.7 24 24l0 112c0 13.3-10.7 24-24 24s-24-10.7-24-24l0-112c0-13.3 10.7-24 24-24zM160 288a32 32 0 1 1 64 0 32 32 0 1 1 -64 0z\"/>\n </svg>\n </ng-template>\n </ng-container>\n </ng-container>\n\n</span>\n\n<!-- Message body -->\n<div class=\"flex-grow-1\" style=\"min-width: 0;\" [ngClass]=\"'message-'+message.role\">\n\n <!-- Progress steps -->\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-spinner fa-pulse step-ongoing\" *ngIf=\"!step.done && streaming\"></i>\n <i class=\"fas fa-fw fa-ban step-error\" *ngIf=\"!step.done && !streaming\"></i>\n <span class=\"ms-2 fw-bold\">{{step.title}}</span>\n <span *ngIf=\"step.content\" [innerHTML]=\"': ' + step.content\"></span>\n </li>\n </ul>\n </ng-template>\n </sq-collapse>\n </div>\n\n <!-- Message content -->\n <div class=\"message-content\" *ngIf=\"message.content\">\n\n <div *ngIf=\"message?.role === 'assistant' && message.messageType === 'CHART'\">\n <sq-assistant-chart [rawChartData]=\"message.content\"></sq-assistant-chart>\n </div>\n\n <!-- This section is responsible for customizing the template nodes used in the application.\n Template nodes are predefined structures that serve as blueprints for creating/customizing dynamic content -->\n <remark *ngIf=\"(message?.role === 'assistant' && message.messageType === 'MARKDOWN') || message?.role === 'connection-error' || message?.role === 'search-warning'\" [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)=\"copyCode(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 <details role=\"button\" class=\"select-none\" [open]=\"showReferences\">\n <summary class=\"references-title\">References</summary>\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 </details>\n </div>\n\n </div>\n\n <!-- Edit / Regenerate floating actions -->\n <div class=\"sq-chat-message-actions\" *ngIf=\"message\">\n <!-- Common action buttons for \"user\" & \"assistant\" message -->\n <button class=\"btn btn-sm\" *ngIf=\"canCopy\" sqTooltip=\"Copy text\" (click)=\"copyMessage(message)\">\n <i class=\"far fa-clipboard\"></i>\n </button>\n <!-- Action buttons for \"user\" message -->\n <button class=\"btn btn-sm\" *ngIf=\"canEdit\" sqTooltip=\"Edit message\" (click)=\"edit.emit(message)\">\n <i class=\"fas fa-edit\"></i>\n </button>\n <!-- Action buttons for \"assistant\" message -->\n <button class=\"btn btn-sm\" [class.bounce]=\"message.additionalProperties.$liked === true\" *ngIf=\"canLike\" sqTooltip=\"Like the answer\" (click)=\"like.emit()\">\n <i class=\"fa-thumbs-up {{message.additionalProperties.$liked === true ? 'fas' : 'far'}}\"></i>\n </button>\n <button class=\"btn btn-sm bounce\" [class.bounce]=\"message.additionalProperties.$disliked === true\" *ngIf=\"canDislike\" sqTooltip=\"Report an issue\" (click)=\"dislike.emit()\">\n <i class=\"fa-thumbs-down {{message.additionalProperties.$disliked === true ? 'fas' : 'far'}}\"></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\" *ngIf=\"canDebug\" sqTooltip=\"Show Log Information\" (click)=\"debug.emit(message);\">\n <i class=\"far fa-list-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-icon\" [style.width.px]=\"iconSize\"></div>\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 *ngIf=\"!hiddenTooltip\"\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-error{background-color:var(--ast-error-bg, rgba(249, 58, 55, .2));color:var(--ast-action-buttons-color, inherit)}.ast-error:hover{color:var(--ast-error-color, rgba(249, 58, 55, .7))}.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-uploaded-doc-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;--ast-action-buttons-color: white;--ast-action-buttons-hover-color: #6dbee6;--ast-report-bg: #070707}: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:table;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-connection-error .message-content{background:var(--ast-error-bg, rgba(249, 58, 55, .2))}.message-search-warning .message-content{background:var(--ast-warning-bg, #fff1b8);color:var(--ast-message-reference-color, inherit)}.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;color:var(--ast-action-buttons-color, #212529)}.sq-chat-message-actions button:hover{color:var(--ast-action-buttons-hover-color, var(--ast-primary-color, #005DA7))}.message-icon{margin-top:var(--ast-size-3, .75rem);margin-right:var(--ast-size-4, 1rem)}.connection-error{height:var(--sq-size);width:var(--sq-size);color:var(--ast-error-color, rgba(249, 58, 55, .7))}.search-warning{height:var(--sq-size);width:var(--sq-size);color:var(--ast-warning-color, #fed86f)}.step-success{color:var(--ast-primary-color, #005DA7)}.step-ongoing{color:var(--ast-secondary-color, #FF732E)}.step-error{color:var(--ast-error-color, rgba(249, 58, 55, .7))}.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"] }]
682
- }], ctorParameters: function () { return [{ type: i1$1.SearchService }, { type: i2$1.UIService }, { type: i3.PrincipalWebService }, { type: i0.ChangeDetectorRef }, { type: i0.ElementRef }]; }, propDecorators: { message: [{
683
- type: Input
684
- }], conversation: [{
685
- type: Input
686
- }], suggestedActions: [{
687
- type: Input
688
- }], assistantMessageIcon: [{
689
- type: Input
690
- }], userMessageIcon: [{
691
- type: Input
692
- }], connectionErrorMessageIcon: [{
693
- type: Input
694
- }], searchWarningMessageIcon: [{
695
- type: Input
696
- }], streaming: [{
697
- type: Input
698
- }], canEdit: [{
699
- type: Input
700
- }], canRegenerate: [{
701
- type: Input
702
- }], canCopy: [{
703
- type: Input
704
- }], canDebug: [{
705
- type: Input
706
- }], canLike: [{
707
- type: Input
708
- }], canDislike: [{
709
- type: Input
710
- }], openDocument: [{
711
- type: Output
712
- }], openPreview: [{
713
- type: Output
714
- }], suggestAction: [{
715
- type: Output
716
- }], edit: [{
717
- type: Output
718
- }], copy: [{
719
- type: Output
720
- }], regenerate: [{
721
- type: Output
722
- }], like: [{
723
- type: Output
724
- }], dislike: [{
725
- type: Output
726
- }], debug: [{
727
- type: Output
728
- }] } });
729
-
730
- // Define the Zod representation for the connectionSettings object
731
- const connectionSettingsSchema = z.object({
732
- connectionErrorMessage: z.string(),
733
- restEndpoint: z.string().optional(),
734
- websocketEndpoint: z.string().optional(),
735
- signalRTransport: z.enum(["WebSockets", "ServerSentEvents", "LongPolling", "None"]),
736
- signalRLogLevel: z.enum(["Critical", "Debug", "Error", "Information", "None", "Trace", "Warning"]),
737
- signalRServerTimeoutInMilliseconds: z.number().optional()
738
- }).refine(data => (!!data.restEndpoint || !!data.websocketEndpoint), {
739
- message: "Based on the provided input() protocol ('REST' or 'WEBSOCKET') to the Chat Component, either 'restEndpoint' or 'websocketEndpoint' property should be provided in the 'globalSettings' of the assistant instance.",
740
- });
741
- // Define the Zod representation for the serviceSettings object
742
- const serviceSettingsSchema = z.object({
743
- service_id: z.string(),
744
- model_id: z.string(),
745
- temperature: z.number(),
746
- top_p: z.number(),
747
- max_tokens: z.number()
748
- });
749
- // Define the Zod representation for the additionalServiceSettings object
750
- const additionalServiceSettingsSchema = z.object({});
751
- // Define the Zod representation for the additionalWorkflowProperties object
752
- const additionalWorkflowPropertiesSchema = z.object({});
753
- // Define the Zod representation for the uiSettings object
754
- const uiSettingsSchema = z.object({
755
- display: z.boolean(),
756
- servicesModels: z.boolean(),
757
- functions: z.boolean(),
758
- temperature: z.boolean(),
759
- top_p: z.boolean(),
760
- max_tokens: z.boolean(),
761
- debug: z.boolean().optional(),
762
- displaySystemPrompt: z.boolean(),
763
- displayUserPrompt: z.boolean()
764
- });
765
- // Define the Zod representation for the defaultValues object
766
- const defaultValuesSchema = z.object({
767
- service_id: z.string(),
768
- model_id: z.string(),
769
- functions: z.array(z.object({
770
- name: z.string(),
771
- enabled: z.boolean()
772
- })),
773
- temperature: z.number(),
774
- top_p: z.number(),
775
- max_tokens: z.number(),
776
- debug: z.boolean().optional(),
777
- systemPrompt: z.string(),
778
- userPrompt: z.string()
779
- });
780
- // Define the Zod representation for the action object
781
- const actionSchema = z.object({
782
- forcedWorkflow: z.string(),
783
- forcedWorkflowProperties: z.record(z.unknown()).optional() // forcedWorkflowProperties must be an object (Map equivalent)
784
- });
785
- // Define the Zod representation for the modeSettings object
786
- const initializationSchema = z.object({
787
- event: z.enum(['Query', 'Prompt']),
788
- forcedWorkflow: z.string().optional(),
789
- forcedFunction: z.string().optional(),
790
- displayUserQuery: z.boolean().optional()
791
- })
792
- // ❌ Rule 1: Cannot have both forcedWorkflow and forcedFunction
793
- .refine(data => (data.event !== "Query") || !(data.forcedWorkflow && data.forcedFunction), {
794
- message: "Both 'forcedFunction' and 'forcedWorkflow' properties are set, but they should not.",
795
- })
796
- // ❌ Rule 2: Must have one of forcedWorkflow or forcedFunction when event is 'Query'
797
- .refine(data => (data.event !== "Query") || !!data.forcedWorkflow || !!data.forcedFunction, {
798
- message: "One of 'forcedWorkflow' or 'forcedFunction' must be provided when the initialization's event is 'Query'."
799
- })
800
- // ❌ Rule 3: Must have displayUserQuery set when event is 'Query'
801
- .refine(data => (data.event !== "Query") || (data.displayUserQuery !== undefined && data.displayUserQuery !== null), {
802
- message: "'displayUserQuery' must be provided when the initialization's event is 'Query'."
803
- });
804
- const modeSettingsSchema = z.object({
805
- enabledUserInput: z.boolean(),
806
- displayUserPrompt: z.boolean(),
807
- sendUserPrompt: z.boolean(),
808
- initialization: initializationSchema,
809
- actions: z.record(actionSchema).optional()
810
- });
811
- // Define the Zod representation for the savedChatSettings object
812
- const savedChatSettingsSchema = z.object({
813
- enabled: z.boolean(),
814
- display: z.boolean()
815
- });
816
- // Define the Zod representation for the globalSettings object
817
- const globalSettingsSchema = z.object({
818
- searchWarningMessage: z.string(),
819
- disclaimer: z.string().optional(),
820
- genericChatErrorMessage: z.string().optional(),
821
- displayUserQuotaConsumption: z.boolean().optional(),
822
- displayChatTokensConsumption: z.boolean().optional()
823
- });
824
- // Define the Zod representation for the auditSettings object
825
- const auditSettingsSchema = z.object({
826
- issueTypes: z.array(z.string()).optional()
827
- });
828
- // Define the Zod representation for the entire ChatConfig object
829
- const chatConfigSchema = z.object({
830
- connectionSettings: connectionSettingsSchema,
831
- defaultValues: defaultValuesSchema,
832
- modeSettings: modeSettingsSchema,
833
- uiSettings: uiSettingsSchema,
834
- savedChatSettings: savedChatSettingsSchema,
835
- globalSettings: globalSettingsSchema,
836
- auditSettings: auditSettingsSchema.optional(),
837
- additionalServiceSettings: additionalServiceSettingsSchema,
838
- additionalWorkflowProperties: additionalWorkflowPropertiesSchema
839
- });
840
-
841
- class ChatService {
842
- constructor() {
843
- /** Emit true once the initialization of the assistant process is done. */
844
- this.initProcess$ = new BehaviorSubject(false);
845
- /** Emit true once the initialization of the assistant config is done. */
846
- this.initConfig$ = new BehaviorSubject(false);
847
- /** Emit the global configuration of the assistant. */
848
- this.assistantConfig$ = new BehaviorSubject(undefined);
849
- /** Emit true if the user has been overridden, false otherwise. */
850
- this.userOverride$ = new BehaviorSubject(undefined);
851
- /**
852
- * Emit true if the fetch of an assistant's response is ongoing (it includes Streaming status of the assistant endpoint AND saving the discussion if save Chat is enabled).
853
- * This is used to prevent multiple fetches at the same time.
854
- * Typically, there is no problem chaining fetches, but when forcing a reload after query changes cases, it can't be allowed because it breaks the whole business logic.
855
- */
856
- this.streaming$ = new BehaviorSubject(false);
857
- /** List of saved chats. */
858
- this.savedChats$ = new BehaviorSubject([]);
859
- /** Emit the saved chat to load. */
860
- this.loadSavedChat$ = new BehaviorSubject(undefined);
861
- /** Emit the quota each time the chat is invoked. */
862
- this.quota$ = new BehaviorSubject(undefined);
863
- /** Emit the calculated user's token consumption based on the quota. */
864
- this.userTokenConsumption$ = new BehaviorSubject(undefined);
865
- /** Emit the chat usage metrics each time the generation of the assistant response is completed. */
866
- this.chatUsageMetrics$ = new BehaviorSubject(undefined);
867
- /** Emit the calculated chat's token consumption based on the chat usage metrics. */
868
- this.chatTokenConsumption$ = new BehaviorSubject(undefined);
869
- /** Emit true if "CancelTasks" is ongoing. */
870
- this.stoppingGeneration$ = new BehaviorSubject(false);
871
- this.userSettingsService = inject(UserSettingsWebService);
872
- this.notificationsService = inject(NotificationsService);
873
- this.auditService = inject(AuditWebService);
874
- this.prefs = inject(UserPreferences);
875
- this.loginService = inject(LoginService);
876
- this.appService = inject(AppService);
877
- this.intlService = inject(IntlService);
878
- this.modalService = inject(ModalService);
879
- this.principalService = inject(PrincipalWebService);
880
- }
881
- get assistants() {
882
- if (!this.userSettingsService.userSettings)
883
- this.userSettingsService.userSettings = {};
884
- if (!this.userSettingsService.userSettings["assistants"])
885
- this.userSettingsService.userSettings["assistants"] = {};
886
- return this.userSettingsService.userSettings["assistants"];
887
- }
888
- /**
889
- * Get the instance ID of the chat service
890
- * @returns The instance ID of the chat service
891
- */
892
- get chatInstanceId() {
893
- return this._chatInstanceId;
894
- }
895
- /**
896
- * Persist the instance ID of the chat service
897
- * @param instanceId The instance ID of the chat service
898
- */
899
- setChatInstanceId(instanceId) {
900
- this._chatInstanceId = instanceId;
901
- }
902
- /**
903
- * Get the ID of the current chat discussion which is used to save/get/delete it
904
- * @returns The ID of the current chat discussion
905
- */
906
- get savedChatId() {
907
- return this._savedChatId;
908
- }
909
- /**
910
- * Persist the ID of the current chat discussion which is used to save/get/delete it
911
- * @param savedChatId The ID of the current chat discussion which is used to save/get/delete it
912
- */
913
- setSavedChatId(savedChatId) {
914
- this._savedChatId = savedChatId;
915
- }
916
- /**
917
- * Get the ID of the current chat discussion which is used to identify audit events
918
- * @returns The ID of the current chat discussion
919
- */
920
- get chatId() {
921
- return this._chatId;
922
- }
923
- /**
924
- * Generate an GUID for the current chat discussion which is used to identify audit events
925
- * If the discussion is saved, the savedChatId is initialized with the value of this chatId
926
- * @param chatId if provided, it will be considered as the ID of the current chat discussion which is used to identify audit events
927
- */
928
- generateChatId(chatId) {
929
- this._chatId = chatId || Utils.guid();
930
- }
931
- /**
932
- * 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.
933
- * 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.
934
- * The rest of the config object coming from "standard app config" is used as it is without any override.
935
- * Thus, the user preferences are used only for the defaultValues object.
936
- * This provide a centralized way to manage the rest of the config object by admins and ensure a unique common behavior for all users.
937
- */
938
- initChatConfig() {
939
- var _a, _b, _c, _d, _e;
940
- const key = this.chatInstanceId;
941
- const userSettingsConfig = this.assistants[key] || {};
942
- const standardChatConfig = (_c = (_b = (_a = this.appService.app) === null || _a === void 0 ? void 0 : _a.data) === null || _b === void 0 ? void 0 : _b.assistants) === null || _c === void 0 ? void 0 : _c[key];
943
- try {
944
- // Validate the whole config object against the schema
945
- chatConfigSchema.parse(standardChatConfig);
946
- // If the user preferences do not contain a config's defaultValues object, keep using the standard app config and nothing to store in the user preferences
947
- if (!userSettingsConfig.defaultValues) {
948
- this.assistantConfig$.next(Object.assign({}, standardChatConfig));
949
- this.initConfig$.next(true);
950
- }
951
- else { // If the user has its own defaultValues in its userSettings, then we need to check for potential updates made by admins in the meantime and how he wants to manage them
952
- // Retrieve already stored hashes in the user settings if exists
953
- const appliedDefaultValuesHash = (_d = userSettingsConfig.hashes) === null || _d === void 0 ? void 0 : _d["applied-defaultValues-hash"];
954
- const skippedDefaultValuesHash = (_e = userSettingsConfig.hashes) === null || _e === void 0 ? void 0 : _e["skipped-defaultValues-hash"];
955
- // Create a hash of the current defaultValues of the standardChatConfig
956
- const currentDefaultValuesHash = Utils.sha512(JSON.stringify(standardChatConfig.defaultValues));
957
- // Implement the tracking mechanism to notify the user about the available updates in the defaultValues object of the standard app config
958
- const condition = (currentDefaultValuesHash !== appliedDefaultValuesHash) && (currentDefaultValuesHash !== skippedDefaultValuesHash);
959
- if (condition) {
960
- this.modalService
961
- .confirm({
962
- title: "Available updates !",
963
- message: "Changes have been made to the default configuration. Do you want to update your own version ?",
964
- buttons: [
965
- new ModalButton({ result: -4 /* ModalResult.No */, text: "See no more" }),
966
- new ModalButton({ result: -7 /* ModalResult.Ignore */, text: "Remind me later" }),
967
- new ModalButton({ result: -1 /* ModalResult.OK */, text: "Update", primary: true })
968
- ],
969
- confirmType: 2 /* ConfirmType.Warning */
970
- }).then(res => {
971
- if (res === -1 /* ModalResult.OK */) {
972
- const hashes = Object.assign(Object.assign({}, userSettingsConfig.hashes), { "applied-defaultValues-hash": currentDefaultValuesHash, "skipped-defaultValues-hash": undefined });
973
- // Update the chat config and store its defaultValues in the user preferences
974
- this.updateChatConfig(Object.assign({}, standardChatConfig), hashes, true);
975
- this.initConfig$.next(true);
976
- this.generateAuditEvent("configuration.edit", { 'configuration': JSON.stringify(Object.assign({}, standardChatConfig)) });
977
- }
978
- else if (res === -4 /* ModalResult.No */) {
979
- // Do not notify the user about changes while this skipped version is not updated
980
- const hashes = Object.assign(Object.assign({}, userSettingsConfig.hashes), { "skipped-defaultValues-hash": currentDefaultValuesHash });
981
- this.updateChatConfig(Object.assign(Object.assign({}, standardChatConfig), { defaultValues: userSettingsConfig.defaultValues }), hashes, false);
982
- this.initConfig$.next(true);
983
- }
984
- else {
985
- // Just pick the version in the user settings, nothing to be updated
986
- this.assistantConfig$.next(Object.assign(Object.assign({}, standardChatConfig), { defaultValues: userSettingsConfig.defaultValues }));
987
- this.initConfig$.next(true);
988
- }
989
- });
990
- }
991
- else { // No available updates Or updates has been already skipped, then just pick the version in the user settings
992
- this.assistantConfig$.next(Object.assign(Object.assign({}, standardChatConfig), { defaultValues: userSettingsConfig.defaultValues }));
993
- this.initConfig$.next(true);
994
- }
995
- }
996
- }
997
- catch (error) {
998
- this.notificationsService.error(`Missing valid configuration for the assistant instance '${key}'. See the browser console messages for details on the missing or incorrect properties.`);
999
- throw new Error(`Missing valid configuration for the assistant instance '${key}' . \n ${JSON.stringify(error.issues, null, 2)}`);
1000
- }
1001
- }
1002
- /**
1003
- * Update the chat config and store its defaultValues in the user preferences
1004
- * @param config The updated chat config
1005
- * @param hashes The updated hashes to store in the user preferences
1006
- * @param notify Whether to notify the user about the update
1007
- * @param successCallback The callback to execute if the update is successful
1008
- * @param errorCallback The callback to execute if the update fails
1009
- */
1010
- updateChatConfig(config, hashes, notify = true, successCallback, errorCallback) {
1011
- this.assistantConfig$.next(config);
1012
- const assistants = Object.assign({}, this.assistants);
1013
- assistants[this.chatInstanceId] = Object.assign(Object.assign({}, assistants[this.chatInstanceId]), { defaultValues: config.defaultValues });
1014
- if (hashes)
1015
- assistants[this.chatInstanceId].hashes = hashes;
1016
- this.userSettingsService.patch({ assistants }).subscribe(next => { }, error => {
1017
- if (notify) {
1018
- errorCallback ? errorCallback() : this.notificationsService.error(`The update of the assistant instance '${this.chatInstanceId}' configuration failed`);
1019
- }
1020
- console.error("Could not patch assistants!", error);
1021
- }, () => {
1022
- if (notify) {
1023
- successCallback ? successCallback() : this.notificationsService.success(`The assistant instance '${this.chatInstanceId}' configuration has been successfully updated`);
1024
- }
1025
- });
1026
- }
1027
- /**
1028
- * A handler for quota updates each time the chat is invoked.
1029
- * It emits the updated quota to the quota$ subject, emits accordingly the updated user's tokens consumption and notifies the user if the max quota is reached.
1030
- * @param quota The updated quota
1031
- * @param propagateError Whether to propagate the error to the caller
1032
- */
1033
- updateQuota(quota, propagateError = false) {
1034
- this.quota$.next(quota);
1035
- const nextResetDate = this.formatDateTime(quota.nextResetUTC + "+00:00"); // This +00:00 is to ensure dates will be properly converted to local time
1036
- const consumptionPercentage = Math.round((quota.tokenCount * 100 / quota.periodTokens) * 100) / 100;
1037
- this.userTokenConsumption$.next({ percentage: consumptionPercentage, nextResetDate });
1038
- if (quota.maxQuotaReached) {
1039
- this.generateAuditEvent('quota.exceeded', {});
1040
- const msg = `Sorry, you have exceeded the allowed quota. Please retry starting from ${nextResetDate}.`;
1041
- this.notificationsService.error(msg);
1042
- if (propagateError)
1043
- throw new Error(msg);
1044
- }
1045
- }
1046
- /**
1047
- * A handler for chat usage metrics each time the generation of the assistant response is completed.
1048
- * It emits the chat usage metrics to the chatUsageMetrics$ subject, emits accordingly the updated chat's tokens consumption
1049
- * @param chatUsageMetrics The chat usage metrics
1050
- */
1051
- updateChatUsageMetrics(chatUsageMetrics) {
1052
- this.chatUsageMetrics$.next(chatUsageMetrics);
1053
- const currentModel = this.getModel(this.assistantConfig$.value.defaultValues.service_id, this.assistantConfig$.value.defaultValues.model_id);
1054
- const consumptionPercentage = Math.round((chatUsageMetrics.totalTokenCount * 100 / (currentModel.contextWindowSize - currentModel.maxGenerationSize)) * 100) / 100;
1055
- this.chatTokenConsumption$.next({ percentage: consumptionPercentage });
1056
- }
1057
- /**
1058
- * Get the model description for the given (serviceId + modelId)
1059
- * If a model is not found, an error message is returned
1060
- * @param serviceId The serviceId of the model
1061
- * @param modelId The modelId of the model
1062
- * @returns The model description
1063
- */
1064
- getModel(serviceId, modelId) {
1065
- var _a;
1066
- let model = (_a = this.models) === null || _a === void 0 ? void 0 : _a.find(m => m.serviceId === serviceId && m.modelId === modelId);
1067
- // Handle obsolete config
1068
- if (!model) {
1069
- this.notificationsService.error(`FATAL ERROR : The model (serviceId = '${serviceId}', modelId = '${modelId}') is no longer available. Please contact an admin for further information.`);
1070
- throw new Error(`FATAL ERROR : The model (serviceId = '${serviceId}', modelId = '${modelId}') is no longer available`);
1071
- }
1072
- return model;
1073
- }
1074
- /**
1075
- * Generate an audit event with the given type and details. The generated audit event is sent afterwards via the AuditWebService
1076
- * @param type Audit event type
1077
- * @param details Audit event details
1078
- * @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
1079
- */
1080
- generateAuditEvent(type, details, id) {
1081
- var _a;
1082
- const baseDetails = {
1083
- "url": decodeURIComponent(window.location.href),
1084
- "app": this.appService.appName,
1085
- "user-id": (_a = this.principalService.principal) === null || _a === void 0 ? void 0 : _a.userId,
1086
- "instance-id": this.chatInstanceId,
1087
- "chat-id": id || this.chatId,
1088
- "service-id": this.assistantConfig$.value.defaultValues.service_id,
1089
- "model-id": this.assistantConfig$.value.defaultValues.model_id,
1090
- };
1091
- const audit = {
1092
- type,
1093
- detail: Object.assign(Object.assign({}, baseDetails), details)
1094
- };
1095
- this.auditService.notify(audit);
1096
- }
1097
- /**
1098
- * Traverse the array from the end and track the first 'assistant' message among the last group of "assistant" messages where display is true
1099
- * @param array The array of ChatMessage to traverse
1100
- * @returns The index of the first visible assistant message among the last group of "assistant" messages in the array
1101
- */
1102
- firstVisibleAssistantMessageIndex(array) {
1103
- if (!array) {
1104
- return -1;
1105
- }
1106
- let index = array.length - 1;
1107
- let firstVisibleAssistantMessageIndex = -1;
1108
- while (index >= 0 && array[index].role === 'assistant') {
1109
- if (array[index].additionalProperties.display === true) {
1110
- firstVisibleAssistantMessageIndex = index;
1111
- }
1112
- index--;
1113
- }
1114
- return firstVisibleAssistantMessageIndex;
1115
- }
1116
- /**
1117
- * Traverse the array from the end and pick the last 'assistant' message among the last group of "assistant" messages where display is true
1118
- * @param array The array of ChatMessage to traverse
1119
- * @returns The index of the last visible assistant message among the last group of "assistant" messages in the array
1120
- */
1121
- lastVisibleAssistantMessageIndex(array) {
1122
- if (!array) {
1123
- return -1;
1124
- }
1125
- let index = array.length - 1;
1126
- let lastVisibleAssistantMessageIndex = -1;
1127
- while (index >= 0 && array[index].role === 'assistant') {
1128
- if (array[index].additionalProperties.display === true) {
1129
- lastVisibleAssistantMessageIndex = index;
1130
- break;
1131
- }
1132
- index--;
1133
- }
1134
- return lastVisibleAssistantMessageIndex;
1135
- }
1136
- /**
1137
- * Format a date string in UTC to a local date string
1138
- * @param value Date string in UTC to format
1139
- * @returns A formatted local date string
1140
- */
1141
- formatDateTime(value) {
1142
- const localDate = toDate(parseISO(value));
1143
- return this.intlService["formatTime"](localDate, { day: "numeric", month: "short", year: "numeric", timeZoneName: 'short' });
1144
- }
1145
- /**
1146
- * Takes a text prompt that may contain placeholders for variables
1147
- * and replaces these placeholders if it finds a match in the given
1148
- * context object.
1149
- */
1150
- static formatPrompt(prompt, context) {
1151
- return prompt.replace(/{{(.*?)}}/g, (match, expr) => { var _a; return (_a = get(context, expr)) !== null && _a !== void 0 ? _a : match; });
1152
- }
1153
- }
1154
- ChatService.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.0.6", ngImport: i0, type: ChatService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
1155
- ChatService.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "14.0.6", ngImport: i0, type: ChatService });
1156
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.0.6", ngImport: i0, type: ChatService, decorators: [{
1157
- type: Injectable
1158
- }] });
1159
-
1160
- class DebugMessageComponent {
1161
- constructor(ui) {
1162
- this.ui = ui;
1163
- this.level = 0; // Track the nesting level
1164
- this.parentColor = ''; // Track the parent row color
1165
- }
1166
- ngAfterViewInit() {
1167
- Prism$1.highlightAll();
1168
- }
1169
- isObject(value) {
1170
- return Utils.isObject(value);
1171
- }
1172
- getRowClass(item, index) {
1173
- if (item.isError)
1174
- return 'row-error';
1175
- if (this.level === 0)
1176
- return index % 2 === 0 ? 'row-even' : 'row-odd';
1177
- return this.parentColor;
1178
- }
1179
- copyToClipboard(code) {
1180
- this.ui.copyToClipboard(JSON.stringify(code, null, 2));
1181
- }
1182
- }
1183
- DebugMessageComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.0.6", ngImport: i0, type: DebugMessageComponent, deps: [{ token: i2$1.UIService }], target: i0.ɵɵFactoryTarget.Component });
1184
- DebugMessageComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "14.0.6", type: DebugMessageComponent, isStandalone: true, selector: "sq-debug-message", inputs: { data: "data", level: "level", parentColor: "parentColor" }, ngImport: i0, template: "<div *ngIf=\"data\" class=\"table-root\">\n <ng-container *ngFor=\"let item of data; let i = index\">\n <div *ngIf=\"item.type === 'KV'\" [ngClass]=\"getRowClass(item, i)\" class=\"table-row kv-object\">\n <div class=\"kv-key\">{{ item.data.key }}</div>\n <div class=\"kv-value\">\n <ng-container *ngIf=\"isObject(item.data.value); else normalValue\">\n <div class=\"card mb-2\">\n <div class=\"card-header\">\n <button class=\"btn btn-light btn-sm\" (click)=\"copyToClipboard(item.data.value)\"><i class=\"far fa-fw fa-clipboard\"></i> Copy code</button>\n </div>\n <pre class=\"language-json my-0 rounded-0 rounded-bottom\"><code class=\"language-json\">{{ item.data.value | json }}</code></pre>\n </div>\n </ng-container>\n <ng-template #normalValue><div class=\"data-value\">{{ item.data.value }}</div></ng-template>\n </div>\n </div>\n <div *ngIf=\"item.type === 'LIST'\" [ngClass]=\"getRowClass(item, i)\" class=\"table-row list-object\">\n <div class=\"list-name w-100\" [class.fw-bold]=\"level === 0\" (click)=\"item.expanded=!item.expanded\">\n <i class=\"fas\" [class.fa-chevron-up]=\"item.expanded\" [class.fa-chevron-down]=\"!item.expanded\"></i>\n {{ item.name }}\n </div>\n <div class=\"list-items w-100\" *ngIf=\"item.expanded\">\n <sq-debug-message [data]=\"item.items\" [level]=\"level + 1\" [parentColor]=\"getRowClass(item, i)\"></sq-debug-message>\n </div>\n </div>\n </ng-container>\n</div>\n", styles: [".table-root{display:flex;flex-direction:column;border:1px solid #ccc;width:100%;border-spacing:0}.table-row{display:flex;width:100%}.list-name{width:15%;cursor:pointer}.list-items{width:85%}.kv-key,.kv-value,.list-name{padding:8px;border:1px solid #ccc;box-sizing:border-box;word-wrap:break-word}.kv-key{width:20%}.kv-value{width:80%}.kv-value .data-value{white-space:pre-line}.kv-object,.list-object{display:flex;flex:1}.list-object{flex-direction:column}.row-even{background-color:#fff}.row-odd{background-color:#f2f8fe}.row-error{background-color:#f08080}.table-row:not(:last-child){border-bottom:1px solid #ccc}.kv-key:last-child,.kv-value:last-child,.list-name:last-child{border-right:none}\n"], dependencies: [{ kind: "component", type: DebugMessageComponent, selector: "sq-debug-message", inputs: ["data", "level", "parentColor"] }, { 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: "pipe", type: i1.JsonPipe, name: "json" }] });
1185
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.0.6", ngImport: i0, type: DebugMessageComponent, decorators: [{
1186
- type: Component,
1187
- args: [{ selector: "sq-debug-message", standalone: true, imports: [CommonModule], template: "<div *ngIf=\"data\" class=\"table-root\">\n <ng-container *ngFor=\"let item of data; let i = index\">\n <div *ngIf=\"item.type === 'KV'\" [ngClass]=\"getRowClass(item, i)\" class=\"table-row kv-object\">\n <div class=\"kv-key\">{{ item.data.key }}</div>\n <div class=\"kv-value\">\n <ng-container *ngIf=\"isObject(item.data.value); else normalValue\">\n <div class=\"card mb-2\">\n <div class=\"card-header\">\n <button class=\"btn btn-light btn-sm\" (click)=\"copyToClipboard(item.data.value)\"><i class=\"far fa-fw fa-clipboard\"></i> Copy code</button>\n </div>\n <pre class=\"language-json my-0 rounded-0 rounded-bottom\"><code class=\"language-json\">{{ item.data.value | json }}</code></pre>\n </div>\n </ng-container>\n <ng-template #normalValue><div class=\"data-value\">{{ item.data.value }}</div></ng-template>\n </div>\n </div>\n <div *ngIf=\"item.type === 'LIST'\" [ngClass]=\"getRowClass(item, i)\" class=\"table-row list-object\">\n <div class=\"list-name w-100\" [class.fw-bold]=\"level === 0\" (click)=\"item.expanded=!item.expanded\">\n <i class=\"fas\" [class.fa-chevron-up]=\"item.expanded\" [class.fa-chevron-down]=\"!item.expanded\"></i>\n {{ item.name }}\n </div>\n <div class=\"list-items w-100\" *ngIf=\"item.expanded\">\n <sq-debug-message [data]=\"item.items\" [level]=\"level + 1\" [parentColor]=\"getRowClass(item, i)\"></sq-debug-message>\n </div>\n </div>\n </ng-container>\n</div>\n", styles: [".table-root{display:flex;flex-direction:column;border:1px solid #ccc;width:100%;border-spacing:0}.table-row{display:flex;width:100%}.list-name{width:15%;cursor:pointer}.list-items{width:85%}.kv-key,.kv-value,.list-name{padding:8px;border:1px solid #ccc;box-sizing:border-box;word-wrap:break-word}.kv-key{width:20%}.kv-value{width:80%}.kv-value .data-value{white-space:pre-line}.kv-object,.list-object{display:flex;flex:1}.list-object{flex-direction:column}.row-even{background-color:#fff}.row-odd{background-color:#f2f8fe}.row-error{background-color:#f08080}.table-row:not(:last-child){border-bottom:1px solid #ccc}.kv-key:last-child,.kv-value:last-child,.list-name:last-child{border-right:none}\n"] }]
1188
- }], ctorParameters: function () { return [{ type: i2$1.UIService }]; }, propDecorators: { data: [{
1189
- type: Input
1190
- }], level: [{
1191
- type: Input
1192
- }], parentColor: [{
1193
- type: Input
1194
- }] } });
1195
-
1196
- class RestChatService extends ChatService {
1197
- constructor() {
1198
- super();
1199
- this.jsonMethodWebService = inject(JsonMethodPluginService);
1200
- }
1201
- /**
1202
- * Initialize the chat process after the login is complete.
1203
- * It listens for the 'login-complete' event, initializes necessary URL, and performs parallel requests for models, functions and quota data.
1204
- * @returns An Observable<boolean> indicating the success of the initialization process.
1205
- */
1206
- init() {
1207
- return this.loginService.events.pipe(filter((e) => e.type === 'login-complete'), tap(() => this.getRequestsUrl()),
1208
- // Execute parallel requests for models and functions
1209
- switchMap(() => forkJoin([
1210
- this.listModels(),
1211
- this.listFunctions()
1212
- ])),
1213
- // Map the results of parallel requests to a boolean indicating success
1214
- map(([models, functions]) => {
1215
- const result = !!models && !!functions;
1216
- this.initProcess$.next(result);
1217
- return result;
1218
- }),
1219
- // Any errors during the process are caught, logged, and re-thrown to propagate the error further
1220
- catchError((error) => {
1221
- console.error('Error occurred:', error);
1222
- return throwError(() => error);
1223
- }),
1224
- // cache and replay the emitted value for subsequent subscribers, ensuring the initialization logic is only executed once even if there are multiple subscribers
1225
- shareReplay(1));
1226
- }
1227
- /**
1228
- * Define the GLLM plugin to use for the http requests
1229
- * It can be overridden by the app config
1230
- */
1231
- getRequestsUrl() {
1232
- if (this.assistantConfig$.value.connectionSettings.restEndpoint) {
1233
- this.REQUEST_URL = this.assistantConfig$.value.connectionSettings.restEndpoint;
1234
- }
1235
- else {
1236
- throw new Error(`The property 'restEndpoint' must be provided when attempting to use 'REST' in assistant instance`);
1237
- }
1238
- }
1239
- overrideUser() {
1240
- const error = new Error('Override user is not supported in REST');
1241
- console.error(error);
1242
- }
1243
- listModels() {
1244
- const data = {
1245
- action: "listmodels",
1246
- debug: this.assistantConfig$.value.defaultValues.debug
1247
- };
1248
- return this.jsonMethodWebService.get(this.REQUEST_URL, data).pipe(map(res => res.models), tap(models => this.models = models === null || models === void 0 ? void 0 : models.filter(model => !!model.enable)), catchError((error) => {
1249
- console.error('Error invoking listmodels:', error);
1250
- return throwError(() => error);
1251
- }));
1252
- }
1253
- listFunctions() {
1254
- const data = {
1255
- action: "listfunctions",
1256
- debug: this.assistantConfig$.value.defaultValues.debug
1257
- };
1258
- return this.jsonMethodWebService.get(this.REQUEST_URL, data).pipe(map(res => res.functions), tap((functions) => this.functions = functions === null || functions === void 0 ? void 0 : functions.filter(func => func.enabled && !!this.assistantConfig$.value.defaultValues.functions.find(fn => fn.name === func.functionName))), catchError((error) => {
1259
- console.error('Error invoking listfunctions:', error);
1260
- return throwError(() => error);
1261
- }));
1262
- }
1263
- fetch(messages, query) {
1264
- var _a;
1265
- // Start streaming by invoking the Chat method
1266
- this.streaming$.next(true);
1267
- // Prepare the payload to send to the Chat method
1268
- const data = {
1269
- action: "chat",
1270
- history: messages,
1271
- functions: (_a = this.assistantConfig$.value.defaultValues.functions) === null || _a === void 0 ? void 0 : _a.filter(func => func.enabled).map(func => func.name),
1272
- debug: this.assistantConfig$.value.defaultValues.debug,
1273
- serviceSettings: Object.assign({ service_id: this.assistantConfig$.value.defaultValues.service_id, model_id: this.assistantConfig$.value.defaultValues.model_id, top_p: this.assistantConfig$.value.defaultValues.top_p, temperature: this.assistantConfig$.value.defaultValues.temperature, max_tokens: this.assistantConfig$.value.defaultValues.max_tokens }, this.assistantConfig$.value.additionalServiceSettings),
1274
- appQuery: {
1275
- app: this.appService.appName,
1276
- query
1277
- },
1278
- genericChatErrorMessage: this.assistantConfig$.value.globalSettings.genericChatErrorMessage
1279
- };
1280
- if (this.assistantConfig$.value.savedChatSettings.enabled) {
1281
- data.instanceId = this.chatInstanceId;
1282
- data.savedChatId = this.savedChatId;
1283
- }
1284
- // Request the Chat endpoint
1285
- return this.jsonMethodWebService.post(this.REQUEST_URL, data).pipe(tap((res) => this.updateQuota(res.quota, true)), map((res) => {
1286
- var _a, _b, _c, _d;
1287
- // Define $progress from the actions property of the response
1288
- let $progress;
1289
- if (((_a = res.actions) === null || _a === void 0 ? void 0 : _a.length) > 0) {
1290
- const actions = Object.values(res.actions.reduce((acc, item) => {
1291
- acc[item.guid] = Object.assign(Object.assign({}, (acc[item.guid] || {})), item);
1292
- return acc;
1293
- }, {}));
1294
- $progress = actions.map((a) => {
1295
- var _a, _b;
1296
- return ({
1297
- title: (_a = a.displayName) !== null && _a !== void 0 ? _a : "",
1298
- content: (_b = a.displayValue) !== null && _b !== void 0 ? _b : "",
1299
- done: a.executionTime !== undefined,
1300
- time: a.executionTime,
1301
- });
1302
- });
1303
- }
1304
- // Re-attach the $progress and $attachment of the last response to the last assistant's response in the chat history
1305
- const response = Object.assign({}, res.history.at(-1));
1306
- if ($progress)
1307
- response.additionalProperties.$progress = $progress;
1308
- if (res.context)
1309
- response.additionalProperties.$attachment = res.context.map((ctx) => ctx.additionalProperties);
1310
- if (res.suggestedActions)
1311
- response.additionalProperties.$suggestedAction = res.suggestedActions;
1312
- // Emit the updated chat usage metrics once the generation of the assistant response is completed
1313
- if (!!response.additionalProperties.usageMetrics) {
1314
- this.updateChatUsageMetrics(response.additionalProperties.usageMetrics);
1315
- }
1316
- // Update the chat history with the incoming history property of the res AND the processed response message
1317
- this.chatHistory = res.history;
1318
- this.chatHistory[this.chatHistory.length - 1] = response;
1319
- // Save/update the chat if savedChat enabled
1320
- if (this.assistantConfig$.value.savedChatSettings.enabled && this.chatHistory.some((msg) => { var _a; return ((_a = msg.additionalProperties) === null || _a === void 0 ? void 0 : _a.isUserInput) === true; })) {
1321
- const action = !this.savedChatId ? this.addSavedChat(this.chatHistory) : this.updateSavedChat(this.savedChatId, undefined, this.chatHistory);
1322
- action.pipe(take(1)).subscribe();
1323
- }
1324
- // Generate audit event
1325
- const details = {
1326
- 'duration': res.executionTime,
1327
- 'text': response.content,
1328
- 'role': response.role,
1329
- 'rank': this.chatHistory.length - 1,
1330
- 'generation-tokencount': (_b = response.additionalProperties.usageMetrics) === null || _b === void 0 ? void 0 : _b.completionTokenCount,
1331
- 'prompt-tokencount': (_c = response.additionalProperties.usageMetrics) === null || _c === void 0 ? void 0 : _c.promptTokenCount,
1332
- 'attachments': (_d = response.additionalProperties.$attachment) === null || _d === void 0 ? void 0 : _d.map(({ recordId, contextId, parts, type }) => ({
1333
- recordId,
1334
- contextId,
1335
- parts: parts.map(({ partId, text }) => ({ partId, text })),
1336
- type
1337
- }))
1338
- };
1339
- this.generateAuditEvent('message', details);
1340
- // Return the result
1341
- return { history: [...messages, response], executionTime: res.executionTime };
1342
- }), finalize(() => this.streaming$.next(false)));
1343
- }
1344
- stopGeneration() {
1345
- const error = new Error('Not supported in REST');
1346
- console.error(error);
1347
- return throwError(() => error);
1348
- }
1349
- listSavedChat() {
1350
- if (!this.assistantConfig$.value.savedChatSettings.enabled) {
1351
- return;
1352
- }
1353
- const data = {
1354
- action: "SavedChatList",
1355
- instanceId: this.chatInstanceId,
1356
- debug: this.assistantConfig$.value.defaultValues.debug
1357
- };
1358
- this.jsonMethodWebService.get(this.REQUEST_URL, data).subscribe(res => this.savedChats$.next(res.savedChats), error => {
1359
- console.error('Error occurred while calling the SavedChatList API:', error.error.errorMessage);
1360
- this.notificationsService.error('Error occurred while calling the SavedChatList API:', error.error.errorMessage);
1361
- });
1362
- }
1363
- addSavedChat(messages) {
1364
- const data = {
1365
- action: "SavedChatAdd",
1366
- instanceId: this.chatInstanceId,
1367
- savedChatId: this.chatId,
1368
- history: messages,
1369
- debug: this.assistantConfig$.value.defaultValues.debug
1370
- };
1371
- return this.jsonMethodWebService.post(this.REQUEST_URL, data).pipe(map(res => res.savedChat), tap((savedChat) => {
1372
- this.setSavedChatId(savedChat.id); // Persist the savedChatId
1373
- this.generateAuditEvent('saved-chat.add', {}, savedChat.id); // Generate audit event
1374
- }), catchError((error) => {
1375
- console.error('Error occurred while calling the SavedChatAdd API:', error.error.errorMessage);
1376
- this.notificationsService.error('Error occurred while calling the SavedChatAdd API:', error.error.errorMessage);
1377
- return throwError(() => error);
1378
- }));
1379
- }
1380
- getSavedChat(id) {
1381
- const data = {
1382
- action: "SavedChatGet",
1383
- instanceId: this.chatInstanceId,
1384
- savedChatId: id,
1385
- debug: this.assistantConfig$.value.defaultValues.debug
1386
- };
1387
- return this.jsonMethodWebService.get(this.REQUEST_URL, data).pipe(map(res => res.savedChat), catchError((error) => {
1388
- console.error('Error occurred while calling the SavedChatGet API:', error.error.errorMessage);
1389
- this.notificationsService.error('Error occurred while calling the SavedChatGet API:', error.error.errorMessage);
1390
- return throwError(() => error);
1391
- }));
1392
- }
1393
- updateSavedChat(id, name, messages) {
1394
- const data = {
1395
- action: "SavedChatUpdate",
1396
- instanceId: this.chatInstanceId,
1397
- savedChatId: id,
1398
- debug: this.assistantConfig$.value.defaultValues.debug
1399
- };
1400
- if (name)
1401
- data["title"] = name;
1402
- if (messages)
1403
- data["history"] = messages;
1404
- return this.jsonMethodWebService.post(this.REQUEST_URL, data).pipe(map(res => res.savedChat), catchError((error) => {
1405
- console.error('Error occurred while calling the SavedChatUpdate API:', error.error.errorMessage);
1406
- this.notificationsService.error('Error occurred while calling the SavedChatUpdate API:', error.error.errorMessage);
1407
- return throwError(() => error);
1408
- }));
1409
- }
1410
- deleteSavedChat(ids) {
1411
- const data = {
1412
- action: "SavedChatDelete",
1413
- instanceId: this.chatInstanceId,
1414
- savedChatIds: ids,
1415
- debug: this.assistantConfig$.value.defaultValues.debug
1416
- };
1417
- return this.jsonMethodWebService.post(this.REQUEST_URL, data).pipe(map(res => res.deletedCount), catchError((error) => {
1418
- console.error('Error occurred while calling the SavedChatDelete API:', error.error.errorMessage);
1419
- this.notificationsService.error('Error occurred while calling the SavedChatDelete API:', error.error.errorMessage);
1420
- return throwError(() => error);
1421
- }));
1422
- }
1423
- }
1424
- RestChatService.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.0.6", ngImport: i0, type: RestChatService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
1425
- RestChatService.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "14.0.6", ngImport: i0, type: RestChatService });
1426
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.0.6", ngImport: i0, type: RestChatService, decorators: [{
1427
- type: Injectable
1428
- }], ctorParameters: function () { return []; } });
1429
-
1430
- class TokenProgressBarComponent {
1431
- constructor() {
1432
- this.subscription = new Subscription();
1433
- this.loginService = inject(LoginService);
1434
- this.instanceManagerService = inject(InstanceManagerService);
1435
- }
1436
- ngOnInit() {
1437
- this.subscription.add(this.loginService.events.pipe(filter(e => e.type === 'login-complete'), tap(_ => this.instantiateChatService()), switchMap(_ => this.chatService.initProcess$), filter(success => !!success), tap(_ => {
1438
- this.config = this.chatService.assistantConfig$.value;
1439
- this.onUserTokensConsumption();
1440
- this.onChatTokensConsumption();
1441
- })).subscribe());
1442
- }
1443
- ngOnDestroy() {
1444
- this.subscription.unsubscribe();
1445
- }
1446
- instantiateChatService() {
1447
- this.chatService = this.instanceManagerService.getInstance(this.instanceId);
1448
- }
1449
- onUserTokensConsumption() {
1450
- this.subscription.add(this.chatService.userTokenConsumption$.subscribe((data) => {
1451
- if (data) {
1452
- this.userPercentage = data.percentage;
1453
- this.userTitle = `Max generation allowed: ${this.userPercentage}% consumed. Resets at ${data.nextResetDate}`;
1454
- }
1455
- }));
1456
- }
1457
- onChatTokensConsumption() {
1458
- this.subscription.add(this.chatService.chatTokenConsumption$.subscribe((data) => {
1459
- if (data) {
1460
- this.chatPercentage = data.percentage;
1461
- this.chatTitle = `Max length of conversation: ${this.chatPercentage}% reached.`;
1462
- }
1463
- }));
1464
- }
1465
- }
1466
- TokenProgressBarComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.0.6", ngImport: i0, type: TokenProgressBarComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
1467
- TokenProgressBarComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "14.0.6", type: TokenProgressBarComponent, isStandalone: true, selector: "sq-token-progress-bar", inputs: { instanceId: "instanceId" }, ngImport: i0, template: "<div class=\"bars-container d-flex flex-row gap-2 p-2 me-4\" *ngIf=\"(config?.globalSettings?.displayUserQuotaConsumption && userPercentage !== undefined) || (config?.globalSettings?.displayChatTokensConsumption && chatPercentage !== undefined)\">\n <div *ngIf=\"(config?.globalSettings?.displayUserQuotaConsumption && userPercentage !== undefined)\" class=\"token-progress-bar\" [sqTooltip]=\"userTitle\"\n [style.background]=\"'radial-gradient(closest-side, var(--ast-primary-bg, #F8F8F8) 70%, transparent 75% 100%), conic-gradient(#FF854A ' + userPercentage + '%, #0040BF 0)'\">\n </div>\n <div *ngIf=\"(config?.globalSettings?.displayChatTokensConsumption && chatPercentage !== undefined)\" class=\"token-progress-bar\" [sqTooltip]=\"chatTitle\"\n [style.background]=\"'radial-gradient(closest-side, var(--ast-primary-bg, #F8F8F8) 70%, transparent 75% 100%), conic-gradient(#FF854A ' + chatPercentage + '%, #0040BF 0)'\">\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-error{background-color:var(--ast-error-bg, rgba(249, 58, 55, .2));color:var(--ast-action-buttons-color, inherit)}.ast-error:hover{color:var(--ast-error-color, rgba(249, 58, 55, .7))}.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-uploaded-doc-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;--ast-action-buttons-color: white;--ast-action-buttons-hover-color: #6dbee6;--ast-report-bg: #070707}.bars-container{background-color:var(--ast-primary-bg, #f2f8fe);border-bottom-left-radius:1rem;border-bottom-right-radius:1rem}.token-progress-bar{width:1.5rem;height:1.5rem;border-radius:50%}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { 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"] }] });
1468
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.0.6", ngImport: i0, type: TokenProgressBarComponent, decorators: [{
1469
- type: Component,
1470
- args: [{ selector: 'sq-token-progress-bar', standalone: true, imports: [CommonModule, UtilsModule], template: "<div class=\"bars-container d-flex flex-row gap-2 p-2 me-4\" *ngIf=\"(config?.globalSettings?.displayUserQuotaConsumption && userPercentage !== undefined) || (config?.globalSettings?.displayChatTokensConsumption && chatPercentage !== undefined)\">\n <div *ngIf=\"(config?.globalSettings?.displayUserQuotaConsumption && userPercentage !== undefined)\" class=\"token-progress-bar\" [sqTooltip]=\"userTitle\"\n [style.background]=\"'radial-gradient(closest-side, var(--ast-primary-bg, #F8F8F8) 70%, transparent 75% 100%), conic-gradient(#FF854A ' + userPercentage + '%, #0040BF 0)'\">\n </div>\n <div *ngIf=\"(config?.globalSettings?.displayChatTokensConsumption && chatPercentage !== undefined)\" class=\"token-progress-bar\" [sqTooltip]=\"chatTitle\"\n [style.background]=\"'radial-gradient(closest-side, var(--ast-primary-bg, #F8F8F8) 70%, transparent 75% 100%), conic-gradient(#FF854A ' + chatPercentage + '%, #0040BF 0)'\">\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-error{background-color:var(--ast-error-bg, rgba(249, 58, 55, .2));color:var(--ast-action-buttons-color, inherit)}.ast-error:hover{color:var(--ast-error-color, rgba(249, 58, 55, .7))}.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-uploaded-doc-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;--ast-action-buttons-color: white;--ast-action-buttons-hover-color: #6dbee6;--ast-report-bg: #070707}.bars-container{background-color:var(--ast-primary-bg, #f2f8fe);border-bottom-left-radius:1rem;border-bottom-right-radius:1rem}.token-progress-bar{width:1.5rem;height:1.5rem;border-radius:50%}\n"] }]
1471
- }], propDecorators: { instanceId: [{
1472
- type: Input
1473
- }] } });
1474
-
1475
- class WebSocketChatService extends ChatService {
1476
- constructor() {
1477
- super();
1478
- this._messageHandlers = new Map();
1479
- this._actionMap = new Map();
1480
- this._progress = undefined;
1481
- this._attachments = [];
1482
- this._debugMessages = [];
1483
- this.signalRService = inject(SignalRWebService);
1484
- this.authenticationService = inject(AuthenticationService);
1485
- }
1486
- /**
1487
- * Initialize the assistant process.
1488
- * It includes building and starting a connection, executing parallel requests for models and functions, and handling errors during the process.
1489
- * ⚠️ This method MUST be called ONLY if the user is loggedIn and once when the assistant is initialized.
1490
- *
1491
- * @returns An Observable<boolean> indicating the success of the initialization process.
1492
- */
1493
- init() {
1494
- // Ensure all logic is executed when subscribed to the observable
1495
- return defer(() => {
1496
- this.getRequestsUrl();
1497
- return from(
1498
- // Build the connection
1499
- this.buildConnection()).pipe(tap(() => this.initMessageHandlers()),
1500
- // Start the connection
1501
- switchMap(() => this.startConnection()),
1502
- // Execute parallel requests for models and functions
1503
- switchMap(() => forkJoin([
1504
- this.listModels(),
1505
- this.listFunctions()
1506
- ])),
1507
- // Map the results of parallel requests to a boolean indicating success
1508
- map(([models, functions]) => {
1509
- const result = !!models && !!functions;
1510
- this.initProcess$.next(result);
1511
- return result;
1512
- }),
1513
- // Any errors during the process are caught, logged, and re-thrown to propagate the error further
1514
- catchError((error) => {
1515
- console.error('Error occurred:', error);
1516
- return throwError(() => error);
1517
- }), take(1));
1518
- });
1519
- }
1520
- /**
1521
- * Define the assistant endpoint to use for the websocket requests
1522
- * It can be overridden by the app config
1523
- */
1524
- getRequestsUrl() {
1525
- if (this.assistantConfig$.value.connectionSettings.websocketEndpoint) {
1526
- this.REQUEST_URL = this.assistantConfig$.value.connectionSettings.websocketEndpoint;
1527
- }
1528
- else {
1529
- throw new Error(`The property 'websocketEndpoint' must be provided when attempting to use 'WebSocket' in assistant instance`);
1530
- }
1531
- }
1532
- overrideUser() {
1533
- if (!(this.authenticationService.userOverrideActive && this.authenticationService.userOverride)) {
1534
- this.userOverride$.next(false);
1535
- return;
1536
- }
1537
- // Prepare the payload to send to the OverrideUser method
1538
- const data = {
1539
- instanceId: this.chatInstanceId,
1540
- user: this.authenticationService.userOverride.userName,
1541
- domain: this.authenticationService.userOverride.domain
1542
- };
1543
- // Invoke the OverrideUser method and handle errors
1544
- this.connection.invoke('OverrideUser', data)
1545
- .then((res) => this.userOverride$.next(!!res))
1546
- .catch(error => {
1547
- console.error('Error invoking OverrideUser:', error);
1548
- return Promise.resolve(); // Return a resolved promise to handle the error and prevent unhandled promise rejection when no further error handling exists downstream
1549
- });
1550
- }
1551
- listModels() {
1552
- const modelsSubject$ = new Subject();
1553
- this.connection.on('ListModels', (res) => {
1554
- var _a;
1555
- this.models = (_a = res.models) === null || _a === void 0 ? void 0 : _a.filter(model => !!model.enable);
1556
- modelsSubject$.next(this.models);
1557
- modelsSubject$.complete();
1558
- });
1559
- // Send the request to get the list of models
1560
- this.connection.invoke('ListModels', { debug: this.assistantConfig$.value.defaultValues.debug })
1561
- .catch(error => {
1562
- console.error('Error invoking ListModels:', error);
1563
- modelsSubject$.error(new Error(error));
1564
- return Promise.resolve(); // Return a resolved promise to handle the error and prevent unhandled promise rejection when no further error handling exists downstream
1565
- });
1566
- return modelsSubject$.asObservable();
1567
- }
1568
- listFunctions() {
1569
- const functionsSubject$ = new Subject();
1570
- this.connection.on('ListFunctions', (res) => {
1571
- var _a;
1572
- this.functions = (_a = res.functions) === null || _a === void 0 ? void 0 : _a.filter(func => func.enabled);
1573
- functionsSubject$.next(this.functions);
1574
- functionsSubject$.complete();
1575
- });
1576
- // Send the request to get the list of functions
1577
- this.connection.invoke('ListFunctions', { debug: this.assistantConfig$.value.defaultValues.debug })
1578
- .catch(error => {
1579
- console.error('Error invoking ListFunctions:', error);
1580
- functionsSubject$.error(new Error(error));
1581
- return Promise.resolve(); // Return a resolved promise to handle the error and prevent unhandled promise rejection when no further error handling exists downstream
1582
- });
1583
- return functionsSubject$.asObservable();
1584
- }
1585
- fetch(messages, query) {
1586
- var _a;
1587
- // Start streaming by invoking the Chat method
1588
- this.streaming$.next(true);
1589
- // Prepare the payload to send to the Chat method
1590
- const data = {
1591
- history: messages,
1592
- functions: (_a = this.assistantConfig$.value.defaultValues.functions) === null || _a === void 0 ? void 0 : _a.filter(func => func.enabled).map(func => func.name),
1593
- debug: this.assistantConfig$.value.defaultValues.debug,
1594
- serviceSettings: Object.assign({ service_id: this.assistantConfig$.value.defaultValues.service_id, model_id: this.assistantConfig$.value.defaultValues.model_id, top_p: this.assistantConfig$.value.defaultValues.top_p, temperature: this.assistantConfig$.value.defaultValues.temperature, max_tokens: this.assistantConfig$.value.defaultValues.max_tokens }, this.assistantConfig$.value.additionalServiceSettings),
1595
- appQuery: {
1596
- app: this.appService.appName,
1597
- query
1598
- },
1599
- genericChatErrorMessage: this.assistantConfig$.value.globalSettings.genericChatErrorMessage
1600
- };
1601
- if (this.assistantConfig$.value.savedChatSettings.enabled) {
1602
- data.instanceId = this.chatInstanceId;
1603
- data.savedChatId = this.savedChatId;
1604
- }
1605
- // Initialize the response with an empty assistant message
1606
- this._response = [{ role: "assistant", content: "", additionalProperties: { display: true } }]; // here display: true is needed in order to be able to show the progress
1607
- // Create a Subject to signal completion
1608
- const completion$ = new Subject();
1609
- // Create observables for each non-global handler in the _messageHandlers map (default and eventual custom ones) once it is triggered by the hub connection
1610
- const observables = Array
1611
- .from(this._messageHandlers.entries())
1612
- .filter(([eventName, eventHandler]) => !eventHandler.isGlobalHandler)
1613
- .map(([eventName, eventHandler]) => {
1614
- return fromEvent(this.connection, eventName).pipe(mergeMap((event) => {
1615
- // Wrap the handler in a try-catch block to prevent the entire stream from failing if an error occurs in a single handler
1616
- try {
1617
- // Execute the handler and emit the result
1618
- // NB: here we could use [eventHandler.handler(event)] which behind the scenes mergeMap interprets this array as an observable sequence with one item, which it then emits
1619
- return of(eventHandler.handler(event));
1620
- }
1621
- catch (error) {
1622
- console.error(`Error in event handler for ${eventName}:`, error);
1623
- // Use throwError to propagate the error downstream
1624
- return throwError(() => new Error(`Error in event handler for ${eventName}: ${error}`));
1625
- }
1626
- }));
1627
- });
1628
- // Then merge them into a single observable in order to simulate the streaming behavior
1629
- const combined$ = merge(...observables).pipe(map(() => {
1630
- // Define $progress from the _actionMap
1631
- const actions = Array.from(this._actionMap.values());
1632
- this._progress = actions.length > 0
1633
- ? actions.map((a) => {
1634
- var _a, _b;
1635
- return ({
1636
- title: (_a = a.displayName) !== null && _a !== void 0 ? _a : "",
1637
- content: (_b = a.displayValue) !== null && _b !== void 0 ? _b : "",
1638
- done: a.executionTime !== undefined,
1639
- time: a.executionTime,
1640
- });
1641
- })
1642
- : undefined;
1643
- // Always update ONLY the first assistant message of the _response with the new $progress, $attachment and $debug
1644
- // Assuming that the first assistant message is always visible since the hub does not send hidden messages by design
1645
- // So even if the first assistant message is hidden (display: false), the _response[0] will and should contain :
1646
- // - $progress, $attachment and $debug
1647
- // - the content of the first visible assistant message in the workflow
1648
- // This is mandatory in order to match the behavior of consecutive messages and maintain consistency with the chatHistory
1649
- if (!!this._progress || this._attachments.length > 0 || this._debugMessages.length > 0) {
1650
- this._response[0].additionalProperties.$progress = this._progress;
1651
- this._response[0].additionalProperties.$attachment = this._attachments;
1652
- this._response[0].additionalProperties.$debug = this._debugMessages;
1653
- }
1654
- // Return the result
1655
- return { history: [...messages, ...this._response], executionTime: this._executionTime };
1656
- }), takeUntil(completion$));
1657
- // return a new Observable that emits the result of the combined stream and handles the eventual errors of the invocation of the Chat method
1658
- return new Observable(observer => {
1659
- // Subscribe to combined stream
1660
- combined$.subscribe({
1661
- next: (value) => observer.next(value),
1662
- error: (err) => observer.error(err)
1663
- });
1664
- // Invoke the Chat method and handle errors
1665
- this.connection.invoke('Chat', data)
1666
- .then(() => {
1667
- // If a valid assistant message with (display: true) was found, update it
1668
- // and it should always the case
1669
- const index = this.firstVisibleAssistantMessageIndex(this.chatHistory);
1670
- if (index !== -1) {
1671
- this.chatHistory[index].additionalProperties.$progress = this._progress;
1672
- this.chatHistory[index].additionalProperties.$attachment = this._attachments;
1673
- this.chatHistory[index].additionalProperties.$debug = this._debugMessages;
1674
- }
1675
- // Save/update the chat if savedChat enabled
1676
- if (this.assistantConfig$.value.savedChatSettings.enabled && this.chatHistory.some((msg) => { var _a; return ((_a = msg.additionalProperties) === null || _a === void 0 ? void 0 : _a.isUserInput) === true; })) {
1677
- const action = !this.savedChatId ? this.addSavedChat(this.chatHistory).pipe(tap(() => this.listSavedChat())) : this.updateSavedChat(this.savedChatId, undefined, this.chatHistory);
1678
- action.pipe(take(1)).subscribe({
1679
- next: () => { },
1680
- error: (error) => {
1681
- this.streaming$.next(false);
1682
- observer.error(error);
1683
- },
1684
- complete: () => {
1685
- this.streaming$.next(false);
1686
- observer.complete();
1687
- }
1688
- });
1689
- }
1690
- else {
1691
- this.streaming$.next(false);
1692
- observer.complete();
1693
- }
1694
- })
1695
- .catch(error => {
1696
- console.error('Error invoking Chat:', error);
1697
- this.streaming$.next(false);
1698
- // Emit the error to the newly created observable
1699
- observer.error(error);
1700
- // Return a resolved promise to handle the error and prevent unhandled promise rejection
1701
- return Promise.resolve();
1702
- })
1703
- .finally(() => {
1704
- // This block concerns ONLY the completion of the "Chat" method invocation.
1705
- // This means the completion of the combined$ stream.
1706
- // It does not take into account the completion of the entire fetch method (the observable returned by fetch) and which depends on the completion of the save chat action if enabled
1707
- this._response = []; // Clear the _response
1708
- this._actionMap.clear(); // Clear the _actionMap
1709
- this._progress = undefined; // Clear the _progress
1710
- this._attachments = []; // Clear the _attachments
1711
- this._debugMessages = []; // Clear the _debugMessages
1712
- this._executionTime = ""; // Clear the _executionTime
1713
- completion$.next(); // Emit a signal to complete the observables
1714
- completion$.complete(); // Complete the subject
1715
- });
1716
- });
1717
- }
1718
- stopGeneration() {
1719
- // Start stopping generation by invoking the CancelTasks method
1720
- this.stoppingGeneration$.next(true);
1721
- // Create a Subject to hold the result of the CancelTasks method
1722
- const stopGenerationSubject$ = new Subject();
1723
- this.connection.on('CancelTasks', (res) => {
1724
- var _a;
1725
- // When the generation is stopped before streaming any VISIBLE assistant message, this means that $progress, $attachment and $debug properties will be lost.
1726
- // However, the "ContextMessage" frames will be persisted in the chatHistory and the assistant may reference them in the next generation.
1727
- // This leads to the problem of referencing undisplayed attachments in the next generation.
1728
- // To solve this problem, we need to persist $progress, $attachment and $debug properties by adding a new assistant message with empty content and these properties.
1729
- if (this._response.length === 1 && this._response[0].content === "") {
1730
- (_a = this.chatHistory) === null || _a === void 0 ? void 0 : _a.push({ role: "assistant", content: "", additionalProperties: { display: true, $progress: this._progress, $attachment: this._attachments, $debug: this._debugMessages } });
1731
- }
1732
- stopGenerationSubject$.next(!!res); // Emit the result of the CancelTasks method
1733
- stopGenerationSubject$.complete(); // Complete the subject
1734
- this.stoppingGeneration$.next(false); // Complete stopping generation
1735
- });
1736
- // Invoke the CancelTasks method and handle errors
1737
- this.connection.invoke('CancelTasks')
1738
- .catch(error => {
1739
- console.error('Error invoking CancelTasks:', error);
1740
- stopGenerationSubject$.error(new Error(error));
1741
- this.stoppingGeneration$.next(false); // Complete stopping generation
1742
- return Promise.resolve(); // Return a resolved promise to handle the error and prevent unhandled promise rejection when no further error handling exists downstream
1743
- });
1744
- return stopGenerationSubject$.asObservable();
1745
- }
1746
- listSavedChat() {
1747
- if (!this.assistantConfig$.value.savedChatSettings.enabled) {
1748
- return;
1749
- }
1750
- const data = {
1751
- instanceId: this.chatInstanceId,
1752
- debug: this.assistantConfig$.value.defaultValues.debug
1753
- };
1754
- this.connection.on('SavedChatList', (res) => {
1755
- this.savedChats$.next(res.savedChats); // emits the result to the savedChats$ subject
1756
- });
1757
- // Invoke the method SavedChatList
1758
- this.connection.invoke('SavedChatList', data)
1759
- .catch(error => {
1760
- console.error('Error invoking SavedChatList:', error);
1761
- return Promise.resolve();
1762
- });
1763
- }
1764
- getSavedChat(id) {
1765
- const savedChatSubject$ = new Subject();
1766
- const data = {
1767
- instanceId: this.chatInstanceId,
1768
- savedChatId: id,
1769
- debug: this.assistantConfig$.value.defaultValues.debug
1770
- };
1771
- this.connection.on('SavedChatGet', (res) => {
1772
- savedChatSubject$.next(res.savedChat);
1773
- savedChatSubject$.complete();
1774
- });
1775
- // Invoke the method SavedChatGet
1776
- this.connection.invoke('SavedChatGet', data)
1777
- .catch(error => {
1778
- console.error('Error invoking SavedChatGet:', error);
1779
- savedChatSubject$.error(new Error(error));
1780
- return Promise.resolve(); // Return a resolved promise to handle the error and prevent unhandled promise rejection when no further error handling exists downstream
1781
- });
1782
- return savedChatSubject$.asObservable();
1783
- }
1784
- addSavedChat(messages) {
1785
- const addSavedChatSubject$ = new Subject();
1786
- const data = {
1787
- instanceId: this.chatInstanceId,
1788
- savedChatId: this.chatId,
1789
- history: messages,
1790
- debug: this.assistantConfig$.value.defaultValues.debug
1791
- };
1792
- this.connection.on('SavedChatAdd', (res) => {
1793
- this.setSavedChatId(res.savedChat.id); // Persist the savedChatId
1794
- this.generateAuditEvent('saved-chat.add', {}, res.savedChat.id); // Generate audit event
1795
- addSavedChatSubject$.next(res.savedChat);
1796
- addSavedChatSubject$.complete();
1797
- });
1798
- // Invoke the method SavedChatAdd
1799
- this.connection.invoke('SavedChatAdd', data)
1800
- .catch(error => {
1801
- console.error('Error invoking SavedChatAdd:', error);
1802
- addSavedChatSubject$.error(new Error(error));
1803
- return Promise.resolve(); // Return a resolved promise to handle the error and prevent unhandled promise rejection when no further error handling exists downstream
1804
- });
1805
- return addSavedChatSubject$.asObservable();
1806
- }
1807
- updateSavedChat(id, name, messages) {
1808
- const updateSavedChatSubject$ = new Subject();
1809
- const data = {
1810
- instanceId: this.chatInstanceId,
1811
- savedChatId: id,
1812
- debug: this.assistantConfig$.value.defaultValues.debug
1813
- };
1814
- if (name)
1815
- data["title"] = name;
1816
- if (messages)
1817
- data["history"] = messages;
1818
- this.connection.on('SavedChatUpdate', (res) => {
1819
- updateSavedChatSubject$.next(res.savedChat);
1820
- updateSavedChatSubject$.complete();
1821
- });
1822
- // Invoke the method SavedChatUpdate
1823
- this.connection.invoke('SavedChatUpdate', data)
1824
- .catch(error => {
1825
- console.error('Error invoking SavedChatUpdate:', error);
1826
- updateSavedChatSubject$.error(new Error(error));
1827
- return Promise.resolve(); // Return a resolved promise to handle the error and prevent unhandled promise rejection when no further error handling exists downstream
1828
- });
1829
- return updateSavedChatSubject$.asObservable();
1830
- }
1831
- deleteSavedChat(ids) {
1832
- const deleteSavedChatSubject$ = new Subject();
1833
- const data = {
1834
- instanceId: this.chatInstanceId,
1835
- SavedChatIds: ids,
1836
- debug: this.assistantConfig$.value.defaultValues.debug
1837
- };
1838
- this.connection.on('SavedChatDelete', (res) => {
1839
- deleteSavedChatSubject$.next(res.deleteCount);
1840
- deleteSavedChatSubject$.complete();
1841
- });
1842
- // Invoke the method SavedChatDelete
1843
- this.connection.invoke('SavedChatDelete', data)
1844
- .catch(error => {
1845
- console.error('Error invoking SavedChatDelete:', error);
1846
- deleteSavedChatSubject$.error(new Error(error));
1847
- return Promise.resolve(); // Return a resolved promise to handle the error and prevent unhandled promise rejection when no further error handling exists downstream
1848
- });
1849
- return deleteSavedChatSubject$.asObservable();
1850
- }
1851
- /**
1852
- * Initialize out-of-the-box handlers
1853
- * It is a placeholder for non-streaming scenarios, where you invoke a specific hub method, and the server responds with frame message(s)
1854
- */
1855
- initMessageHandlers() {
1856
- this.addMessageHandler("Error", {
1857
- handler: (error) => {
1858
- console.error(error);
1859
- this.notificationsService.error(error);
1860
- },
1861
- isGlobalHandler: true
1862
- });
1863
- this.addMessageHandler("Quota", {
1864
- handler: (message) => {
1865
- try {
1866
- this.updateQuota(message.quota);
1867
- }
1868
- catch (error) {
1869
- console.error(error);
1870
- }
1871
- },
1872
- isGlobalHandler: true
1873
- });
1874
- this.addMessageHandler("Debug", { handler: () => { },
1875
- isGlobalHandler: true
1876
- });
1877
- this.addMessageHandler("ActionStart", { handler: (action) => this._actionMap.set(action.guid, action),
1878
- isGlobalHandler: false });
1879
- this.addMessageHandler("ActionResult", {
1880
- handler: (action) => this._actionMap.set(action.guid, Object.assign(Object.assign({}, this._actionMap.get(action.guid)), action)),
1881
- isGlobalHandler: false
1882
- });
1883
- this.addMessageHandler("ActionStop", {
1884
- handler: (action) => this._actionMap.set(action.guid, Object.assign(Object.assign({}, this._actionMap.get(action.guid)), action)),
1885
- isGlobalHandler: false
1886
- });
1887
- this.addMessageHandler("ContextMessage", {
1888
- handler: (message) => this._attachments.push(message.metadata),
1889
- isGlobalHandler: false
1890
- });
1891
- this.addMessageHandler("Message", {
1892
- handler: (message) => this._response.at(-1).content += message !== null && message !== void 0 ? message : "",
1893
- isGlobalHandler: false
1894
- });
1895
- this.addMessageHandler("History", {
1896
- handler: (history) => {
1897
- var _a;
1898
- // The ChatHistory is updated: it is the current copy concatenated with the new items ONLY (it can have multiple messages: the context messages + the response message)
1899
- // This is mandatory to not lose the previous updates of the chatHistory when the assistant is streaming multiple message steps
1900
- this.chatHistory = [...this.chatHistory, ...(history.history.slice(this.chatHistory.length))];
1901
- // Emit the updated chat usage metrics
1902
- if (!!((_a = this.chatHistory.at(-1)) === null || _a === void 0 ? void 0 : _a.additionalProperties.usageMetrics)) {
1903
- this.updateChatUsageMetrics(this.chatHistory.at(-1).additionalProperties.usageMetrics);
1904
- }
1905
- this._executionTime = history.executionTime;
1906
- },
1907
- isGlobalHandler: false
1908
- });
1909
- this.addMessageHandler("SuggestedActions", {
1910
- handler: (message) => {
1911
- // Since after the "History" and "MessageBreak" that this event is caught,
1912
- // $suggestedAction needs to be updated directly to the last visible "assistant" message in the _response and the chatHistory
1913
- this._response.at(-1).additionalProperties.$suggestedAction = (this._response.at(-1).additionalProperties.$suggestedAction || []).concat(message.suggestedActions);
1914
- const index = this.lastVisibleAssistantMessageIndex(this.chatHistory);
1915
- if (index !== -1) {
1916
- this.chatHistory[index].additionalProperties.$suggestedAction = (this.chatHistory[index].additionalProperties.$suggestedAction || []).concat(message.suggestedActions);
1917
- }
1918
- },
1919
- isGlobalHandler: false
1920
- });
1921
- this.addMessageHandler("DebugDisplay", {
1922
- handler: (message) => this._debugMessages = this._debugMessages.concat(message),
1923
- isGlobalHandler: false
1924
- });
1925
- this.addMessageHandler("MessageBreak", {
1926
- handler: () => {
1927
- var _a, _b;
1928
- // Generate audit event
1929
- const details = {
1930
- 'duration': this._executionTime,
1931
- 'text': this.chatHistory.at(-1).content,
1932
- 'role': this.chatHistory.at(-1).role,
1933
- 'rank': this.chatHistory.length - 1,
1934
- 'generation-tokencount': (_a = this.chatHistory.at(-1).additionalProperties.usageMetrics) === null || _a === void 0 ? void 0 : _a.completionTokenCount,
1935
- 'prompt-tokencount': (_b = this.chatHistory.at(-1).additionalProperties.usageMetrics) === null || _b === void 0 ? void 0 : _b.promptTokenCount,
1936
- 'attachments': JSON.stringify(this._attachments.map(({ recordId, contextId, parts, type }) => ({
1937
- recordId,
1938
- contextId,
1939
- parts: parts.map(({ partId, text }) => ({ partId, text })),
1940
- type
1941
- })))
1942
- };
1943
- this.generateAuditEvent('message', details);
1944
- // Push a new assistant message to the _response array ONLY if the content of the last message is not empty
1945
- if (this._response.at(-1).content !== "") {
1946
- this._response.push({ role: "assistant", content: "", additionalProperties: { display: true } });
1947
- }
1948
- },
1949
- isGlobalHandler: false
1950
- });
1951
- }
1952
- /**
1953
- * Override and register the entire _messageHandlers map by merging the provided map with the default one
1954
- * @param _messageHandlers
1955
- */
1956
- overrideMessageHandlers(_messageHandlers) {
1957
- // Clear the already registered global chat handlers before merging the new ones
1958
- this._messageHandlers.forEach((eventHandler, eventName) => {
1959
- if (eventHandler.isGlobalHandler) {
1960
- this.unsubscribeMessageHandler(eventName);
1961
- }
1962
- });
1963
- // Merge the new event handlers with the existing ones
1964
- this._messageHandlers = new Map([...this._messageHandlers, ..._messageHandlers]);
1965
- // Register the global handlers among the merged map
1966
- this._messageHandlers.forEach((eventHandler, eventName) => {
1967
- if (eventHandler.isGlobalHandler) {
1968
- this.registerMessageHandler(eventName, eventHandler);
1969
- }
1970
- });
1971
- }
1972
- /**
1973
- * Add a listener for a specific event.
1974
- * If a listener for this same event already exists, it will be overridden.
1975
- * If the listener has "isGlobalHandler" set to true, it will be registered to the hub connection.
1976
- * @param eventName Name of the event to register a listener for
1977
- * @param eventHandler The handler to be called when the event is received
1978
- */
1979
- addMessageHandler(eventName, eventHandler) {
1980
- this._messageHandlers.set(eventName, eventHandler);
1981
- if (eventHandler.isGlobalHandler) {
1982
- this.registerMessageHandler(eventName, eventHandler);
1983
- }
1984
- }
1985
- /**
1986
- * Dynamically register a listener for a specific event.
1987
- * If a listener for this event already exists, it will be overridden.
1988
- * @param eventName Name of the event to register a listener for
1989
- * @param eventHandler The handler to be called when the event is received
1990
- */
1991
- registerMessageHandler(eventName, eventHandler) {
1992
- if (!this.connection) {
1993
- console.log("No connection found to register the listener" + eventName);
1994
- return;
1995
- }
1996
- this.connection.on(eventName, (data) => {
1997
- eventHandler.handler(data);
1998
- });
1999
- }
2000
- /**
2001
- * Remove a listener for a specific event from the _messageHandlers map and unsubscribe from receiving messages for this event from the SignalR hub.
2002
- * @param eventName Name of the event to remove the listener for
2003
- */
2004
- removeMessageHandler(eventName) {
2005
- this._messageHandlers.delete(eventName);
2006
- this.unsubscribeMessageHandler(eventName);
2007
- }
2008
- /**
2009
- * Unsubscribe from receiving messages for a specific event from the SignalR hub.
2010
- * ALL its related listeners will be removed from hub connection
2011
- * This is needed to prevent accumulating old listeners when overriding the entire _messageHandlers map
2012
- * @param eventName Name of the event
2013
- */
2014
- unsubscribeMessageHandler(eventName) {
2015
- this.connection.off(eventName);
2016
- }
2017
- /**
2018
- * Build a connection to the signalR websocket and register default listeners to the methods defined in the server hub class
2019
- * @param options The options for the connection. It overrides the default options
2020
- * @param logLevel Define the log level displayed in the console
2021
- * @returns Promise that resolves when the connection is built
2022
- */
2023
- buildConnection(options) {
2024
- return new Promise((resolve, reject) => {
2025
- var _a;
2026
- if (!this.REQUEST_URL) {
2027
- reject(new Error("No endpoint provided to connect the websocket to"));
2028
- return;
2029
- }
2030
- const logLevel = this._getLogLevel();
2031
- this.connection = this.signalRService.buildConnection(this.REQUEST_URL, Object.assign(Object.assign({}, this.defaultOptions), options), logLevel, true);
2032
- const signalRServerTimeoutInMilliseconds = (_a = this.assistantConfig$.value) === null || _a === void 0 ? void 0 : _a.connectionSettings.signalRServerTimeoutInMilliseconds;
2033
- if (signalRServerTimeoutInMilliseconds) {
2034
- this.connection.serverTimeoutInMilliseconds = signalRServerTimeoutInMilliseconds;
2035
- }
2036
- resolve();
2037
- });
2038
- }
2039
- /**
2040
- * Start the connection
2041
- * @returns Promise that resolves when the connection is started
2042
- */
2043
- startConnection() {
2044
- return this.signalRService.startConnection(this.connection);
2045
- }
2046
- /**
2047
- * Stop the connection
2048
- * @returns Promise that resolves when the connection is stopped
2049
- */
2050
- stopConnection() {
2051
- return this.signalRService.stopConnection(this.connection);
2052
- }
2053
- _getTransports() {
2054
- var _a;
2055
- switch ((_a = this.assistantConfig$.value) === null || _a === void 0 ? void 0 : _a.connectionSettings.signalRTransport) {
2056
- case "WebSockets":
2057
- return HttpTransportType.WebSockets;
2058
- case "ServerSentEvents":
2059
- return HttpTransportType.ServerSentEvents;
2060
- case "LongPolling":
2061
- return HttpTransportType.LongPolling;
2062
- default:
2063
- return HttpTransportType.None;
2064
- }
2065
- }
2066
- _getLogLevel() {
2067
- var _a;
2068
- switch ((_a = this.assistantConfig$.value) === null || _a === void 0 ? void 0 : _a.connectionSettings.signalRLogLevel) {
2069
- case "Critical":
2070
- return LogLevel.Critical; // Log level for diagnostic messages that indicate a failure that will terminate the entire application.
2071
- case "Debug":
2072
- return LogLevel.Debug; // Log level for low severity diagnostic messages.
2073
- case "Error":
2074
- return LogLevel.Error; // Log level for diagnostic messages that indicate a failure in the current operation.
2075
- case "Information":
2076
- return LogLevel.Information; // Log level for informational diagnostic messages.
2077
- case "None":
2078
- return LogLevel.None; // The highest possible log level. Used when configuring logging to indicate that no log messages should be emitted.
2079
- case "Trace":
2080
- return LogLevel.Trace; // Log level for very low severity diagnostic messages.
2081
- case "Warning":
2082
- return LogLevel.Warning; // Log level for diagnostic messages that indicate a non-fatal problem.
2083
- default:
2084
- return LogLevel.None; // The highest possible log level. Used when configuring logging to indicate that no log messages should be emitted.
2085
- }
2086
- }
2087
- get defaultOptions() {
2088
- let headers = {
2089
- "sinequa-force-camel-case": "true",
2090
- "x-language": this.intlService.currentLocale.name,
2091
- "ui-language": this.intlService.currentLocale.name,
2092
- };
2093
- if (this.authenticationService.processedCredentials) {
2094
- headers = Object.assign(Object.assign({}, headers), { "sinequa-csrf-token": this.authenticationService.processedCredentials.data.csrfToken });
2095
- }
2096
- ;
2097
- // For the first GET request sent by signalR to start a WebSocket protocol,
2098
- // as far as we know, signalR only lets us tweak the request with this access token factory
2099
- // so we pass along the Sinequa CSRF token to pass the CSRF check..
2100
- return {
2101
- transport: this._getTransports(),
2102
- withCredentials: true,
2103
- headers,
2104
- accessTokenFactory: () => { var _a, _b; return ((_b = (_a = this.authenticationService.processedCredentials) === null || _a === void 0 ? void 0 : _a.data) === null || _b === void 0 ? void 0 : _b.csrfToken) || ""; }
2105
- };
2106
- }
2107
- }
2108
- WebSocketChatService.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.0.6", ngImport: i0, type: WebSocketChatService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
2109
- WebSocketChatService.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "14.0.6", ngImport: i0, type: WebSocketChatService });
2110
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.0.6", ngImport: i0, type: WebSocketChatService, decorators: [{
2111
- type: Injectable
2112
- }], ctorParameters: function () { return []; } });
2113
-
2114
- class ChatComponent extends AbstractFacet {
2115
- constructor() {
2116
- super();
2117
- this.loginService = inject(LoginService);
2118
- this.websocketService = inject(WebSocketChatService);
2119
- this.restService = inject(RestChatService);
2120
- this.instanceManagerService = inject(InstanceManagerService);
2121
- this.searchService = inject(SearchService);
2122
- this.principalService = inject(PrincipalWebService);
2123
- this.cdr = inject(ChangeDetectorRef);
2124
- this.appService = inject(AppService);
2125
- this.notificationsService = inject(NotificationsService);
2126
- /** Define the query to use to fetch answers */
2127
- this.query = this.searchService.query;
2128
- /** Define the protocol to be used for this chat instance*/
2129
- this.protocol = "WEBSOCKET";
2130
- /** Map of listeners overriding default registered ones*/
2131
- this.messageHandlers = new Map();
2132
- /** When the assistant answer a user question, automatically scroll down to the bottom of the discussion */
2133
- this.automaticScrollToLastResponse = false;
2134
- /** When the assistant answer a user question, automatically focus to the chat input */
2135
- this.focusAfterResponse = false;
2136
- /** Icon to use for the assistant messages */
2137
- this.assistantMessageIcon = 'sq-sinequa';
2138
- /** Event emitter triggered once the signalR connection is established */
2139
- this.connection = new EventEmitter();
2140
- /** Event emitter triggered each time the assistant updates the current chat */
2141
- /** Event emitter triggered when the chat is loading new content */
2142
- this.loading$ = new EventEmitter(false);
2143
- /** Emits the assistant configuration used when instantiating the component */
2144
- this._config = new EventEmitter();
2145
- this.data = new EventEmitter();
2146
- /** Event emitter triggered when the user clicks to open the original document representing the context attachment*/
2147
- this.openDocument = new EventEmitter();
2148
- /** Event emitter triggered when the user clicks to open the preview of a document representing the context attachment */
2149
- this.openPreview = new EventEmitter();
2150
- /** Event emitter triggered when the user clicks on a suggested action */
2151
- this.suggestAction = new EventEmitter();
2152
- this.messages$ = new BehaviorSubject(undefined);
2153
- this.isAdminOrDeletedAdmin = false;
2154
- this.question = '';
2155
- this._actions = [];
2156
- this._resetChatAction = new Action({
2157
- icon: 'fas fa-sync',
2158
- title: "Reset assistant",
2159
- action: () => this.newChat()
2160
- });
2161
- this._sub = new Subscription();
2162
- this.changes$ = new BehaviorSubject(undefined);
2163
- this.firstChangesHandled = false;
2164
- this.isAtBottom = true;
2165
- this.initializationError = false;
2166
- this.enabledUserInput = false;
2167
- this.isConnected = true; // By default, the chat is considered connected
2168
- // Flag to track whether the 'reconnected' listener is already registered
2169
- this._isReconnectedListenerRegistered = false;
2170
- this.defaultIssueTypes = [
2171
- 'User Interface bug',
2172
- 'Incorrect or misleading response',
2173
- 'Incomplete response',
2174
- 'Technical issue',
2175
- 'Privacy/data security issue',
2176
- 'Other'
2177
- ];
2178
- this.issueType = '';
2179
- this.reportType = 'dislike';
2180
- this.showReport = false;
2181
- this.showDebugMessages = false;
2182
- this._reloadSubscription = undefined;
2183
- this._actions.push(this._resetChatAction);
2184
- }
2185
- ngOnInit() {
2186
- this._sub.add(this.loginService.events.pipe(filter(e => e.type === 'login-complete'), tap(_ => this.instantiateChatService()), map(_ => this.chatService.initChatConfig()), switchMap(() => this.chatService.initConfig$), filter(initConfig => !!initConfig), switchMap(_ => this.chatService.init()), switchMap(_ => this.chatService.initProcess$), filter(success => !!success), tap(_ => {
2187
- if (this.chatService instanceof WebSocketChatService) {
2188
- this.connection.emit(this.chatService.connection);
2189
- }
2190
- this.onLoadChat();
2191
- }), tap(_ => this.chatService.overrideUser()), switchMap(_ => this.chatService.userOverride$), switchMap(_ => this.chatService.assistantConfig$), tap(config => {
2192
- var _a, _b;
2193
- // Setup admin status
2194
- this.isAdminOrDeletedAdmin = this.principalService.principal.isAdministrator || this.principalService.principal.isDelegatedAdmin || false;
2195
- this.config = config;
2196
- this.enabledUserInput = this.config.modeSettings.enabledUserInput;
2197
- this.issueTypes = ((_b = (_a = this.config.auditSettings) === null || _a === void 0 ? void 0 : _a.issueTypes) === null || _b === void 0 ? void 0 : _b.length) ? this.config.auditSettings.issueTypes : undefined;
2198
- this._config.emit(config);
2199
- try {
2200
- this.updateModelDescription();
2201
- if (!this.firstChangesHandled) {
2202
- this._previousQuery = JSON.parse(JSON.stringify(this.query)); // Initialize the previous query
2203
- this._handleChanges();
2204
- this._addScrollListener();
2205
- this.firstChangesHandled = true;
2206
- }
2207
- }
2208
- catch (error) {
2209
- this.initializationError = true;
2210
- throw error;
2211
- }
2212
- })).subscribe());
2213
- this._sub.add(combineLatest([
2214
- this.chatService.streaming$,
2215
- this.chatService.stoppingGeneration$
2216
- ]).pipe(map(([streaming, stoppingGeneration]) => !!(streaming || stoppingGeneration))).subscribe((result) => {
2217
- this._resetChatAction.disabled = result;
2218
- }));
2219
- }
2220
- ngOnChanges(changes) {
2221
- this.changes$.next(changes);
2222
- if (this.config) {
2223
- this._handleChanges();
2224
- }
2225
- }
2226
- ngOnDestroy() {
2227
- var _a, _b;
2228
- this._sub.unsubscribe();
2229
- (_a = this._dataSubscription) === null || _a === void 0 ? void 0 : _a.unsubscribe();
2230
- (_b = this._reloadSubscription) === null || _b === void 0 ? void 0 : _b.unsubscribe();
2231
- if (this.chatService instanceof WebSocketChatService) {
2232
- this.chatService.stopConnection();
2233
- }
2234
- }
2235
- get isAdmin() {
2236
- var _a;
2237
- return ((_a = this.principalService.principal) === null || _a === void 0 ? void 0 : _a.isAdministrator) || false;
2238
- }
2239
- /**
2240
- * Instantiate the chat service based on the provided @input protocol
2241
- * This chat service instance will then be stored in the instanceManagerService with provided @input instanceId as a key
2242
- */
2243
- instantiateChatService() {
2244
- switch (this.protocol) {
2245
- case 'REST':
2246
- this.chatService = this.restService;
2247
- break;
2248
- case 'WEBSOCKET':
2249
- this.chatService = this.websocketService;
2250
- break;
2251
- default:
2252
- throw new Error(`Could not found a ChatService implementation corresponding to the provided protocol: '${this.protocol}'`);
2253
- }
2254
- this.chatService.setChatInstanceId(this.instanceId);
2255
- this.instanceManagerService.storeInstance(this.instanceId, this.chatService);
2256
- }
2257
- get actions() { return this._actions; }
2258
- /**
2259
- * Handles the changes in the chat component.
2260
- * If the chat service is a WebSocketChatService, it handles the override of the message handlers if they exist.
2261
- * Initializes the chat with the provided chat messages if they exist, otherwise loads the default chat.
2262
- * If the chat is initialized, the initialization event is "Query", the query changes, and the queryChangeShouldTriggerReload function is provided,
2263
- * then the chat should be reloaded if the function returns true. Otherwise, the chat should be reloaded by default.
2264
- * It takes into account the ongoing streaming process and the ongoing stopping process to trigger that conditionally define the logic
2265
- * of the reload :
2266
- * - If the chat is streaming, then stop the generation and wait for the fetch to complete before reloading the chat.
2267
- * - If the chat is stopping the generation, then wait for the fetch to complete before reloading the chat.
2268
- */
2269
- _handleChanges() {
2270
- const changes = this.changes$.value;
2271
- // If the chat service is a WebSocketChatService, handle the override of the message handlers if exists
2272
- if ((changes === null || changes === void 0 ? void 0 : changes.messageHandlers) && this.messageHandlers && this.chatService instanceof WebSocketChatService) {
2273
- this.chatService.overrideMessageHandlers(this.messageHandlers);
2274
- }
2275
- /**
2276
- * Initialize the chat with the provided chat messages if exists, otherwise load the default chat
2277
- * Once the chat is initialized (firstChangesHandled is true), allow opening the chat with the new provided messages (if exists)
2278
- */
2279
- if (!this.firstChangesHandled || (changes === null || changes === void 0 ? void 0 : changes.chat)) {
2280
- const openChat = () => {
2281
- if (this.messages$.value) {
2282
- this.chatService.listSavedChat(); // Refresh the list of saved chats
2283
- }
2284
- this.openChat(this.chat.messages);
2285
- };
2286
- this.chatService.generateChatId();
2287
- if (this.chat) {
2288
- this.chatService.generateAuditEvent('new-chat', { 'configuration': JSON.stringify(this.chatService.assistantConfig$.value), 'chat-init': JSON.stringify(this.chat) });
2289
- openChat();
2290
- }
2291
- else {
2292
- this.chatService.generateAuditEvent('new-chat', { 'configuration': JSON.stringify(this.chatService.assistantConfig$.value) });
2293
- this.loadDefaultChat();
2294
- }
2295
- }
2296
- /**
2297
- * If the chat is initialized, the initialization event is "Query", the query changes and the queryChangeShouldTriggerReload function is provided,
2298
- * then the chat should be reloaded if the function returns true
2299
- * Otherwise, the chat should be reloaded by default
2300
- */
2301
- if (this.firstChangesHandled && (changes === null || changes === void 0 ? void 0 : changes.query) && this.config.modeSettings.initialization.event === 'Query') {
2302
- if (this.queryChangeShouldTriggerReload ? this.queryChangeShouldTriggerReload(this._previousQuery, this.query) : true) {
2303
- if (!!this.chatService.stoppingGeneration$.value) {
2304
- if (!this._reloadSubscription) {
2305
- // Create a subscription to wait for both streaming$ and stoppingGeneration$ to be false
2306
- this._reloadSubscription = combineLatest([
2307
- this.chatService.streaming$,
2308
- this.chatService.stoppingGeneration$
2309
- ])
2310
- .pipe(filter(([streaming, stopping]) => !streaming && !stopping), // Wait until both are false
2311
- take(1) // Complete after the first match
2312
- ).subscribe(() => {
2313
- // Execute the reload after the query change
2314
- this._triggerReloadAfterQueryChange();
2315
- // Update _previousQuery with the current query
2316
- this._previousQuery = JSON.parse(JSON.stringify(this.query));
2317
- // Clean up subscription and reset its value
2318
- this._reloadSubscription = undefined;
2319
- });
2320
- }
2321
- }
2322
- else if (!!this.chatService.streaming$.value) {
2323
- if (!this._reloadSubscription) {
2324
- this._reloadSubscription = this.chatService.stopGeneration()
2325
- .subscribe({
2326
- next: () => { },
2327
- error: () => {
2328
- var _a;
2329
- // Clean up subscription and reset its value
2330
- (_a = this._reloadSubscription) === null || _a === void 0 ? void 0 : _a.unsubscribe();
2331
- this._reloadSubscription = undefined;
2332
- },
2333
- complete: () => {
2334
- // Wait for the ongoing fetch to complete, then trigger the reload
2335
- this.chatService.streaming$.pipe(filter((streaming) => !streaming), take(1)).subscribe(() => {
2336
- // Execute the reload after the query change
2337
- this._triggerReloadAfterQueryChange();
2338
- // Update _previousQuery with the current query
2339
- this._previousQuery = JSON.parse(JSON.stringify(this.query));
2340
- // Clean up subscription and reset its value
2341
- this._reloadSubscription.unsubscribe();
2342
- this._reloadSubscription = undefined;
2343
- });
2344
- }
2345
- });
2346
- }
2347
- }
2348
- else {
2349
- // Execute the reload after the query change
2350
- this._triggerReloadAfterQueryChange();
2351
- // Update _previousQuery with the current query
2352
- this._previousQuery = JSON.parse(JSON.stringify(this.query));
2353
- }
2354
- }
2355
- else {
2356
- // Update _previousQuery with the current query
2357
- this._previousQuery = JSON.parse(JSON.stringify(this.query));
2358
- }
2359
- }
2360
- }
2361
- /**
2362
- * Triggers a reload after the query change.
2363
- * This method performs the necessary operations to reload the chat after a query change.
2364
- * It sets the system and user messages, resets the savedChatId, generates a new chatId,
2365
- * generates a new chat audit event, and handles the query mode.
2366
- */
2367
- _triggerReloadAfterQueryChange() {
2368
- const systemMsg = { role: 'system', content: this.config.defaultValues.systemPrompt, additionalProperties: { display: false } };
2369
- const userMsg = { role: 'user', content: ChatService.formatPrompt(this.config.defaultValues.userPrompt, { principal: this.principalService.principal }), additionalProperties: { display: this.config.modeSettings.displayUserPrompt } };
2370
- this.chatService.setSavedChatId(undefined); // Reset the savedChatId
2371
- this.chatService.generateChatId(); // Generate a new chatId
2372
- this.chatService.generateAuditEvent('new-chat', { 'configuration': JSON.stringify(this.chatService.assistantConfig$.value) }); // Generate a new chat audit event
2373
- this._handleQueryMode(systemMsg, userMsg);
2374
- }
2375
- /**
2376
- * Adds a scroll listener to the message list element.
2377
- * The listener is triggered when any of the following events occur:
2378
- * - Loading state changes
2379
- * - Messages change
2380
- * - Streaming state changes
2381
- * - Scroll event occurs on the message list element
2382
- *
2383
- * When the listener is triggered, it updates the `isAtBottom` property.
2384
- */
2385
- _addScrollListener() {
2386
- this._sub.add(merge(this.loading$, this.messages$, this.chatService.streaming$, fromEvent(this.messageList.nativeElement, 'scroll')).subscribe(() => {
2387
- this.isAtBottom = this._toggleScrollButtonVisibility();
2388
- this.cdr.detectChanges();
2389
- }));
2390
- }
2391
- /**
2392
- * Get the model description based on the defaultValues service_id and model_id
2393
- */
2394
- updateModelDescription() {
2395
- this.modelDescription = this.chatService.getModel(this.config.defaultValues.service_id, this.config.defaultValues.model_id);
2396
- this.cdr.detectChanges();
2397
- }
2398
- /**
2399
- * Submits a question from the user.
2400
- * If the user is editing a previous message, removes all subsequent messages from the chat history.
2401
- * Triggers the fetch of the answer for the submitted question by calling _fetchAnswer().
2402
- * Clears the input value in the UI.
2403
- * ⚠️ If the chat is streaming or stopping the generation, the operation is not allowed.
2404
- */
2405
- submitQuestion() {
2406
- var _a;
2407
- if (!!this.chatService.streaming$.value || !!this.chatService.stoppingGeneration$.value) {
2408
- return;
2409
- }
2410
- if (this.question.trim() && this.messages$.value && this.chatService.chatHistory) {
2411
- // When the user submits a question, if the user is editing a previous message, remove all subsequent messages from the chat history
2412
- if (this.messageToEdit !== undefined) {
2413
- // Update the messages in the UI
2414
- this.messages$.next(this.messages$.value.slice(0, this.messageToEdit));
2415
- // Update the raw messages in the chat history which is the clean version used to make the next request
2416
- this.chatService.chatHistory = this.chatService.chatHistory.slice(0, this.remappedMessageToEdit);
2417
- this.messageToEdit = undefined;
2418
- this.remappedMessageToEdit = undefined;
2419
- }
2420
- // Remove the search warning message if exists
2421
- if (((_a = this.chatService.chatHistory.at(-1)) === null || _a === void 0 ? void 0 : _a.role) === 'search-warning') {
2422
- this.chatService.chatHistory.pop();
2423
- }
2424
- // Fetch the answer
2425
- this._fetchAnswer(this.question.trim(), this.chatService.chatHistory);
2426
- // Clear the input value in the UI
2427
- this.questionInput.nativeElement.value = '';
2428
- this.questionInput.nativeElement.style.height = `auto`;
2429
- }
2430
- }
2431
- /**
2432
- * Triggers the fetch of the answer for the given question and updates the conversation.
2433
- * Generates an audit event for the user input.
2434
- *
2435
- * @param question - The question asked by the user.
2436
- * @param conversation - The current conversation messages.
2437
- */
2438
- _fetchAnswer(question, conversation) {
2439
- var _a;
2440
- const userMsg = { role: 'user', content: question, additionalProperties: { display: true, isUserInput: true, additionalWorkflowProperties: this.config.additionalWorkflowProperties } };
2441
- const messages = [...conversation, userMsg];
2442
- this.messages$.next(messages);
2443
- this.fetch(messages);
2444
- this.chatService.generateAuditEvent('message', Object.assign(Object.assign({}, this._defineMessageAuditDetails(userMsg, messages.length - 1)), { 'query': JSON.stringify(this.query), 'is-user-input': true, 'enabled-functions': (_a = this.config.defaultValues.functions) === null || _a === void 0 ? void 0 : _a.filter(func => func.enabled).map(func => func.name), 'additional-workflow-properties': JSON.stringify(this.config.additionalWorkflowProperties) }));
2445
- }
2446
- /**
2447
- * Depending on the connection's state :
2448
- * - If connected => given a list of messages, the chat endpoint is invoked for a continuation and updates the list of messages accordingly.
2449
- * - If any other state => a connection error message is displayed in the chat.
2450
- * ⚠️ If the assistant is streaming or stopping the generation, the operation is not allowed.
2451
- * @param messages The list of messages to invoke the chat endpoint with
2452
- */
2453
- fetch(messages) {
2454
- var _a;
2455
- if (!!this.chatService.streaming$.value || !!this.chatService.stoppingGeneration$.value) {
2456
- return;
2457
- }
2458
- this._updateConnectionStatus();
2459
- this.cdr.detectChanges();
2460
- if (this.isConnected) {
2461
- this.loading$.next(true);
2462
- (_a = this._dataSubscription) === null || _a === void 0 ? void 0 : _a.unsubscribe();
2463
- this._dataSubscription = this.chatService.fetch(messages, this.query)
2464
- .subscribe({
2465
- next: res => this.updateData(res.history),
2466
- error: () => {
2467
- this._updateConnectionStatus();
2468
- if (!this.isConnected) {
2469
- const message = { role: 'connection-error', content: this.config.connectionSettings.connectionErrorMessage, additionalProperties: { display: true } };
2470
- this.messages$.next([...messages, message]);
2471
- }
2472
- this.terminateFetch();
2473
- },
2474
- complete: () => {
2475
- var _a, _b;
2476
- // Remove the last message if it's an empty message
2477
- // This is due to the manner in which the chat service handles consecutive messages
2478
- const lastMessage = (_a = this.messages$.value) === null || _a === void 0 ? void 0 : _a.at(-1);
2479
- if (this.isEmptyAssistantMessage(lastMessage)) {
2480
- this.messages$.next((_b = this.messages$.value) === null || _b === void 0 ? void 0 : _b.slice(0, -1));
2481
- }
2482
- this.terminateFetch();
2483
- }
2484
- });
2485
- }
2486
- else {
2487
- const message = { role: 'connection-error', content: this.config.connectionSettings.connectionErrorMessage, additionalProperties: { display: true } };
2488
- this.messages$.next([...messages, message]);
2489
- }
2490
- if (this.automaticScrollToLastResponse) {
2491
- this.scrollDown();
2492
- }
2493
- }
2494
- /**
2495
- * Retry to fetch the messages if the connection issues.
2496
- * - If reconnecting => keep display the connection error message even when clicking on the "retry" button and increasing the number of retrial attempts, until the connection is re-established
2497
- * - If disconnected => On click on the "retry" button, start the connection process while displaying the connection error message :
2498
- * * If successful => given a list of messages, the chat endpoint is invoked for a continuation and updates the list of messages accordingly.
2499
- * * If failed => increase the number of retrial attempts
2500
- */
2501
- retryFetch() {
2502
- if (this.chatService instanceof WebSocketChatService) {
2503
- // A one-time listener for reconnected event
2504
- const onReconnectedHandler = () => {
2505
- // Get the messages without the last one (the connection error message)
2506
- const messages = this.messages$.value.slice(0, -1);
2507
- // Find the last "user" message in the messages list
2508
- let index = messages.length - 1;
2509
- while (index >= 0 && messages[index].role !== 'user') {
2510
- index--;
2511
- }
2512
- // If a user message is found (and it should always be the case), remove all subsequent messages from the chat history
2513
- // Update the messages in the UI
2514
- // and fetch the answer from the assistant
2515
- if (index >= 0) {
2516
- this.messages$.next(this.messages$.value.slice(0, index + 1));
2517
- const remappedIndex = this._remapIndexInChatHistory(index);
2518
- this.chatService.chatHistory = this.chatService.chatHistory.slice(0, remappedIndex + 1);
2519
- this.fetch(this.chatService.chatHistory);
2520
- }
2521
- this.retrialAttempts = undefined; // Reset the number of retrial attempts
2522
- /**
2523
- * To remove the handler for onreconnected() after it's been registered,cannot directly use off() like you would for normal events registered with connection.on().
2524
- * Instead, you need to explicitly remove or reset the handler by assigning it to null or an empty function
2525
- */
2526
- this.chatService.connection.onreconnected(() => { });
2527
- // Reset the flag to ensure the handler is registered again when needed
2528
- this._isReconnectedListenerRegistered = false;
2529
- };
2530
- // Depending on the connection's state, take the appropriate action
2531
- switch (this.chatService.connection.state) {
2532
- case HubConnectionState.Connected:
2533
- // If the connection is re-established in the meantime, fetch the messages
2534
- onReconnectedHandler();
2535
- break;
2536
- case HubConnectionState.Reconnecting:
2537
- // Attach the reconnected listener if not already registered
2538
- if (!this._isReconnectedListenerRegistered) {
2539
- this.chatService.connection.onreconnected(onReconnectedHandler);
2540
- this._isReconnectedListenerRegistered = true;
2541
- }
2542
- // Increase the number of retrial attempts
2543
- this.retrialAttempts = this.retrialAttempts ? this.retrialAttempts + 1 : 1;
2544
- break;
2545
- case HubConnectionState.Disconnected:
2546
- // Start the new connection
2547
- this.chatService.startConnection()
2548
- .then(() => onReconnectedHandler())
2549
- .catch(() => {
2550
- this.retrialAttempts = this.retrialAttempts ? this.retrialAttempts + 1 : 1;
2551
- });
2552
- break;
2553
- default:
2554
- break;
2555
- }
2556
- }
2557
- }
2558
- /**
2559
- * Check if the signalR connection is connected.
2560
- * For the REST protocol, the connection is always considered connected (for the moment).
2561
- */
2562
- _updateConnectionStatus() {
2563
- this.isConnected = (this.chatService instanceof WebSocketChatService) ? this.chatService.connection.state === HubConnectionState.Connected : true;
2564
- }
2565
- /**
2566
- * Update the UI with the new messages
2567
- * @param messages
2568
- */
2569
- updateData(messages) {
2570
- this.messages$.next(messages);
2571
- this.data.emit(messages);
2572
- this.loading$.next(false);
2573
- this.question = '';
2574
- if (this.automaticScrollToLastResponse) {
2575
- this.scrollDown();
2576
- }
2577
- }
2578
- /**
2579
- * @returns true if the chat discussion is scrolled down to the bottom, false otherwise
2580
- */
2581
- _toggleScrollButtonVisibility() {
2582
- var _a, _b, _c, _d;
2583
- if ((_a = this.messageList) === null || _a === void 0 ? void 0 : _a.nativeElement) {
2584
- return Math.round(((_b = this.messageList) === null || _b === void 0 ? void 0 : _b.nativeElement.scrollHeight) - ((_c = this.messageList) === null || _c === void 0 ? void 0 : _c.nativeElement.scrollTop) - 1) <= ((_d = this.messageList) === null || _d === void 0 ? void 0 : _d.nativeElement.clientHeight);
2585
- }
2586
- return true;
2587
- }
2588
- /**
2589
- * Scroll down to the bottom of the chat discussion
2590
- */
2591
- scrollDown() {
2592
- setTimeout(() => {
2593
- var _a;
2594
- if ((_a = this.messageList) === null || _a === void 0 ? void 0 : _a.nativeElement) {
2595
- this.messageList.nativeElement.scrollTop = this.messageList.nativeElement.scrollHeight;
2596
- this.cdr.detectChanges();
2597
- }
2598
- }, 10);
2599
- }
2600
- /**
2601
- * Start a new chat with the defaultValues settings.
2602
- * The savedChatId in the chat service will be reset, so that the upcoming saved chat operations will be performed on the fresh new chat.
2603
- * If the savedChat feature is enabled, the list of saved chats will be refreshed.
2604
- * ⚠️ If the assistant is streaming or stopping the generation, the operation is not allowed.
2605
- */
2606
- newChat() {
2607
- if (!!this.chatService.streaming$.value || !!this.chatService.stoppingGeneration$.value) {
2608
- return;
2609
- }
2610
- this.chatService.setSavedChatId(undefined); // Reset the savedChatId
2611
- this.chatService.generateChatId(); // Generate a new chatId
2612
- this.chatService.listSavedChat(); // Refresh the list of saved chats
2613
- this.chatService.generateAuditEvent('new-chat', { 'configuration': JSON.stringify(this.chatService.assistantConfig$.value) }); // Generate a new chat audit event
2614
- this.loadDefaultChat(); // Start a new chat
2615
- }
2616
- /**
2617
- * Attaches the specified document IDs to the assistant.
2618
- * If no document IDs are provided, the operation is not allowed.
2619
- * If the action for attaching a document is not defined at the application customization level, an error is logged.
2620
- * ⚠️ If the assistant is streaming or stopping the generation, the operation is not allowed.
2621
- * @param ids - An array of document IDs to attach.
2622
- */
2623
- attachToChat(ids) {
2624
- var _a, _b;
2625
- if (!!this.chatService.streaming$.value || !!this.chatService.stoppingGeneration$.value) {
2626
- return;
2627
- }
2628
- if (!ids || (ids === null || ids === void 0 ? void 0 : ids.length) < 1) {
2629
- return;
2630
- }
2631
- const attachDocAction = (_a = this.config.modeSettings.actions) === null || _a === void 0 ? void 0 : _a["attachDocAction"];
2632
- if (!attachDocAction) {
2633
- console.error(`No action is defined for attaching a document to the assistant "${this.instanceId}"`);
2634
- return;
2635
- }
2636
- const userMsg = { role: 'user', content: '', additionalProperties: { display: false, isUserInput: false, type: "Action", forcedWorkflow: attachDocAction.forcedWorkflow, forcedWorkflowProperties: Object.assign(Object.assign({}, (attachDocAction.forcedWorkflowProperties || {})), { ids }), additionalWorkflowProperties: this.config.additionalWorkflowProperties } };
2637
- // Remove the search warning message if exists
2638
- if (((_b = this.chatService.chatHistory.at(-1)) === null || _b === void 0 ? void 0 : _b.role) === 'search-warning') {
2639
- this.chatService.chatHistory.pop();
2640
- }
2641
- const messages = [...this.chatService.chatHistory, userMsg];
2642
- this.messages$.next(messages);
2643
- this.fetch(messages);
2644
- this.chatService.generateAuditEvent('ActionRequested', {
2645
- 'detail': 'attachDocAction',
2646
- 'forced-workflow': 'SinequaAddSelectedDocumentsWorkflow',
2647
- 'forced-workflow-properties': JSON.stringify(ids),
2648
- });
2649
- }
2650
- /**
2651
- * Start the default chat with the defaultValues settings
2652
- * If the chat is meant to be initialized with event === "Query", the corresponding user query message will be added to the chat history
2653
- */
2654
- loadDefaultChat() {
2655
- // Define the default system prompt and user prompt messages
2656
- const systemMsg = { role: 'system', content: this.config.defaultValues.systemPrompt, additionalProperties: { display: false } };
2657
- const userMsg = { role: 'user', content: ChatService.formatPrompt(this.config.defaultValues.userPrompt, { principal: this.principalService.principal }), additionalProperties: { display: this.config.modeSettings.displayUserPrompt } };
2658
- if (this.config.modeSettings.initialization.event === 'Query') {
2659
- this._handleQueryMode(systemMsg, userMsg);
2660
- }
2661
- else {
2662
- this._handlePromptMode(systemMsg, userMsg);
2663
- }
2664
- }
2665
- /**
2666
- * Handles the prompt mode of the chat component.
2667
- * If `sendUserPrompt` is true, it opens the chat with both system and user messages,
2668
- * and generates audit events for both messages.
2669
- * If `sendUserPrompt` is false, it opens the chat with only the system message,
2670
- * and generates an audit event for the system message.
2671
- *
2672
- * @param systemMsg - The system message to be displayed in the chat.
2673
- * @param userMsg - The user message to be displayed in the chat (optional).
2674
- */
2675
- _handlePromptMode(systemMsg, userMsg) {
2676
- if (this.config.modeSettings.sendUserPrompt) {
2677
- this.openChat([systemMsg, userMsg]);
2678
- this.chatService.generateAuditEvent('message', this._defineMessageAuditDetails(systemMsg, 0));
2679
- this.chatService.generateAuditEvent('message', this._defineMessageAuditDetails(userMsg, 1));
2680
- }
2681
- else {
2682
- this.openChat([systemMsg]);
2683
- this.chatService.generateAuditEvent('message', this._defineMessageAuditDetails(systemMsg, 0));
2684
- }
2685
- }
2686
- /**
2687
- * Handles the query mode by displaying the system message, user message, and user query message.
2688
- * If the provided query text is not empty, then add the user query message to the chat history and invoke the assistant
2689
- * Otherwise, just start a new chat with a warning message inviting the user to perform a full text search to retrieve some results
2690
- * @param systemMsg - The system message to be displayed.
2691
- * @param userMsg - The user message to be displayed.
2692
- */
2693
- _handleQueryMode(systemMsg, userMsg) {
2694
- var _a, _b;
2695
- if (!!this.query.text) {
2696
- const userQueryMsg = { role: 'user', content: this.query.text, additionalProperties: { display: this.config.modeSettings.initialization.displayUserQuery, query: this.query, forcedWorkflow: this.config.modeSettings.initialization.forcedWorkflow, forcedFunction: this.config.modeSettings.initialization.forcedFunction, isUserInput: true, additionalWorkflowProperties: this.config.additionalWorkflowProperties } };
2697
- if (this.config.modeSettings.sendUserPrompt) {
2698
- this.openChat([systemMsg, userMsg, userQueryMsg]);
2699
- this.chatService.generateAuditEvent('message', this._defineMessageAuditDetails(systemMsg, 0));
2700
- this.chatService.generateAuditEvent('message', this._defineMessageAuditDetails(userMsg, 1));
2701
- this.chatService.generateAuditEvent('message', Object.assign(Object.assign({}, this._defineMessageAuditDetails(userQueryMsg, 2)), { 'query': JSON.stringify(this.query), 'is-user-input': true, 'forced-workflow': this.config.modeSettings.initialization.forcedWorkflow, 'forced-function': this.config.modeSettings.initialization.forcedFunction, 'enabled-functions': (_a = this.config.defaultValues.functions) === null || _a === void 0 ? void 0 : _a.filter(func => func.enabled).map(func => func.name), 'additional-workflow-properties': JSON.stringify(this.config.additionalWorkflowProperties) }));
2702
- }
2703
- else {
2704
- this.openChat([systemMsg, userQueryMsg]);
2705
- this.chatService.generateAuditEvent('message', this._defineMessageAuditDetails(systemMsg, 0));
2706
- this.chatService.generateAuditEvent('message', Object.assign(Object.assign({}, this._defineMessageAuditDetails(userQueryMsg, 1)), { 'query': JSON.stringify(this.query), 'is-user-input': true, 'forced-workflow': this.config.modeSettings.initialization.forcedWorkflow, 'forced-function': this.config.modeSettings.initialization.forcedFunction, 'enabled-functions': (_b = this.config.defaultValues.functions) === null || _b === void 0 ? void 0 : _b.filter(func => func.enabled).map(func => func.name), 'additional-workflow-properties': JSON.stringify(this.config.additionalWorkflowProperties) }));
2707
- }
2708
- }
2709
- else {
2710
- const warningMsg = { role: 'search-warning', content: this.config.globalSettings.searchWarningMessage, additionalProperties: { display: true } };
2711
- this.openChat([systemMsg, warningMsg]);
2712
- this.chatService.generateAuditEvent('message', this._defineMessageAuditDetails(warningMsg, 0));
2713
- }
2714
- }
2715
- _defineMessageAuditDetails(message, rank) {
2716
- return {
2717
- 'duration': 0,
2718
- 'text': message.content,
2719
- 'role': message.role,
2720
- 'rank': rank
2721
- };
2722
- }
2723
- /**
2724
- * Start/open a new chat with the provided messages and chatId
2725
- * If the last message is from the user, a request to the assistant is made to get an answer
2726
- * If the last message is from the assistant, the conversation is loaded right away
2727
- * @param messages The list of messages of the chat
2728
- * @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
2729
- */
2730
- openChat(messages, savedChatId) {
2731
- if (!messages || !Array.isArray(messages)) {
2732
- console.error('Error occurs while trying to load the discussion. Invalid messages received :', messages);
2733
- return;
2734
- }
2735
- if (savedChatId) {
2736
- this.chatService.setSavedChatId(savedChatId);
2737
- this.chatService.generateChatId(savedChatId);
2738
- }
2739
- this.resetChat();
2740
- this.messages$.next(messages);
2741
- this.chatService.chatHistory = messages;
2742
- const lastMessage = messages.at(-1);
2743
- if (lastMessage && lastMessage.role === 'user') {
2744
- this.fetch(messages); // If the last message if from a user, an answer from the assistant is expected
2745
- }
2746
- else {
2747
- this.updateData(messages); // If the last message if from the assistant, we can load the conversation right away
2748
- this.terminateFetch();
2749
- }
2750
- this._addScrollListener();
2751
- }
2752
- /**
2753
- * Reset the chat by clearing the chat history and the UI accordingly
2754
- * The user input will be cleared
2755
- * The fetch subscription will be terminated
2756
- */
2757
- resetChat() {
2758
- if (this.messages$.value) {
2759
- this.messages$.next(undefined); // Reset chat
2760
- }
2761
- this.chatService.chatHistory = undefined; // Reset chat history
2762
- this.question = '';
2763
- this.terminateFetch();
2764
- }
2765
- /**
2766
- * Fetch and Load the saved chat from the saved chat index.
2767
- * If the saved chat is found, the chat discussion will be loaded with the provided messages and chatId
2768
- */
2769
- onLoadChat() {
2770
- this.loading$.next(true);
2771
- this._sub.add(this.chatService.loadSavedChat$
2772
- .pipe(filter(savedChat => !!savedChat), switchMap(savedChat => this.chatService.getSavedChat(savedChat.id)), filter(savedChatHistory => !!savedChatHistory), tap(savedChatHistory => this.openChat(savedChatHistory.history, savedChatHistory.id))).subscribe());
2773
- }
2774
- /**
2775
- * Stop the generation of the current assistant's answer.
2776
- * The fetch subscription will be terminated.
2777
- */
2778
- stopGeneration() {
2779
- this.chatService.stopGeneration().subscribe(() => this.terminateFetch());
2780
- }
2781
- /**
2782
- * Terminate the fetch process by unsubscribing from the data subscription and updating the loading status to false.
2783
- * Additionally, focus on the chat input if the focusAfterResponse flag is set to true.
2784
- */
2785
- terminateFetch() {
2786
- var _a;
2787
- (_a = this._dataSubscription) === null || _a === void 0 ? void 0 : _a.unsubscribe();
2788
- this._dataSubscription = undefined;
2789
- this.loading$.next(false);
2790
- this.cdr.detectChanges();
2791
- if (this.focusAfterResponse) {
2792
- setTimeout(() => {
2793
- var _a;
2794
- (_a = this.questionInput) === null || _a === void 0 ? void 0 : _a.nativeElement.focus();
2795
- });
2796
- }
2797
- }
2798
- /**
2799
- * Copy a previous user message of the chat history to the chat user input.
2800
- * Thus, the user can edit and resubmit the message.
2801
- * Once the edited message is submitted, all subsequent messages starting from @param index will be removed from the history and the UI will be updated accordingly.
2802
- * The assistant will regenerate a new answer based on the updated chat history.
2803
- * ⚠️ If the assistant is streaming or stopping the generation, the operation is not allowed.
2804
- * @param index The index of the user's message to edit
2805
- */
2806
- editMessage(index) {
2807
- if (!!this.chatService.streaming$.value || !!this.chatService.stoppingGeneration$.value) {
2808
- return;
2809
- }
2810
- this.messageToEdit = index;
2811
- this.remappedMessageToEdit = this._remapIndexInChatHistory(index);
2812
- this.question = this.chatService.chatHistory[this._remapIndexInChatHistory(index)].content;
2813
- this.chatService.generateAuditEvent('edit.click', { 'rank': this._remapIndexInChatHistory(index) });
2814
- }
2815
- /**
2816
- * Copy a previous assistant message of the chat history to the clipboard.
2817
- * @param index The index of the assistant's message to edit
2818
- */
2819
- copyMessage(index) {
2820
- // Remap the index in the chat history
2821
- const idx = this._remapIndexInChatHistory(index);
2822
- this.chatService.generateAuditEvent('copy.click', { 'rank': idx });
2823
- }
2824
- /**
2825
- * Starting from the provided index, remove all subsequent messages from the chat history and the UI accordingly.
2826
- * The assistant will regenerate a new answer based on the updated chat history.
2827
- * ⚠️ If the assistant is streaming or stopping the generation, the operation is not allowed.
2828
- * @param index The index of the assistant's message to regenerate
2829
- */
2830
- regenerateMessage(index) {
2831
- if (!!this.chatService.streaming$.value || !!this.chatService.stoppingGeneration$.value) {
2832
- return;
2833
- }
2834
- // Update the messages in the UI by removing all subsequent 'assistant' messages starting from the provided index until the first previous 'user' message
2835
- let i = index;
2836
- while (i >= 0 && (this.messages$.value)[i].role !== 'user') {
2837
- i--;
2838
- }
2839
- // It should always be the case that i > 0
2840
- if (i >= 0) {
2841
- this.messages$.next(this.messages$.value.slice(0, i + 1));
2842
- // Remap the index of this found first previous 'user' message in the chat history
2843
- const idx = this._remapIndexInChatHistory(i);
2844
- // Define and Update the chat history based on which the assistant will generate a new answer
2845
- this.chatService.chatHistory = this.chatService.chatHistory.slice(0, idx + 1);
2846
- // Fetch the answer
2847
- this.fetch(this.chatService.chatHistory);
2848
- this.chatService.generateAuditEvent('regenerate.click', { 'rank': idx });
2849
- }
2850
- }
2851
- /**
2852
- * Remaps the index in the chat history.
2853
- * The chat history is a list of messages where some messages can be hidden (display set to false).
2854
- * The index provided as input is the index of the message in the chat history displayed in the UI.
2855
- * This function should be removed once the backend is updated to add the ids of the messages in the chat history
2856
- * @param index - The index to be remapped.
2857
- */
2858
- _remapIndexInChatHistory(index) {
2859
- // a copy of the chat history is created to avoid modifying the original chat history.
2860
- // Additionally, a rank is giving to each message.
2861
- // All messages having role 'user' are updated with the display property set to true, this is mandatory to get the correct rank of the message in the chat history when the user message to remap is hidden
2862
- const history = this.chatService.chatHistory
2863
- .slice()
2864
- .map((message, idx) => (Object.assign(Object.assign({}, message), { additionalProperties: Object.assign(Object.assign({}, message.additionalProperties), { rank: idx }) })))
2865
- .map((message) => {
2866
- if (message.role === "user") {
2867
- return Object.assign(Object.assign({}, message), { additionalProperties: Object.assign(Object.assign({}, message.additionalProperties), { display: true }) });
2868
- }
2869
- return message;
2870
- });
2871
- // Count the number of hidden messages (of role different then "user") in messages$ before the provided index
2872
- // All messages having role 'user' are updated with the display property set to true, this is mandatory to get the correct rank of the message in the chat history when the user message to remap is hidden
2873
- // This is mandatory to get the correct rank of the message in the chat history
2874
- // Since some hidden messages (like 'system' messages) are not displayed in the UI but have been counted in the provided index
2875
- const numberOfHiddenMessagesInMessages$BeforeIndex = this.messages$.value
2876
- .slice(0, index)
2877
- .map((message) => {
2878
- if (message.role === "user") {
2879
- return Object.assign(Object.assign({}, message), { additionalProperties: Object.assign(Object.assign({}, message.additionalProperties), { display: true }) });
2880
- }
2881
- return message;
2882
- })
2883
- .filter(message => !message.additionalProperties.display).length;
2884
- // remove all messages that have display set to false
2885
- // this is mandatory since at the point of time when the assistant answers a question,
2886
- // it might have some hidden messages (for example contextMessages) that are available in the chat history but don't figure in messages$ unless a new question is asked
2887
- const filteredHistory = history.filter(message => message.additionalProperties.display);
2888
- // return the index of the message in the filtered history
2889
- return filteredHistory[index - numberOfHiddenMessagesInMessages$BeforeIndex].additionalProperties.rank;
2890
- }
2891
- /**
2892
- * Handles the key up event for 'Backspace' and 'Enter' keys.
2893
- * @param event - The keyboard event.
2894
- */
2895
- onKeyUp(event) {
2896
- switch (event.key) {
2897
- case 'Backspace':
2898
- this.calculateHeight();
2899
- break;
2900
- case 'Enter':
2901
- if (!event.shiftKey) {
2902
- event.preventDefault();
2903
- this.submitQuestion();
2904
- }
2905
- this.calculateHeight();
2906
- break;
2907
- default:
2908
- break;
2909
- }
2910
- }
2911
- /**
2912
- * Calculates and adjusts the height of the question input element based on its content.
2913
- * If the Enter key is pressed without the Shift key, it prevents the default behavior.
2914
- * @param event The keyboard event
2915
- */
2916
- calculateHeight(event) {
2917
- if ((event === null || event === void 0 ? void 0 : event.key) === 'Enter' && !event.shiftKey) {
2918
- event === null || event === void 0 ? void 0 : event.preventDefault();
2919
- }
2920
- const maxHeight = 170;
2921
- const el = this.questionInput.nativeElement;
2922
- el.style.maxHeight = `${maxHeight}px`;
2923
- el.style.height = 'auto';
2924
- el.style.height = `${el.scrollHeight}px`;
2925
- el.style.overflowY = el.scrollHeight >= maxHeight ? 'scroll' : 'hidden';
2926
- }
2927
- /**
2928
- * Send a "like" event on clicking on the thumb-up icon of an assistant's message
2929
- * @param message The assistant message to like
2930
- * @param rank The rank of the message to like
2931
- */
2932
- onLike(message, rank) {
2933
- // Remap the index in the chat history
2934
- const idx = this._remapIndexInChatHistory(rank);
2935
- this.chatService.generateAuditEvent('thumb-up.click', { rank: idx });
2936
- this.reportType = 'like';
2937
- this.messageToReport = message;
2938
- this.reportComment = undefined;
2939
- this.reportRank = rank;
2940
- this.showReport = true;
2941
- this.chatService.chatHistory[this._remapIndexInChatHistory(rank)].additionalProperties.$liked = true;
2942
- this._updateChatHistory();
2943
- }
2944
- /**
2945
- * Send a "dislike" event on clicking on the thumb-down icon of an assistant's message.
2946
- * It also opens the issue reporting dialog.
2947
- * @param message The assistant message to dislike
2948
- * @param index The rank of the message to dislike
2949
- */
2950
- onDislike(message, rank) {
2951
- // Remap the index in the chat history
2952
- const idx = this._remapIndexInChatHistory(rank);
2953
- this.chatService.generateAuditEvent('thumb-down.click', { rank: idx });
2954
- this.reportType = 'dislike';
2955
- this.messageToReport = message;
2956
- this.issueType = '';
2957
- this.reportComment = undefined;
2958
- this.reportRank = rank;
2959
- this.showReport = true;
2960
- this.chatService.chatHistory[this._remapIndexInChatHistory(rank)].additionalProperties.$disliked = true;
2961
- this._updateChatHistory();
2962
- }
2963
- _updateChatHistory() {
2964
- this.messages$.next(this.chatService.chatHistory);
2965
- if (this.chatService.savedChatId) {
2966
- this.chatService.updateSavedChat(this.chatService.savedChatId, undefined, this.chatService.chatHistory).subscribe();
2967
- }
2968
- }
2969
- /**
2970
- * Report an issue related to the assistant's message.
2971
- */
2972
- sendReport() {
2973
- const details = {
2974
- 'comment': this.reportComment,
2975
- 'text': this.messageToReport.content,
2976
- 'rank': this.reportRank,
2977
- };
2978
- if (this.reportType === 'dislike') {
2979
- details['report-type'] = this.issueType;
2980
- this.chatService.generateAuditEvent('negative-report.send', details);
2981
- }
2982
- else {
2983
- this.chatService.generateAuditEvent('positive-report.send', details);
2984
- }
2985
- this.notificationsService.success('Your report has been successfully sent');
2986
- this.showReport = false;
2987
- }
2988
- /**
2989
- * Close the reporting dialog.
2990
- */
2991
- ignoreReport() {
2992
- this.showReport = false;
2993
- }
2994
- /**
2995
- * Handle the click on a reference's 'open preview'.
2996
- * @param data
2997
- */
2998
- openAttachmentPreview(data) {
2999
- this.openPreview.emit(data.reference);
3000
- const details = {
3001
- 'doc-id': data.reference.recordId,
3002
- 'title': data.reference.record.title,
3003
- 'source': data.reference.record.treepath,
3004
- 'collection': data.reference.record.collection,
3005
- 'index': data.reference.record.databasealias,
3006
- };
3007
- if (!!data.partId)
3008
- details['part-id'] = data.partId;
3009
- this.chatService.generateAuditEvent('attachment.preview.click', details);
3010
- }
3011
- /**
3012
- * Handle the click on a reference's 'open original document'.
3013
- * @param data
3014
- */
3015
- openOriginalAttachment(data) {
3016
- this.openDocument.emit(data.reference.record);
3017
- const details = {
3018
- 'doc-id': data.reference.recordId,
3019
- 'title': data.reference.record.title,
3020
- 'source': data.reference.record.treepath,
3021
- 'collection': data.reference.record.collection,
3022
- 'index': data.reference.record.databasealias,
3023
- };
3024
- if (!!data.partId)
3025
- details['part-id'] = data.partId;
3026
- this.chatService.generateAuditEvent('attachment.link.click', details);
3027
- }
3028
- /**
3029
- * Handle the click on a suggested action.
3030
- * @param action Suggested action.
3031
- * @param index Rank of the message in the chatHistory related to the suggested action.
3032
- */
3033
- suggestActionClick(action, index) {
3034
- this.suggestAction.emit(action);
3035
- this.chatService.generateAuditEvent('suggestedAction.click', { 'text': action.content, 'suggestedAction-type': action.type });
3036
- }
3037
- /**
3038
- * It looks for the debug messages available in the current group of "assistant" messages.
3039
- * By design, the debug messages are only available in the first visible message among the group "assistant" messages.
3040
- * @param index The rank of the message
3041
- * @returns The debug messages available in the current group of "assistant" messages
3042
- */
3043
- getDebugMessages(index) {
3044
- // If it is not an assistant message, return
3045
- if ((this.messages$.value)[index].role !== 'assistant') {
3046
- return [];
3047
- }
3048
- // Get the array of messages up to the indicated index
3049
- const array = this.messages$.value.slice(0, index + 1);
3050
- // If it is an assistant message, look for the debug messages available in the current group of "assistant" messages
3051
- // By design, the debug messages are only available in the first visible message among the group "assistant" messages.
3052
- const idx = this.chatService.firstVisibleAssistantMessageIndex(array);
3053
- if (idx > -1) {
3054
- return (this.messages$.value)[idx].additionalProperties.$debug || [];
3055
- }
3056
- return [];
3057
- }
3058
- /**
3059
- * Handle the click on the 'show log info' button of a message.
3060
- * @param index The rank of the message
3061
- */
3062
- showDebug(index) {
3063
- this.debugMessages = this.getDebugMessages(index);
3064
- this.showDebugMessages = true;
3065
- this.cdr.detectChanges();
3066
- }
3067
- /**
3068
- * Verify whether the current message is an assistant message and that all following messages are assistant ones
3069
- * Used to keep the "View progress" opened even though the assistant is sending additional messages after the current one
3070
- * @param messages the list of current messages
3071
- * @param index the index of the current message
3072
- * @returns if this messages and the following ones (if any) are the last ones
3073
- */
3074
- isAssistantLastMessages(messages, index) {
3075
- for (let i = index; i < messages.length; i++) {
3076
- if (messages[i].role !== 'assistant')
3077
- return false;
3078
- }
3079
- return true;
3080
- }
3081
- /**
3082
- * Checks if the given message is an empty assistant message.
3083
- * An empty assistant message is defined as a message with the role 'assistant',
3084
- * an empty content, and no additional properties such as attachments, progress,
3085
- * debug information, or suggested actions.
3086
- *
3087
- * @param message - The message to check.
3088
- * @returns `true` if the message is an empty assistant message, `false` otherwise.
3089
- */
3090
- isEmptyAssistantMessage(message) {
3091
- var _a, _b, _c, _d;
3092
- if ((message === null || message === void 0 ? void 0 : message.role) === 'assistant'
3093
- && (message === null || message === void 0 ? void 0 : message.content) === ""
3094
- && !((_a = message === null || message === void 0 ? void 0 : message.additionalProperties) === null || _a === void 0 ? void 0 : _a.$attachment)
3095
- && !((_b = message === null || message === void 0 ? void 0 : message.additionalProperties) === null || _b === void 0 ? void 0 : _b.$progress)
3096
- && !((_c = message === null || message === void 0 ? void 0 : message.additionalProperties) === null || _c === void 0 ? void 0 : _c.$debug)
3097
- && !((_d = message === null || message === void 0 ? void 0 : message.additionalProperties) === null || _d === void 0 ? void 0 : _d.$suggestedAction)) {
3098
- return true;
3099
- }
3100
- return false;
3101
- }
3102
- }
3103
- ChatComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.0.6", ngImport: i0, type: ChatComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
3104
- 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", focusAfterResponse: "focusAfterResponse", chat: "chat", assistantMessageIcon: "assistantMessageIcon", userMessageIcon: "userMessageIcon", connectionErrorMessageIcon: "connectionErrorMessageIcon", searchWarningMessageIcon: "searchWarningMessageIcon" }, outputs: { connection: "connection", loading$: "loading", _config: "config", data: "data", openDocument: "openDocument", openPreview: "openPreview", suggestAction: "suggestAction" }, providers: [
3105
- RestChatService,
3106
- WebSocketChatService
3107
- ], queries: [{ propertyName: "loadingTpl", first: true, predicate: ["loadingTpl"], descendants: true }, { propertyName: "reportTpl", first: true, predicate: ["reportTpl"], descendants: true }, { propertyName: "tokenConsumptionTpl", first: true, predicate: ["tokenConsumptionTpl"], descendants: true }, { propertyName: "debugMessagesTpl", first: true, predicate: ["debugMessagesTpl"], 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 <!-- Token consumption -->\n <div class=\"ms-1\" *ngIf=\"config?.globalSettings?.displayUserQuotaConsumption || config?.globalSettings?.displayChatTokensConsumption\">\n <ng-container *ngTemplateOutlet=\"tokenConsumptionTpl || defaultTokenConsumptionTpl; context: { $implicit: instanceId }\"></ng-container>\n </div>\n\n <!-- Chat Messages -->\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\"\n *ngIf=\"message.additionalProperties.display && !isEmptyAssistantMessage(message)\"\n [style.--bs-list-group-item-padding-y.rem]=\"'0.6'\"\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 [connectionErrorMessageIcon]=\"connectionErrorMessageIcon\"\n [searchWarningMessageIcon]=\"searchWarningMessageIcon\"\n [streaming]=\"(chatService.streaming$ | async) && (last || isAssistantLastMessages(messages, index))\"\n [canEdit]=\"(chatService.streaming$ | async) === false && messageToEdit === undefined && message.role === 'user'\"\n [canCopy]=\"((chatService.streaming$ | async) === false || !last) && messageToEdit === undefined && message.role !== 'connection-error' && message.role !== 'search-warning'\"\n [canLike]=\"((chatService.streaming$ | async) === false || !last) && message.role === 'assistant'\"\n [canDislike]=\"((chatService.streaming$ | async) === false || !last) && message.role === 'assistant'\"\n [canDebug]=\"(((chatService.streaming$ | async) === false && last) || (!last && messages[index+1].role !== 'assistant')) && message.role === 'assistant' && (getDebugMessages(index).length > 0) && ((isAdminOrDeletedAdmin || (chatService.userOverride$ | async)) && config?.defaultValues.debug)\"\n [canRegenerate]=\"(chatService.streaming$ | async) === false && (last || (!last && messages[index+1].role !== 'assistant')) && message.role === 'assistant' && messageToEdit === undefined\"\n (edit)=\"editMessage(index)\"\n (copy)=\"copyMessage(index)\"\n (regenerate)=\"regenerateMessage(index)\"\n (openDocument)=\"openOriginalAttachment($event)\"\n (openPreview)=\"openAttachmentPreview($event)\"\n (suggestAction)=\"suggestActionClick($event, index)\"\n (like)=\"onLike(message, index)\"\n (dislike)=\"onDislike(message, index)\"\n (debug)=\"showDebug(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 a feedback form -->\n <div class=\"issue-report pt-3 pb-2\" *ngIf=\"showReport\">\n <ng-container *ngTemplateOutlet=\"reportTpl || reportTplDefault; context: { $implicit: messageToReport, rank: reportRank, type: reportType }\"></ng-container>\n </div>\n\n <!-- User text input -->\n <div class=\"user-input mt-auto\" *ngIf=\"!showReport\">\n <div class=\"py-2\">\n <div [hidden]=\"!isConnected\">\n <ng-container *ngIf=\"enabledUserInput\" [ngTemplateOutlet]=\"inputTpl\"></ng-container>\n </div>\n <!-- Retry button -->\n <button [hidden]=\"isConnected\" class=\"btn mb-4 ast-error ast-btn sq-retry\" (click)=\"retryFetch()\">\n <span>Try again</span>\n <span *ngIf=\"retrialAttempts\" class=\"ms-2 attempts\">{{ retrialAttempts }}</span>\n </button>\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\n <!-- Floating scroll button -->\n <div *ngIf=\"!isAtBottom && !showReport\" 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($event)\"\n [disabled]=\"(loading$ | async) || (chatService.streaming$ | async) || (chatService.stoppingGeneration$ | async)\">\n </textarea>\n <button\n *ngIf=\"!(chatService.streaming$ | async) && !(loading$ | async) && !(chatService.stoppingGeneration$ | async)\"\n type=\"button\"\n class=\"btn btn-light ms-2\"\n sqTooltip=\"Send message\"\n (click)=\"submitQuestion()\">\n <i class=\"fas fa-paper-plane\"></i>\n </button>\n <button\n *ngIf=\"messageToEdit\"\n type=\"button\"\n class=\"btn btn-light ms-2\"\n sqTooltip=\"Cancel edition\"\n (click)=\"messageToEdit = undefined; question = ''\">\n <i class=\"fas fa-undo-alt\"></i>\n </button>\n <span *ngIf=\"(chatService.streaming$ | async) && !(chatService.stoppingGeneration$ | async)\" class=\"processing ms-2\">\n Generating <i class=\"ms-1 fas fa-spinner fa-pulse\"></i>\n </span>\n <span *ngIf=\"(chatService.stoppingGeneration$ | async)\" class=\"processing ms-2\">\n Stopping <i class=\"ms-1 fas fa-spinner fa-pulse\"></i>\n </span>\n <button\n *ngIf=\"(chatService.streaming$ | async) && !(chatService.stoppingGeneration$ | async)\"\n type=\"button\"\n class=\"btn btn-light ms-2\"\n sqTooltip=\"Stop generating\"\n (click)=\"stopGeneration()\">\n <i class=\"fas fa-stop\"></i>\n </button>\n </div>\n </div>\n</ng-template>\n\n<ng-template #reportTplDefault let-message let-rank=\"rank\" let-type=\"type\">\n <div class=\"px-3\">\n <ng-container *ngIf=\"type === 'dislike'\">\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 ?? defaultIssueTypes)\">{{type}}</option>\n </select>\n <h5>What was unsatisfying about this response? (optional)</h5>\n </ng-container>\n <ng-container *ngIf=\"type === 'like'\">\n <h5>Why did you like this answer? (optional)</h5>\n </ng-container>\n <textarea class=\"form-control\" [(ngModel)]=\"reportComment\" placeholder=\"Write your comment\"></textarea>\n <div class=\"d-flex flex-row-reverse gap-1 mt-2\">\n <button class=\"btn btn-primary\" [disabled]=\"type === 'dislike' && !issueType\" (click)=\"sendReport()\">Send</button>\n <button class=\"btn btn-light\" (click)=\"ignoreReport()\">Do not send report</button>\n </div>\n </div>\n</ng-template>\n\n<ng-template #defaultTokenConsumptionTpl let-instanceId>\n <sq-token-progress-bar\n [instanceId]=\"instanceId\">\n </sq-token-progress-bar>\n</ng-template>\n\n<div class=\"debug-messages\" [class.displayed]=\"showDebugMessages\">\n <button *ngIf=\"showDebugMessages\" class=\"btn btn-light shadow back-btn\" (click)=\"showDebugMessages=false\">\n <i class=\"fas fa-chevron-right\"></i>\n </button>\n <ng-container *ngTemplateOutlet=\"debugMessagesTpl || defaultDebugMessagesTpl; context: { $implicit: debugMessages }\">\n </ng-container>\n</div>\n\n<ng-template #defaultDebugMessagesTpl let-debugMessages>\n <sq-debug-message [data]=\"debugMessages\"></sq-debug-message>\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-error{background-color:var(--ast-error-bg, rgba(249, 58, 55, .2));color:var(--ast-action-buttons-color, inherit)}.ast-error:hover{color:var(--ast-error-color, rgba(249, 58, 55, .7))}.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-uploaded-doc-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;--ast-action-buttons-color: white;--ast-action-buttons-hover-color: #6dbee6;--ast-report-bg: #070707}: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:center;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))}.ast-input-container .processing{display:flex;align-items:center;color:var(--ast-secondary-color, #FF732E)}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}.debug-messages{position:fixed;z-index:999999;right:-60%;top:0;width:60%;height:100%;transition:all .5s ease;background-color:var(--bs-body-bg);overflow:auto}.debug-messages .back-btn{position:fixed;right:0%;transition:all .5s ease}.debug-messages.displayed{right:0}.debug-messages.displayed .back-btn{right:60%}.debug-messages sq-debug-message:first-of-type{display:block;width:100%}.sq-retry{display:flex;margin:auto;font-weight:var(--font-weight-bold, 500)}.sq-retry .attempts{display:flex;border-radius:100%;background:#fff;height:20px;width:20px;place-content:center;align-items:center}.issue-report{background-color:var(--ast-report-bg, white)}\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", "connectionErrorMessageIcon", "searchWarningMessageIcon", "streaming", "canEdit", "canRegenerate", "canCopy", "canDebug", "canLike", "canDislike"], outputs: ["openDocument", "openPreview", "suggestAction", "edit", "copy", "regenerate", "like", "dislike", "debug"] }, { kind: "component", type: TokenProgressBarComponent, selector: "sq-token-progress-bar", inputs: ["instanceId"] }, { kind: "component", type: DebugMessageComponent, selector: "sq-debug-message", inputs: ["data", "level", "parentColor"] }, { kind: "ngmodule", type: UtilsModule }, { kind: "directive", type: i2$1.TooltipDirective, selector: "[sqTooltip]", inputs: ["sqTooltip", "sqTooltipData", "sqTooltipTemplate", "placement", "fallbackPlacements", "delay", "hoverableTooltip", "tooltipClass"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
3108
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.0.6", ngImport: i0, type: ChatComponent, decorators: [{
3109
- type: Component,
3110
- args: [{ selector: 'sq-chat-v3', providers: [
3111
- RestChatService,
3112
- WebSocketChatService
3113
- ], changeDetection: ChangeDetectionStrategy.OnPush, standalone: true, imports: [CommonModule, FormsModule, ChatMessageComponent, TokenProgressBarComponent, DebugMessageComponent, UtilsModule], template: "<ng-container *ngIf=\"!initializationError\">\n <div *ngIf=\"messages$ | async as messages; else loadingTpl || loadingTplDefault\" class=\"h-100 d-flex flex-column\">\n <!-- Token consumption -->\n <div class=\"ms-1\" *ngIf=\"config?.globalSettings?.displayUserQuotaConsumption || config?.globalSettings?.displayChatTokensConsumption\">\n <ng-container *ngTemplateOutlet=\"tokenConsumptionTpl || defaultTokenConsumptionTpl; context: { $implicit: instanceId }\"></ng-container>\n </div>\n\n <!-- Chat Messages -->\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\"\n *ngIf=\"message.additionalProperties.display && !isEmptyAssistantMessage(message)\"\n [style.--bs-list-group-item-padding-y.rem]=\"'0.6'\"\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 [connectionErrorMessageIcon]=\"connectionErrorMessageIcon\"\n [searchWarningMessageIcon]=\"searchWarningMessageIcon\"\n [streaming]=\"(chatService.streaming$ | async) && (last || isAssistantLastMessages(messages, index))\"\n [canEdit]=\"(chatService.streaming$ | async) === false && messageToEdit === undefined && message.role === 'user'\"\n [canCopy]=\"((chatService.streaming$ | async) === false || !last) && messageToEdit === undefined && message.role !== 'connection-error' && message.role !== 'search-warning'\"\n [canLike]=\"((chatService.streaming$ | async) === false || !last) && message.role === 'assistant'\"\n [canDislike]=\"((chatService.streaming$ | async) === false || !last) && message.role === 'assistant'\"\n [canDebug]=\"(((chatService.streaming$ | async) === false && last) || (!last && messages[index+1].role !== 'assistant')) && message.role === 'assistant' && (getDebugMessages(index).length > 0) && ((isAdminOrDeletedAdmin || (chatService.userOverride$ | async)) && config?.defaultValues.debug)\"\n [canRegenerate]=\"(chatService.streaming$ | async) === false && (last || (!last && messages[index+1].role !== 'assistant')) && message.role === 'assistant' && messageToEdit === undefined\"\n (edit)=\"editMessage(index)\"\n (copy)=\"copyMessage(index)\"\n (regenerate)=\"regenerateMessage(index)\"\n (openDocument)=\"openOriginalAttachment($event)\"\n (openPreview)=\"openAttachmentPreview($event)\"\n (suggestAction)=\"suggestActionClick($event, index)\"\n (like)=\"onLike(message, index)\"\n (dislike)=\"onDislike(message, index)\"\n (debug)=\"showDebug(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 a feedback form -->\n <div class=\"issue-report pt-3 pb-2\" *ngIf=\"showReport\">\n <ng-container *ngTemplateOutlet=\"reportTpl || reportTplDefault; context: { $implicit: messageToReport, rank: reportRank, type: reportType }\"></ng-container>\n </div>\n\n <!-- User text input -->\n <div class=\"user-input mt-auto\" *ngIf=\"!showReport\">\n <div class=\"py-2\">\n <div [hidden]=\"!isConnected\">\n <ng-container *ngIf=\"enabledUserInput\" [ngTemplateOutlet]=\"inputTpl\"></ng-container>\n </div>\n <!-- Retry button -->\n <button [hidden]=\"isConnected\" class=\"btn mb-4 ast-error ast-btn sq-retry\" (click)=\"retryFetch()\">\n <span>Try again</span>\n <span *ngIf=\"retrialAttempts\" class=\"ms-2 attempts\">{{ retrialAttempts }}</span>\n </button>\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\n <!-- Floating scroll button -->\n <div *ngIf=\"!isAtBottom && !showReport\" 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($event)\"\n [disabled]=\"(loading$ | async) || (chatService.streaming$ | async) || (chatService.stoppingGeneration$ | async)\">\n </textarea>\n <button\n *ngIf=\"!(chatService.streaming$ | async) && !(loading$ | async) && !(chatService.stoppingGeneration$ | async)\"\n type=\"button\"\n class=\"btn btn-light ms-2\"\n sqTooltip=\"Send message\"\n (click)=\"submitQuestion()\">\n <i class=\"fas fa-paper-plane\"></i>\n </button>\n <button\n *ngIf=\"messageToEdit\"\n type=\"button\"\n class=\"btn btn-light ms-2\"\n sqTooltip=\"Cancel edition\"\n (click)=\"messageToEdit = undefined; question = ''\">\n <i class=\"fas fa-undo-alt\"></i>\n </button>\n <span *ngIf=\"(chatService.streaming$ | async) && !(chatService.stoppingGeneration$ | async)\" class=\"processing ms-2\">\n Generating <i class=\"ms-1 fas fa-spinner fa-pulse\"></i>\n </span>\n <span *ngIf=\"(chatService.stoppingGeneration$ | async)\" class=\"processing ms-2\">\n Stopping <i class=\"ms-1 fas fa-spinner fa-pulse\"></i>\n </span>\n <button\n *ngIf=\"(chatService.streaming$ | async) && !(chatService.stoppingGeneration$ | async)\"\n type=\"button\"\n class=\"btn btn-light ms-2\"\n sqTooltip=\"Stop generating\"\n (click)=\"stopGeneration()\">\n <i class=\"fas fa-stop\"></i>\n </button>\n </div>\n </div>\n</ng-template>\n\n<ng-template #reportTplDefault let-message let-rank=\"rank\" let-type=\"type\">\n <div class=\"px-3\">\n <ng-container *ngIf=\"type === 'dislike'\">\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 ?? defaultIssueTypes)\">{{type}}</option>\n </select>\n <h5>What was unsatisfying about this response? (optional)</h5>\n </ng-container>\n <ng-container *ngIf=\"type === 'like'\">\n <h5>Why did you like this answer? (optional)</h5>\n </ng-container>\n <textarea class=\"form-control\" [(ngModel)]=\"reportComment\" placeholder=\"Write your comment\"></textarea>\n <div class=\"d-flex flex-row-reverse gap-1 mt-2\">\n <button class=\"btn btn-primary\" [disabled]=\"type === 'dislike' && !issueType\" (click)=\"sendReport()\">Send</button>\n <button class=\"btn btn-light\" (click)=\"ignoreReport()\">Do not send report</button>\n </div>\n </div>\n</ng-template>\n\n<ng-template #defaultTokenConsumptionTpl let-instanceId>\n <sq-token-progress-bar\n [instanceId]=\"instanceId\">\n </sq-token-progress-bar>\n</ng-template>\n\n<div class=\"debug-messages\" [class.displayed]=\"showDebugMessages\">\n <button *ngIf=\"showDebugMessages\" class=\"btn btn-light shadow back-btn\" (click)=\"showDebugMessages=false\">\n <i class=\"fas fa-chevron-right\"></i>\n </button>\n <ng-container *ngTemplateOutlet=\"debugMessagesTpl || defaultDebugMessagesTpl; context: { $implicit: debugMessages }\">\n </ng-container>\n</div>\n\n<ng-template #defaultDebugMessagesTpl let-debugMessages>\n <sq-debug-message [data]=\"debugMessages\"></sq-debug-message>\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-error{background-color:var(--ast-error-bg, rgba(249, 58, 55, .2));color:var(--ast-action-buttons-color, inherit)}.ast-error:hover{color:var(--ast-error-color, rgba(249, 58, 55, .7))}.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-uploaded-doc-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;--ast-action-buttons-color: white;--ast-action-buttons-hover-color: #6dbee6;--ast-report-bg: #070707}: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:center;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))}.ast-input-container .processing{display:flex;align-items:center;color:var(--ast-secondary-color, #FF732E)}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}.debug-messages{position:fixed;z-index:999999;right:-60%;top:0;width:60%;height:100%;transition:all .5s ease;background-color:var(--bs-body-bg);overflow:auto}.debug-messages .back-btn{position:fixed;right:0%;transition:all .5s ease}.debug-messages.displayed{right:0}.debug-messages.displayed .back-btn{right:60%}.debug-messages sq-debug-message:first-of-type{display:block;width:100%}.sq-retry{display:flex;margin:auto;font-weight:var(--font-weight-bold, 500)}.sq-retry .attempts{display:flex;border-radius:100%;background:#fff;height:20px;width:20px;place-content:center;align-items:center}.issue-report{background-color:var(--ast-report-bg, white)}\n"] }]
3114
- }], ctorParameters: function () { return []; }, propDecorators: { instanceId: [{
3115
- type: Input
3116
- }], query: [{
3117
- type: Input
3118
- }], queryChangeShouldTriggerReload: [{
3119
- type: Input
3120
- }], protocol: [{
3121
- type: Input
3122
- }], messageHandlers: [{
3123
- type: Input
3124
- }], automaticScrollToLastResponse: [{
3125
- type: Input
3126
- }], focusAfterResponse: [{
3127
- type: Input
3128
- }], chat: [{
3129
- type: Input
3130
- }], assistantMessageIcon: [{
3131
- type: Input
3132
- }], userMessageIcon: [{
3133
- type: Input
3134
- }], connectionErrorMessageIcon: [{
3135
- type: Input
3136
- }], searchWarningMessageIcon: [{
3137
- type: Input
3138
- }], connection: [{
3139
- type: Output
3140
- }], loading$: [{
3141
- type: Output,
3142
- args: ["loading"]
3143
- }], _config: [{
3144
- type: Output,
3145
- args: ["config"]
3146
- }], data: [{
3147
- type: Output
3148
- }], openDocument: [{
3149
- type: Output
3150
- }], openPreview: [{
3151
- type: Output
3152
- }], suggestAction: [{
3153
- type: Output
3154
- }], messageList: [{
3155
- type: ViewChild,
3156
- args: ['messageList']
3157
- }], questionInput: [{
3158
- type: ViewChild,
3159
- args: ['questionInput']
3160
- }], loadingTpl: [{
3161
- type: ContentChild,
3162
- args: ['loadingTpl']
3163
- }], reportTpl: [{
3164
- type: ContentChild,
3165
- args: ['reportTpl']
3166
- }], tokenConsumptionTpl: [{
3167
- type: ContentChild,
3168
- args: ['tokenConsumptionTpl']
3169
- }], debugMessagesTpl: [{
3170
- type: ContentChild,
3171
- args: ['debugMessagesTpl']
3172
- }] } });
3173
-
3174
- class SavedChatsComponent {
3175
- constructor() {
3176
- this.load = new EventEmitter();
3177
- this.delete = new EventEmitter();
3178
- this.subscription = new Subscription();
3179
- this.groupedSavedChats$ = new BehaviorSubject([]);
3180
- this.loginService = inject(LoginService);
3181
- this.instanceManagerService = inject(InstanceManagerService);
3182
- this.modalService = inject(ModalService);
3183
- this.notificationsService = inject(NotificationsService);
3184
- }
3185
- ngOnInit() {
3186
- this.subscription.add(this.loginService.events.pipe(filter(e => e.type === 'login-complete'), tap(_ => this.instantiateChatService()), switchMap(_ => this.chatService.userOverride$), filter(isOverridden => isOverridden !== undefined), tap(_ => {
3187
- this.onListSavedChat();
3188
- this.chatService.listSavedChat();
3189
- })).subscribe());
3190
- }
3191
- ngOnDestroy() {
3192
- this.subscription.unsubscribe();
3193
- }
3194
- instantiateChatService() {
3195
- this.chatService = this.instanceManagerService.getInstance(this.instanceId);
3196
- }
3197
- onListSavedChat() {
3198
- this.subscription.add(this.chatService.savedChats$.subscribe((savedChats) => {
3199
- this.groupedSavedChats$.next(this._groupSavedChatsByDate(savedChats));
3200
- }));
3201
- }
3202
- onLoad(savedChat) {
3203
- if (!!this.chatService.streaming$.value || !!this.chatService.stoppingGeneration$.value) {
3204
- return;
3205
- }
3206
- this.chatService.setSavedChatId(savedChat.id);
3207
- this.chatService.generateChatId(savedChat.id);
3208
- this.chatService.loadSavedChat$.next(savedChat);
3209
- this.chatService.generateAuditEvent('saved-chat.load', {}, savedChat.id);
3210
- this.chatService.listSavedChat();
3211
- this.load.emit(savedChat);
3212
- }
3213
- onRename(event, savedChat) {
3214
- event.stopPropagation();
3215
- if (!!this.chatService.streaming$.value || !!this.chatService.stoppingGeneration$.value) {
3216
- return;
3217
- }
3218
- const model = {
3219
- title: 'Rename saved discussion',
3220
- message: `Please enter a new name for the discussion "${savedChat.title}".`,
3221
- buttons: [
3222
- new ModalButton({ result: -2 /* ModalResult.Cancel */ }),
3223
- new ModalButton({ result: -1 /* ModalResult.OK */, text: "Rename", primary: true })
3224
- ],
3225
- output: savedChat.title,
3226
- validators: [Validators.required]
3227
- };
3228
- this.modalService.prompt(model).then(res => {
3229
- if (res === -1 /* ModalResult.OK */) {
3230
- this.subscription.add(this.chatService.updateSavedChat(savedChat.id, model.output)
3231
- .pipe(tap(() => {
3232
- this.notificationsService.success(`The saved discussion "${savedChat.title}" has been successfully renamed to "${model.output}".`);
3233
- this.chatService.listSavedChat();
3234
- }), catchError((error) => {
3235
- console.error('Error occurred while updating the saved chat:', error);
3236
- this.notificationsService.error(`Error occurred while updating the saved discussion "${savedChat.title}"`);
3237
- return throwError(() => error);
3238
- })).subscribe());
3239
- this.chatService.generateAuditEvent('saved-chat.rename', { 'text': model.output }, savedChat.id);
3240
- }
3241
- });
3242
- }
3243
- onDelete(event, savedChat) {
3244
- event.stopPropagation();
3245
- if (!!this.chatService.streaming$.value || !!this.chatService.stoppingGeneration$.value) {
3246
- return;
3247
- }
3248
- this.modalService
3249
- .confirm({
3250
- title: "Delete saved discussion",
3251
- message: `You are about to delete the discussion "${savedChat.title}". Do you want to continue?`,
3252
- buttons: [
3253
- new ModalButton({ result: -2 /* ModalResult.Cancel */ }),
3254
- new ModalButton({ result: -1 /* ModalResult.OK */, text: "Confirm", primary: true })
3255
- ],
3256
- confirmType: 2 /* ConfirmType.Warning */
3257
- }).then(res => {
3258
- if (res === -1 /* ModalResult.OK */) {
3259
- this.subscription.add(this.chatService.deleteSavedChat([savedChat.id])
3260
- .pipe(tap(() => {
3261
- this.notificationsService.success(`The saved discussion "${savedChat.title}" has been successfully deleted.`);
3262
- this.delete.emit(savedChat);
3263
- this.chatService.listSavedChat();
3264
- }), catchError((error) => {
3265
- console.error('Error occurred while deleting the saved chat:', error);
3266
- this.notificationsService.error(`Error occurred while deleting the saved discussion "${savedChat.title}"`);
3267
- return throwError(() => error);
3268
- })).subscribe());
3269
- this.chatService.generateAuditEvent('saved-chat.delete', {}, savedChat.id);
3270
- }
3271
- });
3272
- }
3273
- _groupSavedChatsByDate(savedChats) {
3274
- const groupedSavedChats = new Map();
3275
- savedChats
3276
- .sort((a, b) => parseISO(b.modifiedUTC).getTime() - parseISO(a.modifiedUTC).getTime())
3277
- .forEach(savedChat => {
3278
- const groupKey = this._getTimeKey(toDate(parseISO(savedChat.modifiedUTC))); // Must convert the UTC date to local date before passing to _getTimeKey
3279
- if (!groupedSavedChats.has(groupKey)) {
3280
- groupedSavedChats.set(groupKey, []);
3281
- }
3282
- groupedSavedChats.get(groupKey).push(savedChat);
3283
- });
3284
- return Array.from(groupedSavedChats, ([key, value]) => ({ key, value }));
3285
- ;
3286
- }
3287
- _getTimeKey(date) {
3288
- if (isToday(date)) {
3289
- return 'Today';
3290
- }
3291
- else if (isYesterday(date)) {
3292
- return 'Yesterday';
3293
- }
3294
- else if (isThisWeek(date)) {
3295
- return 'This week';
3296
- }
3297
- else if (differenceInDays(endOfYesterday(), date) <= 7) {
3298
- return 'Last week';
3299
- }
3300
- else if (isThisMonth(date)) {
3301
- return 'This month';
3302
- }
3303
- else if (differenceInMonths(endOfYesterday(), date) <= 1) {
3304
- return 'Last month';
3305
- }
3306
- else if (isThisQuarter(date)) {
3307
- return 'This quarter';
3308
- }
3309
- else if (differenceInMonths(endOfYesterday(), date) <= 3) {
3310
- return 'Last quarter';
3311
- }
3312
- else if (isThisYear(date)) {
3313
- return 'This year';
3314
- }
3315
- else if (differenceInYears(endOfYesterday(), date) === 1) {
3316
- return 'Last year';
3317
- }
3318
- else {
3319
- return format(date, 'yyyy');
3320
- }
3321
- }
3322
- }
3323
- SavedChatsComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.0.6", ngImport: i0, type: SavedChatsComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
3324
- SavedChatsComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "14.0.6", type: SavedChatsComponent, isStandalone: true, selector: "sq-saved-chats-v3", inputs: { instanceId: "instanceId" }, outputs: { load: "load", delete: "delete" }, ngImport: i0, template: "<ng-container *ngIf=\"(chatService.assistantConfig$ | async)?.savedChatSettings.display\">\n <div *ngFor=\"let group of (groupedSavedChats$ | async)\" class=\"saved-chats\">\n <div class=\"saved-chat-date\">{{group.key}}</div>\n <div *ngFor=\"let savedChat of group.value\"\n (click)=\"onLoad(savedChat)\"\n class=\"saved-chat p-2\"\n [class.forbidden]=\"(chatService.streaming$ | async) || (chatService.stoppingGeneration$ | async)\"\n [class.active]=\"chatService.savedChatId === savedChat.id\">\n <span class=\"title me-1\" [sqTooltip]=\"savedChat.title\">{{savedChat.title}}</span>\n <i class=\"saved-chat-actions fas fa-pen mx-1\" [sqTooltip]=\"'Rename'\"\n (click)=\"onRename($event, savedChat)\"></i>\n <i class=\"saved-chat-actions fas fa-trash ms-1\" [sqTooltip]=\"'Delete'\"\n (click)=\"onDelete($event, savedChat)\"></i>\n </div>\n </div>\n</ng-container>\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-error{background-color:var(--ast-error-bg, rgba(249, 58, 55, .2));color:var(--ast-action-buttons-color, inherit)}.ast-error:hover{color:var(--ast-error-color, rgba(249, 58, 55, .7))}.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-uploaded-doc-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;--ast-action-buttons-color: white;--ast-action-buttons-hover-color: #6dbee6;--ast-report-bg: #070707}.saved-chats .saved-chat-date{font-size:12px;font-weight:500;color:#a9a9a9;margin-top:.5rem}.saved-chats .saved-chat{display:flex;align-items:center;cursor:pointer;margin-left:.25rem}.saved-chats .saved-chat span{flex-grow:1;text-overflow:ellipsis;overflow:hidden;white-space:nowrap}.saved-chats .saved-chat .saved-chat-actions{display:none}.saved-chats .saved-chat:hover,.saved-chats .saved-chat.active{color:var(--ast-secondary-color, #FF732E);background-color:var(--ast-saved-chat-hover-background, #FFF8F1)}.saved-chats .saved-chat:hover .saved-chat-actions{display:block}.saved-chats .saved-chat.forbidden{cursor:not-allowed}.saved-chats .title{display:block;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}\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: "pipe", type: i1.AsyncPipe, name: "async" }, { kind: "ngmodule", type: ModalModule }, { kind: "ngmodule", type: UtilsModule }, { kind: "directive", type: i2$1.TooltipDirective, selector: "[sqTooltip]", inputs: ["sqTooltip", "sqTooltipData", "sqTooltipTemplate", "placement", "fallbackPlacements", "delay", "hoverableTooltip", "tooltipClass"] }] });
3325
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.0.6", ngImport: i0, type: SavedChatsComponent, decorators: [{
3326
- type: Component,
3327
- args: [{ selector: 'sq-saved-chats-v3', standalone: true, imports: [CommonModule, ModalModule, UtilsModule], template: "<ng-container *ngIf=\"(chatService.assistantConfig$ | async)?.savedChatSettings.display\">\n <div *ngFor=\"let group of (groupedSavedChats$ | async)\" class=\"saved-chats\">\n <div class=\"saved-chat-date\">{{group.key}}</div>\n <div *ngFor=\"let savedChat of group.value\"\n (click)=\"onLoad(savedChat)\"\n class=\"saved-chat p-2\"\n [class.forbidden]=\"(chatService.streaming$ | async) || (chatService.stoppingGeneration$ | async)\"\n [class.active]=\"chatService.savedChatId === savedChat.id\">\n <span class=\"title me-1\" [sqTooltip]=\"savedChat.title\">{{savedChat.title}}</span>\n <i class=\"saved-chat-actions fas fa-pen mx-1\" [sqTooltip]=\"'Rename'\"\n (click)=\"onRename($event, savedChat)\"></i>\n <i class=\"saved-chat-actions fas fa-trash ms-1\" [sqTooltip]=\"'Delete'\"\n (click)=\"onDelete($event, savedChat)\"></i>\n </div>\n </div>\n</ng-container>\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-error{background-color:var(--ast-error-bg, rgba(249, 58, 55, .2));color:var(--ast-action-buttons-color, inherit)}.ast-error:hover{color:var(--ast-error-color, rgba(249, 58, 55, .7))}.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-uploaded-doc-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;--ast-action-buttons-color: white;--ast-action-buttons-hover-color: #6dbee6;--ast-report-bg: #070707}.saved-chats .saved-chat-date{font-size:12px;font-weight:500;color:#a9a9a9;margin-top:.5rem}.saved-chats .saved-chat{display:flex;align-items:center;cursor:pointer;margin-left:.25rem}.saved-chats .saved-chat span{flex-grow:1;text-overflow:ellipsis;overflow:hidden;white-space:nowrap}.saved-chats .saved-chat .saved-chat-actions{display:none}.saved-chats .saved-chat:hover,.saved-chats .saved-chat.active{color:var(--ast-secondary-color, #FF732E);background-color:var(--ast-saved-chat-hover-background, #FFF8F1)}.saved-chats .saved-chat:hover .saved-chat-actions{display:block}.saved-chats .saved-chat.forbidden{cursor:not-allowed}.saved-chats .title{display:block;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}\n"] }]
3328
- }], propDecorators: { instanceId: [{
3329
- type: Input
3330
- }], load: [{
3331
- type: Output
3332
- }], delete: [{
3333
- type: Output
3334
- }] } });
3335
-
3336
- var _enAssistant = {
3337
- "assistant": {}
3338
- };
3339
-
3340
- var _frAssistant = {
3341
- "assistant": {}
3342
- };
3343
-
3344
- var _deAssistant = {
3345
- "assistant": {}
3346
- };
3347
-
3348
- const enAssistant = Utils.merge({}, _enAssistant);
3349
- const frAssistant = Utils.merge({}, _frAssistant);
3350
- const deAssistant = Utils.merge({}, _deAssistant);
3351
-
3352
- class ChatPrompt {
3353
- constructor(model, modalRef, formBuilder) {
3354
- this.model = model;
3355
- this.modalRef = modalRef;
3356
- this.formBuilder = formBuilder;
3357
- this.defaultButtons = [
3358
- new ModalButton({
3359
- result: -1 /* ModalResult.OK */,
3360
- primary: true,
3361
- validation: this.form
3362
- }),
3363
- new ModalButton({
3364
- result: -2 /* ModalResult.Cancel */
3365
- })
3366
- ];
3367
- }
3368
- ngOnInit() {
3369
- this.inputControl = new UntypedFormControl(this.model.output, this.model.validators || Validators.required);
3370
- this.form = this.formBuilder.group({
3371
- input: this.inputControl
3372
- });
3373
- this.formChanges = Utils.subscribe(this.form.valueChanges, (value) => {
3374
- this.model.output = this.inputControl.value;
3375
- });
3376
- }
3377
- ngOnDestroy() {
3378
- this.formChanges.unsubscribe();
3379
- }
3380
- get title() {
3381
- return this.model.title ? this.model.title : "msg#modal.prompt.title";
3382
- }
3383
- get buttons() {
3384
- return (this.model.buttons && this.model.buttons.length > 0) ? this.model.buttons : this.defaultButtons;
3385
- }
3386
- }
3387
- ChatPrompt.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.0.6", ngImport: i0, type: ChatPrompt, deps: [{ token: MODAL_MODEL }, { token: i1$2.ModalRef }, { token: i2.UntypedFormBuilder }], target: i0.ɵɵFactoryTarget.Component });
3388
- ChatPrompt.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "14.0.6", type: ChatPrompt, isStandalone: true, selector: "sq-chat-prompt", ngImport: i0, template: `
3389
- <form name="prompt" novalidate [formGroup]="form">
3390
- <sq-modal [title]="title" [buttons]="buttons">
3391
- <div class="mb-3 sq-form-group">
3392
- <label class="form-label" for="input">{{model.message | sqMessage:model.messageParams}}</label>
3393
- <input [sqValidation]="form" type="text" class="form-control" id="input" formControlName="input" spellcheck="off" sqAutofocus *ngIf="!model.rowCount">
3394
- <textarea [sqValidation]="form" type="text" class="form-control" id="input" formControlName="input" spellcheck="on" rows="{{model.rowCount}}" sqAutofocus *ngIf="!!model.rowCount">
3395
- </textarea>
3396
- </div>
3397
- </sq-modal>
3398
- </form>
3399
- `, isInline: true, dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "ngmodule", type: BsModalModule }, { kind: "component", type: i4.BsModal, selector: "sq-modal", inputs: ["title", "buttons", "showHeader", "showFooter", "isProcessingState"] }, { kind: "ngmodule", type: ValidationModule }, { kind: "directive", type: i5$1.ValidationDirective, selector: "[sqValidation]", inputs: ["sqValidation"] }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i2.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { 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.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i2.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i2.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "ngmodule", type: IntlModule }, { kind: "pipe", type: i6$1.MessagePipe, name: "sqMessage" }] });
3400
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.0.6", ngImport: i0, type: ChatPrompt, decorators: [{
3401
- type: Component,
3402
- args: [{
3403
- selector: "sq-chat-prompt",
3404
- template: `
3405
- <form name="prompt" novalidate [formGroup]="form">
3406
- <sq-modal [title]="title" [buttons]="buttons">
3407
- <div class="mb-3 sq-form-group">
3408
- <label class="form-label" for="input">{{model.message | sqMessage:model.messageParams}}</label>
3409
- <input [sqValidation]="form" type="text" class="form-control" id="input" formControlName="input" spellcheck="off" sqAutofocus *ngIf="!model.rowCount">
3410
- <textarea [sqValidation]="form" type="text" class="form-control" id="input" formControlName="input" spellcheck="on" rows="{{model.rowCount}}" sqAutofocus *ngIf="!!model.rowCount">
3411
- </textarea>
3412
- </div>
3413
- </sq-modal>
3414
- </form>
3415
- `,
3416
- standalone: true,
3417
- imports: [CommonModule, BsModalModule, ValidationModule, ReactiveFormsModule, IntlModule],
3418
- }]
3419
- }], ctorParameters: function () {
3420
- return [{ type: undefined, decorators: [{
3421
- type: Inject,
3422
- args: [MODAL_MODEL]
3423
- }] }, { type: i1$2.ModalRef }, { type: i2.UntypedFormBuilder }];
3424
- } });
3425
-
3426
- class DocumentsUploadService {
3427
- constructor() {
3428
- /** Documents' upload configuration. */
3429
- this.uploadConfig$ = new BehaviorSubject(undefined);
3430
- this.jsonMethodWebService = inject(JsonMethodPluginService);
3431
- this.loginService = inject(LoginService);
3432
- this.appService = inject(AppService);
3433
- this.http = inject(HttpClient);
3434
- }
3435
- /**
3436
- * Initializes the file upload service with the provided chat service instance.
3437
- *
3438
- * @param chatService - An instance of ChatService. If not provided, an error is thrown.
3439
- * @throws {Error} If the chatService instance is not provided or if there is an error during initialization.
3440
- */
3441
- init(chatService) {
3442
- if (!chatService)
3443
- throw new Error('A chatService instance must be provided');
3444
- this.chatService = chatService;
3445
- try {
3446
- this.getRequestsUrl();
3447
- this.getUploadConfig();
3448
- }
3449
- catch (error) {
3450
- throw new Error(error);
3451
- }
3452
- }
3453
- /**
3454
- * Fetch the endpoint to use for the file upload related requests
3455
- * @throws {Error} If the property 'restEndpoint' is not provided in the assistant configuration
3456
- */
3457
- getRequestsUrl() {
3458
- if (!!this.chatService.assistantConfig$.value.connectionSettings.restEndpoint) {
3459
- this.REQUEST_URL = this.chatService.assistantConfig$.value.connectionSettings.restEndpoint;
3460
- }
3461
- else {
3462
- throw new Error(`The property 'restEndpoint' must be provided when attempting to use 'REST' in assistant instance`);
3463
- }
3464
- }
3465
- /**
3466
- * Retrieves the upload configuration for documents.
3467
- * The response, which contains the upload configuration, is emitted to the `uploadConfig$` observable.
3468
- *
3469
- * @throws {Error} If there is an error invoking the `documentsUploadConfigGet` action.
3470
- */
3471
- getUploadConfig() {
3472
- const data = {
3473
- action: "documentsUploadConfigGet",
3474
- appName: this.appService.appName,
3475
- debug: this.chatService.assistantConfig$.value.defaultValues.debug
3476
- };
3477
- this.jsonMethodWebService.post(this.REQUEST_URL, data).pipe(take(1), catchError((error) => {
3478
- throw new Error(`Error invoking documentsUploadConfigGet: ${error}`);
3479
- })).subscribe((res) => this.uploadConfig$.next(res));
3480
- }
3481
- /**
3482
- * Uploads documents to the server.
3483
- *
3484
- * This method takes a FormData object which should contain all files to be uploaded.
3485
- * Each file should be appended to the FormData object with its original name.
3486
- *
3487
- * For example:
3488
- * ```typescript
3489
- * const formData = new FormData();
3490
- * formData.append(file.name, file.file);
3491
- * ```
3492
- *
3493
- * @param formData - The FormData object containing the files to be uploaded.
3494
- * - Each file should be appended to the FormData object with its original name.
3495
- * For example:
3496
- * ```typescript
3497
- * const formData = new FormData();
3498
- * formData.append(file.name, file.file);
3499
- * ```
3500
- *
3501
- * @param enableProgress - (Optional) A boolean parameter that controls whether progress reporting is enabled.
3502
- * - Defaults to `true`.
3503
- * - If `true`, the `reportProgress` option is set in the request options, enabling the track of upload progress based on the underlying browser's progress events.
3504
- * - If `false`, progress events are not reported.
3505
- *
3506
- * @returns An Observable that emits events of type `UploadEvent`:
3507
- * - `UploadProgressEvent`: Emitted during the upload process, containing:
3508
- * - * `type`: "uploadProgress" - The type of the event.
3509
- * - * `loaded`: number - The number of bytes uploaded so far.
3510
- * - * `total`: number - The total number of bytes to be uploaded (if available).
3511
- * - * `progress`: number - The percentage of upload progress (calculated as `(loaded / total) * 100`).
3512
- * - `UploadResponseEvent`: Emitted when the upload is complete, containing:
3513
- * - * `type`: "response" - The type of the event.
3514
- * - * `body`: `UploadResponse` - The server's response, including:
3515
- * - * - * `statusToken`: string - A token to track the status of the indexing process.
3516
- * - * - * `executionTime`: string - The time taken to execute the upload.
3517
- * - * - * `connectorResponse` (optional): Additional response data from the connector, containing:
3518
- * - * - * - * `collection`: string - The collection name.
3519
- * - * - * - * `action`: string - The action performed.
3520
- * - * - * - * `stats`: object - Statistics about the upload, including:
3521
- * - * - * - * - * `nbDocUpdated`: number - The number of documents updated.
3522
- *
3523
- * @throws {Error} If there is an error invoking the `documentsupload` action.
3524
- *
3525
- * Example usage:
3526
- * ```typescript
3527
- * const formData = new FormData();
3528
- * formData.append(file.name, file.file);
3529
- * this.documentsUploadService.uploadDocuments(formData).subscribe(event => {
3530
- * if (event.type === 'uploadProgress') {
3531
- * console.log(`Progress: ${event.progress}%`);
3532
- * } else if (event.type === 'response') {
3533
- * console.log('Upload complete:', event.body);
3534
- * }
3535
- * });
3536
- * ```
3537
- */
3538
- uploadDocuments(formData, enableProgress = true) {
3539
- // Add the required "data" field as a JSON string
3540
- const data = {
3541
- action: "documentsupload",
3542
- appName: this.appService.appName,
3543
- debug: this.chatService.assistantConfig$.value.defaultValues.debug
3544
- };
3545
- formData.append('data', JSON.stringify(data));
3546
- const options = {
3547
- headers: new HttpHeaders({ 'Accept': 'application/json' }),
3548
- observe: 'events',
3549
- };
3550
- if (enableProgress)
3551
- options['reportProgress'] = true;
3552
- const url = this.jsonMethodWebService.makeUrl(this.REQUEST_URL);
3553
- /**
3554
- * Need to use the HttpClient directly to avoid the JSONMethodPluginService's behavior
3555
- * because it is altering the request and thus making it unable to properly set the Content-Type header with the boundary
3556
- */
3557
- return this.http.post(url, formData, options).pipe(filter((event) => event.type === HttpEventType.Response || (enableProgress && event.type === HttpEventType.UploadProgress)), map((event) => {
3558
- switch (event.type) {
3559
- case HttpEventType.UploadProgress: {
3560
- const { loaded = 0, total = 0 } = event;
3561
- return { type: 'uploadProgress', loaded, total, progress: total > 0 ? Math.round((loaded / total) * 100) : 0 };
3562
- }
3563
- case HttpEventType.Response:
3564
- return { type: 'response', body: event.body };
3565
- default:
3566
- throw new Error("Unexpected event type"); // Should not reach here, but for typescript sake.
3567
- }
3568
- }), catchError((error) => {
3569
- throw new Error(`Error invoking documentsupload: ${error}`);
3570
- }));
3571
- }
3572
- /**
3573
- * Retrieves the indexing status of a predefined uploaded document(s)
3574
- * based on the provided status token returned from the upload process.
3575
- *
3576
- * @param token - The status token used to track the indexing process.
3577
- * @returns An Observable that emits the server response of type `IndexingResponse`.
3578
- * The `IndexingResponse` object contains:
3579
- * - `docs`: array - An array of document status objects, each containing:
3580
- * - * `id`: string - The document ID.
3581
- * - * `fileName`: string - The name of the file.
3582
- * - * `operation`: "Add" | "Update" - The operation performed on the document.
3583
- * - * `status`: "Indexing" | "Indexed" | "Error" - The status of the document.
3584
- * - * `previousIndexationTime`: string | null - The previous indexation time.
3585
- * - * `currentIndexationTime`: string | null - The current indexation time.
3586
- * - `executionTime`: string - The time taken to retrieve the indexing status.
3587
- * @throws {Error} If there is an error invoking the `documentsUploadStatus` action.
3588
- */
3589
- getIndexingStatus(token) {
3590
- const data = {
3591
- action: "documentsUploadStatus",
3592
- appName: this.appService.appName,
3593
- statusToken: token,
3594
- debug: this.chatService.assistantConfig$.value.defaultValues.debug
3595
- };
3596
- return this.jsonMethodWebService.post(this.REQUEST_URL, data).pipe(catchError((error) => {
3597
- throw new Error(`Error invoking documentsUploadStatus: ${error}`);
3598
- }));
3599
- }
3600
- /**
3601
- * Retrieves a list of uploaded documents.
3602
- *
3603
- * @param {string[]} [columns] - Optional array of additional index columns to include for each document.
3604
- * @param {number} [skip] - Optional number of documents to skip for pagination. If set, count also needs to be set. No skip if not set.
3605
- * @param {number} [count] - Optional number of documents to take for pagination (unlimited if not set).
3606
- * @returns {Observable<UploadedDocuments>} An observable that emits the list of uploaded documents.
3607
- * The `UploadedDocuments` object contains:
3608
- * - `docs`: array - An array of objects representing a single document with the following properties:
3609
- * - * `id`: string - A unique identifier for the document.
3610
- * - * `fileName`: string - The name of the file.
3611
- * - * `title`: string - The title of the document.
3612
- * - * `fileExt`: string - The file extension of the document.
3613
- * - * `indexationTime`: string - The time when the document was indexed.
3614
- * - * `size`: number - The size of the document.
3615
- * - * `sizeDisplay`: string - A human-readable representation of the document size.
3616
- * - * `[key: string]`: any - Additional columns asked in the request, value of the column for the document.
3617
- * - `count`: number - The count of documents in the current batch or subset.
3618
- * - `totalCount`: number - The total count of documents available.
3619
- * - `executionTime`: string - The time taken to process the documents.
3620
- * @throws {Error} Throws an error if the request fails.
3621
- */
3622
- getDocumentsList(columns, skip, count) {
3623
- const data = {
3624
- action: "documentsList",
3625
- appName: this.appService.appName,
3626
- debug: this.chatService.assistantConfig$.value.defaultValues.debug
3627
- };
3628
- if (columns)
3629
- data["columns"] = columns;
3630
- if (skip)
3631
- data["skip"] = skip;
3632
- if (count)
3633
- data["count"] = count;
3634
- return this.jsonMethodWebService.post(this.REQUEST_URL, data).pipe(catchError((error) => {
3635
- throw new Error(`Error invoking documentsList: ${error}`);
3636
- }));
3637
- }
3638
- /**
3639
- * Deletes the uploaded documents with the specified IDs.
3640
- *
3641
- * @param docIds - An array of document IDs to delete.
3642
- * @returns An observable that emits the server response of type `deleteDocumentsResponse`.
3643
- * The `deleteDocumentsResponse` object contains:
3644
- * - `deletedCount`: number - The number of deleted documents.
3645
- * - `executionTime`: string - The time taken to delete the documents.
3646
- * @throws {Error} If there is an error invoking the `documentsDelete` action.
3647
- */
3648
- deleteDocuments(docIds) {
3649
- const data = {
3650
- action: "documentsDelete",
3651
- docIds,
3652
- appName: this.appService.appName,
3653
- debug: this.chatService.assistantConfig$.value.defaultValues.debug
3654
- };
3655
- return this.jsonMethodWebService.post(this.REQUEST_URL, data).pipe(catchError((error) => {
3656
- throw new Error(`Error invoking documentsDelete: ${error}`);
3657
- }));
3658
- }
3659
- /**
3660
- * Deletes all uploaded documents.
3661
- *
3662
- * @returns An observable that emits the server response of type `deleteDocumentsResponse`.
3663
- * The `deleteDocumentsResponse` object contains:
3664
- * - `deletedCount`: number - The number of deleted documents.
3665
- * - `executionTime`: string - The time taken to delete the documents.
3666
- * @throws {Error} If there is an error invoking the `documentsDeleteAll` action.
3667
- */
3668
- deleteAllDocuments() {
3669
- const data = {
3670
- action: "documentsDeleteAll",
3671
- appName: this.appService.appName,
3672
- debug: this.chatService.assistantConfig$.value.defaultValues.debug
3673
- };
3674
- return this.jsonMethodWebService.post(this.REQUEST_URL, data).pipe(catchError((error) => {
3675
- throw new Error(`Error invoking documentsDeleteAll: ${error}`);
3676
- }));
3677
- }
3678
- /**
3679
- * Formats the given file size in bytes into a human-readable string.
3680
- *
3681
- * @param bytes - The size of the file in bytes.
3682
- * @returns A string representing the file size in a human-readable format (e.g., "10.24 KB", "1.00 MB").
3683
- */
3684
- formatFileSize(bytes) {
3685
- if (bytes === 0)
3686
- return "0 Bytes";
3687
- const k = 1024;
3688
- const sizes = ["Bytes", "KB", "MB", "GB", "TB"];
3689
- const i = Math.floor(Math.log(bytes) / Math.log(k));
3690
- return (Number.parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + " " + sizes[i]);
3691
- }
3692
- /**
3693
- * Converts the given file size in bytes into megabytes.
3694
- *
3695
- * @param bytes - The size of the file in bytes.
3696
- * @returns The size of the file in megabytes.
3697
- */
3698
- convertBytesToMB(bytes) {
3699
- return bytes / (1024 * 1024);
3700
- }
3701
- }
3702
- DocumentsUploadService.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.0.6", ngImport: i0, type: DocumentsUploadService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
3703
- DocumentsUploadService.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "14.0.6", ngImport: i0, type: DocumentsUploadService });
3704
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.0.6", ngImport: i0, type: DocumentsUploadService, decorators: [{
3705
- type: Injectable
3706
- }] });
3707
-
3708
- class DocumentUploadComponent {
3709
- constructor() {
3710
- /** Polling interval in milliseconds to update the indexing status of the uploaded documents */
3711
- this.pollingInterval = 1000;
3712
- this.flowConfig = {
3713
- // Disables chunk testing before uploading actual file data. Thus, avoids unnecessary requests and speeds up uploads
3714
- testChunks: false,
3715
- // Allows multiple file uploads simultaneously
3716
- singleFile: false,
3717
- // Allows the same file to be uploaded multiple times
3718
- allowDuplicateUploads: true,
3719
- query: () => {
3720
- return {}; // Empty object to prevent query parameters
3721
- },
3722
- preprocess: (chunk) => chunk.abort() // Prevents the default flow upload of chunks
3723
- };
3724
- this.errorAlerts = [];
3725
- this.dragging$ = new BehaviorSubject(false);
3726
- this.indexing$ = new BehaviorSubject(false);
3727
- this.indexingInfos$ = new BehaviorSubject(undefined);
3728
- this.uploading$ = new BehaviorSubject(false);
3729
- this.uploadingInfos$ = new BehaviorSubject(undefined);
3730
- this._subscription = new Subscription();
3731
- this.loginService = inject(LoginService);
3732
- this.instanceManagerService = inject(InstanceManagerService);
3733
- this.documentsUploadService = inject(DocumentsUploadService);
3734
- this.notificationsService = inject(NotificationsService);
3735
- }
3736
- ngOnInit() {
3737
- this._subscription.add(this.loginService.events
3738
- .pipe(filter((e) => e.type === "login-complete"), tap((_) => this.instantiateChatService()), switchMap((_) => this.chatService.assistantConfig$), filter((config) => !!config), tap((_) => this.documentsUploadService.init(this.chatService)), catchError((error) => {
3739
- console.error(error);
3740
- this.notificationsService.error(error);
3741
- return EMPTY;
3742
- }))
3743
- .subscribe());
3744
- this._subscription.add(this.flow.events$.subscribe((event) => {
3745
- const evt = event;
3746
- // Kept it just for reference to show how to access data of different events
3747
- if (event.type === "filesAdded") {
3748
- const addedFiles = evt.event[0];
3749
- this._onFilesAdded(addedFiles);
3750
- }
3751
- if (event.type === "filesSubmitted") {
3752
- this._onFilesSubmitted();
3753
- }
3754
- }));
3755
- // Override the upload method of the flow directive
3756
- this.flow.upload = this.startUpload.bind(this);
3757
- }
3758
- ngOnDestroy() {
3759
- this._subscription.unsubscribe();
3760
- }
3761
- instantiateChatService() {
3762
- this.chatService = this.instanceManagerService.getInstance(this.instanceId);
3763
- }
3764
- /**
3765
- * Handles the submission of files.
3766
- *
3767
- * @remarks
3768
- * This method performs the following checks on the submitted files:
3769
- *
3770
- * 1. Checks if the number of files submitted exceeds the remainingFileCount defined in the constraints.
3771
- * - If the number of files exceeds the allowed count, all files are removed from the flow.
3772
- * - An error message is added to the error alerts.
3773
- *
3774
- * 2. Checks if the total size of the files submitted exceeds the remainingFileSize defined in the constraints.
3775
- * - If the total size exceeds the allowed size, all files are removed from the flow.
3776
- * - An error message is added to the error alerts.
3777
- *
3778
- * 3. Checks if the file extension of the files submitted is not allowed.
3779
- * - If the file extension is not allowed, the file is removed from the flow.
3780
- * - An error message is added to the error alerts.
3781
- */
3782
- _onFilesSubmitted() {
3783
- // Get all files from the flow
3784
- const files = [...this.flow.flowJs.files];
3785
- // Clear the error alerts
3786
- this.errorAlerts = [];
3787
- const errors = [];
3788
- /**
3789
- * Checks if the number of files submitted exceeds the remainingFileCount defined in the constraints.
3790
- * If the number of files exceeds the allowed count, all files are removed from the flow
3791
- * An error message is added to the error alerts.
3792
- */
3793
- if (files.length > this.documentsUploadService.uploadConfig$.value.constraints.remainingFileCount) {
3794
- for (const file of files) {
3795
- this.flow.flowJs.removeFile(file);
3796
- }
3797
- errors.push(`The number of files exceeds the allowed limit. You can upload up to ${this.documentsUploadService.uploadConfig$.value.constraints.remainingFileCount} files.`);
3798
- }
3799
- /**
3800
- * Checks if the total size of the files submitted exceeds the remainingFileSize defined in the constraints.
3801
- * If the total size exceeds the allowed size, all files are removed from the flow
3802
- * An error message is added to the error alerts.
3803
- *
3804
- * @remarks
3805
- * The maximum size of the files that can be uploaded is temporary set to the maximum of 128 MB and the remainingFileSizeMB defined in the constraints.
3806
- * Waiting for the plugin to handle this kestrel issue
3807
- */
3808
- else if (this.documentsUploadService.convertBytesToMB(this.flow.flowJs.getSize()) > Math.min(128, this.documentsUploadService.uploadConfig$.value.constraints.remainingFileSizeMB)) {
3809
- for (const file of files) {
3810
- this.flow.flowJs.removeFile(file);
3811
- }
3812
- errors.push(`The total size of the files exceeds the allowed limit. You can upload files up to ${Math.min(128, this.documentsUploadService.uploadConfig$.value.constraints.remainingFileSizeMB)} MB.`);
3813
- }
3814
- /**
3815
- * Checks if the file extension of the files submitted is not allowed.
3816
- * If the file extension is not allowed, the file is removed from the flow
3817
- * An error message is added to the error alerts.
3818
- */
3819
- else {
3820
- for (const file of files) {
3821
- if (!this.documentsUploadService.uploadConfig$.value.constraints.supportedFileExtensions.includes(`${file.getExtension()}`)) {
3822
- this.flow.flowJs.removeFile(file);
3823
- errors.push(`The file extension "${file.getExtension()}" is not supported.`);
3824
- }
3825
- }
3826
- }
3827
- // Add unique error messages to the error alerts
3828
- this.errorAlerts = [...new Set(errors)];
3829
- }
3830
- _onFilesAdded(files) { }
3831
- onDragenter() {
3832
- this.dragging$.next(true);
3833
- }
3834
- onDragleave(event) {
3835
- if (!event.relatedTarget ||
3836
- !event.relatedTarget.closest(".dropzone")) {
3837
- this.dragging$.next(false);
3838
- }
3839
- }
3840
- onDrop() {
3841
- this.dragging$.next(false);
3842
- }
3843
- trackTransfer(transfer) {
3844
- return transfer.id;
3845
- }
3846
- /**
3847
- * Initiates the upload process by invoking the `upload` method
3848
- * of the `flow` instance.
3849
- * The `upload` method is overridden in the `ngOnInit` method to match the requirements of the assistant API.
3850
- */
3851
- upload() {
3852
- this.flow.upload();
3853
- }
3854
- /**
3855
- * Initiates the file upload process by preparing the form data,
3856
- * setting the uploading flag, and making an API call to upload the files.
3857
- *
3858
- * @remarks
3859
- * - Collects all files from the `flowJs` instance and appends them to a `FormData` object.
3860
- * - Subscribes to the upload process to handle progress updates, completion, and errors.
3861
- * - Triggers tracking the indexing process upon successful upload completion.
3862
- * - Cleans up the `flow` instance after the upload process is finalized.
3863
- */
3864
- startUpload() {
3865
- const formData = new FormData();
3866
- // Add all files with their original names
3867
- this.flow.flowJs.files.forEach((file) => {
3868
- formData.append(file.name, file.file);
3869
- });
3870
- // Clear the error alerts
3871
- this.errorAlerts = [];
3872
- // Set the uploading flag to true
3873
- this.uploading$.next(true);
3874
- // Make the API call to upload the files
3875
- this._subscription.add(this.documentsUploadService.uploadDocuments(formData).pipe(tap((event) => {
3876
- if (event.type === 'uploadProgress') {
3877
- this.uploadingInfos$.next(event);
3878
- }
3879
- if (event.type === 'response') {
3880
- this.trackIndexingProcess(event.body.statusToken);
3881
- }
3882
- }), catchError((err) => {
3883
- this.uploading$.next(false); // Set the uploading flag to false ONLY in case of an error. Otherwise, keep it true until the start of indexing process in order to prevent flickering of the UI
3884
- this.uploadingInfos$.next(undefined); // Clear the uploading infos
3885
- console.error(err);
3886
- this.notificationsService.error("Error uploading files");
3887
- return EMPTY;
3888
- }), finalize(() => {
3889
- // Clear the flow instance after the upload is complete or an error occurs
3890
- this.flow.cancel();
3891
- })).subscribe());
3892
- }
3893
- /**
3894
- * Tracks the indexing process for a single documents upload session by polling the API for its status.
3895
- *
3896
- * @param token - A unique token representing the single documents upload session.
3897
- *
3898
- * @remarks
3899
- * - Clears the uploading flags and sets the indexing flag to true before starting the process.
3900
- * - Polls the API every second to retrieve the current indexing status.
3901
- * - Updates the `indexingInfos` property with the latest status.
3902
- * - Stops polling when the indexing process is completed or an error occurs.
3903
- * - Cleans up by resetting the indexing flags and clearing the `indexingInfos` property when the process is finalized.
3904
- */
3905
- trackIndexingProcess(token) {
3906
- // Clear the uploading flags
3907
- this.uploading$.next(false);
3908
- this.uploadingInfos$.next(undefined);
3909
- // Set the indexing flag to true
3910
- this.indexing$.next(true);
3911
- // Combine immediate API call with interval-based polling
3912
- this._subscription.add(interval(this.pollingInterval).pipe(startWith(0), // Trigger the API call immediately
3913
- switchMap(() => this.documentsUploadService.getIndexingStatus(token)), tap((res) => this.indexingInfos$.next(res)), takeWhile((response) => !response.isCompleted, true), catchError((err) => {
3914
- console.error(err);
3915
- this.notificationsService.error("Error retrieving indexing status");
3916
- return EMPTY;
3917
- }), finalize(() => {
3918
- // Clear the indexing flags
3919
- this.indexing$.next(false);
3920
- this.indexingInfos$.next(undefined);
3921
- })).subscribe());
3922
- }
3923
- /**
3924
- * Calculates the indexing progress as a percentage of completed documents.
3925
- *
3926
- * @param infos - The indexing information containing the list of documents and their statuses.
3927
- * @returns The progress as a number between 0 and 1, where 0 indicates no progress and 1 indicates all documents are processed (processed means either indexed or errored).
3928
- * Returns 0 if the input is invalid or there are no documents.
3929
- */
3930
- getIndexingProgress(infos) {
3931
- if (!infos || !infos.docs)
3932
- return 0;
3933
- const completed = infos.docs.filter(doc => doc.status === "Indexed" || doc.status === "Error").length;
3934
- const total = infos.docs.length;
3935
- return completed / total;
3936
- }
3937
- }
3938
- DocumentUploadComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.0.6", ngImport: i0, type: DocumentUploadComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
3939
- DocumentUploadComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "14.0.6", type: DocumentUploadComponent, isStandalone: true, selector: "sq-document-upload", inputs: { instanceId: "instanceId", pollingInterval: "pollingInterval" }, providers: [DocumentsUploadService], viewQueries: [{ propertyName: "flow", first: true, predicate: ["flow"], descendants: true, static: true }], ngImport: i0, template: "<ng-container #flow=\"flow\" [flowConfig]=\"flowConfig\"></ng-container>\n<div class=\"file-upload-container\">\n <input\n type=\"file\"\n flowButton\n [flow]=\"flow.flowJs\"\n multiple\n hidden\n #fileInput>\n <div\n flowDrop\n [flow]=\"flow.flowJs\"\n (dragenter)=\"onDragenter()\"\n (dragleave)=\"onDragleave($event)\"\n (drop)=\"onDrop()\"\n (click)=\"fileInput.click()\"\n class=\"dropzone\"\n [ngClass]=\"{'dropzone--active': (dragging$ | async)}\"\n [hidden]=\"(uploading$ | async) || (indexing$ | async)\">\n <ng-container *ngIf=\"!(dragging$ | async); else draggingContent\">\n <i class=\"fas fa-cloud-upload-alt\"></i>\n <span>Drag and drop files here Or</span>\n <span class=\"text-orange\">Click to browse</span>\n </ng-container>\n <ng-template #draggingContent>\n <span>Drop files here</span>\n </ng-template>\n </div>\n\n <div *ngIf=\"(uploading$ | async) || (indexing$ | async)\" class=\"dropzone dropzone--active\">\n <ng-container *ngIf=\"(uploading$ | async); else indexingState\">\n <i class=\"fas fa-spinner fa-pulse\"></i>\n <span>\n Uploading {{ (flow.transfers$ | async)!.transfers.length }} files\n </span>\n <span *ngIf=\"(uploadingInfos$ | async) as uploadingInfos\">{{ uploadingInfos.progress | number:'1.0-0' }}%</span>\n </ng-container>\n <ng-template #indexingState>\n <i class=\"fas fa-spinner fa-pulse\"></i>\n <ng-container *ngIf=\"indexingInfos$ | async as indexingInfo\">\n <span>Indexing {{ indexingInfo.docs.length }} files</span>\n <span>{{ getIndexingProgress(indexingInfo) * 100 | number:'1.0-0' }}%</span>\n </ng-container>\n </ng-template>\n </div>\n\n <ul *ngIf=\"errorAlerts.length > 0\" class=\"error-list mt-3\">\n <li *ngFor=\"let error of errorAlerts\">\n {{ error }}\n </li>\n </ul>\n\n <div *ngIf=\"((flow.transfers$ | async)?.transfers?.length > 0) && !(uploading$ | async)\" class=\"file-list mt-3\">\n <ul>\n <li *ngFor=\"let transfer of (flow.transfers$ | async)!.transfers; trackBy: trackTransfer\">\n <sq-format-icon [extension]=\"transfer.flowFile.getExtension()\" class=\"me-1\"></sq-format-icon>\n <span [sqTooltip]=\"transfer.name\">{{ transfer.name }}</span>\n <i class=\"fas fa-trash ms-1\" (click)=\"flow.cancelFile(transfer)\" [sqTooltip]=\"'Cancel'\"></i>\n </li>\n </ul>\n </div>\n\n <div class=\"d-flex mt-2\">\n <button\n type=\"button\"\n class=\"btn btn-light cancel-btn me-2\"\n (click)=\"flow.cancel()\"\n [disabled]=\"!((flow.transfers$ | async)?.transfers?.length > 0) || (uploading$ | async) || (indexing$ | async)\">\n Cancel\n </button>\n <button\n type=\"button\"\n class=\"upload-btn\"\n (click)=\"upload()\"\n [disabled]=\"!((flow.transfers$ | async)?.transfers?.length > 0) || (uploading$ | async) || (indexing$ | async)\">\n Upload\n </button>\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-error{background-color:var(--ast-error-bg, rgba(249, 58, 55, .2));color:var(--ast-action-buttons-color, inherit)}.ast-error:hover{color:var(--ast-error-color, rgba(249, 58, 55, .7))}.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-uploaded-doc-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;--ast-action-buttons-color: white;--ast-action-buttons-hover-color: #6dbee6;--ast-report-bg: #070707}@keyframes dash-move{0%{background-position:0 0}to{background-position:100% 0}}.file-upload-container{width:100%;position:relative;padding:20px;background-color:#fff}.file-upload-container .dropzone{display:flex;flex-direction:column;justify-content:center;align-items:center;height:100px;border:2px dashed var(--ast-primary-color, #005DA7);border-color:var(--ast-secondary-color, #FF732E) var(--ast-primary-color, #005DA7) var(--ast-primary-color, #005DA7) var(--ast-secondary-color, #FF732E);border-radius:5px;padding:10px;cursor:pointer;background-color:#fff;transition:background-color .3s ease;color:var(--ast-primary-color, #005DA7)}.file-upload-container .dropzone--active{background-color:#fff8f1;color:var(--ast-secondary-color, #FF732E);border-color:var(--ast-secondary-color, #FF732E)}.file-upload-container .dropzone i{font-size:x-large}.file-upload-container .dropzone span{margin:0;font-size:small}.file-upload-container .dropzone span.text-orange{color:var(--ast-secondary-color, #FF732E)}.file-upload-container .file-list h6{color:#a9a9a9}.file-upload-container .file-list ul{list-style-type:none;padding:0}.file-upload-container .file-list ul li{display:flex;align-items:center;padding:10px;background-color:var(--ast-primary-bg, #f2f8fe);margin-bottom:5px;border-radius:5px;font-size:small}.file-upload-container .file-list ul li span{flex-grow:1;text-overflow:ellipsis;overflow:hidden;white-space:nowrap}.file-upload-container .file-list ul li i{cursor:pointer}.file-upload-container .file-list ul li i:hover{color:var(--ast-primary-color, #005DA7)}.file-upload-container .error-list{display:flex;flex-direction:column;list-style:disc;background:var(--ast-error-color, rgba(249, 58, 55, .7));color:#fff;border-radius:5px}.file-upload-container .error-list li{padding:3px}.file-upload-container .upload-btn{background:linear-gradient(to right,#1d4ed8,#ec4899,#f97316);color:#fff;border:none;padding:8px 16px;border-radius:5px;cursor:pointer;width:100%}.file-upload-container .upload-btn:hover{background:linear-gradient(to right,#1d4ed8cc,#ec4899cc,#f97316cc)}.file-upload-container .upload-btn[disabled]{opacity:.3;cursor:not-allowed}.file-upload-container .cancel-btn{cursor:pointer;pointer-events:unset;width:100%}.file-upload-container .cancel-btn[disabled]{opacity:.3;cursor:not-allowed}\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: "pipe", type: i1.AsyncPipe, name: "async" }, { kind: "pipe", type: i1.DecimalPipe, name: "number" }, { kind: "ngmodule", type: NgxFlowModule }, { kind: "directive", type: i2$2.ButtonDirective, selector: "[flowButton]", inputs: ["flowDirectoryOnly", "flowAttributes", "flow"] }, { kind: "directive", type: i2$2.DropDirective, selector: "[flowDrop]", inputs: ["flow"], exportAs: ["flowDrop"] }, { kind: "directive", type: i2$2.FlowDirective, selector: "[flowConfig]", inputs: ["flowConfig"], exportAs: ["flow"] }, { kind: "component", type: FormatIconComponent, selector: "sq-format-icon", inputs: ["extension"] }, { kind: "ngmodule", type: UtilsModule }, { kind: "directive", type: i2$1.TooltipDirective, selector: "[sqTooltip]", inputs: ["sqTooltip", "sqTooltipData", "sqTooltipTemplate", "placement", "fallbackPlacements", "delay", "hoverableTooltip", "tooltipClass"] }] });
3940
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.0.6", ngImport: i0, type: DocumentUploadComponent, decorators: [{
3941
- type: Component,
3942
- args: [{ selector: "sq-document-upload", standalone: true, providers: [DocumentsUploadService], imports: [CommonModule, NgxFlowModule, FormatIconComponent, UtilsModule], template: "<ng-container #flow=\"flow\" [flowConfig]=\"flowConfig\"></ng-container>\n<div class=\"file-upload-container\">\n <input\n type=\"file\"\n flowButton\n [flow]=\"flow.flowJs\"\n multiple\n hidden\n #fileInput>\n <div\n flowDrop\n [flow]=\"flow.flowJs\"\n (dragenter)=\"onDragenter()\"\n (dragleave)=\"onDragleave($event)\"\n (drop)=\"onDrop()\"\n (click)=\"fileInput.click()\"\n class=\"dropzone\"\n [ngClass]=\"{'dropzone--active': (dragging$ | async)}\"\n [hidden]=\"(uploading$ | async) || (indexing$ | async)\">\n <ng-container *ngIf=\"!(dragging$ | async); else draggingContent\">\n <i class=\"fas fa-cloud-upload-alt\"></i>\n <span>Drag and drop files here Or</span>\n <span class=\"text-orange\">Click to browse</span>\n </ng-container>\n <ng-template #draggingContent>\n <span>Drop files here</span>\n </ng-template>\n </div>\n\n <div *ngIf=\"(uploading$ | async) || (indexing$ | async)\" class=\"dropzone dropzone--active\">\n <ng-container *ngIf=\"(uploading$ | async); else indexingState\">\n <i class=\"fas fa-spinner fa-pulse\"></i>\n <span>\n Uploading {{ (flow.transfers$ | async)!.transfers.length }} files\n </span>\n <span *ngIf=\"(uploadingInfos$ | async) as uploadingInfos\">{{ uploadingInfos.progress | number:'1.0-0' }}%</span>\n </ng-container>\n <ng-template #indexingState>\n <i class=\"fas fa-spinner fa-pulse\"></i>\n <ng-container *ngIf=\"indexingInfos$ | async as indexingInfo\">\n <span>Indexing {{ indexingInfo.docs.length }} files</span>\n <span>{{ getIndexingProgress(indexingInfo) * 100 | number:'1.0-0' }}%</span>\n </ng-container>\n </ng-template>\n </div>\n\n <ul *ngIf=\"errorAlerts.length > 0\" class=\"error-list mt-3\">\n <li *ngFor=\"let error of errorAlerts\">\n {{ error }}\n </li>\n </ul>\n\n <div *ngIf=\"((flow.transfers$ | async)?.transfers?.length > 0) && !(uploading$ | async)\" class=\"file-list mt-3\">\n <ul>\n <li *ngFor=\"let transfer of (flow.transfers$ | async)!.transfers; trackBy: trackTransfer\">\n <sq-format-icon [extension]=\"transfer.flowFile.getExtension()\" class=\"me-1\"></sq-format-icon>\n <span [sqTooltip]=\"transfer.name\">{{ transfer.name }}</span>\n <i class=\"fas fa-trash ms-1\" (click)=\"flow.cancelFile(transfer)\" [sqTooltip]=\"'Cancel'\"></i>\n </li>\n </ul>\n </div>\n\n <div class=\"d-flex mt-2\">\n <button\n type=\"button\"\n class=\"btn btn-light cancel-btn me-2\"\n (click)=\"flow.cancel()\"\n [disabled]=\"!((flow.transfers$ | async)?.transfers?.length > 0) || (uploading$ | async) || (indexing$ | async)\">\n Cancel\n </button>\n <button\n type=\"button\"\n class=\"upload-btn\"\n (click)=\"upload()\"\n [disabled]=\"!((flow.transfers$ | async)?.transfers?.length > 0) || (uploading$ | async) || (indexing$ | async)\">\n Upload\n </button>\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-error{background-color:var(--ast-error-bg, rgba(249, 58, 55, .2));color:var(--ast-action-buttons-color, inherit)}.ast-error:hover{color:var(--ast-error-color, rgba(249, 58, 55, .7))}.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-uploaded-doc-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;--ast-action-buttons-color: white;--ast-action-buttons-hover-color: #6dbee6;--ast-report-bg: #070707}@keyframes dash-move{0%{background-position:0 0}to{background-position:100% 0}}.file-upload-container{width:100%;position:relative;padding:20px;background-color:#fff}.file-upload-container .dropzone{display:flex;flex-direction:column;justify-content:center;align-items:center;height:100px;border:2px dashed var(--ast-primary-color, #005DA7);border-color:var(--ast-secondary-color, #FF732E) var(--ast-primary-color, #005DA7) var(--ast-primary-color, #005DA7) var(--ast-secondary-color, #FF732E);border-radius:5px;padding:10px;cursor:pointer;background-color:#fff;transition:background-color .3s ease;color:var(--ast-primary-color, #005DA7)}.file-upload-container .dropzone--active{background-color:#fff8f1;color:var(--ast-secondary-color, #FF732E);border-color:var(--ast-secondary-color, #FF732E)}.file-upload-container .dropzone i{font-size:x-large}.file-upload-container .dropzone span{margin:0;font-size:small}.file-upload-container .dropzone span.text-orange{color:var(--ast-secondary-color, #FF732E)}.file-upload-container .file-list h6{color:#a9a9a9}.file-upload-container .file-list ul{list-style-type:none;padding:0}.file-upload-container .file-list ul li{display:flex;align-items:center;padding:10px;background-color:var(--ast-primary-bg, #f2f8fe);margin-bottom:5px;border-radius:5px;font-size:small}.file-upload-container .file-list ul li span{flex-grow:1;text-overflow:ellipsis;overflow:hidden;white-space:nowrap}.file-upload-container .file-list ul li i{cursor:pointer}.file-upload-container .file-list ul li i:hover{color:var(--ast-primary-color, #005DA7)}.file-upload-container .error-list{display:flex;flex-direction:column;list-style:disc;background:var(--ast-error-color, rgba(249, 58, 55, .7));color:#fff;border-radius:5px}.file-upload-container .error-list li{padding:3px}.file-upload-container .upload-btn{background:linear-gradient(to right,#1d4ed8,#ec4899,#f97316);color:#fff;border:none;padding:8px 16px;border-radius:5px;cursor:pointer;width:100%}.file-upload-container .upload-btn:hover{background:linear-gradient(to right,#1d4ed8cc,#ec4899cc,#f97316cc)}.file-upload-container .upload-btn[disabled]{opacity:.3;cursor:not-allowed}.file-upload-container .cancel-btn{cursor:pointer;pointer-events:unset;width:100%}.file-upload-container .cancel-btn[disabled]{opacity:.3;cursor:not-allowed}\n"] }]
3943
- }], propDecorators: { instanceId: [{
3944
- type: Input
3945
- }], pollingInterval: [{
3946
- type: Input
3947
- }], flow: [{
3948
- type: ViewChild,
3949
- args: ["flow", { static: true }]
3950
- }] } });
3951
-
3952
- class DocumentListComponent extends AbstractFacet {
3953
- constructor() {
3954
- super();
3955
- this.groupedUploadedDocuments$ = new BehaviorSubject([]);
3956
- this.deletingAll = false;
3957
- this._subscription = new Subscription();
3958
- this._actions = [];
3959
- this._updateDocumentsListAction = new Action({
3960
- icon: 'fas fa-sync',
3961
- title: "Refresh",
3962
- action: () => this.updateUploadedDocumentsList()
3963
- });
3964
- this._deleteAllDocumentsAction = new Action({
3965
- icon: 'fas fa-trash',
3966
- title: "Delete all",
3967
- action: () => this.deleteAllDocuments()
3968
- });
3969
- this.loginService = inject(LoginService);
3970
- this.instanceManagerService = inject(InstanceManagerService);
3971
- this.documentsUploadService = inject(DocumentsUploadService);
3972
- this.notificationsService = inject(NotificationsService);
3973
- this._actions = [this._updateDocumentsListAction, this._deleteAllDocumentsAction];
3974
- }
3975
- ngOnInit() {
3976
- this._subscription.add(this.loginService.events
3977
- .pipe(filter((e) => e.type === "login-complete"), tap((_) => this.instantiateChatService()), switchMap((_) => this.chatService.assistantConfig$), filter((config) => !!config), tap((_) => this.documentsUploadService.init(this.chatService)), tap((_) => this.updateUploadedDocumentsList()), catchError((error) => {
3978
- console.error(error);
3979
- this.notificationsService.error(error);
3980
- return EMPTY;
3981
- }))
3982
- .subscribe());
3983
- }
3984
- ngOnDestroy() {
3985
- this._subscription.unsubscribe();
3986
- }
3987
- get actions() { return this._actions; }
3988
- instantiateChatService() {
3989
- this.chatService = this.instanceManagerService.getInstance(this.instanceId);
3990
- }
3991
- /**
3992
- * Updates the list of uploaded documents by fetching the latest data from the service.
3993
- * The fetched documents are grouped by date.
3994
- *
3995
- * @returns {void}
3996
- */
3997
- updateUploadedDocumentsList() {
3998
- this._subscription.add(this.documentsUploadService.getDocumentsList()
3999
- .pipe(tap((uploadedDocuments) => {
4000
- this.groupedUploadedDocuments$.next(this._groupUploadedDocumentsByDate(uploadedDocuments.docs));
4001
- }), catchError((error) => {
4002
- console.error(error);
4003
- this.notificationsService.error(error);
4004
- return EMPTY;
4005
- }), take(1))
4006
- .subscribe());
4007
- }
4008
- deleteDocument(event, doc) {
4009
- event.stopPropagation();
4010
- this.documentToDelete = doc;
4011
- }
4012
- /**
4013
- * Handles the deletion of an uploaded document. This method triggers a confirmation modal
4014
- * to ensure the user wants to proceed with the deletion. If confirmed, it sends a request
4015
- * to delete the document and provides feedback to the user upon success or failure.
4016
- *
4017
- * @param event - The event object associated with the delete action, used to stop propagation.
4018
- * @param doc - The document to be deleted, represented as an instance of `UploadedDocument`.
4019
- *
4020
- * @remarks
4021
- * - Displays a confirmation modal with a warning message.
4022
- * - On confirmation, deletes the document.
4023
- * - Shows a success notification upon successful deletion.
4024
- * - Logs and notifies the user of any errors encountered during the deletion process.
4025
- */
4026
- performDeleteDocument() {
4027
- this._subscription.add(this.documentsUploadService.deleteDocuments([this.documentToDelete.id])
4028
- .pipe(tap(() => {
4029
- this.notificationsService.success(`The document "${this.documentToDelete.fileName}" has been successfully deleted.`);
4030
- this.documentToDelete = undefined;
4031
- this.updateUploadedDocumentsList();
4032
- }), catchError((error) => {
4033
- console.error(error);
4034
- this.notificationsService.error(error);
4035
- return EMPTY;
4036
- }), take(1))
4037
- .subscribe());
4038
- }
4039
- deleteAllDocuments(event) {
4040
- event === null || event === void 0 ? void 0 : event.stopPropagation();
4041
- this.deletingAll = true;
4042
- }
4043
- /**
4044
- * Handles the deletion of all uploaded documents. This method triggers a confirmation modal
4045
- * to ensure the user wants to proceed with the deletion. If confirmed, it sends a request
4046
- * to delete the whole list of documents and provides feedback to the user upon success or failure.
4047
- *
4048
- * @param event - (Optional) The event object associated with the delete action, used to stop propagation.
4049
- *
4050
- * @remarks
4051
- * - Displays a confirmation modal with a warning message.
4052
- * - On confirmation, deletes all documents.
4053
- * - Shows a success notification upon successful deletion.
4054
- * - Logs and notifies the user of any errors encountered during the deletion process.
4055
- */
4056
- performDeleteAllDocuments() {
4057
- this._subscription.add(this.documentsUploadService.deleteAllDocuments()
4058
- .pipe(tap(() => {
4059
- this.deletingAll = false;
4060
- this.notificationsService.success(`All documents have been successfully deleted.`);
4061
- this.updateUploadedDocumentsList();
4062
- }), catchError((error) => {
4063
- console.error(error);
4064
- this.notificationsService.error(error);
4065
- return EMPTY;
4066
- }), take(1))
4067
- .subscribe());
4068
- }
4069
- _groupUploadedDocumentsByDate(docs) {
4070
- const groupedUploadedDocuments = new Map();
4071
- docs
4072
- .sort((a, b) => parseISO(b.indexationTime).getTime() - parseISO(a.indexationTime).getTime())
4073
- .forEach(doc => {
4074
- const groupKey = this._getTimeKey(toDate(parseISO(doc.indexationTime))); // Must convert the UTC date to local date before passing to _getTimeKey
4075
- if (!groupedUploadedDocuments.has(groupKey)) {
4076
- groupedUploadedDocuments.set(groupKey, []);
4077
- }
4078
- groupedUploadedDocuments.get(groupKey).push(doc);
4079
- });
4080
- return Array.from(groupedUploadedDocuments, ([key, value]) => ({ key, value }));
4081
- ;
4082
- }
4083
- _getTimeKey(date) {
4084
- if (isToday(date)) {
4085
- return 'Today';
4086
- }
4087
- else if (isYesterday(date)) {
4088
- return 'Yesterday';
4089
- }
4090
- else if (isThisWeek(date)) {
4091
- return 'This week';
4092
- }
4093
- else if (differenceInDays(endOfYesterday(), date) <= 7) {
4094
- return 'Last week';
4095
- }
4096
- else if (isThisMonth(date)) {
4097
- return 'This month';
4098
- }
4099
- else if (differenceInMonths(endOfYesterday(), date) <= 1) {
4100
- return 'Last month';
4101
- }
4102
- else if (isThisQuarter(date)) {
4103
- return 'This quarter';
4104
- }
4105
- else if (differenceInMonths(endOfYesterday(), date) <= 3) {
4106
- return 'Last quarter';
4107
- }
4108
- else if (isThisYear(date)) {
4109
- return 'This year';
4110
- }
4111
- else if (differenceInYears(endOfYesterday(), date) === 1) {
4112
- return 'Last year';
4113
- }
4114
- else {
4115
- return format(date, 'yyyy');
4116
- }
4117
- }
4118
- }
4119
- DocumentListComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.0.6", ngImport: i0, type: DocumentListComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
4120
- DocumentListComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "14.0.6", type: DocumentListComponent, isStandalone: true, selector: "sq-document-list", inputs: { instanceId: "instanceId" }, providers: [DocumentsUploadService], usesInheritance: true, ngImport: i0, template: "<div class=\"alert alert-danger\" *ngIf=\"deletingAll\">\n <span>You are about to delete all the documents. Do you want to continue ?</span>\n <div class=\"d-flex gap-2\">\n <button class=\"btn btn-light w-100\" (click)=\"deletingAll=false\">Cancel</button>\n <button class=\"btn btn-secondary w-100\" (click)=\"performDeleteAllDocuments()\">Confirm</button>\n </div>\n</div>\n\n<div *ngFor=\"let group of (groupedUploadedDocuments$ | async)\" class=\"uploaded-docs\">\n <span class=\"uploaded-docs-date\">{{group.key}}</span>\n <div *ngFor=\"let doc of group.value\">\n <div class=\"uploaded-doc p-2\">\n <sq-format-icon [extension]=\"doc.fileExt\" class=\"me-1\"></sq-format-icon>\n <span class=\"title me-1\" [sqTooltip]=\"doc.fileName\">{{doc.fileName}}</span>\n <i class=\"uploaded-doc-actions fas fa-trash ms-1\" [sqTooltip]=\"'Delete'\"\n (click)=\"deleteDocument($event, doc)\"></i> \n </div>\n\n <div class=\"alert alert-warning\" *ngIf=\"documentToDelete && documentToDelete.id === doc.id\">\n <span>You are about to delete the document {{documentToDelete!.fileName}}. Do you want to continue ?</span>\n <div class=\"d-flex gap-2\">\n <button class=\"btn btn-light w-100\" (click)=\"documentToDelete=undefined\">Cancel</button>\n <button class=\"btn btn-secondary w-100\" (click)=\"performDeleteDocument()\">Confirm</button>\n </div>\n </div>\n </div>\n</div>", 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-error{background-color:var(--ast-error-bg, rgba(249, 58, 55, .2));color:var(--ast-action-buttons-color, inherit)}.ast-error:hover{color:var(--ast-error-color, rgba(249, 58, 55, .7))}.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-uploaded-doc-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;--ast-action-buttons-color: white;--ast-action-buttons-hover-color: #6dbee6;--ast-report-bg: #070707}.uploaded-docs{padding:.3rem 1rem}.uploaded-docs-date{font-size:12px;font-weight:500;color:#a9a9a9;margin-top:.5rem}.uploaded-docs .uploaded-doc{display:flex;align-items:center;cursor:pointer;margin-left:.25rem}.uploaded-docs .uploaded-doc span{flex-grow:1;text-overflow:ellipsis;overflow:hidden;white-space:nowrap}.uploaded-docs .uploaded-doc .uploaded-doc-actions{display:none}.uploaded-docs .uploaded-doc .uploaded-doc-actions:hover{color:var(--ast-primary-color, #005DA7)}.uploaded-docs .uploaded-doc:hover{background-color:var(--ast-uploaded-doc-hover-background, #f2f8fe)}.uploaded-docs .uploaded-doc:hover .uploaded-doc-actions{display:block}.uploaded-docs .title{display:block;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}\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: "pipe", type: i1.AsyncPipe, name: "async" }, { kind: "component", type: FormatIconComponent, selector: "sq-format-icon", inputs: ["extension"] }, { kind: "ngmodule", type: UtilsModule }, { kind: "directive", type: i2$1.TooltipDirective, selector: "[sqTooltip]", inputs: ["sqTooltip", "sqTooltipData", "sqTooltipTemplate", "placement", "fallbackPlacements", "delay", "hoverableTooltip", "tooltipClass"] }] });
4121
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.0.6", ngImport: i0, type: DocumentListComponent, decorators: [{
4122
- type: Component,
4123
- args: [{ selector: "sq-document-list", standalone: true, providers: [DocumentsUploadService], imports: [CommonModule, FormatIconComponent, UtilsModule], template: "<div class=\"alert alert-danger\" *ngIf=\"deletingAll\">\n <span>You are about to delete all the documents. Do you want to continue ?</span>\n <div class=\"d-flex gap-2\">\n <button class=\"btn btn-light w-100\" (click)=\"deletingAll=false\">Cancel</button>\n <button class=\"btn btn-secondary w-100\" (click)=\"performDeleteAllDocuments()\">Confirm</button>\n </div>\n</div>\n\n<div *ngFor=\"let group of (groupedUploadedDocuments$ | async)\" class=\"uploaded-docs\">\n <span class=\"uploaded-docs-date\">{{group.key}}</span>\n <div *ngFor=\"let doc of group.value\">\n <div class=\"uploaded-doc p-2\">\n <sq-format-icon [extension]=\"doc.fileExt\" class=\"me-1\"></sq-format-icon>\n <span class=\"title me-1\" [sqTooltip]=\"doc.fileName\">{{doc.fileName}}</span>\n <i class=\"uploaded-doc-actions fas fa-trash ms-1\" [sqTooltip]=\"'Delete'\"\n (click)=\"deleteDocument($event, doc)\"></i> \n </div>\n\n <div class=\"alert alert-warning\" *ngIf=\"documentToDelete && documentToDelete.id === doc.id\">\n <span>You are about to delete the document {{documentToDelete!.fileName}}. Do you want to continue ?</span>\n <div class=\"d-flex gap-2\">\n <button class=\"btn btn-light w-100\" (click)=\"documentToDelete=undefined\">Cancel</button>\n <button class=\"btn btn-secondary w-100\" (click)=\"performDeleteDocument()\">Confirm</button>\n </div>\n </div>\n </div>\n</div>", 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-error{background-color:var(--ast-error-bg, rgba(249, 58, 55, .2));color:var(--ast-action-buttons-color, inherit)}.ast-error:hover{color:var(--ast-error-color, rgba(249, 58, 55, .7))}.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-uploaded-doc-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;--ast-action-buttons-color: white;--ast-action-buttons-hover-color: #6dbee6;--ast-report-bg: #070707}.uploaded-docs{padding:.3rem 1rem}.uploaded-docs-date{font-size:12px;font-weight:500;color:#a9a9a9;margin-top:.5rem}.uploaded-docs .uploaded-doc{display:flex;align-items:center;cursor:pointer;margin-left:.25rem}.uploaded-docs .uploaded-doc span{flex-grow:1;text-overflow:ellipsis;overflow:hidden;white-space:nowrap}.uploaded-docs .uploaded-doc .uploaded-doc-actions{display:none}.uploaded-docs .uploaded-doc .uploaded-doc-actions:hover{color:var(--ast-primary-color, #005DA7)}.uploaded-docs .uploaded-doc:hover{background-color:var(--ast-uploaded-doc-hover-background, #f2f8fe)}.uploaded-docs .uploaded-doc:hover .uploaded-doc-actions{display:block}.uploaded-docs .title{display:block;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}\n"] }]
4124
- }], ctorParameters: function () { return []; }, propDecorators: { instanceId: [{
4125
- type: Input
4126
- }] } });
4127
-
4128
- class DocumentOverviewComponent extends AbstractFacet {
4129
- constructor() {
4130
- super();
4131
- this.disabledUpload = true;
4132
- this.onUpload = new EventEmitter();
4133
- this.uploadedDocuments$ = new BehaviorSubject([]);
4134
- this._subscription = new Subscription();
4135
- this._actions = [];
4136
- this._updateDocumentsListAction = new Action({
4137
- icon: 'fas fa-sync',
4138
- title: "Refresh",
4139
- action: () => this.updateUploadedDocumentsList()
4140
- });
4141
- this.loginService = inject(LoginService);
4142
- this.instanceManagerService = inject(InstanceManagerService);
4143
- this.documentsUploadService = inject(DocumentsUploadService);
4144
- this.notificationsService = inject(NotificationsService);
4145
- this.modalService = inject(ModalService);
4146
- this._actions = [this._updateDocumentsListAction];
4147
- }
4148
- ;
4149
- ngOnInit() {
4150
- this._subscription.add(this.loginService.events
4151
- .pipe(filter((e) => e.type === "login-complete"), tap((_) => this.instantiateChatService()), switchMap((_) => this.chatService.assistantConfig$), filter((config) => !!config), tap((_) => this.documentsUploadService.init(this.chatService)), tap((_) => this.updateUploadedDocumentsList()), catchError((error) => {
4152
- console.error(error);
4153
- this.notificationsService.error(error);
4154
- return EMPTY;
4155
- }))
4156
- .subscribe());
4157
- }
4158
- ngOnDestroy() {
4159
- this._subscription.unsubscribe();
4160
- }
4161
- get actions() { return this._actions; }
4162
- instantiateChatService() {
4163
- this.chatService = this.instanceManagerService.getInstance(this.instanceId);
4164
- }
4165
- /**
4166
- * Updates the list of uploaded documents by fetching the latest data from the service.
4167
- *
4168
- * @returns {void}
4169
- */
4170
- updateUploadedDocumentsList() {
4171
- this._subscription.add(this.documentsUploadService.getDocumentsList()
4172
- .pipe(tap((uploadedDocuments) => {
4173
- this.uploadedDocuments$.next(uploadedDocuments.docs);
4174
- }), catchError((error) => {
4175
- console.error(error);
4176
- this.notificationsService.error(error);
4177
- return EMPTY;
4178
- }), take(1))
4179
- .subscribe());
4180
- }
4181
- }
4182
- DocumentOverviewComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.0.6", ngImport: i0, type: DocumentOverviewComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
4183
- DocumentOverviewComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "14.0.6", type: DocumentOverviewComponent, isStandalone: true, selector: "sq-document-overview", inputs: { instanceId: "instanceId", disabledUpload: "disabledUpload" }, outputs: { onUpload: "onUpload" }, providers: [DocumentsUploadService], usesInheritance: true, ngImport: i0, template: "<div class=\"d-flex\">\n <span class=\"overview-text flex-grow-1\">{{ (uploadedDocuments$ | async)?.length || 0 }} uploaded document(s)</span>\n <button disabled class=\"btn btn-light\" title=\"Upload to Sources\" (click)=\"onUpload.emit()\" [disabled]=\"disabledUpload\">\n <i class=\"fas fa-cloud-upload-alt\"></i>\n </button>\n</div>", styles: [".overview-text{font-style:italic;padding:.5rem .8rem}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "pipe", type: i1.AsyncPipe, name: "async" }, { kind: "ngmodule", type: UtilsModule }, { kind: "ngmodule", type: ModalModule }] });
4184
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.0.6", ngImport: i0, type: DocumentOverviewComponent, decorators: [{
4185
- type: Component,
4186
- args: [{ selector: "sq-document-overview", standalone: true, providers: [DocumentsUploadService], imports: [CommonModule, UtilsModule, ModalModule], template: "<div class=\"d-flex\">\n <span class=\"overview-text flex-grow-1\">{{ (uploadedDocuments$ | async)?.length || 0 }} uploaded document(s)</span>\n <button disabled class=\"btn btn-light\" title=\"Upload to Sources\" (click)=\"onUpload.emit()\" [disabled]=\"disabledUpload\">\n <i class=\"fas fa-cloud-upload-alt\"></i>\n </button>\n</div>", styles: [".overview-text{font-style:italic;padding:.5rem .8rem}\n"] }]
4187
- }], ctorParameters: function () { return []; }, propDecorators: { instanceId: [{
4188
- type: Input
4189
- }], disabledUpload: [{
4190
- type: Input
4191
- }], onUpload: [{
4192
- type: Output
4193
- }] } });
4194
-
4195
- /**
4196
- * Generated bundle index. Do not edit.
4197
- */
4198
-
4199
- export { ChatComponent, ChatPrompt, ChatService, ChatSettingsV3Component, DocumentListComponent, DocumentOverviewComponent, DocumentUploadComponent, DocumentsUploadService, FormatIconComponent, InitialsAvatarComponent, InstanceManagerService, RestChatService, SavedChatsComponent, WebSocketChatService, chatConfigSchema, connectionSettingsSchema, deAssistant, enAssistant, frAssistant };
4200
- //# sourceMappingURL=sinequa-assistant-chat.mjs.map