@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.
Files changed (178) hide show
  1. package/CHANGELOG.md +25 -0
  2. package/dist/api-client/__mocks__/chatbot-api-client.mock.d.ts +1 -0
  3. package/dist/api-client/__mocks__/chatbot-api-client.mock.d.ts.map +1 -1
  4. package/dist/api-client/__mocks__/chatbot-api-client.mock.js +22 -47
  5. package/dist/api-client/__mocks__/chatbot-api-client.mock.js.map +1 -1
  6. package/dist/api-client/base/chatbot-api-client.d.ts +7 -0
  7. package/dist/api-client/base/chatbot-api-client.d.ts.map +1 -1
  8. package/dist/api-client/base/chatbot-api-client.js +3 -4
  9. package/dist/api-client/base/chatbot-api-client.js.map +1 -1
  10. package/dist/api-client/index.d.ts +2 -2
  11. package/dist/api-client/index.d.ts.map +1 -1
  12. package/dist/api-client/index.js +12 -7
  13. package/dist/api-client/index.js.map +1 -1
  14. package/dist/api-client/models/__mocks__/models.mock.js +154 -124
  15. package/dist/api-client/models/__mocks__/models.mock.js.map +1 -1
  16. package/dist/api-client/models/index.d.ts +2 -1
  17. package/dist/api-client/models/index.d.ts.map +1 -1
  18. package/dist/api-client/models/index.js +8 -7
  19. package/dist/api-client/models/index.js.map +1 -1
  20. package/dist/api-client/titan-chat/__tests__/chatbot-api-client-stream.test.d.ts +2 -0
  21. package/dist/api-client/titan-chat/__tests__/chatbot-api-client-stream.test.d.ts.map +1 -0
  22. package/dist/api-client/titan-chat/__tests__/chatbot-api-client-stream.test.js +240 -0
  23. package/dist/api-client/titan-chat/__tests__/chatbot-api-client-stream.test.js.map +1 -0
  24. package/dist/api-client/titan-chat/__tests__/native-client.test.js +6 -6
  25. package/dist/api-client/titan-chat/__tests__/native-client.test.js.map +1 -1
  26. package/dist/api-client/titan-chat/chatbot-api-client.d.ts +11 -0
  27. package/dist/api-client/titan-chat/chatbot-api-client.d.ts.map +1 -1
  28. package/dist/api-client/titan-chat/chatbot-api-client.js +69 -35
  29. package/dist/api-client/titan-chat/chatbot-api-client.js.map +1 -1
  30. package/dist/api-client/titan-chat/index.d.ts +2 -1
  31. package/dist/api-client/titan-chat/index.d.ts.map +1 -1
  32. package/dist/api-client/titan-chat/index.js +1 -0
  33. package/dist/api-client/titan-chat/index.js.map +1 -1
  34. package/dist/api-client/titan-chat/native-client.js +359 -812
  35. package/dist/api-client/titan-chat/native-client.js.map +1 -1
  36. package/dist/api-client/utils/__tests__/model-utils.test.js +454 -191
  37. package/dist/api-client/utils/__tests__/model-utils.test.js.map +1 -1
  38. package/dist/api-client/utils/model-utils.d.ts.map +1 -1
  39. package/dist/api-client/utils/model-utils.js +28 -25
  40. package/dist/api-client/utils/model-utils.js.map +1 -1
  41. package/dist/hooks/use-customization-chatbot.js +2 -1
  42. package/dist/hooks/use-customization-chatbot.js.map +1 -1
  43. package/dist/index.d.ts +3 -2
  44. package/dist/index.d.ts.map +1 -1
  45. package/dist/index.js +7 -5
  46. package/dist/index.js.map +1 -1
  47. package/dist/models/__tests__/chatbot-customizations.test.d.ts +2 -0
  48. package/dist/models/__tests__/chatbot-customizations.test.d.ts.map +1 -0
  49. package/dist/models/__tests__/chatbot-customizations.test.js +36 -0
  50. package/dist/models/__tests__/chatbot-customizations.test.js.map +1 -0
  51. package/dist/models/chatbot-customizations.d.ts +17 -0
  52. package/dist/models/chatbot-customizations.d.ts.map +1 -1
  53. package/dist/models/chatbot-customizations.js +7 -1
  54. package/dist/models/chatbot-customizations.js.map +1 -1
  55. package/dist/models/index.js +1 -0
  56. package/dist/models/index.js.map +1 -1
  57. package/dist/stores/__tests__/chatbot-ui-backend.store.observability.test.d.ts +2 -0
  58. package/dist/stores/__tests__/chatbot-ui-backend.store.observability.test.d.ts.map +1 -0
  59. package/dist/stores/__tests__/chatbot-ui-backend.store.observability.test.js +107 -0
  60. package/dist/stores/__tests__/chatbot-ui-backend.store.observability.test.js.map +1 -0
  61. package/dist/stores/__tests__/chatbot-ui-backend.store.streaming.test.d.ts +2 -0
  62. package/dist/stores/__tests__/chatbot-ui-backend.store.streaming.test.d.ts.map +1 -0
  63. package/dist/stores/__tests__/chatbot-ui-backend.store.streaming.test.js +312 -0
  64. package/dist/stores/__tests__/chatbot-ui-backend.store.streaming.test.js.map +1 -0
  65. package/dist/stores/__tests__/chatbot-ui-backend.store.test.js +267 -172
  66. package/dist/stores/__tests__/chatbot-ui-backend.store.test.js.map +1 -1
  67. package/dist/stores/__tests__/chatbot-ui.store.test.js +61 -64
  68. package/dist/stores/__tests__/chatbot-ui.store.test.js.map +1 -1
  69. package/dist/stores/__tests__/filter.store.test.js +243 -116
  70. package/dist/stores/__tests__/filter.store.test.js.map +1 -1
  71. package/dist/stores/__tests__/initialize.store.test.js +9 -8
  72. package/dist/stores/__tests__/initialize.store.test.js.map +1 -1
  73. package/dist/stores/__tests__/message-feedback-guardrail.store.test.js +8 -7
  74. package/dist/stores/__tests__/message-feedback-guardrail.store.test.js.map +1 -1
  75. package/dist/stores/__tests__/message-feedback.store.test.js +34 -27
  76. package/dist/stores/__tests__/message-feedback.store.test.js.map +1 -1
  77. package/dist/stores/__tests__/session-feedback.store.test.js +9 -8
  78. package/dist/stores/__tests__/session-feedback.store.test.js.map +1 -1
  79. package/dist/stores/chatbot-ui-backend.store.d.ts +26 -2
  80. package/dist/stores/chatbot-ui-backend.store.d.ts.map +1 -1
  81. package/dist/stores/chatbot-ui-backend.store.js +295 -239
  82. package/dist/stores/chatbot-ui-backend.store.js.map +1 -1
  83. package/dist/stores/chatbot-ui.store.js +73 -46
  84. package/dist/stores/chatbot-ui.store.js.map +1 -1
  85. package/dist/stores/filter.store.js +298 -378
  86. package/dist/stores/filter.store.js.map +1 -1
  87. package/dist/stores/index.d.ts +5 -3
  88. package/dist/stores/index.d.ts.map +1 -1
  89. package/dist/stores/index.js +3 -2
  90. package/dist/stores/index.js.map +1 -1
  91. package/dist/stores/initialize.store.js +55 -51
  92. package/dist/stores/initialize.store.js.map +1 -1
  93. package/dist/stores/message-feedback-base.store.js +2 -1
  94. package/dist/stores/message-feedback-base.store.js.map +1 -1
  95. package/dist/stores/message-feedback-guardrail.store.js +50 -47
  96. package/dist/stores/message-feedback-guardrail.store.js.map +1 -1
  97. package/dist/stores/message-feedback.store.js +84 -89
  98. package/dist/stores/message-feedback.store.js.map +1 -1
  99. package/dist/stores/session-feedback.store.js +46 -39
  100. package/dist/stores/session-feedback.store.js.map +1 -1
  101. package/dist/streaming/__tests__/agent-stream.test.d.ts +2 -0
  102. package/dist/streaming/__tests__/agent-stream.test.d.ts.map +1 -0
  103. package/dist/streaming/__tests__/agent-stream.test.js +92 -0
  104. package/dist/streaming/__tests__/agent-stream.test.js.map +1 -0
  105. package/dist/streaming/agent-stream.d.ts +83 -0
  106. package/dist/streaming/agent-stream.d.ts.map +1 -0
  107. package/dist/streaming/agent-stream.js +28 -0
  108. package/dist/streaming/agent-stream.js.map +1 -0
  109. package/dist/streaming/index.d.ts +3 -0
  110. package/dist/streaming/index.d.ts.map +1 -0
  111. package/dist/streaming/index.js +4 -0
  112. package/dist/streaming/index.js.map +1 -0
  113. package/dist/streaming/run-agent-stream.d.ts +23 -0
  114. package/dist/streaming/run-agent-stream.d.ts.map +1 -0
  115. package/dist/streaming/run-agent-stream.js +83 -0
  116. package/dist/streaming/run-agent-stream.js.map +1 -0
  117. package/dist/utils/__tests__/axios-utils.test.js +8 -7
  118. package/dist/utils/__tests__/axios-utils.test.js.map +1 -1
  119. package/dist/utils/axios-utils.js +9 -7
  120. package/dist/utils/axios-utils.js.map +1 -1
  121. package/dist/utils/test-utils.js +5 -5
  122. package/dist/utils/test-utils.js.map +1 -1
  123. package/package.json +6 -3
  124. package/src/api-client/__mocks__/chatbot-api-client.mock.ts +1 -0
  125. package/src/api-client/base/chatbot-api-client.ts +11 -0
  126. package/src/api-client/index.ts +2 -7
  127. package/src/api-client/models/index.ts +15 -13
  128. package/src/api-client/titan-chat/__tests__/chatbot-api-client-stream.test.ts +208 -0
  129. package/src/api-client/titan-chat/chatbot-api-client.ts +46 -0
  130. package/src/api-client/titan-chat/index.ts +2 -1
  131. package/src/api-client/utils/model-utils.ts +4 -8
  132. package/src/index.ts +7 -2
  133. package/src/models/__tests__/chatbot-customizations.test.ts +26 -0
  134. package/src/models/chatbot-customizations.ts +20 -0
  135. package/src/stores/__tests__/chatbot-ui-backend.store.observability.test.ts +105 -0
  136. package/src/stores/__tests__/chatbot-ui-backend.store.streaming.test.ts +261 -0
  137. package/src/stores/chatbot-ui-backend.store.ts +179 -4
  138. package/src/stores/index.ts +5 -12
  139. package/src/streaming/__tests__/agent-stream.test.ts +80 -0
  140. package/src/streaming/agent-stream.ts +103 -0
  141. package/src/streaming/index.ts +2 -0
  142. package/src/streaming/run-agent-stream.ts +109 -0
  143. package/tsconfig.tsbuildinfo +1 -1
  144. package/dist/api-client/help-center/__tests__/converter-from-models.test.d.ts +0 -2
  145. package/dist/api-client/help-center/__tests__/converter-from-models.test.d.ts.map +0 -1
  146. package/dist/api-client/help-center/__tests__/converter-from-models.test.js +0 -34
  147. package/dist/api-client/help-center/__tests__/converter-from-models.test.js.map +0 -1
  148. package/dist/api-client/help-center/__tests__/converter-to-models.test.d.ts +0 -2
  149. package/dist/api-client/help-center/__tests__/converter-to-models.test.d.ts.map +0 -1
  150. package/dist/api-client/help-center/__tests__/converter-to-models.test.js +0 -82
  151. package/dist/api-client/help-center/__tests__/converter-to-models.test.js.map +0 -1
  152. package/dist/api-client/help-center/chatbot-api-client.d.ts +0 -32
  153. package/dist/api-client/help-center/chatbot-api-client.d.ts.map +0 -1
  154. package/dist/api-client/help-center/chatbot-api-client.js +0 -102
  155. package/dist/api-client/help-center/chatbot-api-client.js.map +0 -1
  156. package/dist/api-client/help-center/converter-from-models.d.ts +0 -13
  157. package/dist/api-client/help-center/converter-from-models.d.ts.map +0 -1
  158. package/dist/api-client/help-center/converter-from-models.js +0 -114
  159. package/dist/api-client/help-center/converter-from-models.js.map +0 -1
  160. package/dist/api-client/help-center/converter-to-models.d.ts +0 -13
  161. package/dist/api-client/help-center/converter-to-models.d.ts.map +0 -1
  162. package/dist/api-client/help-center/converter-to-models.js +0 -98
  163. package/dist/api-client/help-center/converter-to-models.js.map +0 -1
  164. package/dist/api-client/help-center/index.d.ts +0 -2
  165. package/dist/api-client/help-center/index.d.ts.map +0 -1
  166. package/dist/api-client/help-center/index.js +0 -2
  167. package/dist/api-client/help-center/index.js.map +0 -1
  168. package/dist/api-client/help-center/native-client.d.ts +0 -1268
  169. package/dist/api-client/help-center/native-client.d.ts.map +0 -1
  170. package/dist/api-client/help-center/native-client.js +0 -6242
  171. package/dist/api-client/help-center/native-client.js.map +0 -1
  172. package/src/api-client/help-center/__tests__/converter-from-models.test.ts +0 -41
  173. package/src/api-client/help-center/__tests__/converter-to-models.test.ts +0 -89
  174. package/src/api-client/help-center/chatbot-api-client.ts +0 -122
  175. package/src/api-client/help-center/converter-from-models.ts +0 -133
  176. package/src/api-client/help-center/converter-to-models.ts +0 -127
  177. package/src/api-client/help-center/index.ts +0 -1
  178. 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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"}