@constructor-io/constructorio-ui-asa 1.7.2 → 1.9.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/README.md +21 -17
- package/dist/constructorio-ui-asa-bundled.js +17 -17
- package/lib/cjs/components/Button/Button.js +22 -3
- package/lib/cjs/components/Chat/AiMessage.js +2 -3
- package/lib/cjs/components/Chat/Chat.js +50 -9
- package/lib/cjs/components/CioAsa/CioAsa.js +6 -2
- package/lib/cjs/components/CioAsaProvider/CioAsaProvider.js +44 -3
- package/lib/cjs/components/ResultsBlock/ResultsGroup.js +5 -5
- package/lib/cjs/hooks/agentStream.js +113 -0
- package/lib/cjs/hooks/asaStreamHandlers.js +2 -1
- package/lib/cjs/hooks/persistence/backgroundTurns.js +121 -0
- package/lib/cjs/hooks/persistence/useChatPersistence.js +485 -0
- package/lib/cjs/hooks/persistence/useForeignStream.js +34 -0
- package/lib/cjs/hooks/persistence/useThreadList.js +45 -0
- package/lib/cjs/hooks/persistence/useWriteQueue.js +17 -0
- package/lib/cjs/hooks/useAsaResults.js +119 -159
- package/lib/cjs/hooks/useAsaTracking.js +33 -18
- package/lib/cjs/hooks/useIsMounted.js +15 -0
- package/lib/cjs/hooks/useLatest.js +10 -0
- package/lib/cjs/hooks/useViewportTracking.js +4 -3
- package/lib/cjs/index.js +3 -1
- package/lib/cjs/utils/chatRecordValidation.js +54 -0
- package/lib/cjs/utils/chatSession.js +98 -0
- package/lib/cjs/utils/chatThreads.js +248 -0
- package/lib/cjs/utils/clientOptions.js +11 -0
- package/lib/cjs/utils/localStoragePersistence.js +391 -0
- package/lib/cjs/utils/translate.js +1 -0
- package/lib/cjs/version.js +1 -1
- package/lib/mjs/components/Button/Button.js +26 -3
- package/lib/mjs/components/Chat/AiMessage.js +2 -3
- package/lib/mjs/components/Chat/Chat.js +48 -9
- package/lib/mjs/components/CioAsa/CioAsa.js +6 -2
- package/lib/mjs/components/CioAsaProvider/CioAsaProvider.js +44 -4
- package/lib/mjs/components/ResultsBlock/ResultsGroup.js +2 -1
- package/lib/mjs/hooks/agentStream.js +106 -0
- package/lib/mjs/hooks/asaStreamHandlers.js +1 -1
- package/lib/mjs/hooks/persistence/backgroundTurns.js +120 -0
- package/lib/mjs/hooks/persistence/useChatPersistence.js +474 -0
- package/lib/mjs/hooks/persistence/useForeignStream.js +31 -0
- package/lib/mjs/hooks/persistence/useThreadList.js +41 -0
- package/lib/mjs/hooks/persistence/useWriteQueue.js +14 -0
- package/lib/mjs/hooks/useAsaResults.js +113 -156
- package/lib/mjs/hooks/useAsaTracking.js +42 -17
- package/lib/mjs/hooks/useIsMounted.js +12 -0
- package/lib/mjs/hooks/useLatest.js +7 -0
- package/lib/mjs/hooks/useViewportTracking.js +3 -3
- package/lib/mjs/index.js +1 -0
- package/lib/mjs/utils/chatRecordValidation.js +51 -0
- package/lib/mjs/utils/chatSession.js +86 -0
- package/lib/mjs/utils/chatThreads.js +219 -0
- package/lib/mjs/utils/clientOptions.js +7 -0
- package/lib/mjs/utils/localStoragePersistence.js +373 -0
- package/lib/mjs/utils/translate.js +1 -0
- package/lib/mjs/version.js +1 -1
- package/lib/types/components/Button/Button.d.ts +3 -2
- package/lib/types/components/Chat/Chat.d.ts +20 -2
- package/lib/types/components/CioAsa/CioAsa.d.ts +1 -1
- package/lib/types/hooks/agentStream.d.ts +48 -0
- package/lib/types/hooks/asaStreamHandlers.d.ts +1 -0
- package/lib/types/hooks/persistence/backgroundTurns.d.ts +43 -0
- package/lib/types/hooks/persistence/useChatPersistence.d.ts +34 -0
- package/lib/types/hooks/persistence/useForeignStream.d.ts +9 -0
- package/lib/types/hooks/persistence/useThreadList.d.ts +9 -0
- package/lib/types/hooks/persistence/useWriteQueue.d.ts +5 -0
- package/lib/types/hooks/useAsaTracking.d.ts +6 -2
- package/lib/types/hooks/useIsMounted.d.ts +3 -0
- package/lib/types/hooks/useLatest.d.ts +3 -0
- package/lib/types/index.d.ts +3 -2
- package/lib/types/types.d.ts +170 -5
- package/lib/types/utils/chatRecordValidation.d.ts +2 -0
- package/lib/types/utils/chatSession.d.ts +45 -0
- package/lib/types/utils/chatThreads.d.ts +63 -0
- package/lib/types/utils/clientOptions.d.ts +5 -0
- package/lib/types/utils/localStoragePersistence.d.ts +26 -0
- package/lib/types/version.d.ts +1 -1
- package/package.json +2 -2
|
@@ -3,189 +3,143 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
const tslib_1 = require("tslib");
|
|
4
4
|
const react_1 = require("react");
|
|
5
5
|
const useCioAsaContext_1 = require("./useCioAsaContext");
|
|
6
|
-
const asaStreamHandlers_1 = require("./asaStreamHandlers");
|
|
7
6
|
const useAsaTracking_1 = tslib_1.__importDefault(require("./useAsaTracking"));
|
|
7
|
+
const useChatPersistence_1 = tslib_1.__importDefault(require("./persistence/useChatPersistence"));
|
|
8
|
+
const agentStream_1 = require("./agentStream");
|
|
9
|
+
const chatSession_1 = require("../utils/chatSession");
|
|
10
|
+
const productNormalizer_1 = require("../utils/productNormalizer");
|
|
11
|
+
const chatThreads_1 = require("../utils/chatThreads");
|
|
12
|
+
const useLatest_1 = tslib_1.__importDefault(require("./useLatest"));
|
|
8
13
|
function useAsaResults(options) {
|
|
9
|
-
var _a
|
|
10
|
-
const [messages, setMessages] = (0, react_1.useState)([]);
|
|
11
|
-
const [isStreaming, setIsStreaming] = (0, react_1.useState)(false);
|
|
12
|
-
const isStreamingRef = (0, react_1.useRef)(false);
|
|
13
|
-
const killSwitchRef = (0, react_1.useRef)(false);
|
|
14
|
-
const readerRef = (0, react_1.useRef)(null);
|
|
15
|
-
const threadIdRef = (0, react_1.useRef)((_a = options === null || options === void 0 ? void 0 : options.initialThreadId) !== null && _a !== void 0 ? _a : null);
|
|
16
|
-
const idCounterRef = (0, react_1.useRef)(0);
|
|
17
|
-
// The assistant message the in-flight stream is writing into, so `abort` can
|
|
18
|
-
// settle it without having to guess which message that is.
|
|
19
|
-
const activeAssistantIdRef = (0, react_1.useRef)(null);
|
|
14
|
+
var _a;
|
|
20
15
|
const contextValue = (0, useCioAsaContext_1.useCioAsaContext)();
|
|
21
16
|
if (!contextValue) {
|
|
22
17
|
throw new Error('useAsaResults must be used within a CioAsaProvider.');
|
|
23
18
|
}
|
|
24
|
-
const { cioClient, staticRequestConfigs, callbacks, section } = contextValue;
|
|
19
|
+
const { cioClient, staticRequestConfigs, callbacks, section, persistence, persistenceScope, persistenceIndex, } = contextValue;
|
|
25
20
|
const { domain } = staticRequestConfigs || {};
|
|
26
21
|
if (!cioClient || !domain) {
|
|
27
22
|
throw new Error('useAsaResults requires a configured cioClient and domain. Check your CioAsaProvider props.');
|
|
28
23
|
}
|
|
24
|
+
const [messages, setMessages] = (0, react_1.useState)([]);
|
|
25
|
+
const [isStreaming, setIsStreaming] = (0, react_1.useState)(false);
|
|
26
|
+
const normalizeItemRef = (0, useLatest_1.default)(options === null || options === void 0 ? void 0 : options.normalizeItem);
|
|
27
|
+
const sessionRef = (0, react_1.useRef)(null);
|
|
28
|
+
if (!sessionRef.current) {
|
|
29
|
+
sessionRef.current = (0, chatSession_1.createChatSession)(options === null || options === void 0 ? void 0 : options.initialThreadId, (item) => { var _a; return ((_a = normalizeItemRef.current) !== null && _a !== void 0 ? _a : productNormalizer_1.normalizeItemToProduct)(item); });
|
|
30
|
+
}
|
|
31
|
+
const session = sessionRef.current;
|
|
32
|
+
const turnRef = (0, react_1.useRef)(null);
|
|
29
33
|
const tracking = (0, useAsaTracking_1.default)({
|
|
30
34
|
tracker: cioClient.tracker,
|
|
31
35
|
section,
|
|
32
|
-
threadId: (
|
|
36
|
+
threadId: (_a = session.serverThreadId) !== null && _a !== void 0 ? _a : undefined,
|
|
33
37
|
});
|
|
34
|
-
const trackingRef = (0,
|
|
35
|
-
|
|
36
|
-
const
|
|
37
|
-
|
|
38
|
-
const
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
* cancelling makes the pending `reader.read()` resolve `{ done: true }` — identical
|
|
43
|
-
* to a stream that ended on its own. Without the flag already set, the read loop
|
|
44
|
-
* would take the end-of-stream branch and report a load that never finished.
|
|
45
|
-
*
|
|
46
|
-
* Deliberately touches no state, so the unmount cleanup can share it.
|
|
47
|
-
*/
|
|
48
|
-
const teardownStream = (0, react_1.useCallback)(() => {
|
|
49
|
-
killSwitchRef.current = true;
|
|
50
|
-
if (readerRef.current) {
|
|
51
|
-
readerRef.current.cancel();
|
|
52
|
-
readerRef.current = null;
|
|
53
|
-
}
|
|
38
|
+
const trackingRef = (0, useLatest_1.default)(tracking);
|
|
39
|
+
const callbacksRef = (0, useLatest_1.default)(callbacks);
|
|
40
|
+
const staticRequestConfigsRef = (0, useLatest_1.default)(staticRequestConfigs);
|
|
41
|
+
/** Stops this tab's stream, if any. `readAgentStream` settles its `done` without touching state. */
|
|
42
|
+
const cancelStream = (0, react_1.useCallback)(() => {
|
|
43
|
+
var _a;
|
|
44
|
+
(_a = turnRef.current) === null || _a === void 0 ? void 0 : _a.cancel();
|
|
45
|
+
turnRef.current = null;
|
|
54
46
|
}, []);
|
|
55
|
-
const
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
47
|
+
const store = (0, useChatPersistence_1.default)({
|
|
48
|
+
store: persistence,
|
|
49
|
+
scope: persistenceScope,
|
|
50
|
+
index: persistenceIndex,
|
|
51
|
+
session,
|
|
52
|
+
initialThreadId: options === null || options === void 0 ? void 0 : options.initialThreadId,
|
|
53
|
+
messages,
|
|
54
|
+
setMessages,
|
|
55
|
+
isStreaming,
|
|
56
|
+
setIsStreaming,
|
|
57
|
+
turnRef,
|
|
58
|
+
cancelStream,
|
|
59
|
+
});
|
|
60
|
+
const { beginTurn, screenOwner } = store;
|
|
61
|
+
(0, react_1.useEffect)(() => cancelStream, [cancelStream]);
|
|
62
|
+
const sendToCurrentThread = (0, react_1.useCallback)((text, source) => {
|
|
60
63
|
var _a, _b;
|
|
61
64
|
const intent = text.trim();
|
|
62
|
-
if (!intent ||
|
|
65
|
+
if (!intent || session.isStreaming || session.foreignInFlight)
|
|
63
66
|
return;
|
|
64
|
-
|
|
67
|
+
beginTurn();
|
|
68
|
+
trackingRef.current.trackSubmit(intent, source);
|
|
65
69
|
(_b = (_a = callbacksRef.current) === null || _a === void 0 ? void 0 : _a.onAssistantSubmit) === null || _b === void 0 ? void 0 : _b.call(_a, { intent, source });
|
|
66
70
|
const userMessage = {
|
|
67
|
-
id: nextMessageId(),
|
|
71
|
+
id: (0, chatSession_1.nextMessageId)(session),
|
|
68
72
|
role: 'user',
|
|
69
73
|
text: intent,
|
|
70
74
|
status: 'done',
|
|
71
75
|
};
|
|
72
76
|
const assistantMessage = {
|
|
73
|
-
id: nextMessageId(),
|
|
77
|
+
id: (0, chatSession_1.nextMessageId)(session),
|
|
74
78
|
role: 'assistant',
|
|
75
79
|
text: '',
|
|
76
80
|
groups: [],
|
|
77
81
|
status: 'loading',
|
|
78
82
|
intent,
|
|
79
83
|
};
|
|
80
|
-
activeAssistantIdRef.current = assistantMessage.id;
|
|
81
84
|
setMessages((prev) => [...prev, userMessage, assistantMessage]);
|
|
82
85
|
setIsStreaming(true);
|
|
83
|
-
|
|
84
|
-
killSwitchRef.current = false;
|
|
85
|
-
// `intent` is passed as the first argument and `threadId` is managed per-stream
|
|
86
|
-
// below, so both are stripped here. Everything else configured on the provider
|
|
87
|
-
// (filters, guard, numResultsPerEvent, numResultEvents, qsParam,
|
|
88
|
-
// preFilterExpression, fmtOptions, ...) is forwarded to the agent as-is.
|
|
86
|
+
session.isStreaming = true;
|
|
89
87
|
const agentParams = Object.assign({}, staticRequestConfigsRef.current);
|
|
90
88
|
delete agentParams.intent;
|
|
91
89
|
delete agentParams.threadId;
|
|
92
|
-
const stream = cioClient.agent.getAgentResultsStream(intent, Object.assign(Object.assign(Object.assign({}, agentParams), { domain }), (
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
90
|
+
const stream = cioClient.agent.getAgentResultsStream(intent, Object.assign(Object.assign(Object.assign({}, agentParams), { domain }), (session.serverThreadId && { threadId: session.serverThreadId })));
|
|
91
|
+
turnRef.current = (0, agentStream_1.startTurn)(stream, assistantMessage, screenOwner, {
|
|
92
|
+
onLoadStart: (intentResultId) => {
|
|
93
|
+
var _a, _b;
|
|
94
|
+
const args = { intent, intentResultId };
|
|
95
|
+
trackingRef.current.trackResultLoadStarted(args);
|
|
96
|
+
(_b = (_a = callbacksRef.current) === null || _a === void 0 ? void 0 : _a.onResultLoadStart) === null || _b === void 0 ? void 0 : _b.call(_a, args);
|
|
97
|
+
},
|
|
98
|
+
onFinish: (_a) => {
|
|
99
|
+
var _b, _c;
|
|
100
|
+
var { threadId } = _a, result = tslib_1.__rest(_a, ["threadId"]);
|
|
101
|
+
const args = Object.assign({ intent }, result);
|
|
102
|
+
// The turn may have finished in the background, after the chat moved to another thread.
|
|
103
|
+
trackingRef.current.trackResultLoadFinished(Object.assign(Object.assign({}, args), { threadId }));
|
|
104
|
+
(_c = (_b = callbacksRef.current) === null || _b === void 0 ? void 0 : _b.onResultLoadFinish) === null || _c === void 0 ? void 0 : _c.call(_b, args);
|
|
105
|
+
},
|
|
106
|
+
});
|
|
107
|
+
}, [
|
|
108
|
+
cioClient,
|
|
109
|
+
domain,
|
|
110
|
+
session,
|
|
111
|
+
beginTurn,
|
|
112
|
+
screenOwner,
|
|
113
|
+
trackingRef,
|
|
114
|
+
callbacksRef,
|
|
115
|
+
staticRequestConfigsRef,
|
|
116
|
+
]);
|
|
117
|
+
const { newThread, switchThread } = store;
|
|
118
|
+
/**
|
|
119
|
+
* Sends `text`. With `threadId`, first moves to that conversation, loading it from storage
|
|
120
|
+
* when persistence has it and otherwise continuing it on the server with an empty screen.
|
|
121
|
+
*/
|
|
122
|
+
const sendMessage = (0, react_1.useCallback)((text, source = 'input', { threadId } = {}) => {
|
|
123
|
+
if (!threadId ||
|
|
124
|
+
threadId === session.serverThreadId ||
|
|
125
|
+
threadId === session.storageThreadId) {
|
|
126
|
+
sendToCurrentThread(text, source);
|
|
127
|
+
return;
|
|
128
|
+
}
|
|
115
129
|
(() => tslib_1.__awaiter(this, void 0, void 0, function* () {
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
(0, asaStreamHandlers_1.handleStreamEnd)(assistantMessage.id, setMessages);
|
|
126
|
-
const finishArgs = {
|
|
127
|
-
intent,
|
|
128
|
-
searchResultCount: streamState.groupCount,
|
|
129
|
-
intentResultId: streamState.intentResultId,
|
|
130
|
-
};
|
|
131
|
-
trackingRef.current.trackResultLoadFinished(finishArgs);
|
|
132
|
-
(_d = (_c = callbacksRef.current) === null || _c === void 0 ? void 0 : _c.onResultLoadFinish) === null || _d === void 0 ? void 0 : _d.call(_c, finishArgs);
|
|
133
|
-
break;
|
|
134
|
-
}
|
|
135
|
-
const { type, data } = res.value;
|
|
136
|
-
// `intent_result_id` is shared across the stream; capture it from the first
|
|
137
|
-
// event that carries it so load-start/finish can attribute correctly.
|
|
138
|
-
if (!streamState.intentResultId && (data === null || data === void 0 ? void 0 : data.intent_result_id)) {
|
|
139
|
-
streamState.intentResultId = data.intent_result_id;
|
|
140
|
-
patchAssistant({ intentResultId: streamState.intentResultId });
|
|
141
|
-
}
|
|
142
|
-
if (type === 'start') {
|
|
143
|
-
if (data === null || data === void 0 ? void 0 : data.thread_id) {
|
|
144
|
-
threadIdRef.current = data.thread_id;
|
|
145
|
-
patchAssistant({ threadId: data.thread_id });
|
|
146
|
-
}
|
|
147
|
-
fireLoadStart();
|
|
148
|
-
}
|
|
149
|
-
else if (type === 'group') {
|
|
150
|
-
streamState.pendingGroup = {
|
|
151
|
-
display_name: (_f = (_e = data === null || data === void 0 ? void 0 : data.display_name) !== null && _e !== void 0 ? _e : data === null || data === void 0 ? void 0 : data.group) !== null && _f !== void 0 ? _f : '',
|
|
152
|
-
value: (_h = (_g = data === null || data === void 0 ? void 0 : data.value) !== null && _g !== void 0 ? _g : data === null || data === void 0 ? void 0 : data.group) !== null && _h !== void 0 ? _h : '',
|
|
153
|
-
};
|
|
154
|
-
}
|
|
155
|
-
else if (type === 'search_result') {
|
|
156
|
-
fireLoadStart();
|
|
157
|
-
streamState.groupCount += 1;
|
|
158
|
-
streamState.pendingGroup = (0, asaStreamHandlers_1.handleSearchResult)(data, streamState.pendingGroup, assistantMessage.id, setMessages);
|
|
159
|
-
}
|
|
160
|
-
else if (type === 'message') {
|
|
161
|
-
fireLoadStart();
|
|
162
|
-
(0, asaStreamHandlers_1.handleMessage)(data, assistantMessage.id, setMessages);
|
|
163
|
-
}
|
|
164
|
-
else if (type === 'follow_up_refinement') {
|
|
165
|
-
fireLoadStart();
|
|
166
|
-
(0, asaStreamHandlers_1.handleFollowUpRefinement)(data, assistantMessage.id, setMessages);
|
|
167
|
-
}
|
|
168
|
-
else if (type === 'server_error') {
|
|
169
|
-
(0, asaStreamHandlers_1.handleServerError)(assistantMessage.id, setMessages);
|
|
170
|
-
break;
|
|
171
|
-
}
|
|
172
|
-
}
|
|
173
|
-
}
|
|
174
|
-
catch (_j) {
|
|
175
|
-
(0, asaStreamHandlers_1.handleStreamError)(assistantMessage.id, setMessages);
|
|
176
|
-
}
|
|
177
|
-
finally {
|
|
178
|
-
if (readerRef.current === reader) {
|
|
179
|
-
reader.cancel();
|
|
180
|
-
readerRef.current = null;
|
|
181
|
-
activeAssistantIdRef.current = null;
|
|
182
|
-
setIsStreaming(false);
|
|
183
|
-
isStreamingRef.current = false;
|
|
184
|
-
}
|
|
130
|
+
if (persistence)
|
|
131
|
+
yield switchThread(threadId);
|
|
132
|
+
else
|
|
133
|
+
newThread();
|
|
134
|
+
// A turn that started meanwhile (or one still streaming in the background) owns the screen.
|
|
135
|
+
if (session.isStreaming || session.foreignInFlight)
|
|
136
|
+
return;
|
|
137
|
+
if (!session.serverThreadId && !(0, chatThreads_1.isLocalThreadId)(threadId)) {
|
|
138
|
+
session.serverThreadId = threadId;
|
|
185
139
|
}
|
|
140
|
+
sendToCurrentThread(text, source);
|
|
186
141
|
}))();
|
|
187
|
-
}, [
|
|
188
|
-
(0, react_1.useEffect)(() => () => teardownStream(), [teardownStream]);
|
|
142
|
+
}, [session, persistence, newThread, switchThread, sendToCurrentThread]);
|
|
189
143
|
/**
|
|
190
144
|
* Cancels the in-flight request while keeping the conversation: the partial reply is
|
|
191
145
|
* settled as `done` (so the typing indicator stops and streamed text survives) and the
|
|
@@ -202,13 +156,13 @@ function useAsaResults(options) {
|
|
|
202
156
|
* `assistant_result_load_started` with no matching finished event.
|
|
203
157
|
*/
|
|
204
158
|
const abort = (0, react_1.useCallback)(() => {
|
|
205
|
-
|
|
159
|
+
var _a;
|
|
160
|
+
if (!session.isStreaming)
|
|
206
161
|
return;
|
|
207
|
-
|
|
162
|
+
const abortedId = (_a = turnRef.current) === null || _a === void 0 ? void 0 : _a.assistantId;
|
|
163
|
+
cancelStream();
|
|
164
|
+
session.isStreaming = false;
|
|
208
165
|
setIsStreaming(false);
|
|
209
|
-
isStreamingRef.current = false;
|
|
210
|
-
const abortedId = activeAssistantIdRef.current;
|
|
211
|
-
activeAssistantIdRef.current = null;
|
|
212
166
|
if (abortedId) {
|
|
213
167
|
setMessages((prev) => prev.flatMap((msg) => {
|
|
214
168
|
var _a;
|
|
@@ -219,15 +173,21 @@ function useAsaResults(options) {
|
|
|
219
173
|
return [Object.assign(Object.assign({}, msg), { status: 'done' })];
|
|
220
174
|
}));
|
|
221
175
|
}
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
176
|
+
// With persistence on, the cancelled turn is stored as it stands.
|
|
177
|
+
session.dirty = true;
|
|
178
|
+
}, [session, cancelStream]);
|
|
179
|
+
return {
|
|
180
|
+
messages,
|
|
181
|
+
sendMessage,
|
|
182
|
+
isStreaming: isStreaming || store.foreignInFlight,
|
|
183
|
+
canAbort: isStreaming,
|
|
184
|
+
abort,
|
|
185
|
+
clearHistory: store.clearHistory,
|
|
186
|
+
isHydrating: store.isHydrating,
|
|
187
|
+
threads: store.threads,
|
|
188
|
+
activeThreadId: store.activeThreadId,
|
|
189
|
+
newThread,
|
|
190
|
+
switchThread,
|
|
191
|
+
};
|
|
232
192
|
}
|
|
233
193
|
exports.default = useAsaResults;
|
|
@@ -1,7 +1,16 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
const react_1 = require("react");
|
|
4
|
+
/** Beacon `source` values, aligned with the ones the behavioral-actions API documents. */
|
|
5
|
+
const BEACON_SUBMIT_SOURCES = {
|
|
6
|
+
input: 'input',
|
|
7
|
+
suggestion: 'suggestion',
|
|
8
|
+
refinement: 'follow_up',
|
|
9
|
+
// A prompt the host page sent, typically a suggested question clicked outside the chat.
|
|
10
|
+
external: 'suggestion',
|
|
11
|
+
};
|
|
4
12
|
const NOOP_TRACKING = {
|
|
13
|
+
trackAgentButtonClick: () => { },
|
|
5
14
|
trackSubmit: () => { },
|
|
6
15
|
trackResultLoadStarted: () => { },
|
|
7
16
|
trackResultLoadFinished: () => { },
|
|
@@ -15,37 +24,42 @@ const NOOP_TRACKING = {
|
|
|
15
24
|
* Mirrors the tracking-hook pattern used by constructorio-ui-pia.
|
|
16
25
|
*/
|
|
17
26
|
function useAsaTracking({ tracker, section, threadId, }) {
|
|
18
|
-
const assistant = tracker;
|
|
19
27
|
const base = (0, react_1.useMemo)(() => (Object.assign(Object.assign({}, (section && { section })), (threadId && { threadId }))), [section, threadId]);
|
|
20
|
-
const
|
|
21
|
-
|
|
22
|
-
|
|
28
|
+
const trackAgentButtonClick = (0, react_1.useCallback)(({ mode, agentDomain, positionOnPage, pageType, instanceId }) => {
|
|
29
|
+
var _a;
|
|
30
|
+
(_a = tracker === null || tracker === void 0 ? void 0 : tracker.trackAgentButtonClick) === null || _a === void 0 ? void 0 : _a.call(tracker, Object.assign(Object.assign(Object.assign(Object.assign({ mode,
|
|
31
|
+
agentDomain }, (positionOnPage && { positionOnPage })), (pageType && { pageType })), (instanceId && { instanceId })), (section && { section })));
|
|
32
|
+
}, [tracker, section]);
|
|
33
|
+
const trackSubmit = (0, react_1.useCallback)((intent, source) => {
|
|
34
|
+
tracker === null || tracker === void 0 ? void 0 : tracker.trackAssistantSubmit(Object.assign(Object.assign({ intent }, (source && { source: BEACON_SUBMIT_SOURCES[source] })), base));
|
|
35
|
+
}, [tracker, base]);
|
|
23
36
|
const trackResultLoadStarted = (0, react_1.useCallback)(({ intent, intentResultId }) => {
|
|
24
|
-
|
|
25
|
-
}, [
|
|
26
|
-
const trackResultLoadFinished = (0, react_1.useCallback)(({ intent, searchResultCount, intentResultId }) => {
|
|
27
|
-
|
|
28
|
-
searchResultCount }, (intentResultId && { intentResultId })), base));
|
|
29
|
-
}, [
|
|
37
|
+
tracker === null || tracker === void 0 ? void 0 : tracker.trackAssistantResultLoadStarted(Object.assign(Object.assign({ intent }, (intentResultId && { intentResultId })), base));
|
|
38
|
+
}, [tracker, base]);
|
|
39
|
+
const trackResultLoadFinished = (0, react_1.useCallback)(({ intent, searchResultCount, intentResultId, threadId: turnThreadId, }) => {
|
|
40
|
+
tracker === null || tracker === void 0 ? void 0 : tracker.trackAssistantResultLoadFinished(Object.assign(Object.assign(Object.assign({ intent,
|
|
41
|
+
searchResultCount }, (intentResultId && { intentResultId })), base), (turnThreadId && { threadId: turnThreadId })));
|
|
42
|
+
}, [tracker, base]);
|
|
30
43
|
const trackResultClick = (0, react_1.useCallback)(({ intent, searchResultId, intentResultId, itemId, itemName, variationId, }) => {
|
|
31
|
-
|
|
44
|
+
tracker === null || tracker === void 0 ? void 0 : tracker.trackAssistantResultClick(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign({ intent,
|
|
32
45
|
searchResultId }, (intentResultId && { intentResultId })), (itemId && { itemId })), (itemName && { itemName })), (variationId && { variationId })), base));
|
|
33
|
-
}, [
|
|
46
|
+
}, [tracker, base]);
|
|
34
47
|
const trackResultView = (0, react_1.useCallback)(({ intent, searchResultId, numResultsViewed, intentResultId, items }) => {
|
|
35
|
-
|
|
48
|
+
tracker === null || tracker === void 0 ? void 0 : tracker.trackAssistantResultView(Object.assign(Object.assign(Object.assign({ intent,
|
|
36
49
|
searchResultId,
|
|
37
50
|
numResultsViewed }, (intentResultId && { intentResultId })), (items && items.length > 0 && { items })), base));
|
|
38
|
-
}, [
|
|
51
|
+
}, [tracker, base]);
|
|
39
52
|
const trackSearchSubmit = (0, react_1.useCallback)(({ intent, searchTerm, userInput, searchResultId, intentResultId, groupId, }) => {
|
|
40
|
-
|
|
53
|
+
tracker === null || tracker === void 0 ? void 0 : tracker.trackAssistantSearchSubmit(Object.assign(Object.assign(Object.assign({ intent,
|
|
41
54
|
searchTerm,
|
|
42
55
|
userInput,
|
|
43
56
|
searchResultId }, (intentResultId && { intentResultId })), (groupId && { groupId })), base));
|
|
44
|
-
}, [
|
|
57
|
+
}, [tracker, base]);
|
|
45
58
|
return (0, react_1.useMemo)(() => {
|
|
46
|
-
if (!
|
|
59
|
+
if (!tracker)
|
|
47
60
|
return NOOP_TRACKING;
|
|
48
61
|
return {
|
|
62
|
+
trackAgentButtonClick,
|
|
49
63
|
trackSubmit,
|
|
50
64
|
trackResultLoadStarted,
|
|
51
65
|
trackResultLoadFinished,
|
|
@@ -54,7 +68,8 @@ function useAsaTracking({ tracker, section, threadId, }) {
|
|
|
54
68
|
trackSearchSubmit,
|
|
55
69
|
};
|
|
56
70
|
}, [
|
|
57
|
-
|
|
71
|
+
tracker,
|
|
72
|
+
trackAgentButtonClick,
|
|
58
73
|
trackSubmit,
|
|
59
74
|
trackResultLoadStarted,
|
|
60
75
|
trackResultLoadFinished,
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const react_1 = require("react");
|
|
4
|
+
/** A ref that is `true` while mounted, for async work that must not land after unmount. */
|
|
5
|
+
function useIsMounted() {
|
|
6
|
+
const ref = (0, react_1.useRef)(true);
|
|
7
|
+
(0, react_1.useEffect)(() => {
|
|
8
|
+
ref.current = true;
|
|
9
|
+
return () => {
|
|
10
|
+
ref.current = false;
|
|
11
|
+
};
|
|
12
|
+
}, []);
|
|
13
|
+
return ref;
|
|
14
|
+
}
|
|
15
|
+
exports.default = useIsMounted;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const react_1 = require("react");
|
|
4
|
+
/** A ref that always holds the latest `value`, for async code that must not read stale props. */
|
|
5
|
+
function useLatest(value) {
|
|
6
|
+
const ref = (0, react_1.useRef)(value);
|
|
7
|
+
ref.current = value;
|
|
8
|
+
return ref;
|
|
9
|
+
}
|
|
10
|
+
exports.default = useLatest;
|
|
@@ -1,14 +1,15 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const tslib_1 = require("tslib");
|
|
3
4
|
const react_1 = require("react");
|
|
5
|
+
const useLatest_1 = tslib_1.__importDefault(require("./useLatest"));
|
|
4
6
|
/**
|
|
5
7
|
* Fires `onView` once when the observed element scrolls into the viewport, modeling a
|
|
6
8
|
* per-pod `assistant_search_result_view` impression. Unlike the PIA widget-level view
|
|
7
9
|
* tracker, this fires a single impression per pod (not repeated enter/exit cycles).
|
|
8
10
|
*/
|
|
9
11
|
function useViewportTracking({ onView, enabled = true, }) {
|
|
10
|
-
const onViewRef = (0,
|
|
11
|
-
onViewRef.current = onView;
|
|
12
|
+
const onViewRef = (0, useLatest_1.default)(onView);
|
|
12
13
|
const firedRef = (0, react_1.useRef)(false);
|
|
13
14
|
const cleanupRef = (0, react_1.useRef)(null);
|
|
14
15
|
const ref = (0, react_1.useCallback)((node) => {
|
|
@@ -29,7 +30,7 @@ function useViewportTracking({ onView, enabled = true, }) {
|
|
|
29
30
|
}, { threshold: 0.5 });
|
|
30
31
|
observer.observe(node);
|
|
31
32
|
cleanupRef.current = () => observer.disconnect();
|
|
32
|
-
}, [enabled]);
|
|
33
|
+
}, [enabled, onViewRef]);
|
|
33
34
|
(0, react_1.useEffect)(() => () => {
|
|
34
35
|
if (cleanupRef.current)
|
|
35
36
|
cleanupRef.current();
|
package/lib/cjs/index.js
CHANGED
|
@@ -3,7 +3,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
3
3
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.ConstructorIOClient = exports.normalizeItemToProduct = exports.useCioAsaContext = exports.useAsaTracking = exports.useAsaResults = exports.Button = exports.ResultsBlock = exports.Chat = exports.CioAsaProvider = exports.CioAsa = void 0;
|
|
6
|
+
exports.ConstructorIOClient = exports.clearPersistedConversations = exports.normalizeItemToProduct = exports.useCioAsaContext = exports.useAsaTracking = exports.useAsaResults = exports.Button = exports.ResultsBlock = exports.Chat = exports.CioAsaProvider = exports.CioAsa = void 0;
|
|
7
7
|
// Components
|
|
8
8
|
var CioAsa_1 = require("./components/CioAsa/CioAsa");
|
|
9
9
|
Object.defineProperty(exports, "CioAsa", { enumerable: true, get: function () { return __importDefault(CioAsa_1).default; } });
|
|
@@ -25,6 +25,8 @@ Object.defineProperty(exports, "useCioAsaContext", { enumerable: true, get: func
|
|
|
25
25
|
// Utils
|
|
26
26
|
var productNormalizer_1 = require("./utils/productNormalizer");
|
|
27
27
|
Object.defineProperty(exports, "normalizeItemToProduct", { enumerable: true, get: function () { return productNormalizer_1.normalizeItemToProduct; } });
|
|
28
|
+
var localStoragePersistence_1 = require("./utils/localStoragePersistence");
|
|
29
|
+
Object.defineProperty(exports, "clearPersistedConversations", { enumerable: true, get: function () { return localStoragePersistence_1.clearPersistedConversations; } });
|
|
28
30
|
// Re-exported client value from the JS client (used to construct/type a cioClient)
|
|
29
31
|
var types_1 = require("./types");
|
|
30
32
|
Object.defineProperty(exports, "ConstructorIOClient", { enumerable: true, get: function () { return types_1.ConstructorIOClient; } });
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const chatThreads_1 = require("./chatThreads");
|
|
4
|
+
const MESSAGE_ROLES = new Set(['user', 'assistant']);
|
|
5
|
+
const MESSAGE_STATUSES = new Set(['idle', 'loading', 'streaming', 'done', 'error']);
|
|
6
|
+
function isProduct(value) {
|
|
7
|
+
if (!value || typeof value !== 'object')
|
|
8
|
+
return false;
|
|
9
|
+
const p = value;
|
|
10
|
+
return typeof p.id === 'string' && typeof p.name === 'string';
|
|
11
|
+
}
|
|
12
|
+
function isResultGroup(value) {
|
|
13
|
+
if (!value || typeof value !== 'object')
|
|
14
|
+
return false;
|
|
15
|
+
const g = value;
|
|
16
|
+
return (Boolean(g.group) &&
|
|
17
|
+
typeof g.group === 'object' &&
|
|
18
|
+
typeof g.group.display_name === 'string' &&
|
|
19
|
+
Array.isArray(g.searchResults) &&
|
|
20
|
+
g.searchResults.every((r) => Boolean(r) && typeof r === 'object') &&
|
|
21
|
+
(g.products === undefined || (Array.isArray(g.products) && g.products.every(isProduct))));
|
|
22
|
+
}
|
|
23
|
+
function isRefinement(value) {
|
|
24
|
+
if (!value || typeof value !== 'object')
|
|
25
|
+
return false;
|
|
26
|
+
const r = value;
|
|
27
|
+
return (typeof r.question === 'string' &&
|
|
28
|
+
Array.isArray(r.options) &&
|
|
29
|
+
r.options.every((o) => typeof o === 'string'));
|
|
30
|
+
}
|
|
31
|
+
function isChatMessage(value) {
|
|
32
|
+
if (!value || typeof value !== 'object')
|
|
33
|
+
return false;
|
|
34
|
+
const m = value;
|
|
35
|
+
return (typeof m.id === 'string' &&
|
|
36
|
+
MESSAGE_ROLES.has(m.role) &&
|
|
37
|
+
typeof m.text === 'string' &&
|
|
38
|
+
MESSAGE_STATUSES.has(m.status) &&
|
|
39
|
+
(m.groups === undefined || (Array.isArray(m.groups) && m.groups.every(isResultGroup))) &&
|
|
40
|
+
(m.refinement === undefined || isRefinement(m.refinement)));
|
|
41
|
+
}
|
|
42
|
+
function isPersistedChat(value) {
|
|
43
|
+
if (!value || typeof value !== 'object')
|
|
44
|
+
return false;
|
|
45
|
+
const chat = value;
|
|
46
|
+
return (chat.version === chatThreads_1.PERSISTED_CHAT_VERSION &&
|
|
47
|
+
typeof chat.threadId === 'string' &&
|
|
48
|
+
Array.isArray(chat.messages) &&
|
|
49
|
+
chat.messages.every(isChatMessage) &&
|
|
50
|
+
typeof chat.createdAt === 'number' &&
|
|
51
|
+
typeof chat.updatedAt === 'number' &&
|
|
52
|
+
(chat.owner === undefined || typeof chat.owner === 'string'));
|
|
53
|
+
}
|
|
54
|
+
exports.default = isPersistedChat;
|