@memberjunction/server 2.40.0 → 2.42.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/generated/generated.d.ts +796 -27
- package/dist/generated/generated.d.ts.map +1 -1
- package/dist/generated/generated.js +5283 -481
- package/dist/generated/generated.js.map +1 -1
- package/dist/resolvers/AskSkipResolver.d.ts +8 -7
- package/dist/resolvers/AskSkipResolver.d.ts.map +1 -1
- package/dist/resolvers/AskSkipResolver.js +46 -15
- package/dist/resolvers/AskSkipResolver.js.map +1 -1
- package/package.json +22 -22
- package/src/generated/generated.ts +3155 -155
- package/src/resolvers/AskSkipResolver.ts +80 -15
|
@@ -63,7 +63,6 @@ import { CompositeKeyInputType } from '../generic/KeyInputOutputTypes.js';
|
|
|
63
63
|
import { AIAgentEntityExtended, AIEngine } from '@memberjunction/aiengine';
|
|
64
64
|
import { deleteAccessToken, GetDataAccessToken, registerAccessToken, tokenExists } from './GetDataResolver.js';
|
|
65
65
|
import e from 'express';
|
|
66
|
-
import { Skip } from '@graphql-tools/utils';
|
|
67
66
|
|
|
68
67
|
/**
|
|
69
68
|
* Enumeration representing the different phases of a Skip response
|
|
@@ -1323,12 +1322,16 @@ cycle.`);
|
|
|
1323
1322
|
@Ctx() { dataSource, userPayload }: AppContext,
|
|
1324
1323
|
@PubSub() pubSub: PubSubEngine,
|
|
1325
1324
|
@Arg('DataContextId', () => String, { nullable: true }) DataContextId?: string,
|
|
1326
|
-
@Arg('ForceEntityRefresh', () => Boolean, { nullable: true }) ForceEntityRefresh?: boolean
|
|
1325
|
+
@Arg('ForceEntityRefresh', () => Boolean, { nullable: true }) ForceEntityRefresh?: boolean,
|
|
1326
|
+
@Arg('StartTime', () => Date, { nullable: true }) StartTime?: Date
|
|
1327
1327
|
) {
|
|
1328
1328
|
const md = new Metadata();
|
|
1329
1329
|
const user = UserCache.Instance.Users.find((u) => u.Email.trim().toLowerCase() === userPayload.email.trim().toLowerCase());
|
|
1330
1330
|
if (!user) throw new Error(`User ${userPayload.email} not found in UserCache`);
|
|
1331
1331
|
|
|
1332
|
+
// Record the start time if not provided
|
|
1333
|
+
const requestStartTime = StartTime || new Date();
|
|
1334
|
+
|
|
1332
1335
|
const { convoEntity, dataContextEntity, convoDetailEntity, dataContext } = await this.HandleSkipChatInitialObjectLoading(
|
|
1333
1336
|
dataSource,
|
|
1334
1337
|
ConversationId,
|
|
@@ -1339,6 +1342,9 @@ cycle.`);
|
|
|
1339
1342
|
DataContextId
|
|
1340
1343
|
);
|
|
1341
1344
|
|
|
1345
|
+
// Set the conversation status to 'Processing' when a request is initiated
|
|
1346
|
+
this.setConversationStatus(convoEntity, 'Processing');
|
|
1347
|
+
|
|
1342
1348
|
// now load up the messages. We will load up ALL of the messages for this conversation, and then pass them to the Skip API
|
|
1343
1349
|
const messages: SkipMessage[] = await this.LoadConversationDetailsIntoSkipMessages(
|
|
1344
1350
|
dataSource,
|
|
@@ -1363,6 +1369,7 @@ cycle.`);
|
|
|
1363
1369
|
dataContext,
|
|
1364
1370
|
dataContextEntity,
|
|
1365
1371
|
conversationDetailCount,
|
|
1372
|
+
requestStartTime
|
|
1366
1373
|
);
|
|
1367
1374
|
}
|
|
1368
1375
|
|
|
@@ -1843,6 +1850,8 @@ cycle.`);
|
|
|
1843
1850
|
if (user) {
|
|
1844
1851
|
convoEntity.UserID = user.ID;
|
|
1845
1852
|
convoEntity.Name = AskSkipResolver._defaultNewChatName;
|
|
1853
|
+
// Set initial status to Available since no processing has started yet
|
|
1854
|
+
convoEntity.Status = 'Available';
|
|
1846
1855
|
|
|
1847
1856
|
dataContextEntity = await md.GetEntityObject<DataContextEntity>('Data Contexts', user);
|
|
1848
1857
|
if (!DataContextId || DataContextId.length === 0) {
|
|
@@ -1957,7 +1966,8 @@ cycle.`);
|
|
|
1957
1966
|
protected async LoadConversationDetailsIntoSkipMessages(
|
|
1958
1967
|
dataSource: DataSource,
|
|
1959
1968
|
ConversationId: string,
|
|
1960
|
-
maxHistoricalMessages?: number
|
|
1969
|
+
maxHistoricalMessages?: number,
|
|
1970
|
+
roleFilter?: string
|
|
1961
1971
|
): Promise<SkipMessage[]> {
|
|
1962
1972
|
try {
|
|
1963
1973
|
if (!ConversationId || ConversationId.length === 0) {
|
|
@@ -1967,12 +1977,16 @@ cycle.`);
|
|
|
1967
1977
|
// load up all the conversation details from the database server
|
|
1968
1978
|
const md = new Metadata();
|
|
1969
1979
|
const e = md.Entities.find((e) => e.Name === 'Conversation Details');
|
|
1980
|
+
|
|
1981
|
+
// Add role filter if specified
|
|
1982
|
+
const roleFilterClause = roleFilter ? ` AND Role = '${roleFilter}'` : '';
|
|
1983
|
+
|
|
1970
1984
|
const sql = `SELECT
|
|
1971
1985
|
${maxHistoricalMessages ? 'TOP ' + maxHistoricalMessages : ''} *
|
|
1972
1986
|
FROM
|
|
1973
1987
|
${e.SchemaName}.${e.BaseView}
|
|
1974
1988
|
WHERE
|
|
1975
|
-
ConversationID = '${ConversationId}'
|
|
1989
|
+
ConversationID = '${ConversationId}'${roleFilterClause}
|
|
1976
1990
|
ORDER
|
|
1977
1991
|
BY __mj_CreatedAt DESC`;
|
|
1978
1992
|
const result = await dataSource.query(sql);
|
|
@@ -2095,12 +2109,16 @@ cycle.`);
|
|
|
2095
2109
|
convoDetailEntity: ConversationDetailEntity,
|
|
2096
2110
|
dataContext: DataContext,
|
|
2097
2111
|
dataContextEntity: DataContextEntity,
|
|
2098
|
-
conversationDetailCount: number
|
|
2112
|
+
conversationDetailCount: number,
|
|
2113
|
+
startTime: Date
|
|
2099
2114
|
): Promise<AskSkipResultType> {
|
|
2100
2115
|
const skipConfigInfo = configInfo.askSkip;
|
|
2101
2116
|
LogStatus(` >>> HandleSkipRequest: Sending request to Skip API: ${skipConfigInfo.chatURL}`);
|
|
2102
2117
|
|
|
2103
2118
|
if (conversationDetailCount > 10) {
|
|
2119
|
+
// Set status of conversation to Available since we still want to allow the user to ask questions
|
|
2120
|
+
await this.setConversationStatus(convoEntity, 'Available');
|
|
2121
|
+
|
|
2104
2122
|
// At this point it is likely that we are stuck in a loop, so we stop here
|
|
2105
2123
|
pubSub.publish(PUSH_STATUS_UPDATES_TOPIC, {
|
|
2106
2124
|
message: JSON.stringify({
|
|
@@ -2179,7 +2197,8 @@ cycle.`);
|
|
|
2179
2197
|
convoDetailEntity,
|
|
2180
2198
|
dataContext,
|
|
2181
2199
|
dataContextEntity,
|
|
2182
|
-
conversationDetailCount
|
|
2200
|
+
conversationDetailCount,
|
|
2201
|
+
startTime
|
|
2183
2202
|
);
|
|
2184
2203
|
} else if (apiResponse.responsePhase === 'clarifying_question') {
|
|
2185
2204
|
// need to send the request back to the user for a clarifying question
|
|
@@ -2193,7 +2212,8 @@ cycle.`);
|
|
|
2193
2212
|
userPayload,
|
|
2194
2213
|
pubSub,
|
|
2195
2214
|
convoEntity,
|
|
2196
|
-
convoDetailEntity
|
|
2215
|
+
convoDetailEntity,
|
|
2216
|
+
startTime,
|
|
2197
2217
|
);
|
|
2198
2218
|
} else if (apiResponse.responsePhase === 'analysis_complete') {
|
|
2199
2219
|
return await this.HandleAnalysisComplete(
|
|
@@ -2208,13 +2228,17 @@ cycle.`);
|
|
|
2208
2228
|
convoEntity,
|
|
2209
2229
|
convoDetailEntity,
|
|
2210
2230
|
dataContext,
|
|
2211
|
-
dataContextEntity
|
|
2231
|
+
dataContextEntity,
|
|
2232
|
+
startTime
|
|
2212
2233
|
);
|
|
2213
2234
|
} else {
|
|
2214
2235
|
// unknown response phase
|
|
2215
2236
|
throw new Error(`Unknown Skip API response phase: ${apiResponse.responsePhase}`);
|
|
2216
2237
|
}
|
|
2217
2238
|
} else {
|
|
2239
|
+
// Set status of conversation to Available since we still want to allow the user to ask questions
|
|
2240
|
+
await this.setConversationStatus(convoEntity, 'Available');
|
|
2241
|
+
|
|
2218
2242
|
pubSub.publish(PUSH_STATUS_UPDATES_TOPIC, {
|
|
2219
2243
|
message: JSON.stringify({
|
|
2220
2244
|
type: 'AskSkip',
|
|
@@ -2308,7 +2332,8 @@ cycle.`);
|
|
|
2308
2332
|
convoEntity: ConversationEntity,
|
|
2309
2333
|
convoDetailEntity: ConversationDetailEntity,
|
|
2310
2334
|
dataContext: DataContext,
|
|
2311
|
-
dataContextEntity: DataContextEntity
|
|
2335
|
+
dataContextEntity: DataContextEntity,
|
|
2336
|
+
startTime: Date
|
|
2312
2337
|
): Promise<AskSkipResultType> {
|
|
2313
2338
|
// analysis is complete
|
|
2314
2339
|
// all done, wrap things up
|
|
@@ -2328,7 +2353,8 @@ cycle.`);
|
|
|
2328
2353
|
convoEntity,
|
|
2329
2354
|
pubSub,
|
|
2330
2355
|
userPayload,
|
|
2331
|
-
dataSource
|
|
2356
|
+
dataSource,
|
|
2357
|
+
startTime
|
|
2332
2358
|
);
|
|
2333
2359
|
const response: AskSkipResultType = {
|
|
2334
2360
|
Success: true,
|
|
@@ -2368,9 +2394,11 @@ cycle.`);
|
|
|
2368
2394
|
userPayload: UserPayload,
|
|
2369
2395
|
pubSub: PubSubEngine,
|
|
2370
2396
|
convoEntity: ConversationEntity,
|
|
2371
|
-
convoDetailEntity: ConversationDetailEntity
|
|
2397
|
+
convoDetailEntity: ConversationDetailEntity,
|
|
2398
|
+
startTime: Date
|
|
2372
2399
|
): Promise<AskSkipResultType> {
|
|
2373
2400
|
// need to create a message here in the COnversation and then pass that id below
|
|
2401
|
+
const endTime = new Date();
|
|
2374
2402
|
const md = new Metadata();
|
|
2375
2403
|
const convoDetailEntityAI = <ConversationDetailEntity>await md.GetEntityObject('Conversation Details', user);
|
|
2376
2404
|
convoDetailEntityAI.NewRecord();
|
|
@@ -2378,6 +2406,11 @@ cycle.`);
|
|
|
2378
2406
|
convoDetailEntityAI.Message = JSON.stringify(apiResponse); //.clarifyingQuestion;
|
|
2379
2407
|
convoDetailEntityAI.Role = 'AI';
|
|
2380
2408
|
convoDetailEntityAI.HiddenToUser = false;
|
|
2409
|
+
convoDetailEntityAI.CompletionTime = endTime.getTime() - startTime.getTime();
|
|
2410
|
+
|
|
2411
|
+
// Set conversation status back to Available since we need user input for the clarifying question
|
|
2412
|
+
this.setConversationStatus(convoEntity, 'Available');
|
|
2413
|
+
|
|
2381
2414
|
if (await convoDetailEntityAI.Save()) {
|
|
2382
2415
|
return {
|
|
2383
2416
|
Success: true,
|
|
@@ -2438,7 +2471,8 @@ cycle.`);
|
|
|
2438
2471
|
convoDetailEntity: ConversationDetailEntity,
|
|
2439
2472
|
dataContext: DataContext,
|
|
2440
2473
|
dataContextEntity: DataContextEntity,
|
|
2441
|
-
conversationDetailCount: number
|
|
2474
|
+
conversationDetailCount: number,
|
|
2475
|
+
startTime: Date
|
|
2442
2476
|
): Promise<AskSkipResultType> {
|
|
2443
2477
|
// our job in this method is to go through each of the data requests from the Skip API, get the data, and then go back to the Skip API again and to the next phase
|
|
2444
2478
|
try {
|
|
@@ -2567,7 +2601,8 @@ cycle.`);
|
|
|
2567
2601
|
convoDetailEntity,
|
|
2568
2602
|
dataContext,
|
|
2569
2603
|
dataContextEntity,
|
|
2570
|
-
conversationDetailCount
|
|
2604
|
+
conversationDetailCount,
|
|
2605
|
+
startTime
|
|
2571
2606
|
);
|
|
2572
2607
|
} catch (e) {
|
|
2573
2608
|
LogError(e);
|
|
@@ -2599,7 +2634,8 @@ cycle.`);
|
|
|
2599
2634
|
convoEntity: ConversationEntity,
|
|
2600
2635
|
pubSub: PubSubEngine,
|
|
2601
2636
|
userPayload: UserPayload,
|
|
2602
|
-
dataSource: DataSource
|
|
2637
|
+
dataSource: DataSource,
|
|
2638
|
+
startTime: Date
|
|
2603
2639
|
): Promise<{ AIMessageConversationDetailID: string }> {
|
|
2604
2640
|
const sTitle = apiResponse.reportTitle;
|
|
2605
2641
|
const sResult = JSON.stringify(apiResponse);
|
|
@@ -2670,12 +2706,15 @@ cycle.`);
|
|
|
2670
2706
|
}
|
|
2671
2707
|
|
|
2672
2708
|
// Create a conversation detail record for the Skip response
|
|
2709
|
+
const endTime = new Date();
|
|
2673
2710
|
const convoDetailEntityAI = <ConversationDetailEntity>await md.GetEntityObject('Conversation Details', user);
|
|
2674
2711
|
convoDetailEntityAI.NewRecord();
|
|
2675
2712
|
convoDetailEntityAI.ConversationID = convoEntity.ID;
|
|
2676
2713
|
convoDetailEntityAI.Message = sResult;
|
|
2677
2714
|
convoDetailEntityAI.Role = 'AI';
|
|
2678
2715
|
convoDetailEntityAI.HiddenToUser = false;
|
|
2716
|
+
convoDetailEntityAI.CompletionTime = endTime.getTime() - startTime.getTime();
|
|
2717
|
+
|
|
2679
2718
|
if (artifactId && artifactId.length > 0) {
|
|
2680
2719
|
// bind the new convo detail record to the artifact + version for this response
|
|
2681
2720
|
convoDetailEntityAI.ArtifactID = artifactId;
|
|
@@ -2688,9 +2727,23 @@ cycle.`);
|
|
|
2688
2727
|
LogError(`Error saving conversation detail entity for AI message: ${sResult}`, undefined, convoDetailEntityAI.LatestResult);
|
|
2689
2728
|
}
|
|
2690
2729
|
|
|
2691
|
-
//
|
|
2730
|
+
// Update the conversation properties: name if it's the default, and set status back to 'Available'
|
|
2731
|
+
let needToSaveConvo = false;
|
|
2732
|
+
|
|
2733
|
+
// Update name if still default
|
|
2692
2734
|
if (convoEntity.Name === AskSkipResolver._defaultNewChatName && sTitle && sTitle !== AskSkipResolver._defaultNewChatName) {
|
|
2693
2735
|
convoEntity.Name = sTitle; // use the title from the response
|
|
2736
|
+
needToSaveConvo = true;
|
|
2737
|
+
}
|
|
2738
|
+
|
|
2739
|
+
// Set status back to 'Available' since processing is complete
|
|
2740
|
+
if (convoEntity.Status === 'Processing') {
|
|
2741
|
+
convoEntity.Status = 'Available';
|
|
2742
|
+
needToSaveConvo = true;
|
|
2743
|
+
}
|
|
2744
|
+
|
|
2745
|
+
// Save if any changes were made
|
|
2746
|
+
if (needToSaveConvo) {
|
|
2694
2747
|
const convoEntitySaveResult: boolean = await convoEntity.Save();
|
|
2695
2748
|
if (!convoEntitySaveResult) {
|
|
2696
2749
|
LogError(`Error saving conversation entity for AI message: ${sResult}`, undefined, convoEntity.LatestResult);
|
|
@@ -2749,6 +2802,18 @@ cycle.`);
|
|
|
2749
2802
|
};
|
|
2750
2803
|
}
|
|
2751
2804
|
|
|
2805
|
+
private async setConversationStatus(convoEntity: ConversationEntity, status: 'Processing' | 'Available'): Promise<boolean> {
|
|
2806
|
+
if (convoEntity.Status !== status) {
|
|
2807
|
+
convoEntity.Status = status;
|
|
2808
|
+
const convoSaveResult = await convoEntity.Save();
|
|
2809
|
+
if (!convoSaveResult) {
|
|
2810
|
+
LogError(`Error updating conversation status to '${status}'`, undefined, convoEntity.LatestResult);
|
|
2811
|
+
}
|
|
2812
|
+
return convoSaveResult;
|
|
2813
|
+
}
|
|
2814
|
+
return true;
|
|
2815
|
+
}
|
|
2816
|
+
|
|
2752
2817
|
/**
|
|
2753
2818
|
* Gets the ID of an agent note type by its name
|
|
2754
2819
|
* Falls back to a default note type if the specified one is not found
|