@servicetitan/titan-chatbot-api 4.0.1 → 4.1.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/CHANGELOG.md +12 -0
- package/dist/api-client/utils/model-utils.d.ts +1 -1
- package/dist/api-client/utils/model-utils.d.ts.map +1 -1
- package/dist/api-client/utils/model-utils.js +2 -2
- package/dist/api-client/utils/model-utils.js.map +1 -1
- package/dist/stores/__tests__/chatbot-ui-backend.store.test.js +45 -3
- package/dist/stores/__tests__/chatbot-ui-backend.store.test.js.map +1 -1
- package/dist/stores/__tests__/chatbot-ui.store.test.js +12 -0
- package/dist/stores/__tests__/chatbot-ui.store.test.js.map +1 -1
- package/dist/stores/chatbot-ui-backend.store.d.ts +4 -1
- package/dist/stores/chatbot-ui-backend.store.d.ts.map +1 -1
- package/dist/stores/chatbot-ui-backend.store.js +24 -6
- package/dist/stores/chatbot-ui-backend.store.js.map +1 -1
- package/dist/stores/chatbot-ui.store.d.ts +7 -4
- package/dist/stores/chatbot-ui.store.d.ts.map +1 -1
- package/dist/stores/chatbot-ui.store.js +11 -0
- package/dist/stores/chatbot-ui.store.js.map +1 -1
- package/package.json +3 -3
- package/src/api-client/utils/model-utils.ts +2 -2
- package/src/stores/__tests__/chatbot-ui-backend.store.test.ts +67 -3
- package/src/stores/__tests__/chatbot-ui.store.test.ts +21 -0
- package/src/stores/chatbot-ui-backend.store.ts +26 -6
- package/src/stores/chatbot-ui.store.ts +17 -2
- package/tsconfig.tsbuildinfo +1 -1
|
@@ -67,14 +67,14 @@ describe('[ChatbotUiBackendStore]', () => {
|
|
|
67
67
|
|
|
68
68
|
test('should subscribe on external events', () => {
|
|
69
69
|
store.subscribe();
|
|
70
|
-
expect(spyOn).toHaveBeenCalledTimes(
|
|
71
|
-
expect(spyOff).toHaveBeenCalledTimes(
|
|
70
|
+
expect(spyOn).toHaveBeenCalledTimes(8);
|
|
71
|
+
expect(spyOff).toHaveBeenCalledTimes(8);
|
|
72
72
|
});
|
|
73
73
|
|
|
74
74
|
test('should unsubscribe from external events', () => {
|
|
75
75
|
store.unsubscribe();
|
|
76
76
|
expect(chatUiStore.on).toHaveBeenCalledTimes(0);
|
|
77
|
-
expect(chatUiStore.off).toHaveBeenCalledTimes(
|
|
77
|
+
expect(chatUiStore.off).toHaveBeenCalledTimes(8);
|
|
78
78
|
});
|
|
79
79
|
});
|
|
80
80
|
|
|
@@ -259,6 +259,70 @@ describe('[ChatbotUiBackendStore]', () => {
|
|
|
259
259
|
});
|
|
260
260
|
});
|
|
261
261
|
|
|
262
|
+
describe('with session start', () => {
|
|
263
|
+
const createSession = (id: number) =>
|
|
264
|
+
ModelsMocks.mockSession({
|
|
265
|
+
id,
|
|
266
|
+
data: { custom: 'data' },
|
|
267
|
+
});
|
|
268
|
+
|
|
269
|
+
test('should start session', async () => {
|
|
270
|
+
chatbotApi.postSession.mockResolvedValue(createSession(1));
|
|
271
|
+
const { session } = await runChatUiEventListener(store.handleSessionStart, [
|
|
272
|
+
{ custom: 'data' },
|
|
273
|
+
]);
|
|
274
|
+
|
|
275
|
+
expect(session).toEqual(createSession(1));
|
|
276
|
+
expect(chatbotApi.postSession).toHaveBeenCalledWith(
|
|
277
|
+
{ custom: 'data' },
|
|
278
|
+
expect.any(AbortSignal)
|
|
279
|
+
);
|
|
280
|
+
});
|
|
281
|
+
|
|
282
|
+
test('should handle multiple calls', async () => {
|
|
283
|
+
chatbotApi.postSession.mockResolvedValue(createSession(1));
|
|
284
|
+
const p1 = runChatUiEventListener(store.handleSessionStart, [{ custom: 'data' }]);
|
|
285
|
+
const p2 = runChatUiEventListener(store.handleSessionStart, [{ custom: 'data' }]);
|
|
286
|
+
|
|
287
|
+
const [r1, r2] = await Promise.all([p1, p2]);
|
|
288
|
+
|
|
289
|
+
expect(r1.session).toEqual(createSession(1));
|
|
290
|
+
expect(r2.session).toEqual(createSession(1));
|
|
291
|
+
expect(chatbotApi.postSession).toHaveBeenCalledTimes(1);
|
|
292
|
+
expect(chatbotApi.postSession).toHaveBeenCalledWith(
|
|
293
|
+
{ custom: 'data' },
|
|
294
|
+
expect.any(AbortSignal)
|
|
295
|
+
);
|
|
296
|
+
});
|
|
297
|
+
|
|
298
|
+
test('should handle forced calls', async () => {
|
|
299
|
+
chatbotApi.postSession.mockResolvedValue(createSession(1));
|
|
300
|
+
const { session } = await runChatUiEventListener(store.handleSessionStart, [
|
|
301
|
+
{ custom: 'data' },
|
|
302
|
+
]);
|
|
303
|
+
|
|
304
|
+
chatbotApi.postSession.mockResolvedValue(createSession(2));
|
|
305
|
+
const { session: session2 } = await runChatUiEventListener(
|
|
306
|
+
store.handleSessionStart,
|
|
307
|
+
[{ custom: 'data' }]
|
|
308
|
+
);
|
|
309
|
+
expect(chatbotApi.postSession).toHaveBeenCalledTimes(1);
|
|
310
|
+
expect(session?.id).toEqual(1);
|
|
311
|
+
expect(session2?.id).toEqual(1);
|
|
312
|
+
expect(session === session2).toBe(true);
|
|
313
|
+
|
|
314
|
+
// Make api call return another session object and ensure that after
|
|
315
|
+
chatbotApi.postSession.mockResolvedValue(createSession(3));
|
|
316
|
+
const { session: session3 } = await runChatUiEventListener(
|
|
317
|
+
store.handleSessionStart,
|
|
318
|
+
[{ custom: 'data' }, true]
|
|
319
|
+
);
|
|
320
|
+
expect(chatbotApi.postSession).toHaveBeenCalledTimes(2);
|
|
321
|
+
expect(session3?.id).toEqual(3);
|
|
322
|
+
expect(session === session3).toBe(false);
|
|
323
|
+
});
|
|
324
|
+
});
|
|
325
|
+
|
|
262
326
|
describe('with session feedback', () => {
|
|
263
327
|
const createSessionFeedback = () =>
|
|
264
328
|
ModelsMocks.mockFeedback({
|
|
@@ -57,6 +57,27 @@ describe('[ChatbotUiStore]', () => {
|
|
|
57
57
|
});
|
|
58
58
|
|
|
59
59
|
describe('with chat events', () => {
|
|
60
|
+
test('should startSession', async () => {
|
|
61
|
+
mockEmit();
|
|
62
|
+
mockEventEmitter.emit.mockImplementation(
|
|
63
|
+
async (eventName: string, resolve: (session: Models.ISession) => void) => {
|
|
64
|
+
await Promise.resolve();
|
|
65
|
+
resolve(ModelsMocks.mockSession());
|
|
66
|
+
}
|
|
67
|
+
);
|
|
68
|
+
const sessionData = ModelsMocks.mockSession({ id: 1 });
|
|
69
|
+
const session = await store.startSession(sessionData, true);
|
|
70
|
+
expect(session).toBeDefined();
|
|
71
|
+
expect(session?.id).toBe(1);
|
|
72
|
+
expect(mockEventEmitter.emit).toHaveBeenCalledWith(
|
|
73
|
+
ChatbotUiEvent.eventChatbotSessionStart,
|
|
74
|
+
expect.any(Function),
|
|
75
|
+
expect.any(Function),
|
|
76
|
+
sessionData,
|
|
77
|
+
true
|
|
78
|
+
);
|
|
79
|
+
});
|
|
80
|
+
|
|
60
81
|
test('should sendSessionFeedback', async () => {
|
|
61
82
|
mockEmit();
|
|
62
83
|
const feedback = new Models.Feedback({
|
|
@@ -59,6 +59,7 @@ export class ChatbotUiBackendStore extends InitializeStore implements IChatbotUi
|
|
|
59
59
|
this.chatUiStore.on(ChatUiEvent.eventRestart, this.handleRestart);
|
|
60
60
|
this.chatUiStore.on(ChatUiEvent.eventMessageSend, this.handleMessageSend);
|
|
61
61
|
this.chatUiStore.on(ChatUiEvent.eventMessageSendRetry, this.handleMessageSendRetry);
|
|
62
|
+
this.chatUiStore.on(ChatbotUiEvent.eventChatbotSessionStart, this.handleSessionStart);
|
|
62
63
|
this.chatUiStore.on(ChatbotUiEvent.eventChatbotSessionFeedback, this.handleSessionFeedback);
|
|
63
64
|
this.chatUiStore.on(ChatbotUiEvent.eventChatbotMessageFeedback, this.handleMessageFeedback);
|
|
64
65
|
}
|
|
@@ -68,6 +69,7 @@ export class ChatbotUiBackendStore extends InitializeStore implements IChatbotUi
|
|
|
68
69
|
this.chatUiStore.off(ChatUiEvent.eventRestart, this.handleRestart);
|
|
69
70
|
this.chatUiStore.off(ChatUiEvent.eventMessageSend, this.handleMessageSend);
|
|
70
71
|
this.chatUiStore.off(ChatUiEvent.eventMessageSendRetry, this.handleMessageSendRetry);
|
|
72
|
+
this.chatUiStore.off(ChatbotUiEvent.eventChatbotSessionStart, this.handleSessionStart);
|
|
71
73
|
this.chatUiStore.off(
|
|
72
74
|
ChatbotUiEvent.eventChatbotSessionFeedback,
|
|
73
75
|
this.handleSessionFeedback
|
|
@@ -119,6 +121,15 @@ export class ChatbotUiBackendStore extends InitializeStore implements IChatbotUi
|
|
|
119
121
|
resolve();
|
|
120
122
|
};
|
|
121
123
|
|
|
124
|
+
handleSessionStart: ChatUiEventListener<{
|
|
125
|
+
session?: Models.ISession;
|
|
126
|
+
}> = async (resolve, _, sessionData?: Models.ISession, forceRecreate?: boolean) => {
|
|
127
|
+
const session = await this.startSession(sessionData, forceRecreate);
|
|
128
|
+
resolve({
|
|
129
|
+
session,
|
|
130
|
+
});
|
|
131
|
+
};
|
|
132
|
+
|
|
122
133
|
handleSessionFeedback: ChatUiEventListener<{
|
|
123
134
|
state: Models.ChatbotFeedbackState;
|
|
124
135
|
feedback?: Models.Feedback;
|
|
@@ -136,7 +147,7 @@ export class ChatbotUiBackendStore extends InitializeStore implements IChatbotUi
|
|
|
136
147
|
recoverStrategy: false,
|
|
137
148
|
});
|
|
138
149
|
}
|
|
139
|
-
await this.
|
|
150
|
+
await this.startSession();
|
|
140
151
|
runInAction(() => {
|
|
141
152
|
feedback.sessionId = this.session!.id;
|
|
142
153
|
});
|
|
@@ -174,7 +185,7 @@ export class ChatbotUiBackendStore extends InitializeStore implements IChatbotUi
|
|
|
174
185
|
recoverStrategy: false,
|
|
175
186
|
});
|
|
176
187
|
}
|
|
177
|
-
await this.
|
|
188
|
+
await this.startSession();
|
|
178
189
|
runInAction(() => {
|
|
179
190
|
feedback.sessionId = this.session!.id;
|
|
180
191
|
});
|
|
@@ -265,7 +276,7 @@ export class ChatbotUiBackendStore extends InitializeStore implements IChatbotUi
|
|
|
265
276
|
|
|
266
277
|
private async sendMessage(messageModel: ChatMessageModelText, selections?: Models.Selections) {
|
|
267
278
|
try {
|
|
268
|
-
await this.
|
|
279
|
+
await this.startSession();
|
|
269
280
|
this.chatUiStore.setAgentTyping(true);
|
|
270
281
|
const answer = await withTimeout(
|
|
271
282
|
this.api(
|
|
@@ -314,12 +325,20 @@ export class ChatbotUiBackendStore extends InitializeStore implements IChatbotUi
|
|
|
314
325
|
this.session = session;
|
|
315
326
|
}
|
|
316
327
|
|
|
317
|
-
private async
|
|
328
|
+
private async startSession(sessionData?: Models.ISession, forceRecreate = false) {
|
|
329
|
+
if (forceRecreate) {
|
|
330
|
+
this.reset();
|
|
331
|
+
this.sessionPromise = undefined;
|
|
332
|
+
}
|
|
318
333
|
if (this.session) {
|
|
319
|
-
return;
|
|
334
|
+
return this.session;
|
|
320
335
|
}
|
|
321
336
|
if (!this.sessionPromise) {
|
|
322
|
-
this.sessionPromise = this.api(
|
|
337
|
+
this.sessionPromise = this.api(
|
|
338
|
+
'postSession',
|
|
339
|
+
ModelsUtils.createNewSessionModel(sessionData),
|
|
340
|
+
this.abortController.signal
|
|
341
|
+
);
|
|
323
342
|
const session = await this.sessionPromise;
|
|
324
343
|
this.setSession(session);
|
|
325
344
|
this.withSaveState(() => {
|
|
@@ -331,6 +350,7 @@ export class ChatbotUiBackendStore extends InitializeStore implements IChatbotUi
|
|
|
331
350
|
} finally {
|
|
332
351
|
this.sessionPromise = undefined;
|
|
333
352
|
}
|
|
353
|
+
return this.session;
|
|
334
354
|
}
|
|
335
355
|
|
|
336
356
|
private async api<
|
|
@@ -8,6 +8,7 @@ import { FilterStore } from './filter.store';
|
|
|
8
8
|
export const CHATBOT_UI_STORE_TOKEN = symbolToken<IChatbotUiStore>('CHATBOT_UI_STORE_TOKEN');
|
|
9
9
|
|
|
10
10
|
export enum ChatbotUiEvent {
|
|
11
|
+
eventChatbotSessionStart = 'eventChatbotSessionStart',
|
|
11
12
|
eventChatbotSessionFeedback = 'eventChatbotSessionFeedback',
|
|
12
13
|
eventChatbotMessageFeedback = 'eventChatbotMessageFeedback',
|
|
13
14
|
}
|
|
@@ -16,13 +17,17 @@ export interface IChatbotUiStore extends IChatUiStore<ChatbotCustomizations> {
|
|
|
16
17
|
filterStore: FilterStore;
|
|
17
18
|
settings?: Models.IFrontendModel;
|
|
18
19
|
setFilters: (filterOptions: Models.IFrontendModel) => void;
|
|
20
|
+
startSession: (
|
|
21
|
+
sessionData?: Models.ISession,
|
|
22
|
+
forceRecreate?: boolean
|
|
23
|
+
) => Promise<undefined | Models.Session>;
|
|
19
24
|
sendSessionFeedback: (
|
|
20
25
|
feedback: Models.IFeedback
|
|
21
|
-
) => Promise<
|
|
26
|
+
) => Promise<undefined | Models.ChatbotFeedbackState>;
|
|
22
27
|
sendMessageFeedback: (
|
|
23
28
|
message: Models.IBotMessageWithFeedback,
|
|
24
29
|
messageFeedback: Models.IFeedback
|
|
25
|
-
) => Promise<
|
|
30
|
+
) => Promise<undefined | Models.ChatbotFeedbackState>;
|
|
26
31
|
}
|
|
27
32
|
|
|
28
33
|
@injectable()
|
|
@@ -58,6 +63,16 @@ export class ChatbotUiStore extends ChatUiStore<ChatbotCustomizations> implement
|
|
|
58
63
|
this.filterStore.initFilters(filterOptions);
|
|
59
64
|
}
|
|
60
65
|
|
|
66
|
+
@action
|
|
67
|
+
async startSession(sessionData?: Models.ISession, forceRecreate?: boolean) {
|
|
68
|
+
const result = await this.emitAsync<Models.Session>(
|
|
69
|
+
ChatbotUiEvent.eventChatbotSessionStart,
|
|
70
|
+
sessionData,
|
|
71
|
+
forceRecreate
|
|
72
|
+
);
|
|
73
|
+
return result;
|
|
74
|
+
}
|
|
75
|
+
|
|
61
76
|
@action
|
|
62
77
|
async sendSessionFeedback(feedback: Models.Feedback) {
|
|
63
78
|
feedback.messageId = undefined; // Ensure messageId is not set for session feedback
|