@memberjunction/server 2.39.0 → 2.41.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/config.d.ts +99 -30
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +29 -13
- package/dist/config.js.map +1 -1
- package/dist/generated/generated.d.ts +8 -2
- package/dist/generated/generated.d.ts.map +1 -1
- package/dist/generated/generated.js +49 -18
- package/dist/generated/generated.js.map +1 -1
- package/dist/resolvers/AskSkipResolver.d.ts +11 -10
- package/dist/resolvers/AskSkipResolver.d.ts.map +1 -1
- package/dist/resolvers/AskSkipResolver.js +167 -87
- package/dist/resolvers/AskSkipResolver.js.map +1 -1
- package/dist/scheduler/LearningCycleScheduler.d.ts.map +1 -1
- package/dist/scheduler/LearningCycleScheduler.js +4 -1
- package/dist/scheduler/LearningCycleScheduler.js.map +1 -1
- package/package.json +22 -22
- package/src/config.ts +34 -17
- package/src/generated/generated.ts +31 -12
- package/src/resolvers/AskSkipResolver.ts +700 -151
- package/src/scheduler/LearningCycleScheduler.ts +7 -2
|
@@ -22,7 +22,7 @@ import { LoadDataContextItemsServer } from '@memberjunction/data-context-server'
|
|
|
22
22
|
import { LearningCycleScheduler } from '../scheduler/LearningCycleScheduler.js';
|
|
23
23
|
LoadDataContextItemsServer();
|
|
24
24
|
import { PUSH_STATUS_UPDATES_TOPIC } from '../generic/PushStatusResolver.js';
|
|
25
|
-
import {
|
|
25
|
+
import { apiKey, baseUrl, configInfo, graphqlPort, mj_core_schema } from '../config.js';
|
|
26
26
|
import { registerEnumType } from 'type-graphql';
|
|
27
27
|
import { MJGlobal, CopyScalarsAndArrays } from '@memberjunction/global';
|
|
28
28
|
import { sendPostRequest } from '../util.js';
|
|
@@ -191,44 +191,60 @@ StopLearningCycleResultType = __decorate([
|
|
|
191
191
|
ObjectType()
|
|
192
192
|
], StopLearningCycleResultType);
|
|
193
193
|
export { StopLearningCycleResultType };
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
194
|
+
function initializeSkipLearningCycleScheduler() {
|
|
195
|
+
try {
|
|
196
|
+
const eventListener = MJGlobal.Instance.GetEventListener(true);
|
|
197
|
+
eventListener.subscribe(event => {
|
|
198
|
+
if (event.eventCode === MJ_SERVER_EVENT_CODE && event.args?.type === 'setupComplete') {
|
|
199
|
+
try {
|
|
200
|
+
const skipConfigInfo = configInfo.askSkip;
|
|
201
|
+
if (!skipConfigInfo) {
|
|
202
|
+
LogStatus('Skip AI Learning Cycle Scheduler not started: Skip configuration not found');
|
|
203
|
+
return;
|
|
204
|
+
}
|
|
205
|
+
if (!skipConfigInfo.learningCycleEnabled) {
|
|
206
|
+
LogStatus('Skip AI Learning Cycles not enabled in configuration');
|
|
207
|
+
return;
|
|
208
|
+
}
|
|
209
|
+
if (!skipConfigInfo.learningCycleURL || skipConfigInfo.learningCycleURL.trim().length === 0) {
|
|
210
|
+
LogError('Skip AI Learning cycle scheduler not started: Learning cycles are enabled but no Learning Cycle API endpoint is configured');
|
|
211
|
+
return;
|
|
212
|
+
}
|
|
213
|
+
const dataSources = event.args.dataSources;
|
|
214
|
+
if (dataSources && dataSources.length > 0) {
|
|
215
|
+
const scheduler = LearningCycleScheduler.Instance;
|
|
216
|
+
scheduler.setDataSources(dataSources);
|
|
217
|
+
const interval = skipConfigInfo.learningCycleIntervalInMinutes ?? 60;
|
|
218
|
+
if (skipConfigInfo.learningCycleRunUponStartup) {
|
|
219
|
+
LogStatus('Skip API Learning Cycle: Run Upon Startup is enabled, running learning cycle immediately');
|
|
216
220
|
scheduler.start(interval);
|
|
217
221
|
}
|
|
218
222
|
else {
|
|
219
|
-
|
|
223
|
+
LogStatus(`Skip API Learning Cycle: Scheduler first run will start after interval of ${interval} minutes. If you want a learing cycle to run immediately, set the learningCycleRunUponStartup property in the config file to true.`);
|
|
224
|
+
setTimeout(() => {
|
|
225
|
+
LogStatus(`Skip API Learning Cycle: Starting scheduler after ${interval} minutes. If you want a learing cycle to run immediately, set the learningCycleRunUponStartup property in the config file to true.`);
|
|
226
|
+
scheduler.start(interval);
|
|
227
|
+
}, interval * 60 * 1000);
|
|
220
228
|
}
|
|
221
229
|
}
|
|
222
|
-
|
|
223
|
-
LogError(
|
|
230
|
+
else {
|
|
231
|
+
LogError('Cannot initialize Skip learning cycle scheduler: No data sources available');
|
|
224
232
|
}
|
|
225
233
|
}
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
}
|
|
234
|
+
catch (error) {
|
|
235
|
+
LogError(`Error initializing Skip learning cycle scheduler: ${error}`);
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
});
|
|
239
|
+
}
|
|
240
|
+
catch (error) {
|
|
241
|
+
LogError(`Failed to initialize Skip learning cycle scheduler: ${error}`);
|
|
231
242
|
}
|
|
243
|
+
}
|
|
244
|
+
initializeSkipLearningCycleScheduler();
|
|
245
|
+
let AskSkipResolver = class AskSkipResolver {
|
|
246
|
+
static { AskSkipResolver_1 = this; }
|
|
247
|
+
static _defaultNewChatName = 'New Chat';
|
|
232
248
|
static _maxHistoricalMessages = 30;
|
|
233
249
|
async ExecuteAskSkipRecordChat(UserQuestion, ConversationId, EntityName, compositeKey, { dataSource, userPayload }, pubSub) {
|
|
234
250
|
const user = UserCache.Instance.Users.find((u) => u.Email.trim().toLowerCase() === userPayload.email.trim().toLowerCase());
|
|
@@ -271,17 +287,18 @@ let AskSkipResolver = class AskSkipResolver {
|
|
|
271
287
|
return this.handleSimpleSkipChatPostRequest(input, convoEntity.ID, convoDetailEntity.ID, true, user);
|
|
272
288
|
}
|
|
273
289
|
async ExecuteAskSkipLearningCycle({ dataSource, userPayload }, ForceEntityRefresh) {
|
|
274
|
-
|
|
290
|
+
const skipConfigInfo = configInfo.askSkip;
|
|
291
|
+
if (!skipConfigInfo.learningCycleEnabled) {
|
|
275
292
|
return {
|
|
276
293
|
success: false,
|
|
277
|
-
error: 'Learning cycles are
|
|
294
|
+
error: 'Learning cycles are not enabled in configuration',
|
|
278
295
|
elapsedTime: 0,
|
|
279
296
|
noteChanges: [],
|
|
280
297
|
queryChanges: [],
|
|
281
298
|
requestChanges: []
|
|
282
299
|
};
|
|
283
300
|
}
|
|
284
|
-
if (!
|
|
301
|
+
if (!skipConfigInfo.learningCycleURL || skipConfigInfo.learningCycleURL.trim().length === 0) {
|
|
285
302
|
return {
|
|
286
303
|
success: false,
|
|
287
304
|
error: 'Learning cycle API endpoint is not configured',
|
|
@@ -296,7 +313,7 @@ let AskSkipResolver = class AskSkipResolver {
|
|
|
296
313
|
if (!user)
|
|
297
314
|
throw new Error(`User ${userPayload.email} not found in UserCache`);
|
|
298
315
|
await AIEngine.Instance.Config(false, user);
|
|
299
|
-
const organizationId =
|
|
316
|
+
const organizationId = skipConfigInfo.orgID;
|
|
300
317
|
const scheduler = LearningCycleScheduler.Instance;
|
|
301
318
|
const runningStatus = scheduler.isOrganizationRunningCycle(organizationId);
|
|
302
319
|
if (runningStatus.isRunning) {
|
|
@@ -331,17 +348,36 @@ let AskSkipResolver = class AskSkipResolver {
|
|
|
331
348
|
try {
|
|
332
349
|
LogStatus(`Building Skip Learning API request`);
|
|
333
350
|
const input = await this.buildSkipLearningAPIRequest(learningCycleId, lastCompleteLearningCycleDate, true, true, true, false, dataSource, user, ForceEntityRefresh || false);
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
351
|
+
if (input.newConversations.length === 0) {
|
|
352
|
+
LogStatus(` Skip Learning Cycles: No new conversations to process for learning cycle`);
|
|
353
|
+
learningCycleEntity.Status = 'Complete';
|
|
354
|
+
learningCycleEntity.AgentSummary = 'No new conversations to process, learning cycle skipped, but recorded for audit purposes.';
|
|
355
|
+
learningCycleEntity.EndedAt = new Date();
|
|
356
|
+
if (!(await learningCycleEntity.Save())) {
|
|
357
|
+
LogError(`Failed to update learning cycle record: ${learningCycleEntity.LatestResult.Error}`);
|
|
358
|
+
}
|
|
359
|
+
const result = {
|
|
360
|
+
success: true,
|
|
361
|
+
learningCycleSkipped: true,
|
|
362
|
+
elapsedTime: 0,
|
|
363
|
+
noteChanges: [],
|
|
364
|
+
queryChanges: [],
|
|
365
|
+
requestChanges: [],
|
|
366
|
+
};
|
|
367
|
+
return result;
|
|
368
|
+
}
|
|
369
|
+
else {
|
|
370
|
+
const response = await this.handleSimpleSkipLearningPostRequest(input, user, learningCycleId, agentID);
|
|
371
|
+
const endTime = new Date();
|
|
372
|
+
const elapsedTimeMs = endTime.getTime() - startTime.getTime();
|
|
373
|
+
LogStatus(`Learning cycle finished with status: ${response.success ? 'Success' : 'Failed'} in ${elapsedTimeMs / 1000} seconds`);
|
|
374
|
+
learningCycleEntity.Status = response.success ? 'Complete' : 'Failed';
|
|
375
|
+
learningCycleEntity.EndedAt = endTime;
|
|
376
|
+
if (!(await learningCycleEntity.Save())) {
|
|
377
|
+
LogError(`Failed to update learning cycle record: ${learningCycleEntity.LatestResult.Error}`);
|
|
378
|
+
}
|
|
379
|
+
return response;
|
|
342
380
|
}
|
|
343
|
-
scheduler.unregisterRunningCycle(organizationId);
|
|
344
|
-
return response;
|
|
345
381
|
}
|
|
346
382
|
catch (error) {
|
|
347
383
|
learningCycleEntity.Status = 'Failed';
|
|
@@ -352,13 +388,21 @@ let AskSkipResolver = class AskSkipResolver {
|
|
|
352
388
|
catch (saveError) {
|
|
353
389
|
LogError(`Failed to update learning cycle record after error: ${saveError}`);
|
|
354
390
|
}
|
|
355
|
-
scheduler.unregisterRunningCycle(organizationId);
|
|
356
391
|
throw error;
|
|
357
392
|
}
|
|
393
|
+
finally {
|
|
394
|
+
try {
|
|
395
|
+
scheduler.unregisterRunningCycle(organizationId);
|
|
396
|
+
}
|
|
397
|
+
catch (error) {
|
|
398
|
+
LogError(`Failed to unregister organization ${organizationId} from running cycles: ${error}`);
|
|
399
|
+
}
|
|
400
|
+
}
|
|
358
401
|
}
|
|
359
402
|
async handleSimpleSkipLearningPostRequest(input, user, learningCycleId, agentID) {
|
|
360
|
-
|
|
361
|
-
|
|
403
|
+
const skipConfigInfo = configInfo.askSkip;
|
|
404
|
+
LogStatus(` >>> HandleSimpleSkipLearningPostRequest Sending request to Skip API: ${skipConfigInfo.learningCycleURL}`);
|
|
405
|
+
const response = await sendPostRequest(skipConfigInfo.learningCycleURL, input, true, null);
|
|
362
406
|
if (response && response.length > 0) {
|
|
363
407
|
const apiResponse = response[response.length - 1].value;
|
|
364
408
|
LogStatus(` Skip API response: ${apiResponse.success}`);
|
|
@@ -379,8 +423,9 @@ let AskSkipResolver = class AskSkipResolver {
|
|
|
379
423
|
}
|
|
380
424
|
}
|
|
381
425
|
async handleSimpleSkipChatPostRequest(input, conversationID = '', UserMessageConversationDetailId = '', createAIMessageConversationDetail = false, user = null) {
|
|
382
|
-
|
|
383
|
-
|
|
426
|
+
const skipConfigInfo = configInfo.askSkip;
|
|
427
|
+
LogStatus(` >>> HandleSimpleSkipChatPostRequest Sending request to Skip API: ${skipConfigInfo.chatURL}`);
|
|
428
|
+
const response = await sendPostRequest(skipConfigInfo.chatURL, input, true, null);
|
|
384
429
|
if (response && response.length > 0) {
|
|
385
430
|
const apiResponse = response[response.length - 1].value;
|
|
386
431
|
const AIMessageConversationDetailID = createAIMessageConversationDetail
|
|
@@ -445,10 +490,6 @@ let AskSkipResolver = class AskSkipResolver {
|
|
|
445
490
|
}
|
|
446
491
|
}
|
|
447
492
|
else {
|
|
448
|
-
if (change.note.agentNoteType === "Human") {
|
|
449
|
-
LogStatus(`WARNING: Cannot create a new Human note with the learning cycle. Operation ignored.`);
|
|
450
|
-
return false;
|
|
451
|
-
}
|
|
452
493
|
noteEntity.NewRecord();
|
|
453
494
|
noteEntity.AgentID = agentID;
|
|
454
495
|
}
|
|
@@ -506,10 +547,11 @@ cycle.`);
|
|
|
506
547
|
return '';
|
|
507
548
|
}
|
|
508
549
|
}
|
|
509
|
-
async buildBaseSkipRequest(contextUser, dataSource, includeEntities, includeQueries, includeNotes, includeRequests, forceEntitiesRefresh = false, includeCallBackKeyAndAccessToken = false, additionalTokenInfo = {}) {
|
|
550
|
+
async buildBaseSkipRequest(contextUser, dataSource, includeEntities, includeQueries, includeNotes, filterUserNotesToContextUser, includeRequests, forceEntitiesRefresh = false, includeCallBackKeyAndAccessToken = false, additionalTokenInfo = {}) {
|
|
551
|
+
const skipConfigInfo = configInfo.askSkip;
|
|
510
552
|
const entities = includeEntities ? await this.BuildSkipEntities(dataSource, forceEntitiesRefresh) : [];
|
|
511
553
|
const queries = includeQueries ? this.BuildSkipQueries() : [];
|
|
512
|
-
const { notes, noteTypes } = includeNotes ? await this.BuildSkipAgentNotes(contextUser) : { notes: [], noteTypes: [] };
|
|
554
|
+
const { notes, noteTypes } = includeNotes ? await this.BuildSkipAgentNotes(contextUser, filterUserNotesToContextUser) : { notes: [], noteTypes: [] };
|
|
513
555
|
const requests = includeRequests ? await this.BuildSkipRequests(contextUser) : [];
|
|
514
556
|
let accessToken;
|
|
515
557
|
if (includeCallBackKeyAndAccessToken) {
|
|
@@ -529,7 +571,7 @@ cycle.`);
|
|
|
529
571
|
noteTypes,
|
|
530
572
|
requests,
|
|
531
573
|
accessToken,
|
|
532
|
-
organizationID:
|
|
574
|
+
organizationID: skipConfigInfo.orgID,
|
|
533
575
|
organizationInfo: configInfo?.askSkip?.organizationInfo,
|
|
534
576
|
apiKeys: this.buildSkipAPIKeys(),
|
|
535
577
|
callingServerURL: accessToken ? `${baseUrl}:${graphqlPort}` : undefined,
|
|
@@ -538,7 +580,7 @@ cycle.`);
|
|
|
538
580
|
};
|
|
539
581
|
}
|
|
540
582
|
async buildSkipLearningAPIRequest(learningCycleId, lastLearningCycleDate, includeEntities, includeQueries, includeNotes, includeRequests, dataSource, contextUser, forceEntitiesRefresh = false, includeCallBackKeyAndAccessToken = false) {
|
|
541
|
-
const baseRequest = await this.buildBaseSkipRequest(contextUser, dataSource, includeEntities, includeQueries, includeNotes, includeRequests, forceEntitiesRefresh, includeCallBackKeyAndAccessToken);
|
|
583
|
+
const baseRequest = await this.buildBaseSkipRequest(contextUser, dataSource, includeEntities, includeQueries, includeNotes, false, includeRequests, forceEntitiesRefresh, includeCallBackKeyAndAccessToken);
|
|
542
584
|
const newConversations = await this.BuildSkipLearningCycleNewConversations(lastLearningCycleDate, dataSource, contextUser);
|
|
543
585
|
const input = {
|
|
544
586
|
organizationId: baseRequest.organizationID,
|
|
@@ -638,7 +680,7 @@ cycle.`);
|
|
|
638
680
|
conversationId,
|
|
639
681
|
requestPhase,
|
|
640
682
|
};
|
|
641
|
-
const baseRequest = await this.buildBaseSkipRequest(contextUser, dataSource, includeEntities, includeQueries, includeNotes, includeRequests, forceEntitiesRefresh, includeCallBackKeyAndAccessToken, additionalTokenInfo);
|
|
683
|
+
const baseRequest = await this.buildBaseSkipRequest(contextUser, dataSource, includeEntities, includeQueries, includeNotes, true, includeRequests, forceEntitiesRefresh, includeCallBackKeyAndAccessToken, additionalTokenInfo);
|
|
642
684
|
const artifacts = await this.buildSkipAPIArtifacts(contextUser, dataSource, conversationId);
|
|
643
685
|
const input = {
|
|
644
686
|
...baseRequest,
|
|
@@ -753,16 +795,18 @@ cycle.`);
|
|
|
753
795
|
},
|
|
754
796
|
];
|
|
755
797
|
}
|
|
756
|
-
async ExecuteAskSkipAnalysisQuery(UserQuestion, ConversationId, { dataSource, userPayload }, pubSub, DataContextId, ForceEntityRefresh) {
|
|
798
|
+
async ExecuteAskSkipAnalysisQuery(UserQuestion, ConversationId, { dataSource, userPayload }, pubSub, DataContextId, ForceEntityRefresh, StartTime) {
|
|
757
799
|
const md = new Metadata();
|
|
758
800
|
const user = UserCache.Instance.Users.find((u) => u.Email.trim().toLowerCase() === userPayload.email.trim().toLowerCase());
|
|
759
801
|
if (!user)
|
|
760
802
|
throw new Error(`User ${userPayload.email} not found in UserCache`);
|
|
803
|
+
const requestStartTime = StartTime || new Date();
|
|
761
804
|
const { convoEntity, dataContextEntity, convoDetailEntity, dataContext } = await this.HandleSkipChatInitialObjectLoading(dataSource, ConversationId, UserQuestion, user, userPayload, md, DataContextId);
|
|
805
|
+
this.setConversationStatus(convoEntity, 'Processing');
|
|
762
806
|
const messages = await this.LoadConversationDetailsIntoSkipMessages(dataSource, convoEntity.ID, AskSkipResolver_1._maxHistoricalMessages);
|
|
763
807
|
const conversationDetailCount = 1;
|
|
764
808
|
const input = await this.buildSkipChatAPIRequest(messages, ConversationId, dataContext, 'initial_request', true, true, true, false, user, dataSource, ForceEntityRefresh === undefined ? false : ForceEntityRefresh, true);
|
|
765
|
-
return this.HandleSkipChatRequest(input, UserQuestion, user, dataSource, ConversationId, userPayload, pubSub, md, convoEntity, convoDetailEntity, dataContext, dataContextEntity, conversationDetailCount);
|
|
809
|
+
return this.HandleSkipChatRequest(input, UserQuestion, user, dataSource, ConversationId, userPayload, pubSub, md, convoEntity, convoDetailEntity, dataContext, dataContextEntity, conversationDetailCount, requestStartTime);
|
|
766
810
|
}
|
|
767
811
|
BuildSkipQueries(status = 'Approved') {
|
|
768
812
|
const md = new Metadata();
|
|
@@ -804,7 +848,7 @@ cycle.`);
|
|
|
804
848
|
};
|
|
805
849
|
});
|
|
806
850
|
}
|
|
807
|
-
async BuildSkipAgentNotes(contextUser) {
|
|
851
|
+
async BuildSkipAgentNotes(contextUser, filterUserNotesToContextUser) {
|
|
808
852
|
try {
|
|
809
853
|
await AIEngine.Instance.Config(false, contextUser);
|
|
810
854
|
const agent = AIEngine.Instance.GetAgentByName('Skip');
|
|
@@ -824,6 +868,10 @@ cycle.`);
|
|
|
824
868
|
updatedAt: r.__mj_UpdatedAt,
|
|
825
869
|
};
|
|
826
870
|
});
|
|
871
|
+
if (filterUserNotesToContextUser) {
|
|
872
|
+
notes = notes.filter((n) => n.type === 'Global' ||
|
|
873
|
+
(n.type === 'User' && n.userId === contextUser.ID));
|
|
874
|
+
}
|
|
827
875
|
noteTypes = AIEngine.Instance.AgentNoteTypes.map((r) => {
|
|
828
876
|
return {
|
|
829
877
|
id: r.ID,
|
|
@@ -951,7 +999,7 @@ cycle.`);
|
|
|
951
999
|
async refreshSkipEntities(dataSource) {
|
|
952
1000
|
try {
|
|
953
1001
|
const md = new Metadata();
|
|
954
|
-
const skipSpecialIncludeEntities = (configInfo.askSkip?.
|
|
1002
|
+
const skipSpecialIncludeEntities = (configInfo.askSkip?.entitiesToSend?.includeEntitiesFromExcludedSchemas ?? [])
|
|
955
1003
|
.map((e) => e.trim().toLowerCase());
|
|
956
1004
|
const entities = md.Entities.filter((e) => {
|
|
957
1005
|
if (e.SchemaName !== mj_core_schema || skipSpecialIncludeEntities.includes(e.Name.trim().toLowerCase())) {
|
|
@@ -1087,6 +1135,7 @@ cycle.`);
|
|
|
1087
1135
|
if (user) {
|
|
1088
1136
|
convoEntity.UserID = user.ID;
|
|
1089
1137
|
convoEntity.Name = AskSkipResolver_1._defaultNewChatName;
|
|
1138
|
+
convoEntity.Status = 'Available';
|
|
1090
1139
|
dataContextEntity = await md.GetEntityObject('Data Contexts', user);
|
|
1091
1140
|
if (!DataContextId || DataContextId.length === 0) {
|
|
1092
1141
|
dataContextEntity.NewRecord();
|
|
@@ -1172,19 +1221,20 @@ cycle.`);
|
|
|
1172
1221
|
await dataContext.Load(dataContextEntity.ID, dataSource, false, false, 0, user);
|
|
1173
1222
|
return { dataContext, convoEntity, dataContextEntity, convoDetailEntity };
|
|
1174
1223
|
}
|
|
1175
|
-
async LoadConversationDetailsIntoSkipMessages(dataSource, ConversationId, maxHistoricalMessages) {
|
|
1224
|
+
async LoadConversationDetailsIntoSkipMessages(dataSource, ConversationId, maxHistoricalMessages, roleFilter) {
|
|
1176
1225
|
try {
|
|
1177
1226
|
if (!ConversationId || ConversationId.length === 0) {
|
|
1178
1227
|
throw new Error(`ConversationId is required`);
|
|
1179
1228
|
}
|
|
1180
1229
|
const md = new Metadata();
|
|
1181
1230
|
const e = md.Entities.find((e) => e.Name === 'Conversation Details');
|
|
1231
|
+
const roleFilterClause = roleFilter ? ` AND Role = '${roleFilter}'` : '';
|
|
1182
1232
|
const sql = `SELECT
|
|
1183
1233
|
${maxHistoricalMessages ? 'TOP ' + maxHistoricalMessages : ''} *
|
|
1184
1234
|
FROM
|
|
1185
1235
|
${e.SchemaName}.${e.BaseView}
|
|
1186
1236
|
WHERE
|
|
1187
|
-
ConversationID = '${ConversationId}'
|
|
1237
|
+
ConversationID = '${ConversationId}'${roleFilterClause}
|
|
1188
1238
|
ORDER
|
|
1189
1239
|
BY __mj_CreatedAt DESC`;
|
|
1190
1240
|
const result = await dataSource.query(sql);
|
|
@@ -1260,9 +1310,11 @@ cycle.`);
|
|
|
1260
1310
|
return 'user';
|
|
1261
1311
|
}
|
|
1262
1312
|
}
|
|
1263
|
-
async HandleSkipChatRequest(input, UserQuestion, user, dataSource, ConversationId, userPayload, pubSub, md, convoEntity, convoDetailEntity, dataContext, dataContextEntity, conversationDetailCount) {
|
|
1264
|
-
|
|
1313
|
+
async HandleSkipChatRequest(input, UserQuestion, user, dataSource, ConversationId, userPayload, pubSub, md, convoEntity, convoDetailEntity, dataContext, dataContextEntity, conversationDetailCount, startTime) {
|
|
1314
|
+
const skipConfigInfo = configInfo.askSkip;
|
|
1315
|
+
LogStatus(` >>> HandleSkipRequest: Sending request to Skip API: ${skipConfigInfo.chatURL}`);
|
|
1265
1316
|
if (conversationDetailCount > 10) {
|
|
1317
|
+
await this.setConversationStatus(convoEntity, 'Available');
|
|
1266
1318
|
pubSub.publish(PUSH_STATUS_UPDATES_TOPIC, {
|
|
1267
1319
|
message: JSON.stringify({
|
|
1268
1320
|
type: 'AskSkip',
|
|
@@ -1282,7 +1334,7 @@ cycle.`);
|
|
|
1282
1334
|
AIMessageConversationDetailId: '',
|
|
1283
1335
|
};
|
|
1284
1336
|
}
|
|
1285
|
-
const response = await sendPostRequest(
|
|
1337
|
+
const response = await sendPostRequest(skipConfigInfo.chatURL, input, true, null, (message) => {
|
|
1286
1338
|
LogStatus(JSON.stringify(message, null, 4));
|
|
1287
1339
|
if (message.type === 'status_update') {
|
|
1288
1340
|
pubSub.publish(PUSH_STATUS_UPDATES_TOPIC, {
|
|
@@ -1302,19 +1354,20 @@ cycle.`);
|
|
|
1302
1354
|
LogStatus(` Skip API response: ${apiResponse.responsePhase}`);
|
|
1303
1355
|
this.PublishApiResponseUserUpdateMessage(apiResponse, userPayload, ConversationId, pubSub);
|
|
1304
1356
|
if (apiResponse.responsePhase === 'data_request') {
|
|
1305
|
-
return await this.HandleDataRequestPhase(input, apiResponse, UserQuestion, user, dataSource, ConversationId, userPayload, pubSub, convoEntity, convoDetailEntity, dataContext, dataContextEntity, conversationDetailCount);
|
|
1357
|
+
return await this.HandleDataRequestPhase(input, apiResponse, UserQuestion, user, dataSource, ConversationId, userPayload, pubSub, convoEntity, convoDetailEntity, dataContext, dataContextEntity, conversationDetailCount, startTime);
|
|
1306
1358
|
}
|
|
1307
1359
|
else if (apiResponse.responsePhase === 'clarifying_question') {
|
|
1308
|
-
return await this.HandleClarifyingQuestionPhase(input, apiResponse, UserQuestion, user, dataSource, ConversationId, userPayload, pubSub, convoEntity, convoDetailEntity);
|
|
1360
|
+
return await this.HandleClarifyingQuestionPhase(input, apiResponse, UserQuestion, user, dataSource, ConversationId, userPayload, pubSub, convoEntity, convoDetailEntity, startTime);
|
|
1309
1361
|
}
|
|
1310
1362
|
else if (apiResponse.responsePhase === 'analysis_complete') {
|
|
1311
|
-
return await this.HandleAnalysisComplete(input, apiResponse, UserQuestion, user, dataSource, ConversationId, userPayload, pubSub, convoEntity, convoDetailEntity, dataContext, dataContextEntity);
|
|
1363
|
+
return await this.HandleAnalysisComplete(input, apiResponse, UserQuestion, user, dataSource, ConversationId, userPayload, pubSub, convoEntity, convoDetailEntity, dataContext, dataContextEntity, startTime);
|
|
1312
1364
|
}
|
|
1313
1365
|
else {
|
|
1314
1366
|
throw new Error(`Unknown Skip API response phase: ${apiResponse.responsePhase}`);
|
|
1315
1367
|
}
|
|
1316
1368
|
}
|
|
1317
1369
|
else {
|
|
1370
|
+
await this.setConversationStatus(convoEntity, 'Available');
|
|
1318
1371
|
pubSub.publish(PUSH_STATUS_UPDATES_TOPIC, {
|
|
1319
1372
|
message: JSON.stringify({
|
|
1320
1373
|
type: 'AskSkip',
|
|
@@ -1357,12 +1410,12 @@ cycle.`);
|
|
|
1357
1410
|
sessionId: userPayload.sessionId,
|
|
1358
1411
|
});
|
|
1359
1412
|
}
|
|
1360
|
-
async HandleAnalysisComplete(apiRequest, apiResponse, UserQuestion, user, dataSource, ConversationId, userPayload, pubSub, convoEntity, convoDetailEntity, dataContext, dataContextEntity) {
|
|
1413
|
+
async HandleAnalysisComplete(apiRequest, apiResponse, UserQuestion, user, dataSource, ConversationId, userPayload, pubSub, convoEntity, convoDetailEntity, dataContext, dataContextEntity, startTime) {
|
|
1361
1414
|
const md = new Metadata();
|
|
1362
1415
|
if (apiRequest.callingServerAccessToken && tokenExists(apiRequest.callingServerAccessToken)) {
|
|
1363
1416
|
deleteAccessToken(apiRequest.callingServerAccessToken);
|
|
1364
1417
|
}
|
|
1365
|
-
const { AIMessageConversationDetailID } = await this.FinishConversationAndNotifyUser(apiResponse, dataContext, dataContextEntity, md, user, convoEntity, pubSub, userPayload, dataSource);
|
|
1418
|
+
const { AIMessageConversationDetailID } = await this.FinishConversationAndNotifyUser(apiResponse, dataContext, dataContextEntity, md, user, convoEntity, pubSub, userPayload, dataSource, startTime);
|
|
1366
1419
|
const response = {
|
|
1367
1420
|
Success: true,
|
|
1368
1421
|
Status: 'OK',
|
|
@@ -1374,7 +1427,8 @@ cycle.`);
|
|
|
1374
1427
|
};
|
|
1375
1428
|
return response;
|
|
1376
1429
|
}
|
|
1377
|
-
async HandleClarifyingQuestionPhase(apiRequest, apiResponse, UserQuestion, user, dataSource, ConversationId, userPayload, pubSub, convoEntity, convoDetailEntity) {
|
|
1430
|
+
async HandleClarifyingQuestionPhase(apiRequest, apiResponse, UserQuestion, user, dataSource, ConversationId, userPayload, pubSub, convoEntity, convoDetailEntity, startTime) {
|
|
1431
|
+
const endTime = new Date();
|
|
1378
1432
|
const md = new Metadata();
|
|
1379
1433
|
const convoDetailEntityAI = await md.GetEntityObject('Conversation Details', user);
|
|
1380
1434
|
convoDetailEntityAI.NewRecord();
|
|
@@ -1382,6 +1436,8 @@ cycle.`);
|
|
|
1382
1436
|
convoDetailEntityAI.Message = JSON.stringify(apiResponse);
|
|
1383
1437
|
convoDetailEntityAI.Role = 'AI';
|
|
1384
1438
|
convoDetailEntityAI.HiddenToUser = false;
|
|
1439
|
+
convoDetailEntityAI.CompletionTime = endTime.getTime() - startTime.getTime();
|
|
1440
|
+
this.setConversationStatus(convoEntity, 'Available');
|
|
1385
1441
|
if (await convoDetailEntityAI.Save()) {
|
|
1386
1442
|
return {
|
|
1387
1443
|
Success: true,
|
|
@@ -1406,7 +1462,7 @@ cycle.`);
|
|
|
1406
1462
|
};
|
|
1407
1463
|
}
|
|
1408
1464
|
}
|
|
1409
|
-
async HandleDataRequestPhase(apiRequest, apiResponse, UserQuestion, user, dataSource, ConversationId, userPayload, pubSub, convoEntity, convoDetailEntity, dataContext, dataContextEntity, conversationDetailCount) {
|
|
1465
|
+
async HandleDataRequestPhase(apiRequest, apiResponse, UserQuestion, user, dataSource, ConversationId, userPayload, pubSub, convoEntity, convoDetailEntity, dataContext, dataContextEntity, conversationDetailCount, startTime) {
|
|
1410
1466
|
try {
|
|
1411
1467
|
if (!apiResponse.success) {
|
|
1412
1468
|
LogError(`Data request/gathering from Skip API failed: ${apiResponse.error}`);
|
|
@@ -1512,14 +1568,14 @@ cycle.`);
|
|
|
1512
1568
|
apiRequest.requestPhase = 'data_gathering_response';
|
|
1513
1569
|
}
|
|
1514
1570
|
conversationDetailCount++;
|
|
1515
|
-
return this.HandleSkipChatRequest(apiRequest, UserQuestion, user, dataSource, ConversationId, userPayload, pubSub, md, convoEntity, convoDetailEntity, dataContext, dataContextEntity, conversationDetailCount);
|
|
1571
|
+
return this.HandleSkipChatRequest(apiRequest, UserQuestion, user, dataSource, ConversationId, userPayload, pubSub, md, convoEntity, convoDetailEntity, dataContext, dataContextEntity, conversationDetailCount, startTime);
|
|
1516
1572
|
}
|
|
1517
1573
|
catch (e) {
|
|
1518
1574
|
LogError(e);
|
|
1519
1575
|
throw e;
|
|
1520
1576
|
}
|
|
1521
1577
|
}
|
|
1522
|
-
async FinishConversationAndNotifyUser(apiResponse, dataContext, dataContextEntity, md, user, convoEntity, pubSub, userPayload, dataSource) {
|
|
1578
|
+
async FinishConversationAndNotifyUser(apiResponse, dataContext, dataContextEntity, md, user, convoEntity, pubSub, userPayload, dataSource, startTime) {
|
|
1523
1579
|
const sTitle = apiResponse.reportTitle;
|
|
1524
1580
|
const sResult = JSON.stringify(apiResponse);
|
|
1525
1581
|
let artifactId = null;
|
|
@@ -1574,12 +1630,14 @@ cycle.`);
|
|
|
1574
1630
|
}
|
|
1575
1631
|
}
|
|
1576
1632
|
}
|
|
1633
|
+
const endTime = new Date();
|
|
1577
1634
|
const convoDetailEntityAI = await md.GetEntityObject('Conversation Details', user);
|
|
1578
1635
|
convoDetailEntityAI.NewRecord();
|
|
1579
1636
|
convoDetailEntityAI.ConversationID = convoEntity.ID;
|
|
1580
1637
|
convoDetailEntityAI.Message = sResult;
|
|
1581
1638
|
convoDetailEntityAI.Role = 'AI';
|
|
1582
1639
|
convoDetailEntityAI.HiddenToUser = false;
|
|
1640
|
+
convoDetailEntityAI.CompletionTime = endTime.getTime() - startTime.getTime();
|
|
1583
1641
|
if (artifactId && artifactId.length > 0) {
|
|
1584
1642
|
convoDetailEntityAI.ArtifactID = artifactId;
|
|
1585
1643
|
if (artifactVersionId && artifactVersionId.length > 0) {
|
|
@@ -1590,8 +1648,16 @@ cycle.`);
|
|
|
1590
1648
|
if (!convoDetailSaveResult) {
|
|
1591
1649
|
LogError(`Error saving conversation detail entity for AI message: ${sResult}`, undefined, convoDetailEntityAI.LatestResult);
|
|
1592
1650
|
}
|
|
1651
|
+
let needToSaveConvo = false;
|
|
1593
1652
|
if (convoEntity.Name === AskSkipResolver_1._defaultNewChatName && sTitle && sTitle !== AskSkipResolver_1._defaultNewChatName) {
|
|
1594
1653
|
convoEntity.Name = sTitle;
|
|
1654
|
+
needToSaveConvo = true;
|
|
1655
|
+
}
|
|
1656
|
+
if (convoEntity.Status === 'Processing') {
|
|
1657
|
+
convoEntity.Status = 'Available';
|
|
1658
|
+
needToSaveConvo = true;
|
|
1659
|
+
}
|
|
1660
|
+
if (needToSaveConvo) {
|
|
1595
1661
|
const convoEntitySaveResult = await convoEntity.Save();
|
|
1596
1662
|
if (!convoEntitySaveResult) {
|
|
1597
1663
|
LogError(`Error saving conversation entity for AI message: ${sResult}`, undefined, convoEntity.LatestResult);
|
|
@@ -1636,14 +1702,25 @@ cycle.`);
|
|
|
1636
1702
|
AIMessageConversationDetailID: convoDetailEntityAI.ID,
|
|
1637
1703
|
};
|
|
1638
1704
|
}
|
|
1639
|
-
|
|
1705
|
+
async setConversationStatus(convoEntity, status) {
|
|
1706
|
+
if (convoEntity.Status !== status) {
|
|
1707
|
+
convoEntity.Status = status;
|
|
1708
|
+
const convoSaveResult = await convoEntity.Save();
|
|
1709
|
+
if (!convoSaveResult) {
|
|
1710
|
+
LogError(`Error updating conversation status to '${status}'`, undefined, convoEntity.LatestResult);
|
|
1711
|
+
}
|
|
1712
|
+
return convoSaveResult;
|
|
1713
|
+
}
|
|
1714
|
+
return true;
|
|
1715
|
+
}
|
|
1716
|
+
getAgentNoteTypeIDByName(name, defaultNoteType = 'AI') {
|
|
1640
1717
|
const noteTypeID = AIEngine.Instance.AgentNoteTypes.find(nt => nt.Name.trim().toLowerCase() === name.trim().toLowerCase())?.ID;
|
|
1641
1718
|
if (noteTypeID) {
|
|
1642
1719
|
return noteTypeID;
|
|
1643
1720
|
}
|
|
1644
1721
|
else {
|
|
1645
|
-
const
|
|
1646
|
-
return
|
|
1722
|
+
const defaultNoteTypeID = AIEngine.Instance.AgentNoteTypes.find(nt => nt.Name.trim().toLowerCase() === defaultNoteType.trim().toLowerCase())?.ID;
|
|
1723
|
+
return defaultNoteTypeID;
|
|
1647
1724
|
}
|
|
1648
1725
|
}
|
|
1649
1726
|
async getViewData(ViewId, user) {
|
|
@@ -1657,19 +1734,20 @@ cycle.`);
|
|
|
1657
1734
|
async ManuallyExecuteSkipLearningCycle(OrganizationId) {
|
|
1658
1735
|
try {
|
|
1659
1736
|
LogStatus('Manual execution of Skip learning cycle requested via API');
|
|
1660
|
-
|
|
1737
|
+
const skipConfigInfo = configInfo.askSkip;
|
|
1738
|
+
if (!skipConfigInfo.learningCycleEnabled) {
|
|
1661
1739
|
return {
|
|
1662
1740
|
Success: false,
|
|
1663
|
-
Message: 'Learning cycles are
|
|
1741
|
+
Message: 'Learning cycles are not enabled in configuration'
|
|
1664
1742
|
};
|
|
1665
1743
|
}
|
|
1666
|
-
if (!
|
|
1744
|
+
if (!skipConfigInfo.learningCycleURL || skipConfigInfo.learningCycleURL.trim().length === 0) {
|
|
1667
1745
|
return {
|
|
1668
1746
|
Success: false,
|
|
1669
1747
|
Message: 'Learning cycle API endpoint is not configured'
|
|
1670
1748
|
};
|
|
1671
1749
|
}
|
|
1672
|
-
const orgId = OrganizationId ||
|
|
1750
|
+
const orgId = OrganizationId || skipConfigInfo.orgID;
|
|
1673
1751
|
const result = await LearningCycleScheduler.Instance.manuallyExecuteLearningCycle(orgId);
|
|
1674
1752
|
return {
|
|
1675
1753
|
Success: result,
|
|
@@ -1711,7 +1789,8 @@ cycle.`);
|
|
|
1711
1789
|
}
|
|
1712
1790
|
async IsOrganizationRunningLearningCycle(OrganizationId) {
|
|
1713
1791
|
try {
|
|
1714
|
-
const
|
|
1792
|
+
const skipConfigInfo = configInfo.askSkip;
|
|
1793
|
+
const orgId = OrganizationId || skipConfigInfo.orgID;
|
|
1715
1794
|
const status = LearningCycleScheduler.Instance.isOrganizationRunningCycle(orgId);
|
|
1716
1795
|
if (!status.isRunning) {
|
|
1717
1796
|
return null;
|
|
@@ -1730,7 +1809,7 @@ cycle.`);
|
|
|
1730
1809
|
}
|
|
1731
1810
|
async StopLearningCycleForOrganization(OrganizationId) {
|
|
1732
1811
|
try {
|
|
1733
|
-
const orgId = OrganizationId ||
|
|
1812
|
+
const orgId = OrganizationId || configInfo.askSkip.orgID;
|
|
1734
1813
|
const result = LearningCycleScheduler.Instance.stopLearningCycleForOrganization(orgId);
|
|
1735
1814
|
return {
|
|
1736
1815
|
Success: result.success,
|
|
@@ -1792,8 +1871,9 @@ __decorate([
|
|
|
1792
1871
|
__param(3, PubSub()),
|
|
1793
1872
|
__param(4, Arg('DataContextId', () => String, { nullable: true })),
|
|
1794
1873
|
__param(5, Arg('ForceEntityRefresh', () => Boolean, { nullable: true })),
|
|
1874
|
+
__param(6, Arg('StartTime', () => Date, { nullable: true })),
|
|
1795
1875
|
__metadata("design:type", Function),
|
|
1796
|
-
__metadata("design:paramtypes", [String, String, Object, PubSubEngine, String, Boolean]),
|
|
1876
|
+
__metadata("design:paramtypes", [String, String, Object, PubSubEngine, String, Boolean, Date]),
|
|
1797
1877
|
__metadata("design:returntype", Promise)
|
|
1798
1878
|
], AskSkipResolver.prototype, "ExecuteAskSkipAnalysisQuery", null);
|
|
1799
1879
|
__decorate([
|