@paymanai/payman-typescript-ask-sdk 1.2.3 → 1.2.5
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/dist/index.d.mts +8 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.js +198 -23
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +199 -24
- package/dist/index.mjs.map +1 -1
- package/dist/index.native.js +203 -23
- package/dist/index.native.js.map +1 -1
- package/package.json +1 -1
package/dist/index.d.mts
CHANGED
|
@@ -144,6 +144,8 @@ type ChatConfig = {
|
|
|
144
144
|
api: APIConfig;
|
|
145
145
|
/** Workflow name */
|
|
146
146
|
workflowName: string;
|
|
147
|
+
/** User ID for chatStore and activeStreamStore — required for context store messages management. Set to undefined on logout to clear stored messages. */
|
|
148
|
+
userId?: string;
|
|
147
149
|
/** Workflow version */
|
|
148
150
|
workflowVersion?: number;
|
|
149
151
|
/** Stage/Environment */
|
|
@@ -194,6 +196,10 @@ type ChatConfig = {
|
|
|
194
196
|
isChatDisabled?: boolean;
|
|
195
197
|
/** Custom component to render when chat is disabled. If not provided, default disabled UI will be shown */
|
|
196
198
|
disabledComponent?: React.ReactNode;
|
|
199
|
+
/** Pre-populate the chat with messages (e.g. loaded conversation history) */
|
|
200
|
+
initialMessages?: MessageDisplay[];
|
|
201
|
+
/** Resume an existing session by providing its ID */
|
|
202
|
+
initialSessionId?: string;
|
|
197
203
|
};
|
|
198
204
|
type ChatCallbacks = {
|
|
199
205
|
/** Called when a message is sent (before API call) */
|
|
@@ -222,6 +228,8 @@ type UseChatReturn = {
|
|
|
222
228
|
messages: MessageDisplay[];
|
|
223
229
|
sendMessage: (userMessage: string) => Promise<void>;
|
|
224
230
|
clearMessages: () => void;
|
|
231
|
+
/** Prepend older messages to the top of the list (e.g. loaded from history pagination) */
|
|
232
|
+
prependMessages: (messages: MessageDisplay[]) => void;
|
|
225
233
|
cancelStream: () => void;
|
|
226
234
|
resetSession: () => void;
|
|
227
235
|
getSessionId: () => string | undefined;
|
package/dist/index.d.ts
CHANGED
|
@@ -144,6 +144,8 @@ type ChatConfig = {
|
|
|
144
144
|
api: APIConfig;
|
|
145
145
|
/** Workflow name */
|
|
146
146
|
workflowName: string;
|
|
147
|
+
/** User ID for chatStore and activeStreamStore — required for context store messages management. Set to undefined on logout to clear stored messages. */
|
|
148
|
+
userId?: string;
|
|
147
149
|
/** Workflow version */
|
|
148
150
|
workflowVersion?: number;
|
|
149
151
|
/** Stage/Environment */
|
|
@@ -194,6 +196,10 @@ type ChatConfig = {
|
|
|
194
196
|
isChatDisabled?: boolean;
|
|
195
197
|
/** Custom component to render when chat is disabled. If not provided, default disabled UI will be shown */
|
|
196
198
|
disabledComponent?: React.ReactNode;
|
|
199
|
+
/** Pre-populate the chat with messages (e.g. loaded conversation history) */
|
|
200
|
+
initialMessages?: MessageDisplay[];
|
|
201
|
+
/** Resume an existing session by providing its ID */
|
|
202
|
+
initialSessionId?: string;
|
|
197
203
|
};
|
|
198
204
|
type ChatCallbacks = {
|
|
199
205
|
/** Called when a message is sent (before API call) */
|
|
@@ -222,6 +228,8 @@ type UseChatReturn = {
|
|
|
222
228
|
messages: MessageDisplay[];
|
|
223
229
|
sendMessage: (userMessage: string) => Promise<void>;
|
|
224
230
|
clearMessages: () => void;
|
|
231
|
+
/** Prepend older messages to the top of the list (e.g. loaded from history pagination) */
|
|
232
|
+
prependMessages: (messages: MessageDisplay[]) => void;
|
|
225
233
|
cancelStream: () => void;
|
|
226
234
|
resetSession: () => void;
|
|
227
235
|
getSessionId: () => string | undefined;
|
package/dist/index.js
CHANGED
|
@@ -209,11 +209,25 @@ function processStreamEvent(event, state) {
|
|
|
209
209
|
}
|
|
210
210
|
state.activeThinkingText = void 0;
|
|
211
211
|
}
|
|
212
|
-
if (
|
|
213
|
-
|
|
212
|
+
if (eventType === "COMPLETED" || eventType === "WORKFLOW_COMPLETED") {
|
|
213
|
+
let content = extractResponseContent(event.response);
|
|
214
|
+
const trace = event.trace && typeof event.trace === "object" ? event.trace : null;
|
|
215
|
+
if (!content && trace?.workflowMsg && typeof trace.workflowMsg === "string") {
|
|
216
|
+
content = trace.workflowMsg;
|
|
217
|
+
}
|
|
218
|
+
if (!content && trace?.aggregator && typeof trace.aggregator === "object") {
|
|
219
|
+
const agg = trace.aggregator;
|
|
220
|
+
if (typeof agg.response === "string") content = agg.response;
|
|
221
|
+
else content = extractResponseContent(agg.response);
|
|
222
|
+
}
|
|
214
223
|
if (content) {
|
|
215
224
|
state.accumulatedContent = content;
|
|
216
|
-
state.finalData = event.response;
|
|
225
|
+
state.finalData = event.response ?? event.trace;
|
|
226
|
+
state.hasError = false;
|
|
227
|
+
state.errorMessage = "";
|
|
228
|
+
} else {
|
|
229
|
+
state.hasError = true;
|
|
230
|
+
state.errorMessage = "WORKFLOW_FAILED";
|
|
217
231
|
}
|
|
218
232
|
}
|
|
219
233
|
if (eventType === "STARTED" || eventType === "WORKFLOW_STARTED") ; else if (eventType === "COMPLETED" || eventType === "WORKFLOW_COMPLETED") {
|
|
@@ -223,7 +237,6 @@ function processStreamEvent(event, state) {
|
|
|
223
237
|
}
|
|
224
238
|
});
|
|
225
239
|
} else if (eventType === "INTENT_ERROR") {
|
|
226
|
-
state.hasError = true;
|
|
227
240
|
state.errorMessage = message || event.errorMessage || "An error occurred";
|
|
228
241
|
const intentStep = state.steps.find(
|
|
229
242
|
(s) => s.eventType === "INTENT_STARTED" && s.status === "in_progress"
|
|
@@ -453,15 +466,12 @@ function processStreamEvent(event, state) {
|
|
|
453
466
|
}
|
|
454
467
|
|
|
455
468
|
// src/utils/messageStateManager.ts
|
|
469
|
+
var FRIENDLY_ERROR_MESSAGE = "Oops, something went wrong. Please try again.";
|
|
456
470
|
function createStreamingMessageUpdate(state) {
|
|
457
471
|
const hasCompletedContent = state.accumulatedContent && state.finalData !== void 0;
|
|
458
472
|
return {
|
|
459
|
-
streamingContent: state.hasError ?
|
|
460
|
-
|
|
461
|
-
${state.errorMessage}` : hasCompletedContent ? state.accumulatedContent : "",
|
|
462
|
-
content: state.hasError ? `Oops, something went wrong. Please try again.
|
|
463
|
-
|
|
464
|
-
${state.errorMessage}` : "",
|
|
473
|
+
streamingContent: state.hasError ? FRIENDLY_ERROR_MESSAGE : hasCompletedContent ? state.accumulatedContent : "",
|
|
474
|
+
content: state.hasError ? FRIENDLY_ERROR_MESSAGE : "",
|
|
465
475
|
currentMessage: state.hasError ? void 0 : state.currentMessage,
|
|
466
476
|
streamProgress: state.hasError ? "error" : "processing",
|
|
467
477
|
isError: state.hasError,
|
|
@@ -484,9 +494,7 @@ function createErrorMessageUpdate(error, state) {
|
|
|
484
494
|
isError: !isAborted,
|
|
485
495
|
isCancelled: isAborted,
|
|
486
496
|
errorDetails: isAborted ? void 0 : error.message,
|
|
487
|
-
content: isAborted ? state.accumulatedContent || "" : state.accumulatedContent ||
|
|
488
|
-
|
|
489
|
-
${error.message}`,
|
|
497
|
+
content: isAborted ? state.accumulatedContent || "" : state.accumulatedContent || FRIENDLY_ERROR_MESSAGE,
|
|
490
498
|
// Preserve currentMessage when cancelled so UI can show it
|
|
491
499
|
currentMessage: isAborted ? state.currentMessage || "Thinking..." : void 0,
|
|
492
500
|
steps: [...state.steps].map((step) => {
|
|
@@ -503,9 +511,7 @@ function createFinalMessage(streamingId, state) {
|
|
|
503
511
|
id: streamingId,
|
|
504
512
|
sessionId: state.sessionId,
|
|
505
513
|
role: "assistant",
|
|
506
|
-
content: state.hasError ?
|
|
507
|
-
|
|
508
|
-
${state.errorMessage}` : state.accumulatedContent || "",
|
|
514
|
+
content: state.hasError ? FRIENDLY_ERROR_MESSAGE : state.accumulatedContent || "",
|
|
509
515
|
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
510
516
|
isStreaming: false,
|
|
511
517
|
streamProgress: state.hasError ? "error" : "completed",
|
|
@@ -608,6 +614,84 @@ async function cancelUserAction(config, userActionId) {
|
|
|
608
614
|
async function resendUserAction(config, userActionId) {
|
|
609
615
|
return sendUserActionRequest(config, userActionId, "resend");
|
|
610
616
|
}
|
|
617
|
+
|
|
618
|
+
// src/utils/chatStore.ts
|
|
619
|
+
var memoryStore = /* @__PURE__ */ new Map();
|
|
620
|
+
var chatStore = {
|
|
621
|
+
get(key) {
|
|
622
|
+
return memoryStore.get(key) ?? [];
|
|
623
|
+
},
|
|
624
|
+
set(key, messages) {
|
|
625
|
+
memoryStore.set(key, messages);
|
|
626
|
+
},
|
|
627
|
+
delete(key) {
|
|
628
|
+
memoryStore.delete(key);
|
|
629
|
+
}
|
|
630
|
+
};
|
|
631
|
+
|
|
632
|
+
// src/utils/activeStreamStore.ts
|
|
633
|
+
var streams = /* @__PURE__ */ new Map();
|
|
634
|
+
var activeStreamStore = {
|
|
635
|
+
has(key) {
|
|
636
|
+
return streams.has(key);
|
|
637
|
+
},
|
|
638
|
+
get(key) {
|
|
639
|
+
const entry = streams.get(key);
|
|
640
|
+
if (!entry) return null;
|
|
641
|
+
return { messages: entry.messages, isWaiting: entry.isWaiting };
|
|
642
|
+
},
|
|
643
|
+
// Called before startStream — registers the controller and initial messages
|
|
644
|
+
start(key, abortController, initialMessages) {
|
|
645
|
+
const existing = streams.get(key);
|
|
646
|
+
streams.set(key, {
|
|
647
|
+
messages: initialMessages,
|
|
648
|
+
isWaiting: true,
|
|
649
|
+
abortController,
|
|
650
|
+
listeners: existing?.listeners ?? /* @__PURE__ */ new Set()
|
|
651
|
+
});
|
|
652
|
+
},
|
|
653
|
+
// Called by the stream on every event — applies the same updater pattern React uses
|
|
654
|
+
applyMessages(key, updater) {
|
|
655
|
+
const entry = streams.get(key);
|
|
656
|
+
if (!entry) return;
|
|
657
|
+
const next = typeof updater === "function" ? updater(entry.messages) : updater;
|
|
658
|
+
entry.messages = next;
|
|
659
|
+
entry.listeners.forEach((l) => l(next, entry.isWaiting));
|
|
660
|
+
},
|
|
661
|
+
setWaiting(key, waiting) {
|
|
662
|
+
const entry = streams.get(key);
|
|
663
|
+
if (!entry) return;
|
|
664
|
+
entry.isWaiting = waiting;
|
|
665
|
+
entry.listeners.forEach((l) => l(entry.messages, waiting));
|
|
666
|
+
},
|
|
667
|
+
// Called when stream completes — persists to chatStore and cleans up
|
|
668
|
+
complete(key) {
|
|
669
|
+
const entry = streams.get(key);
|
|
670
|
+
if (!entry) return;
|
|
671
|
+
entry.isWaiting = false;
|
|
672
|
+
entry.listeners.forEach((l) => l(entry.messages, false));
|
|
673
|
+
const toSave = entry.messages.filter((m) => !m.isStreaming);
|
|
674
|
+
if (toSave.length > 0) chatStore.set(key, toSave);
|
|
675
|
+
streams.delete(key);
|
|
676
|
+
},
|
|
677
|
+
// Subscribe — returns unsubscribe fn. Component calls this on mount, cleanup on unmount.
|
|
678
|
+
subscribe(key, listener) {
|
|
679
|
+
const entry = streams.get(key);
|
|
680
|
+
if (!entry) return () => {
|
|
681
|
+
};
|
|
682
|
+
entry.listeners.add(listener);
|
|
683
|
+
return () => {
|
|
684
|
+
streams.get(key)?.listeners.delete(listener);
|
|
685
|
+
};
|
|
686
|
+
},
|
|
687
|
+
// Explicit user cancel — aborts the controller and removes the entry
|
|
688
|
+
abort(key) {
|
|
689
|
+
const entry = streams.get(key);
|
|
690
|
+
if (!entry) return;
|
|
691
|
+
entry.abortController.abort();
|
|
692
|
+
streams.delete(key);
|
|
693
|
+
}
|
|
694
|
+
};
|
|
611
695
|
function useStreamManager(config, callbacks, setMessages, setIsWaitingForResponse) {
|
|
612
696
|
const abortControllerRef = react.useRef(null);
|
|
613
697
|
const configRef = react.useRef(config);
|
|
@@ -615,9 +699,9 @@ function useStreamManager(config, callbacks, setMessages, setIsWaitingForRespons
|
|
|
615
699
|
const callbacksRef = react.useRef(callbacks);
|
|
616
700
|
callbacksRef.current = callbacks;
|
|
617
701
|
const startStream = react.useCallback(
|
|
618
|
-
async (userMessage, streamingId, sessionId) => {
|
|
702
|
+
async (userMessage, streamingId, sessionId, externalAbortController) => {
|
|
619
703
|
abortControllerRef.current?.abort();
|
|
620
|
-
const abortController = new AbortController();
|
|
704
|
+
const abortController = externalAbortController ?? new AbortController();
|
|
621
705
|
abortControllerRef.current = abortController;
|
|
622
706
|
const state = {
|
|
623
707
|
accumulatedContent: "",
|
|
@@ -821,13 +905,43 @@ function useStreamManager(config, callbacks, setMessages, setIsWaitingForRespons
|
|
|
821
905
|
|
|
822
906
|
// src/hooks/useChat.ts
|
|
823
907
|
function useChat(config, callbacks = {}) {
|
|
824
|
-
const [messages, setMessages] = react.useState(
|
|
908
|
+
const [messages, setMessages] = react.useState(() => {
|
|
909
|
+
if (config.userId) return chatStore.get(config.userId);
|
|
910
|
+
return config.initialMessages ?? [];
|
|
911
|
+
});
|
|
825
912
|
const [isWaitingForResponse, setIsWaitingForResponse] = react.useState(false);
|
|
826
|
-
const sessionIdRef = react.useRef(
|
|
913
|
+
const sessionIdRef = react.useRef(
|
|
914
|
+
config.userId ? chatStore.get(config.userId).find((m) => m.sessionId)?.sessionId ?? config.initialSessionId ?? void 0 : config.initialSessionId ?? void 0
|
|
915
|
+
);
|
|
916
|
+
const prevUserIdRef = react.useRef(config.userId);
|
|
827
917
|
const callbacksRef = react.useRef(callbacks);
|
|
828
918
|
callbacksRef.current = callbacks;
|
|
829
919
|
const configRef = react.useRef(config);
|
|
830
920
|
configRef.current = config;
|
|
921
|
+
const messagesRef = react.useRef(messages);
|
|
922
|
+
messagesRef.current = messages;
|
|
923
|
+
const storeAwareSetMessages = react.useCallback(
|
|
924
|
+
(updater) => {
|
|
925
|
+
const { userId } = configRef.current;
|
|
926
|
+
if (userId && activeStreamStore.has(userId)) {
|
|
927
|
+
activeStreamStore.applyMessages(userId, updater);
|
|
928
|
+
}
|
|
929
|
+
setMessages(updater);
|
|
930
|
+
},
|
|
931
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
932
|
+
[]
|
|
933
|
+
);
|
|
934
|
+
const storeAwareSetIsWaiting = react.useCallback(
|
|
935
|
+
(waiting) => {
|
|
936
|
+
const { userId } = configRef.current;
|
|
937
|
+
if (userId && activeStreamStore.has(userId)) {
|
|
938
|
+
activeStreamStore.setWaiting(userId, waiting);
|
|
939
|
+
}
|
|
940
|
+
setIsWaitingForResponse(waiting);
|
|
941
|
+
},
|
|
942
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
943
|
+
[]
|
|
944
|
+
);
|
|
831
945
|
const [userActionState, setUserActionState] = react.useState({
|
|
832
946
|
request: null,
|
|
833
947
|
result: null,
|
|
@@ -870,8 +984,8 @@ function useChat(config, callbacks = {}) {
|
|
|
870
984
|
const { startStream, cancelStream: cancelStreamManager, abortControllerRef } = useStreamManager(
|
|
871
985
|
config,
|
|
872
986
|
wrappedCallbacks,
|
|
873
|
-
|
|
874
|
-
|
|
987
|
+
storeAwareSetMessages,
|
|
988
|
+
storeAwareSetIsWaiting
|
|
875
989
|
);
|
|
876
990
|
const sendMessage = react.useCallback(
|
|
877
991
|
async (userMessage) => {
|
|
@@ -908,11 +1022,21 @@ function useChat(config, callbacks = {}) {
|
|
|
908
1022
|
currentMessage: void 0
|
|
909
1023
|
};
|
|
910
1024
|
setMessages((prev) => [...prev, streamingMsg]);
|
|
1025
|
+
const abortController = new AbortController();
|
|
1026
|
+
const { userId } = configRef.current;
|
|
1027
|
+
if (userId) {
|
|
1028
|
+
const initialMessages = [...messagesRef.current, userMsg, streamingMsg];
|
|
1029
|
+
activeStreamStore.start(userId, abortController, initialMessages);
|
|
1030
|
+
}
|
|
911
1031
|
const newSessionId = await startStream(
|
|
912
1032
|
userMessage,
|
|
913
1033
|
streamingId,
|
|
914
|
-
sessionIdRef.current
|
|
1034
|
+
sessionIdRef.current,
|
|
1035
|
+
abortController
|
|
915
1036
|
);
|
|
1037
|
+
if (userId) {
|
|
1038
|
+
activeStreamStore.complete(userId);
|
|
1039
|
+
}
|
|
916
1040
|
if (newSessionId && newSessionId !== sessionIdRef.current) {
|
|
917
1041
|
sessionIdRef.current = newSessionId;
|
|
918
1042
|
}
|
|
@@ -920,9 +1044,18 @@ function useChat(config, callbacks = {}) {
|
|
|
920
1044
|
[startStream]
|
|
921
1045
|
);
|
|
922
1046
|
const clearMessages = react.useCallback(() => {
|
|
1047
|
+
if (configRef.current.userId) {
|
|
1048
|
+
chatStore.delete(configRef.current.userId);
|
|
1049
|
+
}
|
|
923
1050
|
setMessages([]);
|
|
924
1051
|
}, []);
|
|
1052
|
+
const prependMessages = react.useCallback((msgs) => {
|
|
1053
|
+
setMessages((prev) => [...msgs, ...prev]);
|
|
1054
|
+
}, []);
|
|
925
1055
|
const cancelStream = react.useCallback(() => {
|
|
1056
|
+
if (configRef.current.userId) {
|
|
1057
|
+
activeStreamStore.abort(configRef.current.userId);
|
|
1058
|
+
}
|
|
926
1059
|
cancelStreamManager();
|
|
927
1060
|
setIsWaitingForResponse(false);
|
|
928
1061
|
setUserActionState((prev) => ({ ...prev, request: null, result: null }));
|
|
@@ -942,6 +1075,10 @@ function useChat(config, callbacks = {}) {
|
|
|
942
1075
|
);
|
|
943
1076
|
}, [cancelStreamManager]);
|
|
944
1077
|
const resetSession = react.useCallback(() => {
|
|
1078
|
+
if (configRef.current.userId) {
|
|
1079
|
+
activeStreamStore.abort(configRef.current.userId);
|
|
1080
|
+
chatStore.delete(configRef.current.userId);
|
|
1081
|
+
}
|
|
945
1082
|
setMessages([]);
|
|
946
1083
|
sessionIdRef.current = void 0;
|
|
947
1084
|
abortControllerRef.current?.abort();
|
|
@@ -1004,10 +1141,48 @@ function useChat(config, callbacks = {}) {
|
|
|
1004
1141
|
throw error;
|
|
1005
1142
|
}
|
|
1006
1143
|
}, []);
|
|
1144
|
+
react.useEffect(() => {
|
|
1145
|
+
const { userId } = config;
|
|
1146
|
+
if (!userId) return;
|
|
1147
|
+
const unsubscribe = activeStreamStore.subscribe(userId, (msgs, isWaiting) => {
|
|
1148
|
+
setMessages(msgs);
|
|
1149
|
+
setIsWaitingForResponse(isWaiting);
|
|
1150
|
+
});
|
|
1151
|
+
const active = activeStreamStore.get(userId);
|
|
1152
|
+
if (active) {
|
|
1153
|
+
setMessages(active.messages);
|
|
1154
|
+
setIsWaitingForResponse(active.isWaiting);
|
|
1155
|
+
}
|
|
1156
|
+
return unsubscribe;
|
|
1157
|
+
}, []);
|
|
1158
|
+
react.useEffect(() => {
|
|
1159
|
+
if (!config.userId) return;
|
|
1160
|
+
const toSave = messages.filter((m) => !m.isStreaming);
|
|
1161
|
+
if (toSave.length > 0) {
|
|
1162
|
+
chatStore.set(config.userId, toSave);
|
|
1163
|
+
}
|
|
1164
|
+
}, [messages, config.userId]);
|
|
1165
|
+
react.useEffect(() => {
|
|
1166
|
+
const prevUserId = prevUserIdRef.current;
|
|
1167
|
+
prevUserIdRef.current = config.userId;
|
|
1168
|
+
if (prevUserId === config.userId) return;
|
|
1169
|
+
if (prevUserId && !config.userId) {
|
|
1170
|
+
chatStore.delete(prevUserId);
|
|
1171
|
+
setMessages([]);
|
|
1172
|
+
sessionIdRef.current = void 0;
|
|
1173
|
+
setIsWaitingForResponse(false);
|
|
1174
|
+
setUserActionState({ request: null, result: null, clearOtpTrigger: 0 });
|
|
1175
|
+
} else if (config.userId) {
|
|
1176
|
+
const stored = chatStore.get(config.userId);
|
|
1177
|
+
setMessages(stored);
|
|
1178
|
+
sessionIdRef.current = stored.find((m) => m.sessionId)?.sessionId;
|
|
1179
|
+
}
|
|
1180
|
+
}, [config.userId]);
|
|
1007
1181
|
return {
|
|
1008
1182
|
messages,
|
|
1009
1183
|
sendMessage,
|
|
1010
1184
|
clearMessages,
|
|
1185
|
+
prependMessages,
|
|
1011
1186
|
cancelStream,
|
|
1012
1187
|
resetSession,
|
|
1013
1188
|
getSessionId,
|