@servicetitan/titan-chatbot-api 7.1.2 → 9.0.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 +25 -0
- package/dist/api-client/__mocks__/chatbot-api-client.mock.d.ts +1 -0
- package/dist/api-client/__mocks__/chatbot-api-client.mock.d.ts.map +1 -1
- package/dist/api-client/__mocks__/chatbot-api-client.mock.js +22 -47
- package/dist/api-client/__mocks__/chatbot-api-client.mock.js.map +1 -1
- package/dist/api-client/base/chatbot-api-client.d.ts +7 -0
- package/dist/api-client/base/chatbot-api-client.d.ts.map +1 -1
- package/dist/api-client/base/chatbot-api-client.js +3 -4
- package/dist/api-client/base/chatbot-api-client.js.map +1 -1
- package/dist/api-client/index.d.ts +2 -2
- package/dist/api-client/index.d.ts.map +1 -1
- package/dist/api-client/index.js +12 -7
- package/dist/api-client/index.js.map +1 -1
- package/dist/api-client/models/__mocks__/models.mock.js +154 -124
- package/dist/api-client/models/__mocks__/models.mock.js.map +1 -1
- package/dist/api-client/models/index.d.ts +2 -1
- package/dist/api-client/models/index.d.ts.map +1 -1
- package/dist/api-client/models/index.js +8 -7
- package/dist/api-client/models/index.js.map +1 -1
- package/dist/api-client/titan-chat/__tests__/chatbot-api-client-stream.test.d.ts +2 -0
- package/dist/api-client/titan-chat/__tests__/chatbot-api-client-stream.test.d.ts.map +1 -0
- package/dist/api-client/titan-chat/__tests__/chatbot-api-client-stream.test.js +240 -0
- package/dist/api-client/titan-chat/__tests__/chatbot-api-client-stream.test.js.map +1 -0
- package/dist/api-client/titan-chat/__tests__/native-client.test.js +6 -6
- package/dist/api-client/titan-chat/__tests__/native-client.test.js.map +1 -1
- package/dist/api-client/titan-chat/chatbot-api-client.d.ts +11 -0
- package/dist/api-client/titan-chat/chatbot-api-client.d.ts.map +1 -1
- package/dist/api-client/titan-chat/chatbot-api-client.js +69 -35
- package/dist/api-client/titan-chat/chatbot-api-client.js.map +1 -1
- package/dist/api-client/titan-chat/index.d.ts +2 -1
- package/dist/api-client/titan-chat/index.d.ts.map +1 -1
- package/dist/api-client/titan-chat/index.js +1 -0
- package/dist/api-client/titan-chat/index.js.map +1 -1
- package/dist/api-client/titan-chat/native-client.js +359 -812
- package/dist/api-client/titan-chat/native-client.js.map +1 -1
- package/dist/api-client/utils/__tests__/model-utils.test.js +454 -191
- package/dist/api-client/utils/__tests__/model-utils.test.js.map +1 -1
- package/dist/api-client/utils/model-utils.d.ts.map +1 -1
- package/dist/api-client/utils/model-utils.js +28 -25
- package/dist/api-client/utils/model-utils.js.map +1 -1
- package/dist/hooks/use-customization-chatbot.js +2 -1
- package/dist/hooks/use-customization-chatbot.js.map +1 -1
- package/dist/index.d.ts +3 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +7 -5
- package/dist/index.js.map +1 -1
- package/dist/models/__tests__/chatbot-customizations.test.d.ts +2 -0
- package/dist/models/__tests__/chatbot-customizations.test.d.ts.map +1 -0
- package/dist/models/__tests__/chatbot-customizations.test.js +36 -0
- package/dist/models/__tests__/chatbot-customizations.test.js.map +1 -0
- package/dist/models/chatbot-customizations.d.ts +17 -0
- package/dist/models/chatbot-customizations.d.ts.map +1 -1
- package/dist/models/chatbot-customizations.js +7 -1
- package/dist/models/chatbot-customizations.js.map +1 -1
- package/dist/models/index.js +1 -0
- package/dist/models/index.js.map +1 -1
- package/dist/stores/__tests__/chatbot-ui-backend.store.observability.test.d.ts +2 -0
- package/dist/stores/__tests__/chatbot-ui-backend.store.observability.test.d.ts.map +1 -0
- package/dist/stores/__tests__/chatbot-ui-backend.store.observability.test.js +107 -0
- package/dist/stores/__tests__/chatbot-ui-backend.store.observability.test.js.map +1 -0
- package/dist/stores/__tests__/chatbot-ui-backend.store.streaming.test.d.ts +2 -0
- package/dist/stores/__tests__/chatbot-ui-backend.store.streaming.test.d.ts.map +1 -0
- package/dist/stores/__tests__/chatbot-ui-backend.store.streaming.test.js +312 -0
- package/dist/stores/__tests__/chatbot-ui-backend.store.streaming.test.js.map +1 -0
- package/dist/stores/__tests__/chatbot-ui-backend.store.test.js +267 -172
- package/dist/stores/__tests__/chatbot-ui-backend.store.test.js.map +1 -1
- package/dist/stores/__tests__/chatbot-ui.store.test.js +61 -64
- package/dist/stores/__tests__/chatbot-ui.store.test.js.map +1 -1
- package/dist/stores/__tests__/filter.store.test.js +243 -116
- package/dist/stores/__tests__/filter.store.test.js.map +1 -1
- package/dist/stores/__tests__/initialize.store.test.js +9 -8
- package/dist/stores/__tests__/initialize.store.test.js.map +1 -1
- package/dist/stores/__tests__/message-feedback-guardrail.store.test.js +8 -7
- package/dist/stores/__tests__/message-feedback-guardrail.store.test.js.map +1 -1
- package/dist/stores/__tests__/message-feedback.store.test.js +34 -27
- package/dist/stores/__tests__/message-feedback.store.test.js.map +1 -1
- package/dist/stores/__tests__/session-feedback.store.test.js +9 -8
- package/dist/stores/__tests__/session-feedback.store.test.js.map +1 -1
- package/dist/stores/chatbot-ui-backend.store.d.ts +26 -2
- package/dist/stores/chatbot-ui-backend.store.d.ts.map +1 -1
- package/dist/stores/chatbot-ui-backend.store.js +295 -239
- package/dist/stores/chatbot-ui-backend.store.js.map +1 -1
- package/dist/stores/chatbot-ui.store.js +73 -46
- package/dist/stores/chatbot-ui.store.js.map +1 -1
- package/dist/stores/filter.store.js +298 -378
- package/dist/stores/filter.store.js.map +1 -1
- package/dist/stores/index.d.ts +5 -3
- package/dist/stores/index.d.ts.map +1 -1
- package/dist/stores/index.js +3 -2
- package/dist/stores/index.js.map +1 -1
- package/dist/stores/initialize.store.js +55 -51
- package/dist/stores/initialize.store.js.map +1 -1
- package/dist/stores/message-feedback-base.store.js +2 -1
- package/dist/stores/message-feedback-base.store.js.map +1 -1
- package/dist/stores/message-feedback-guardrail.store.js +50 -47
- package/dist/stores/message-feedback-guardrail.store.js.map +1 -1
- package/dist/stores/message-feedback.store.js +84 -89
- package/dist/stores/message-feedback.store.js.map +1 -1
- package/dist/stores/session-feedback.store.js +46 -39
- package/dist/stores/session-feedback.store.js.map +1 -1
- package/dist/streaming/__tests__/agent-stream.test.d.ts +2 -0
- package/dist/streaming/__tests__/agent-stream.test.d.ts.map +1 -0
- package/dist/streaming/__tests__/agent-stream.test.js +92 -0
- package/dist/streaming/__tests__/agent-stream.test.js.map +1 -0
- package/dist/streaming/agent-stream.d.ts +83 -0
- package/dist/streaming/agent-stream.d.ts.map +1 -0
- package/dist/streaming/agent-stream.js +28 -0
- package/dist/streaming/agent-stream.js.map +1 -0
- package/dist/streaming/index.d.ts +3 -0
- package/dist/streaming/index.d.ts.map +1 -0
- package/dist/streaming/index.js +4 -0
- package/dist/streaming/index.js.map +1 -0
- package/dist/streaming/run-agent-stream.d.ts +23 -0
- package/dist/streaming/run-agent-stream.d.ts.map +1 -0
- package/dist/streaming/run-agent-stream.js +83 -0
- package/dist/streaming/run-agent-stream.js.map +1 -0
- package/dist/utils/__tests__/axios-utils.test.js +8 -7
- package/dist/utils/__tests__/axios-utils.test.js.map +1 -1
- package/dist/utils/axios-utils.js +9 -7
- package/dist/utils/axios-utils.js.map +1 -1
- package/dist/utils/test-utils.js +5 -5
- package/dist/utils/test-utils.js.map +1 -1
- package/package.json +6 -3
- package/src/api-client/__mocks__/chatbot-api-client.mock.ts +1 -0
- package/src/api-client/base/chatbot-api-client.ts +11 -0
- package/src/api-client/index.ts +2 -7
- package/src/api-client/models/index.ts +15 -13
- package/src/api-client/titan-chat/__tests__/chatbot-api-client-stream.test.ts +208 -0
- package/src/api-client/titan-chat/chatbot-api-client.ts +46 -0
- package/src/api-client/titan-chat/index.ts +2 -1
- package/src/api-client/utils/model-utils.ts +4 -8
- package/src/index.ts +7 -2
- package/src/models/__tests__/chatbot-customizations.test.ts +26 -0
- package/src/models/chatbot-customizations.ts +20 -0
- package/src/stores/__tests__/chatbot-ui-backend.store.observability.test.ts +105 -0
- package/src/stores/__tests__/chatbot-ui-backend.store.streaming.test.ts +261 -0
- package/src/stores/chatbot-ui-backend.store.ts +179 -4
- package/src/stores/index.ts +5 -12
- package/src/streaming/__tests__/agent-stream.test.ts +80 -0
- package/src/streaming/agent-stream.ts +103 -0
- package/src/streaming/index.ts +2 -0
- package/src/streaming/run-agent-stream.ts +109 -0
- package/tsconfig.tsbuildinfo +1 -1
- package/dist/api-client/help-center/__tests__/converter-from-models.test.d.ts +0 -2
- package/dist/api-client/help-center/__tests__/converter-from-models.test.d.ts.map +0 -1
- package/dist/api-client/help-center/__tests__/converter-from-models.test.js +0 -34
- package/dist/api-client/help-center/__tests__/converter-from-models.test.js.map +0 -1
- package/dist/api-client/help-center/__tests__/converter-to-models.test.d.ts +0 -2
- package/dist/api-client/help-center/__tests__/converter-to-models.test.d.ts.map +0 -1
- package/dist/api-client/help-center/__tests__/converter-to-models.test.js +0 -82
- package/dist/api-client/help-center/__tests__/converter-to-models.test.js.map +0 -1
- package/dist/api-client/help-center/chatbot-api-client.d.ts +0 -32
- package/dist/api-client/help-center/chatbot-api-client.d.ts.map +0 -1
- package/dist/api-client/help-center/chatbot-api-client.js +0 -102
- package/dist/api-client/help-center/chatbot-api-client.js.map +0 -1
- package/dist/api-client/help-center/converter-from-models.d.ts +0 -13
- package/dist/api-client/help-center/converter-from-models.d.ts.map +0 -1
- package/dist/api-client/help-center/converter-from-models.js +0 -114
- package/dist/api-client/help-center/converter-from-models.js.map +0 -1
- package/dist/api-client/help-center/converter-to-models.d.ts +0 -13
- package/dist/api-client/help-center/converter-to-models.d.ts.map +0 -1
- package/dist/api-client/help-center/converter-to-models.js +0 -98
- package/dist/api-client/help-center/converter-to-models.js.map +0 -1
- package/dist/api-client/help-center/index.d.ts +0 -2
- package/dist/api-client/help-center/index.d.ts.map +0 -1
- package/dist/api-client/help-center/index.js +0 -2
- package/dist/api-client/help-center/index.js.map +0 -1
- package/dist/api-client/help-center/native-client.d.ts +0 -1268
- package/dist/api-client/help-center/native-client.d.ts.map +0 -1
- package/dist/api-client/help-center/native-client.js +0 -6242
- package/dist/api-client/help-center/native-client.js.map +0 -1
- package/src/api-client/help-center/__tests__/converter-from-models.test.ts +0 -41
- package/src/api-client/help-center/__tests__/converter-to-models.test.ts +0 -89
- package/src/api-client/help-center/chatbot-api-client.ts +0 -122
- package/src/api-client/help-center/converter-from-models.ts +0 -133
- package/src/api-client/help-center/converter-to-models.ts +0 -127
- package/src/api-client/help-center/index.ts +0 -1
- package/src/api-client/help-center/native-client.ts +0 -5727
|
@@ -0,0 +1,312 @@
|
|
|
1
|
+
function _define_property(obj, key, value) {
|
|
2
|
+
if (key in obj) {
|
|
3
|
+
Object.defineProperty(obj, key, {
|
|
4
|
+
value: value,
|
|
5
|
+
enumerable: true,
|
|
6
|
+
configurable: true,
|
|
7
|
+
writable: true
|
|
8
|
+
});
|
|
9
|
+
} else {
|
|
10
|
+
obj[key] = value;
|
|
11
|
+
}
|
|
12
|
+
return obj;
|
|
13
|
+
}
|
|
14
|
+
import { expect } from '@jest/globals';
|
|
15
|
+
import { Log } from '@servicetitan/log-service';
|
|
16
|
+
import { CHATBOT_API_CLIENT, ModelsMocks } from '../../api-client';
|
|
17
|
+
import { ChatbotApiClientMock } from '../../api-client/__mocks__/chatbot-api-client.mock';
|
|
18
|
+
import { AgentStreamError } from '../../streaming';
|
|
19
|
+
import { initTestContainer } from '../../utils/test-utils';
|
|
20
|
+
import { ChatbotUiBackendStore } from '../chatbot-ui-backend.store';
|
|
21
|
+
import { CHATBOT_UI_STORE_TOKEN, ChatbotUiStore } from '../chatbot-ui.store';
|
|
22
|
+
const initContainer = initTestContainer(ChatbotUiBackendStore, (container)=>{
|
|
23
|
+
container.bind(Log).to(class {
|
|
24
|
+
constructor(){
|
|
25
|
+
_define_property(this, "error", jest.fn());
|
|
26
|
+
_define_property(this, "info", jest.fn());
|
|
27
|
+
_define_property(this, "warning", jest.fn());
|
|
28
|
+
}
|
|
29
|
+
}).inSingletonScope();
|
|
30
|
+
container.bind(CHATBOT_API_CLIENT).toConstantValue(new ChatbotApiClientMock());
|
|
31
|
+
container.bind(CHATBOT_UI_STORE_TOKEN).to(ChatbotUiStore).inSingletonScope();
|
|
32
|
+
});
|
|
33
|
+
describe('[ChatbotUiBackendStore] streaming', ()=>{
|
|
34
|
+
let container;
|
|
35
|
+
let store;
|
|
36
|
+
let chatbotApi;
|
|
37
|
+
let chatUiStore;
|
|
38
|
+
const runListener = async (listener, args)=>new Promise((resolve, reject)=>listener.apply(store, [
|
|
39
|
+
resolve,
|
|
40
|
+
reject,
|
|
41
|
+
...args
|
|
42
|
+
]));
|
|
43
|
+
const mockSession = ()=>{
|
|
44
|
+
chatbotApi.getOptions.mockResolvedValue(ModelsMocks.mockFrontendModel());
|
|
45
|
+
chatbotApi.postSession.mockResolvedValue(ModelsMocks.mockSession());
|
|
46
|
+
};
|
|
47
|
+
const enableStreaming = ()=>chatUiStore.setCustomizationContext({
|
|
48
|
+
streaming: {
|
|
49
|
+
enabled: true
|
|
50
|
+
}
|
|
51
|
+
});
|
|
52
|
+
const sendQuestion = async (text = 'user question')=>{
|
|
53
|
+
await chatUiStore.sendMessageText(text);
|
|
54
|
+
return chatUiStore.messages.at(-1);
|
|
55
|
+
};
|
|
56
|
+
beforeEach(()=>{
|
|
57
|
+
container = initContainer();
|
|
58
|
+
store = container.get(ChatbotUiBackendStore);
|
|
59
|
+
chatbotApi = container.get(CHATBOT_API_CLIENT);
|
|
60
|
+
chatUiStore = container.get(CHATBOT_UI_STORE_TOKEN);
|
|
61
|
+
mockSession();
|
|
62
|
+
});
|
|
63
|
+
test('uses the streaming path when enabled, not postMessage', async ()=>{
|
|
64
|
+
enableStreaming();
|
|
65
|
+
chatbotApi.streamMessage.mockResolvedValue(ModelsMocks.mockBotMessage({
|
|
66
|
+
answer: 'streamed'
|
|
67
|
+
}));
|
|
68
|
+
const message = await sendQuestion();
|
|
69
|
+
await runListener(store.handleMessageSend, [
|
|
70
|
+
message
|
|
71
|
+
]);
|
|
72
|
+
expect(chatbotApi.streamMessage).toHaveBeenCalledTimes(1);
|
|
73
|
+
expect(chatbotApi.postMessage).not.toHaveBeenCalled();
|
|
74
|
+
expect(chatUiStore.messages.at(-1).message).toBe('streamed');
|
|
75
|
+
expect(chatUiStore.isAgentTyping).toBe(false);
|
|
76
|
+
});
|
|
77
|
+
test('uses the non-streaming path when disabled', async ()=>{
|
|
78
|
+
chatbotApi.postMessage.mockResolvedValue(ModelsMocks.mockBotMessage({
|
|
79
|
+
answer: 'regular'
|
|
80
|
+
}));
|
|
81
|
+
const message = await sendQuestion();
|
|
82
|
+
await runListener(store.handleMessageSend, [
|
|
83
|
+
message
|
|
84
|
+
]);
|
|
85
|
+
expect(chatbotApi.postMessage).toHaveBeenCalledTimes(1);
|
|
86
|
+
expect(chatbotApi.streamMessage).not.toHaveBeenCalled();
|
|
87
|
+
expect(chatUiStore.messages.at(-1).message).toBe('regular');
|
|
88
|
+
});
|
|
89
|
+
test('maps progress events onto the observable progress model', async ()=>{
|
|
90
|
+
enableStreaming();
|
|
91
|
+
chatbotApi.streamMessage.mockImplementation((_m, h)=>{
|
|
92
|
+
var _h_onStatus, _h_onText, _h_onPlan;
|
|
93
|
+
(_h_onStatus = h.onStatus) === null || _h_onStatus === void 0 ? void 0 : _h_onStatus.call(h, 'Thinking…');
|
|
94
|
+
(_h_onText = h.onText) === null || _h_onText === void 0 ? void 0 : _h_onText.call(h, '✓ searched');
|
|
95
|
+
(_h_onPlan = h.onPlan) === null || _h_onPlan === void 0 ? void 0 : _h_onPlan.call(h, [
|
|
96
|
+
{
|
|
97
|
+
id: '1',
|
|
98
|
+
title: 'Plan'
|
|
99
|
+
}
|
|
100
|
+
]);
|
|
101
|
+
return ModelsMocks.mockBotMessage({
|
|
102
|
+
answer: 'done'
|
|
103
|
+
});
|
|
104
|
+
});
|
|
105
|
+
const message = await sendQuestion();
|
|
106
|
+
await runListener(store.handleMessageSend, [
|
|
107
|
+
message
|
|
108
|
+
]);
|
|
109
|
+
expect(store.streamingProgress.logLines).toContain('✓ searched');
|
|
110
|
+
expect(store.streamingProgress.steps.map((s)=>s.id)).toEqual([
|
|
111
|
+
'1'
|
|
112
|
+
]);
|
|
113
|
+
});
|
|
114
|
+
test('scrolls the chat after each streaming progress update', async ()=>{
|
|
115
|
+
enableStreaming();
|
|
116
|
+
const triggerScroll = jest.spyOn(chatUiStore, 'triggerScroll');
|
|
117
|
+
chatbotApi.streamMessage.mockImplementation((_m, h)=>{
|
|
118
|
+
/*
|
|
119
|
+
* Each progress event must scroll exactly once (isolated from the scrolls that
|
|
120
|
+
* delivering the final answer also performs).
|
|
121
|
+
*/ const scrollsFor = (fn)=>{
|
|
122
|
+
triggerScroll.mockClear();
|
|
123
|
+
fn();
|
|
124
|
+
return triggerScroll.mock.calls.length;
|
|
125
|
+
};
|
|
126
|
+
expect(scrollsFor(()=>{
|
|
127
|
+
var _h_onStatus;
|
|
128
|
+
return (_h_onStatus = h.onStatus) === null || _h_onStatus === void 0 ? void 0 : _h_onStatus.call(h, 'Thinking…');
|
|
129
|
+
})).toBe(1);
|
|
130
|
+
expect(scrollsFor(()=>{
|
|
131
|
+
var _h_onText;
|
|
132
|
+
return (_h_onText = h.onText) === null || _h_onText === void 0 ? void 0 : _h_onText.call(h, '✓ searched');
|
|
133
|
+
})).toBe(1);
|
|
134
|
+
expect(scrollsFor(()=>{
|
|
135
|
+
var _h_onPlan;
|
|
136
|
+
return (_h_onPlan = h.onPlan) === null || _h_onPlan === void 0 ? void 0 : _h_onPlan.call(h, [
|
|
137
|
+
{
|
|
138
|
+
id: '1',
|
|
139
|
+
title: 'Plan'
|
|
140
|
+
}
|
|
141
|
+
]);
|
|
142
|
+
})).toBe(1);
|
|
143
|
+
expect(scrollsFor(()=>{
|
|
144
|
+
var _h_onStepActive;
|
|
145
|
+
return (_h_onStepActive = h.onStepActive) === null || _h_onStepActive === void 0 ? void 0 : _h_onStepActive.call(h, '1');
|
|
146
|
+
})).toBe(1);
|
|
147
|
+
expect(scrollsFor(()=>{
|
|
148
|
+
var _h_onInactivity;
|
|
149
|
+
return (_h_onInactivity = h.onInactivity) === null || _h_onInactivity === void 0 ? void 0 : _h_onInactivity.call(h);
|
|
150
|
+
})).toBe(1);
|
|
151
|
+
return ModelsMocks.mockBotMessage({
|
|
152
|
+
answer: 'done'
|
|
153
|
+
});
|
|
154
|
+
});
|
|
155
|
+
const message = await sendQuestion();
|
|
156
|
+
await runListener(store.handleMessageSend, [
|
|
157
|
+
message
|
|
158
|
+
]);
|
|
159
|
+
expect(triggerScroll).toHaveBeenCalled();
|
|
160
|
+
});
|
|
161
|
+
test('onStepActive marks the active plan step (earlier done, later pending)', async ()=>{
|
|
162
|
+
enableStreaming();
|
|
163
|
+
chatbotApi.streamMessage.mockImplementation((_m, h)=>{
|
|
164
|
+
var _h_onPlan, _h_onStepActive;
|
|
165
|
+
(_h_onPlan = h.onPlan) === null || _h_onPlan === void 0 ? void 0 : _h_onPlan.call(h, [
|
|
166
|
+
{
|
|
167
|
+
id: '1',
|
|
168
|
+
title: 'Look up'
|
|
169
|
+
},
|
|
170
|
+
{
|
|
171
|
+
id: '2',
|
|
172
|
+
title: 'Search'
|
|
173
|
+
},
|
|
174
|
+
{
|
|
175
|
+
id: '3',
|
|
176
|
+
title: 'Answer'
|
|
177
|
+
}
|
|
178
|
+
]);
|
|
179
|
+
(_h_onStepActive = h.onStepActive) === null || _h_onStepActive === void 0 ? void 0 : _h_onStepActive.call(h, '2');
|
|
180
|
+
// Capture mid-run state before run.finished completes all steps.
|
|
181
|
+
expect(store.streamingProgress.steps.map((s)=>s.status)).toEqual([
|
|
182
|
+
'done',
|
|
183
|
+
'active',
|
|
184
|
+
'pending'
|
|
185
|
+
]);
|
|
186
|
+
return ModelsMocks.mockBotMessage({
|
|
187
|
+
answer: 'done'
|
|
188
|
+
});
|
|
189
|
+
});
|
|
190
|
+
const message = await sendQuestion();
|
|
191
|
+
await runListener(store.handleMessageSend, [
|
|
192
|
+
message
|
|
193
|
+
]);
|
|
194
|
+
});
|
|
195
|
+
test('marks all plan steps done once the run finishes', async ()=>{
|
|
196
|
+
enableStreaming();
|
|
197
|
+
chatbotApi.streamMessage.mockImplementation((_m, h)=>{
|
|
198
|
+
var _h_onPlan, _h_onStepActive;
|
|
199
|
+
(_h_onPlan = h.onPlan) === null || _h_onPlan === void 0 ? void 0 : _h_onPlan.call(h, [
|
|
200
|
+
{
|
|
201
|
+
id: '1',
|
|
202
|
+
title: 'Look up'
|
|
203
|
+
},
|
|
204
|
+
{
|
|
205
|
+
id: '2',
|
|
206
|
+
title: 'Answer'
|
|
207
|
+
}
|
|
208
|
+
]);
|
|
209
|
+
(_h_onStepActive = h.onStepActive) === null || _h_onStepActive === void 0 ? void 0 : _h_onStepActive.call(h, '1');
|
|
210
|
+
return ModelsMocks.mockBotMessage({
|
|
211
|
+
answer: 'done'
|
|
212
|
+
});
|
|
213
|
+
});
|
|
214
|
+
const message = await sendQuestion();
|
|
215
|
+
await runListener(store.handleMessageSend, [
|
|
216
|
+
message
|
|
217
|
+
]);
|
|
218
|
+
expect(store.streamingProgress.steps.map((s)=>s.status)).toEqual([
|
|
219
|
+
'done',
|
|
220
|
+
'done'
|
|
221
|
+
]);
|
|
222
|
+
});
|
|
223
|
+
test('shows the "Still working on it…" keepalive on inactivity', async ()=>{
|
|
224
|
+
enableStreaming();
|
|
225
|
+
chatbotApi.streamMessage.mockImplementation((_m, h)=>{
|
|
226
|
+
var _h_onInactivity;
|
|
227
|
+
(_h_onInactivity = h.onInactivity) === null || _h_onInactivity === void 0 ? void 0 : _h_onInactivity.call(h);
|
|
228
|
+
expect(store.streamingProgress.keepaliveText).toBe('Still working on it…');
|
|
229
|
+
return ModelsMocks.mockBotMessage({
|
|
230
|
+
answer: 'done'
|
|
231
|
+
});
|
|
232
|
+
});
|
|
233
|
+
const message = await sendQuestion();
|
|
234
|
+
await runListener(store.handleMessageSend, [
|
|
235
|
+
message
|
|
236
|
+
]);
|
|
237
|
+
});
|
|
238
|
+
test('passes the configured inactivity timeout (defaulting to 16000)', async ()=>{
|
|
239
|
+
enableStreaming();
|
|
240
|
+
chatbotApi.streamMessage.mockResolvedValue(ModelsMocks.mockBotMessage({
|
|
241
|
+
answer: 'done'
|
|
242
|
+
}));
|
|
243
|
+
const message = await sendQuestion();
|
|
244
|
+
await runListener(store.handleMessageSend, [
|
|
245
|
+
message
|
|
246
|
+
]);
|
|
247
|
+
const handlers = chatbotApi.streamMessage.mock.calls[0][1];
|
|
248
|
+
expect(handlers.inactivityTimeoutMs).toBe(16000);
|
|
249
|
+
});
|
|
250
|
+
test('falls back to non-streaming silently when the stream is unreachable at connect time', async ()=>{
|
|
251
|
+
enableStreaming();
|
|
252
|
+
// Rejects WITHOUT ever calling onConnected → connect-time failure.
|
|
253
|
+
chatbotApi.streamMessage.mockRejectedValue(new Error('connect failed'));
|
|
254
|
+
chatbotApi.postMessage.mockResolvedValue(ModelsMocks.mockBotMessage({
|
|
255
|
+
answer: 'fallback answer'
|
|
256
|
+
}));
|
|
257
|
+
const message = await sendQuestion();
|
|
258
|
+
await runListener(store.handleMessageSend, [
|
|
259
|
+
message
|
|
260
|
+
]);
|
|
261
|
+
expect(chatbotApi.postMessage).toHaveBeenCalledTimes(1);
|
|
262
|
+
expect(chatUiStore.messages.at(-1).message).toBe('fallback answer');
|
|
263
|
+
expect(chatUiStore.isError).toBe(false);
|
|
264
|
+
expect(store.streamingFallbackCount).toBe(1);
|
|
265
|
+
});
|
|
266
|
+
test('shows the drop error when the connection fails after connecting', async ()=>{
|
|
267
|
+
var _chatUiStore_error;
|
|
268
|
+
enableStreaming();
|
|
269
|
+
chatbotApi.streamMessage.mockImplementation((_m, h)=>{
|
|
270
|
+
var _h_onConnected;
|
|
271
|
+
(_h_onConnected = h.onConnected) === null || _h_onConnected === void 0 ? void 0 : _h_onConnected.call(h);
|
|
272
|
+
throw new Error('socket dropped');
|
|
273
|
+
});
|
|
274
|
+
const message = await sendQuestion();
|
|
275
|
+
await runListener(store.handleMessageSend, [
|
|
276
|
+
message
|
|
277
|
+
]);
|
|
278
|
+
expect(chatbotApi.postMessage).not.toHaveBeenCalled();
|
|
279
|
+
expect(chatUiStore.isError).toBe(true);
|
|
280
|
+
expect((_chatUiStore_error = chatUiStore.error) === null || _chatUiStore_error === void 0 ? void 0 : _chatUiStore_error.message).toContain('Something went wrong during this step');
|
|
281
|
+
});
|
|
282
|
+
test('surfaces the agent step error message from run.error', async ()=>{
|
|
283
|
+
var _chatUiStore_error;
|
|
284
|
+
enableStreaming();
|
|
285
|
+
chatbotApi.streamMessage.mockImplementation((_m, h)=>{
|
|
286
|
+
var _h_onConnected;
|
|
287
|
+
(_h_onConnected = h.onConnected) === null || _h_onConnected === void 0 ? void 0 : _h_onConnected.call(h);
|
|
288
|
+
throw new AgentStreamError('Search step failed');
|
|
289
|
+
});
|
|
290
|
+
const message = await sendQuestion();
|
|
291
|
+
await runListener(store.handleMessageSend, [
|
|
292
|
+
message
|
|
293
|
+
]);
|
|
294
|
+
expect(chatUiStore.isError).toBe(true);
|
|
295
|
+
expect((_chatUiStore_error = chatUiStore.error) === null || _chatUiStore_error === void 0 ? void 0 : _chatUiStore_error.message).toContain('Search step failed');
|
|
296
|
+
});
|
|
297
|
+
test('sends the active session id with the streamed message (session isolation)', async ()=>{
|
|
298
|
+
var _store_session;
|
|
299
|
+
enableStreaming();
|
|
300
|
+
chatbotApi.streamMessage.mockResolvedValue(ModelsMocks.mockBotMessage({
|
|
301
|
+
answer: 'done'
|
|
302
|
+
}));
|
|
303
|
+
const message = await sendQuestion();
|
|
304
|
+
await runListener(store.handleMessageSend, [
|
|
305
|
+
message
|
|
306
|
+
]);
|
|
307
|
+
const sentBody = chatbotApi.streamMessage.mock.calls[0][0];
|
|
308
|
+
expect(sentBody.sessionId).toBe((_store_session = store.session) === null || _store_session === void 0 ? void 0 : _store_session.id);
|
|
309
|
+
});
|
|
310
|
+
});
|
|
311
|
+
|
|
312
|
+
//# sourceMappingURL=chatbot-ui-backend.store.streaming.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../src/stores/__tests__/chatbot-ui-backend.store.streaming.test.ts"],"sourcesContent":["import { expect } from '@jest/globals';\nimport { ILog, Log, LogError, LogInfo, LogWarning } from '@servicetitan/log-service';\nimport { Container } from '@servicetitan/react-ioc';\nimport { ChatMessageModelText, ChatUiEventListener } from '@servicetitan/titan-chat-ui-common';\nimport { CHATBOT_API_CLIENT, IChatbotApiClient, Models, ModelsMocks } from '../../api-client';\nimport { ChatbotApiClientMock } from '../../api-client/__mocks__/chatbot-api-client.mock';\nimport { AgentStreamError, AgentStreamHandlers } from '../../streaming';\nimport { initTestContainer } from '../../utils/test-utils';\nimport { ChatbotUiBackendStore } from '../chatbot-ui-backend.store';\nimport { CHATBOT_UI_STORE_TOKEN, ChatbotUiStore } from '../chatbot-ui.store';\n\nconst initContainer = initTestContainer(ChatbotUiBackendStore, container => {\n container\n .bind<ILog>(Log)\n .to(\n class implements ILog {\n error: (entry: LogError) => void = jest.fn();\n info: (entry: LogInfo) => void = jest.fn();\n warning: (entry: LogWarning) => void = jest.fn();\n }\n )\n .inSingletonScope();\n container\n .bind<IChatbotApiClient>(CHATBOT_API_CLIENT)\n .toConstantValue(new ChatbotApiClientMock());\n container.bind(CHATBOT_UI_STORE_TOKEN).to(ChatbotUiStore).inSingletonScope();\n});\n\ndescribe('[ChatbotUiBackendStore] streaming', () => {\n let container: Container;\n let store: ChatbotUiBackendStore;\n let chatbotApi: ChatbotApiClientMock;\n let chatUiStore: ChatbotUiStore;\n\n const runListener = async <T = void>(listener: ChatUiEventListener<T>, args: unknown[]) =>\n new Promise<T>((resolve, reject) => listener.apply(store, [resolve, reject, ...args]));\n\n const mockSession = () => {\n chatbotApi.getOptions.mockResolvedValue(ModelsMocks.mockFrontendModel());\n chatbotApi.postSession.mockResolvedValue(ModelsMocks.mockSession());\n };\n\n const enableStreaming = () =>\n chatUiStore.setCustomizationContext({ streaming: { enabled: true } });\n\n const sendQuestion = async (text = 'user question') => {\n await chatUiStore.sendMessageText(text);\n return chatUiStore.messages.at(-1)! as ChatMessageModelText;\n };\n\n beforeEach(() => {\n container = initContainer();\n store = container.get(ChatbotUiBackendStore);\n chatbotApi = container.get<IChatbotApiClient>(CHATBOT_API_CLIENT) as ChatbotApiClientMock;\n chatUiStore = container.get<ChatbotUiStore>(CHATBOT_UI_STORE_TOKEN);\n mockSession();\n });\n\n test('uses the streaming path when enabled, not postMessage', async () => {\n enableStreaming();\n chatbotApi.streamMessage.mockResolvedValue(\n ModelsMocks.mockBotMessage({ answer: 'streamed' })\n );\n\n const message = await sendQuestion();\n await runListener(store.handleMessageSend, [message]);\n\n expect(chatbotApi.streamMessage).toHaveBeenCalledTimes(1);\n expect(chatbotApi.postMessage).not.toHaveBeenCalled();\n expect((chatUiStore.messages.at(-1) as ChatMessageModelText).message).toBe('streamed');\n expect(chatUiStore.isAgentTyping).toBe(false);\n });\n\n test('uses the non-streaming path when disabled', async () => {\n chatbotApi.postMessage.mockResolvedValue(ModelsMocks.mockBotMessage({ answer: 'regular' }));\n\n const message = await sendQuestion();\n await runListener(store.handleMessageSend, [message]);\n\n expect(chatbotApi.postMessage).toHaveBeenCalledTimes(1);\n expect(chatbotApi.streamMessage).not.toHaveBeenCalled();\n expect((chatUiStore.messages.at(-1) as ChatMessageModelText).message).toBe('regular');\n });\n\n test('maps progress events onto the observable progress model', async () => {\n enableStreaming();\n chatbotApi.streamMessage.mockImplementation(\n (_m: Models.IUserMessage, h: AgentStreamHandlers) => {\n h.onStatus?.('Thinking…');\n h.onText?.('✓ searched');\n h.onPlan?.([{ id: '1', title: 'Plan' }]);\n return ModelsMocks.mockBotMessage({ answer: 'done' });\n }\n );\n\n const message = await sendQuestion();\n await runListener(store.handleMessageSend, [message]);\n\n expect(store.streamingProgress.logLines).toContain('✓ searched');\n expect(store.streamingProgress.steps.map(s => s.id)).toEqual(['1']);\n });\n\n test('scrolls the chat after each streaming progress update', async () => {\n enableStreaming();\n const triggerScroll = jest.spyOn(chatUiStore, 'triggerScroll');\n chatbotApi.streamMessage.mockImplementation(\n (_m: Models.IUserMessage, h: AgentStreamHandlers) => {\n /*\n * Each progress event must scroll exactly once (isolated from the scrolls that\n * delivering the final answer also performs).\n */\n const scrollsFor = (fn: () => void) => {\n triggerScroll.mockClear();\n fn();\n return triggerScroll.mock.calls.length;\n };\n expect(scrollsFor(() => h.onStatus?.('Thinking…'))).toBe(1);\n expect(scrollsFor(() => h.onText?.('✓ searched'))).toBe(1);\n expect(scrollsFor(() => h.onPlan?.([{ id: '1', title: 'Plan' }]))).toBe(1);\n expect(scrollsFor(() => h.onStepActive?.('1'))).toBe(1);\n expect(scrollsFor(() => h.onInactivity?.())).toBe(1);\n return ModelsMocks.mockBotMessage({ answer: 'done' });\n }\n );\n\n const message = await sendQuestion();\n await runListener(store.handleMessageSend, [message]);\n expect(triggerScroll).toHaveBeenCalled();\n });\n\n test('onStepActive marks the active plan step (earlier done, later pending)', async () => {\n enableStreaming();\n chatbotApi.streamMessage.mockImplementation(\n (_m: Models.IUserMessage, h: AgentStreamHandlers) => {\n h.onPlan?.([\n { id: '1', title: 'Look up' },\n { id: '2', title: 'Search' },\n { id: '3', title: 'Answer' },\n ]);\n h.onStepActive?.('2');\n // Capture mid-run state before run.finished completes all steps.\n expect(store.streamingProgress.steps.map(s => s.status)).toEqual([\n 'done',\n 'active',\n 'pending',\n ]);\n return ModelsMocks.mockBotMessage({ answer: 'done' });\n }\n );\n\n const message = await sendQuestion();\n await runListener(store.handleMessageSend, [message]);\n });\n\n test('marks all plan steps done once the run finishes', async () => {\n enableStreaming();\n chatbotApi.streamMessage.mockImplementation(\n (_m: Models.IUserMessage, h: AgentStreamHandlers) => {\n h.onPlan?.([\n { id: '1', title: 'Look up' },\n { id: '2', title: 'Answer' },\n ]);\n h.onStepActive?.('1');\n return ModelsMocks.mockBotMessage({ answer: 'done' });\n }\n );\n\n const message = await sendQuestion();\n await runListener(store.handleMessageSend, [message]);\n\n expect(store.streamingProgress.steps.map(s => s.status)).toEqual(['done', 'done']);\n });\n\n test('shows the \"Still working on it…\" keepalive on inactivity', async () => {\n enableStreaming();\n chatbotApi.streamMessage.mockImplementation(\n (_m: Models.IUserMessage, h: AgentStreamHandlers) => {\n h.onInactivity?.();\n expect(store.streamingProgress.keepaliveText).toBe('Still working on it…');\n return ModelsMocks.mockBotMessage({ answer: 'done' });\n }\n );\n\n const message = await sendQuestion();\n await runListener(store.handleMessageSend, [message]);\n });\n\n test('passes the configured inactivity timeout (defaulting to 16000)', async () => {\n enableStreaming();\n chatbotApi.streamMessage.mockResolvedValue(ModelsMocks.mockBotMessage({ answer: 'done' }));\n\n const message = await sendQuestion();\n await runListener(store.handleMessageSend, [message]);\n\n const handlers = chatbotApi.streamMessage.mock.calls[0][1] as AgentStreamHandlers;\n expect(handlers.inactivityTimeoutMs).toBe(16_000);\n });\n\n test('falls back to non-streaming silently when the stream is unreachable at connect time', async () => {\n enableStreaming();\n // Rejects WITHOUT ever calling onConnected → connect-time failure.\n chatbotApi.streamMessage.mockRejectedValue(new Error('connect failed'));\n chatbotApi.postMessage.mockResolvedValue(\n ModelsMocks.mockBotMessage({ answer: 'fallback answer' })\n );\n\n const message = await sendQuestion();\n await runListener(store.handleMessageSend, [message]);\n\n expect(chatbotApi.postMessage).toHaveBeenCalledTimes(1);\n expect((chatUiStore.messages.at(-1) as ChatMessageModelText).message).toBe(\n 'fallback answer'\n );\n expect(chatUiStore.isError).toBe(false);\n expect(store.streamingFallbackCount).toBe(1);\n });\n\n test('shows the drop error when the connection fails after connecting', async () => {\n enableStreaming();\n chatbotApi.streamMessage.mockImplementation(\n (_m: Models.IUserMessage, h: AgentStreamHandlers) => {\n h.onConnected?.();\n throw new Error('socket dropped');\n }\n );\n\n const message = await sendQuestion();\n await runListener(store.handleMessageSend, [message]);\n\n expect(chatbotApi.postMessage).not.toHaveBeenCalled();\n expect(chatUiStore.isError).toBe(true);\n expect(chatUiStore.error?.message).toContain('Something went wrong during this step');\n });\n\n test('surfaces the agent step error message from run.error', async () => {\n enableStreaming();\n chatbotApi.streamMessage.mockImplementation(\n (_m: Models.IUserMessage, h: AgentStreamHandlers) => {\n h.onConnected?.();\n throw new AgentStreamError('Search step failed');\n }\n );\n\n const message = await sendQuestion();\n await runListener(store.handleMessageSend, [message]);\n\n expect(chatUiStore.isError).toBe(true);\n expect(chatUiStore.error?.message).toContain('Search step failed');\n });\n\n test('sends the active session id with the streamed message (session isolation)', async () => {\n enableStreaming();\n chatbotApi.streamMessage.mockResolvedValue(ModelsMocks.mockBotMessage({ answer: 'done' }));\n\n const message = await sendQuestion();\n await runListener(store.handleMessageSend, [message]);\n\n const sentBody = chatbotApi.streamMessage.mock.calls[0][0] as Models.IUserMessage;\n expect(sentBody.sessionId).toBe(store.session?.id);\n });\n});\n"],"names":["expect","Log","CHATBOT_API_CLIENT","ModelsMocks","ChatbotApiClientMock","AgentStreamError","initTestContainer","ChatbotUiBackendStore","CHATBOT_UI_STORE_TOKEN","ChatbotUiStore","initContainer","container","bind","to","error","jest","fn","info","warning","inSingletonScope","toConstantValue","describe","store","chatbotApi","chatUiStore","runListener","listener","args","Promise","resolve","reject","apply","mockSession","getOptions","mockResolvedValue","mockFrontendModel","postSession","enableStreaming","setCustomizationContext","streaming","enabled","sendQuestion","text","sendMessageText","messages","at","beforeEach","get","test","streamMessage","mockBotMessage","answer","message","handleMessageSend","toHaveBeenCalledTimes","postMessage","not","toHaveBeenCalled","toBe","isAgentTyping","mockImplementation","_m","h","onStatus","onText","onPlan","id","title","streamingProgress","logLines","toContain","steps","map","s","toEqual","triggerScroll","spyOn","scrollsFor","mockClear","mock","calls","length","onStepActive","onInactivity","status","keepaliveText","handlers","inactivityTimeoutMs","mockRejectedValue","Error","isError","streamingFallbackCount","onConnected","sentBody","sessionId","session"],"mappings":"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"}
|