@servicetitan/titan-chatbot-api 8.0.0 → 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 +12 -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 +1 -0
- 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.map +1 -1
- package/dist/api-client/index.d.ts +0 -1
- package/dist/api-client/index.d.ts.map +1 -1
- package/dist/api-client/index.js +0 -2
- package/dist/api-client/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/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 +29 -0
- package/dist/api-client/titan-chat/chatbot-api-client.js.map +1 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -2
- 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 +6 -1
- package/dist/models/chatbot-customizations.js.map +1 -1
- package/dist/models/index.d.ts +1 -1
- package/dist/models/index.d.ts.map +1 -1
- package/dist/models/index.js +1 -1
- 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/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 +129 -4
- package/dist/stores/chatbot-ui-backend.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/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 +0 -1
- 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/index.ts +6 -1
- package/src/models/__tests__/chatbot-customizations.test.ts +26 -0
- package/src/models/chatbot-customizations.ts +20 -0
- package/src/models/index.ts +1 -1
- 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/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 -67
- 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 -83
- 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 -101
- 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 -117
- 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 -101
- package/dist/api-client/help-center/converter-to-models.js.map +0 -1
- package/dist/api-client/help-center/index.d.ts +0 -3
- package/dist/api-client/help-center/index.d.ts.map +0 -1
- package/dist/api-client/help-center/index.js +0 -3
- 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 -4550
- 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 -2
- package/src/api-client/help-center/native-client.ts +0 -5727
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { describe, expect, test } from '@jest/globals';
|
|
2
|
+
import { isChatbotStreamingEnabled } from '../chatbot-customizations';
|
|
3
|
+
describe('ChatbotCustomizations streaming setting', ()=>{
|
|
4
|
+
test('streaming is disabled when customizations are undefined', ()=>{
|
|
5
|
+
expect(isChatbotStreamingEnabled(undefined)).toBe(false);
|
|
6
|
+
});
|
|
7
|
+
test('streaming is disabled when the streaming setting is absent', ()=>{
|
|
8
|
+
const c = {
|
|
9
|
+
feedback: {
|
|
10
|
+
title: 'x'
|
|
11
|
+
}
|
|
12
|
+
};
|
|
13
|
+
expect(isChatbotStreamingEnabled(c)).toBe(false);
|
|
14
|
+
});
|
|
15
|
+
test('streaming is disabled when explicitly false', ()=>{
|
|
16
|
+
const c = {
|
|
17
|
+
streaming: {
|
|
18
|
+
enabled: false
|
|
19
|
+
}
|
|
20
|
+
};
|
|
21
|
+
expect(isChatbotStreamingEnabled(c)).toBe(false);
|
|
22
|
+
});
|
|
23
|
+
test('streaming is enabled only when explicitly true', ()=>{
|
|
24
|
+
var _c_streaming;
|
|
25
|
+
const c = {
|
|
26
|
+
streaming: {
|
|
27
|
+
enabled: true,
|
|
28
|
+
inactivityTimeoutMs: 16000
|
|
29
|
+
}
|
|
30
|
+
};
|
|
31
|
+
expect(isChatbotStreamingEnabled(c)).toBe(true);
|
|
32
|
+
expect((_c_streaming = c.streaming) === null || _c_streaming === void 0 ? void 0 : _c_streaming.inactivityTimeoutMs).toBe(16000);
|
|
33
|
+
});
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
//# sourceMappingURL=chatbot-customizations.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../src/models/__tests__/chatbot-customizations.test.ts"],"sourcesContent":["import { describe, expect, test } from '@jest/globals';\nimport { ChatbotCustomizations, isChatbotStreamingEnabled } from '../chatbot-customizations';\n\ndescribe('ChatbotCustomizations streaming setting', () => {\n test('streaming is disabled when customizations are undefined', () => {\n expect(isChatbotStreamingEnabled(undefined)).toBe(false);\n });\n\n test('streaming is disabled when the streaming setting is absent', () => {\n const c: ChatbotCustomizations = { feedback: { title: 'x' } };\n expect(isChatbotStreamingEnabled(c)).toBe(false);\n });\n\n test('streaming is disabled when explicitly false', () => {\n const c: ChatbotCustomizations = { streaming: { enabled: false } };\n expect(isChatbotStreamingEnabled(c)).toBe(false);\n });\n\n test('streaming is enabled only when explicitly true', () => {\n const c: ChatbotCustomizations = {\n streaming: { enabled: true, inactivityTimeoutMs: 16_000 },\n };\n expect(isChatbotStreamingEnabled(c)).toBe(true);\n expect(c.streaming?.inactivityTimeoutMs).toBe(16_000);\n });\n});\n"],"names":["describe","expect","test","isChatbotStreamingEnabled","undefined","toBe","c","feedback","title","streaming","enabled","inactivityTimeoutMs"],"mappings":"AAAA,SAASA,QAAQ,EAAEC,MAAM,EAAEC,IAAI,QAAQ,gBAAgB;AACvD,SAAgCC,yBAAyB,QAAQ,4BAA4B;AAE7FH,SAAS,2CAA2C;IAChDE,KAAK,2DAA2D;QAC5DD,OAAOE,0BAA0BC,YAAYC,IAAI,CAAC;IACtD;IAEAH,KAAK,8DAA8D;QAC/D,MAAMI,IAA2B;YAAEC,UAAU;gBAAEC,OAAO;YAAI;QAAE;QAC5DP,OAAOE,0BAA0BG,IAAID,IAAI,CAAC;IAC9C;IAEAH,KAAK,+CAA+C;QAChD,MAAMI,IAA2B;YAAEG,WAAW;gBAAEC,SAAS;YAAM;QAAE;QACjET,OAAOE,0BAA0BG,IAAID,IAAI,CAAC;IAC9C;IAEAH,KAAK,kDAAkD;YAK5CI;QAJP,MAAMA,IAA2B;YAC7BG,WAAW;gBAAEC,SAAS;gBAAMC,qBAAqB;YAAO;QAC5D;QACAV,OAAOE,0BAA0BG,IAAID,IAAI,CAAC;QAC1CJ,QAAOK,eAAAA,EAAEG,SAAS,cAAXH,mCAAAA,aAAaK,mBAAmB,EAAEN,IAAI,CAAC;IAClD;AACJ"}
|
|
@@ -7,6 +7,19 @@ import { ChatCustomizations, ChatError } from '@servicetitan/titan-chat-ui-commo
|
|
|
7
7
|
*/
|
|
8
8
|
export type FilterDefaultSelectionFn = (filterKey: string, optionKey: string) => boolean;
|
|
9
9
|
export type ChatbotCustomizations = ChatCustomizations & Partial<{
|
|
10
|
+
/**
|
|
11
|
+
* Agent-progress SSE streaming. Disabled by default; when disabled (or when the streaming
|
|
12
|
+
* endpoint is unreachable at connect time) the widget falls back to the non-streaming path.
|
|
13
|
+
*/
|
|
14
|
+
streaming?: {
|
|
15
|
+
/** Opt in to the SSE streaming send-path. Defaults to false. */
|
|
16
|
+
enabled?: boolean;
|
|
17
|
+
/**
|
|
18
|
+
* Inactivity threshold (ms) before the "Still working on it…" keepalive is shown.
|
|
19
|
+
* Defaults to 16000 (matching the legacy hard timeout).
|
|
20
|
+
*/
|
|
21
|
+
inactivityTimeoutMs?: number;
|
|
22
|
+
};
|
|
10
23
|
timeouts?: {
|
|
11
24
|
chatbotRequestTimeoutMs?: number;
|
|
12
25
|
};
|
|
@@ -28,4 +41,8 @@ export type ChatbotCustomizations = ChatCustomizations & Partial<{
|
|
|
28
41
|
isDefaultSelected?: FilterDefaultSelectionFn;
|
|
29
42
|
};
|
|
30
43
|
}>;
|
|
44
|
+
/** Default inactivity threshold before the keepalive message is shown (ms). */
|
|
45
|
+
export declare const DEFAULT_STREAMING_INACTIVITY_TIMEOUT_MS = 16000;
|
|
46
|
+
/** Whether the SSE streaming send-path is enabled. Defaults to off. */
|
|
47
|
+
export declare const isChatbotStreamingEnabled: (customizations?: ChatbotCustomizations) => boolean;
|
|
31
48
|
//# sourceMappingURL=chatbot-customizations.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"chatbot-customizations.d.ts","sourceRoot":"","sources":["../../src/models/chatbot-customizations.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,SAAS,EAAE,MAAM,oCAAoC,CAAC;AAEnF;;;;;GAKG;AACH,MAAM,MAAM,wBAAwB,GAAG,CAAC,SAAS,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,KAAK,OAAO,CAAC;AAEzF,MAAM,MAAM,qBAAqB,GAAG,kBAAkB,GAClD,OAAO,CAAC;IACJ,QAAQ,CAAC,EAAE;QACP,uBAAuB,CAAC,EAAE,MAAM,CAAC;KACpC,CAAC;IACF,KAAK,CAAC,EAAE;QACJ,OAAO,EAAE,CAAC,KAAK,CAAC,EAAE,SAAS,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;KACjD,CAAC;IACF,QAAQ,CAAC,EAAE;QACP,KAAK,CAAC,EAAE,MAAM,CAAC;QACf,qBAAqB,CAAC,EAAE,OAAO,CAAC;QAChC,uBAAuB,CAAC,EAAE,OAAO,CAAC;KACrC,CAAC;IACF,OAAO,CAAC,EAAE;QACN,OAAO,CAAC,EAAE,OAAO,CAAC;QAClB,QAAQ,CAAC,EAAE,KAAK,GAAG,QAAQ,CAAC;QAC5B;;;WAGG;QACH,iBAAiB,CAAC,EAAE,wBAAwB,CAAC;KAChD,CAAC;CACL,CAAC,CAAC"}
|
|
1
|
+
{"version":3,"file":"chatbot-customizations.d.ts","sourceRoot":"","sources":["../../src/models/chatbot-customizations.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,SAAS,EAAE,MAAM,oCAAoC,CAAC;AAEnF;;;;;GAKG;AACH,MAAM,MAAM,wBAAwB,GAAG,CAAC,SAAS,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,KAAK,OAAO,CAAC;AAEzF,MAAM,MAAM,qBAAqB,GAAG,kBAAkB,GAClD,OAAO,CAAC;IACJ;;;OAGG;IACH,SAAS,CAAC,EAAE;QACR,gEAAgE;QAChE,OAAO,CAAC,EAAE,OAAO,CAAC;QAClB;;;WAGG;QACH,mBAAmB,CAAC,EAAE,MAAM,CAAC;KAChC,CAAC;IACF,QAAQ,CAAC,EAAE;QACP,uBAAuB,CAAC,EAAE,MAAM,CAAC;KACpC,CAAC;IACF,KAAK,CAAC,EAAE;QACJ,OAAO,EAAE,CAAC,KAAK,CAAC,EAAE,SAAS,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;KACjD,CAAC;IACF,QAAQ,CAAC,EAAE;QACP,KAAK,CAAC,EAAE,MAAM,CAAC;QACf,qBAAqB,CAAC,EAAE,OAAO,CAAC;QAChC,uBAAuB,CAAC,EAAE,OAAO,CAAC;KACrC,CAAC;IACF,OAAO,CAAC,EAAE;QACN,OAAO,CAAC,EAAE,OAAO,CAAC;QAClB,QAAQ,CAAC,EAAE,KAAK,GAAG,QAAQ,CAAC;QAC5B;;;WAGG;QACH,iBAAiB,CAAC,EAAE,wBAAwB,CAAC;KAChD,CAAC;CACL,CAAC,CAAC;AAEP,+EAA+E;AAC/E,eAAO,MAAM,uCAAuC,QAAS,CAAC;AAE9D,uEAAuE;AACvE,eAAO,MAAM,yBAAyB,GAAI,iBAAiB,qBAAqB,KAAG,OACpC,CAAC"}
|
|
@@ -1,3 +1,8 @@
|
|
|
1
|
-
export
|
|
1
|
+
/** Default inactivity threshold before the keepalive message is shown (ms). */ export const DEFAULT_STREAMING_INACTIVITY_TIMEOUT_MS = 16000;
|
|
2
|
+
/** Whether the SSE streaming send-path is enabled. Defaults to off. */ export const isChatbotStreamingEnabled = (customizations)=>{
|
|
3
|
+
var _ref;
|
|
4
|
+
var _customizations_streaming;
|
|
5
|
+
return (_ref = customizations === null || customizations === void 0 ? void 0 : (_customizations_streaming = customizations.streaming) === null || _customizations_streaming === void 0 ? void 0 : _customizations_streaming.enabled) !== null && _ref !== void 0 ? _ref : false;
|
|
6
|
+
};
|
|
2
7
|
|
|
3
8
|
//# sourceMappingURL=chatbot-customizations.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/models/chatbot-customizations.ts"],"sourcesContent":["import { ChatCustomizations, ChatError } from '@servicetitan/titan-chat-ui-common';\n\n/**\n * Customization function to determine if a filter option should be selected by default.\n * @param filterKey - The key of the filter (e.g., 'Sources', 'ContentTypes', 'ProductAreas')\n * @param optionKey - The key of the option within the filter\n * @returns true if the option should be selected by default, false otherwise\n */\nexport type FilterDefaultSelectionFn = (filterKey: string, optionKey: string) => boolean;\n\nexport type ChatbotCustomizations = ChatCustomizations &\n Partial<{\n timeouts?: {\n chatbotRequestTimeoutMs?: number;\n };\n error?: {\n recover: (error?: ChatError) => Promise<void>;\n };\n feedback?: {\n title?: string;\n showGuardrailFeedback?: boolean;\n isCommentAlwaysRequired?: boolean;\n };\n filters?: {\n enabled?: boolean;\n position?: 'top' | 'bottom';\n /**\n * Function to determine if a filter option should be selected by default.\n * Called for each filter option during initialization.\n */\n isDefaultSelected?: FilterDefaultSelectionFn;\n };\n }>;\n"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"sources":["../../src/models/chatbot-customizations.ts"],"sourcesContent":["import { ChatCustomizations, ChatError } from '@servicetitan/titan-chat-ui-common';\n\n/**\n * Customization function to determine if a filter option should be selected by default.\n * @param filterKey - The key of the filter (e.g., 'Sources', 'ContentTypes', 'ProductAreas')\n * @param optionKey - The key of the option within the filter\n * @returns true if the option should be selected by default, false otherwise\n */\nexport type FilterDefaultSelectionFn = (filterKey: string, optionKey: string) => boolean;\n\nexport type ChatbotCustomizations = ChatCustomizations &\n Partial<{\n /**\n * Agent-progress SSE streaming. Disabled by default; when disabled (or when the streaming\n * endpoint is unreachable at connect time) the widget falls back to the non-streaming path.\n */\n streaming?: {\n /** Opt in to the SSE streaming send-path. Defaults to false. */\n enabled?: boolean;\n /**\n * Inactivity threshold (ms) before the \"Still working on it…\" keepalive is shown.\n * Defaults to 16000 (matching the legacy hard timeout).\n */\n inactivityTimeoutMs?: number;\n };\n timeouts?: {\n chatbotRequestTimeoutMs?: number;\n };\n error?: {\n recover: (error?: ChatError) => Promise<void>;\n };\n feedback?: {\n title?: string;\n showGuardrailFeedback?: boolean;\n isCommentAlwaysRequired?: boolean;\n };\n filters?: {\n enabled?: boolean;\n position?: 'top' | 'bottom';\n /**\n * Function to determine if a filter option should be selected by default.\n * Called for each filter option during initialization.\n */\n isDefaultSelected?: FilterDefaultSelectionFn;\n };\n }>;\n\n/** Default inactivity threshold before the keepalive message is shown (ms). */\nexport const DEFAULT_STREAMING_INACTIVITY_TIMEOUT_MS = 16_000;\n\n/** Whether the SSE streaming send-path is enabled. Defaults to off. */\nexport const isChatbotStreamingEnabled = (customizations?: ChatbotCustomizations): boolean =>\n customizations?.streaming?.enabled ?? false;\n"],"names":["DEFAULT_STREAMING_INACTIVITY_TIMEOUT_MS","isChatbotStreamingEnabled","customizations","streaming","enabled"],"mappings":"AA+CA,6EAA6E,GAC7E,OAAO,MAAMA,0CAA0C,MAAO;AAE9D,qEAAqE,GACrE,OAAO,MAAMC,4BAA4B,CAACC;;QACtCA;mBAAAA,2BAAAA,sCAAAA,4BAAAA,eAAgBC,SAAS,cAAzBD,gDAAAA,0BAA2BE,OAAO,uCAAI;EAAM"}
|
package/dist/models/index.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export
|
|
1
|
+
export * from './chatbot-customizations';
|
|
2
2
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/models/index.ts"],"names":[],"mappings":"AAAA,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/models/index.ts"],"names":[],"mappings":"AAAA,cAAc,0BAA0B,CAAC"}
|
package/dist/models/index.js
CHANGED
package/dist/models/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/models/index.ts"],"sourcesContent":["export
|
|
1
|
+
{"version":3,"sources":["../../src/models/index.ts"],"sourcesContent":["export * from './chatbot-customizations';\n"],"names":[],"mappings":"AAAA,cAAc,2BAA2B"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"chatbot-ui-backend.store.observability.test.d.ts","sourceRoot":"","sources":["../../../src/stores/__tests__/chatbot-ui-backend.store.observability.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,107 @@
|
|
|
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 { initTestContainer } from '../../utils/test-utils';
|
|
19
|
+
import { ChatbotUiBackendStore } from '../chatbot-ui-backend.store';
|
|
20
|
+
import { CHATBOT_UI_STORE_TOKEN, ChatbotUiStore } from '../chatbot-ui.store';
|
|
21
|
+
const initContainer = initTestContainer(ChatbotUiBackendStore, (container)=>{
|
|
22
|
+
container.bind(Log).to(class {
|
|
23
|
+
constructor(){
|
|
24
|
+
_define_property(this, "error", jest.fn());
|
|
25
|
+
_define_property(this, "info", jest.fn());
|
|
26
|
+
_define_property(this, "warning", jest.fn());
|
|
27
|
+
}
|
|
28
|
+
}).inSingletonScope();
|
|
29
|
+
container.bind(CHATBOT_API_CLIENT).toConstantValue(new ChatbotApiClientMock());
|
|
30
|
+
container.bind(CHATBOT_UI_STORE_TOKEN).to(ChatbotUiStore).inSingletonScope();
|
|
31
|
+
});
|
|
32
|
+
describe('[ChatbotUiBackendStore] streaming observability', ()=>{
|
|
33
|
+
let container;
|
|
34
|
+
let store;
|
|
35
|
+
let chatbotApi;
|
|
36
|
+
let chatUiStore;
|
|
37
|
+
let log;
|
|
38
|
+
const runListener = async (listener, args)=>new Promise((resolve, reject)=>listener.apply(store, [
|
|
39
|
+
resolve,
|
|
40
|
+
reject,
|
|
41
|
+
...args
|
|
42
|
+
]));
|
|
43
|
+
const sendStreamed = async ()=>{
|
|
44
|
+
chatUiStore.setCustomizationContext({
|
|
45
|
+
streaming: {
|
|
46
|
+
enabled: true
|
|
47
|
+
}
|
|
48
|
+
});
|
|
49
|
+
await chatUiStore.sendMessageText('q');
|
|
50
|
+
const message = chatUiStore.messages.at(-1);
|
|
51
|
+
await runListener(store.handleMessageSend, [
|
|
52
|
+
message
|
|
53
|
+
]);
|
|
54
|
+
};
|
|
55
|
+
const codesLogged = ()=>log.info.mock.calls.map((c)=>c[0].code);
|
|
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
|
+
log = container.get(Log);
|
|
62
|
+
chatbotApi.getOptions.mockResolvedValue(ModelsMocks.mockFrontendModel());
|
|
63
|
+
chatbotApi.postSession.mockResolvedValue(ModelsMocks.mockSession());
|
|
64
|
+
});
|
|
65
|
+
test('logs the connection lifecycle (connected → completed)', async ()=>{
|
|
66
|
+
chatbotApi.streamMessage.mockImplementation((_m, h)=>{
|
|
67
|
+
var _h_onConnected, _h_onCompleted;
|
|
68
|
+
(_h_onConnected = h.onConnected) === null || _h_onConnected === void 0 ? void 0 : _h_onConnected.call(h);
|
|
69
|
+
(_h_onCompleted = h.onCompleted) === null || _h_onCompleted === void 0 ? void 0 : _h_onCompleted.call(h);
|
|
70
|
+
return ModelsMocks.mockBotMessage({
|
|
71
|
+
answer: 'done'
|
|
72
|
+
});
|
|
73
|
+
});
|
|
74
|
+
await sendStreamed();
|
|
75
|
+
const codes = codesLogged();
|
|
76
|
+
expect(codes).toContain('TitanChatbot_Streaming_connected');
|
|
77
|
+
expect(codes).toContain('TitanChatbot_Streaming_completed');
|
|
78
|
+
});
|
|
79
|
+
test('logs disconnected and timed_out lifecycle phases', async ()=>{
|
|
80
|
+
chatbotApi.streamMessage.mockImplementation((_m, h)=>{
|
|
81
|
+
var _h_onConnected, _h_onDisconnected, _h_onTimeout;
|
|
82
|
+
(_h_onConnected = h.onConnected) === null || _h_onConnected === void 0 ? void 0 : _h_onConnected.call(h);
|
|
83
|
+
(_h_onDisconnected = h.onDisconnected) === null || _h_onDisconnected === void 0 ? void 0 : _h_onDisconnected.call(h);
|
|
84
|
+
(_h_onTimeout = h.onTimeout) === null || _h_onTimeout === void 0 ? void 0 : _h_onTimeout.call(h);
|
|
85
|
+
return ModelsMocks.mockBotMessage({
|
|
86
|
+
answer: 'done'
|
|
87
|
+
});
|
|
88
|
+
});
|
|
89
|
+
await sendStreamed();
|
|
90
|
+
const codes = codesLogged();
|
|
91
|
+
expect(codes).toContain('TitanChatbot_Streaming_disconnected');
|
|
92
|
+
expect(codes).toContain('TitanChatbot_Streaming_timed_out');
|
|
93
|
+
});
|
|
94
|
+
test('tracks fallback separately and does not log a completed streaming run', async ()=>{
|
|
95
|
+
chatbotApi.streamMessage.mockRejectedValue(new Error('unreachable'));
|
|
96
|
+
chatbotApi.postMessage.mockResolvedValue(ModelsMocks.mockBotMessage({
|
|
97
|
+
answer: 'fallback'
|
|
98
|
+
}));
|
|
99
|
+
await sendStreamed();
|
|
100
|
+
expect(store.streamingFallbackCount).toBe(1);
|
|
101
|
+
const codes = codesLogged();
|
|
102
|
+
expect(codes).toContain('TitanChatbot_Streaming_fallback');
|
|
103
|
+
expect(codes).not.toContain('TitanChatbot_Streaming_completed');
|
|
104
|
+
});
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
//# sourceMappingURL=chatbot-ui-backend.store.observability.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../src/stores/__tests__/chatbot-ui-backend.store.observability.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 { 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 observability', () => {\n let container: Container;\n let store: ChatbotUiBackendStore;\n let chatbotApi: ChatbotApiClientMock;\n let chatUiStore: ChatbotUiStore;\n let log: ILog;\n\n const runListener = async (listener: ChatUiEventListener, args: unknown[]) =>\n new Promise<void>((resolve, reject) => listener.apply(store, [resolve, reject, ...args]));\n\n const sendStreamed = async () => {\n chatUiStore.setCustomizationContext({ streaming: { enabled: true } });\n await chatUiStore.sendMessageText('q');\n const message = chatUiStore.messages.at(-1)! as ChatMessageModelText;\n await runListener(store.handleMessageSend, [message]);\n };\n\n const codesLogged = () =>\n (log.info as jest.Mock).mock.calls.map(c => (c[0] as { code: string }).code);\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 log = container.get<ILog>(Log);\n chatbotApi.getOptions.mockResolvedValue(ModelsMocks.mockFrontendModel());\n chatbotApi.postSession.mockResolvedValue(ModelsMocks.mockSession());\n });\n\n test('logs the connection lifecycle (connected → completed)', async () => {\n chatbotApi.streamMessage.mockImplementation(\n (_m: Models.IUserMessage, h: AgentStreamHandlers) => {\n h.onConnected?.();\n h.onCompleted?.();\n return ModelsMocks.mockBotMessage({ answer: 'done' });\n }\n );\n\n await sendStreamed();\n\n const codes = codesLogged();\n expect(codes).toContain('TitanChatbot_Streaming_connected');\n expect(codes).toContain('TitanChatbot_Streaming_completed');\n });\n\n test('logs disconnected and timed_out lifecycle phases', async () => {\n chatbotApi.streamMessage.mockImplementation(\n (_m: Models.IUserMessage, h: AgentStreamHandlers) => {\n h.onConnected?.();\n h.onDisconnected?.();\n h.onTimeout?.();\n return ModelsMocks.mockBotMessage({ answer: 'done' });\n }\n );\n\n await sendStreamed();\n\n const codes = codesLogged();\n expect(codes).toContain('TitanChatbot_Streaming_disconnected');\n expect(codes).toContain('TitanChatbot_Streaming_timed_out');\n });\n\n test('tracks fallback separately and does not log a completed streaming run', async () => {\n chatbotApi.streamMessage.mockRejectedValue(new Error('unreachable'));\n chatbotApi.postMessage.mockResolvedValue(\n ModelsMocks.mockBotMessage({ answer: 'fallback' })\n );\n\n await sendStreamed();\n\n expect(store.streamingFallbackCount).toBe(1);\n const codes = codesLogged();\n expect(codes).toContain('TitanChatbot_Streaming_fallback');\n expect(codes).not.toContain('TitanChatbot_Streaming_completed');\n });\n});\n"],"names":["expect","Log","CHATBOT_API_CLIENT","ModelsMocks","ChatbotApiClientMock","initTestContainer","ChatbotUiBackendStore","CHATBOT_UI_STORE_TOKEN","ChatbotUiStore","initContainer","container","bind","to","error","jest","fn","info","warning","inSingletonScope","toConstantValue","describe","store","chatbotApi","chatUiStore","log","runListener","listener","args","Promise","resolve","reject","apply","sendStreamed","setCustomizationContext","streaming","enabled","sendMessageText","message","messages","at","handleMessageSend","codesLogged","mock","calls","map","c","code","beforeEach","get","getOptions","mockResolvedValue","mockFrontendModel","postSession","mockSession","test","streamMessage","mockImplementation","_m","h","onConnected","onCompleted","mockBotMessage","answer","codes","toContain","onDisconnected","onTimeout","mockRejectedValue","Error","postMessage","streamingFallbackCount","toBe","not"],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"chatbot-ui-backend.store.streaming.test.d.ts","sourceRoot":"","sources":["../../../src/stores/__tests__/chatbot-ui-backend.store.streaming.test.ts"],"names":[],"mappings":""}
|
|
@@ -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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"}
|