impact-chatbot 2.3.61 → 2.3.63
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.cjs.js +335 -74
- package/dist/index.cjs.js.map +1 -1
- package/dist/index.esm.js +335 -74
- package/dist/index.esm.js.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs.js
CHANGED
|
@@ -5225,9 +5225,28 @@ const sseevent = (message, messageToStoreRef) => {
|
|
|
5225
5225
|
messageToStoreRef.current.navSessionId = parsedData.session_id;
|
|
5226
5226
|
}
|
|
5227
5227
|
if (parsedData?.is_error) {
|
|
5228
|
-
|
|
5229
|
-
|
|
5230
|
-
|
|
5228
|
+
// Only append the error message once, even if multiple is_error chunks arrive.
|
|
5229
|
+
// Append as a widget item to appendedData so it renders AFTER any existing
|
|
5230
|
+
// widgets (tables, graphs, text widgets) rather than at the top of the response.
|
|
5231
|
+
if (!messageToStoreRef.current.hasErrorMessage) {
|
|
5232
|
+
messageToStoreRef.current.hasErrorMessage = true;
|
|
5233
|
+
const errorMsg = parsedData?.message && parsedData.message !== "[DONE]"
|
|
5234
|
+
? parsedData.message
|
|
5235
|
+
: "There is an error, please reach out to IA with this use case.";
|
|
5236
|
+
const errorWidget = { type: "text", response: errorMsg };
|
|
5237
|
+
const prevAppended = messageToStoreRef.current.appendedData;
|
|
5238
|
+
if (Array.isArray(prevAppended) && prevAppended.length > 0) {
|
|
5239
|
+
messageToStoreRef.current.appendedData = [...prevAppended, errorWidget];
|
|
5240
|
+
}
|
|
5241
|
+
else if (prevAppended && typeof prevAppended === "object" && Object.keys(prevAppended).length > 0) {
|
|
5242
|
+
messageToStoreRef.current.appendedData = [prevAppended, errorWidget];
|
|
5243
|
+
}
|
|
5244
|
+
else {
|
|
5245
|
+
// No existing widgets — put on chatData.response instead
|
|
5246
|
+
messageToStoreRef.current.chatData.response =
|
|
5247
|
+
messageToStoreRef.current.chatData.response + errorMsg;
|
|
5248
|
+
}
|
|
5249
|
+
}
|
|
5231
5250
|
// Still process completed/follow-up status even on error chunks
|
|
5232
5251
|
// so that initValue is set correctly and dummyButton is suppressed
|
|
5233
5252
|
if (parsedData?.status === "completed" ||
|
|
@@ -5409,6 +5428,7 @@ const AxiosSource = (url, opts, messageToStoreRef) => {
|
|
|
5409
5428
|
})
|
|
5410
5429
|
.catch((error) => {
|
|
5411
5430
|
let reason = "Network request failed";
|
|
5431
|
+
let statusCode = 0;
|
|
5412
5432
|
console.error("[AxiosSource] Request failed:", error);
|
|
5413
5433
|
// Determine specific error reasons
|
|
5414
5434
|
if (axios.isCancel(error)) {
|
|
@@ -5417,8 +5437,15 @@ const AxiosSource = (url, opts, messageToStoreRef) => {
|
|
|
5417
5437
|
else if (error.code === "ECONNABORTED") {
|
|
5418
5438
|
reason = "Network request timed out";
|
|
5419
5439
|
}
|
|
5420
|
-
//
|
|
5421
|
-
|
|
5440
|
+
// Capture HTTP status code from the error response (e.g. 503)
|
|
5441
|
+
if (error.response && error.response.status) {
|
|
5442
|
+
statusCode = error.response.status;
|
|
5443
|
+
reason = `HTTP ${statusCode}: ${reason}`;
|
|
5444
|
+
}
|
|
5445
|
+
// Emit error event with the specific reason and status code
|
|
5446
|
+
const errorEvent = new CloseEvent("error", { reason });
|
|
5447
|
+
errorEvent.statusCode = statusCode;
|
|
5448
|
+
eventTarget.dispatchEvent(errorEvent);
|
|
5422
5449
|
});
|
|
5423
5450
|
// Add method to manually close the connection
|
|
5424
5451
|
eventTarget.close = () => {
|
|
@@ -5459,10 +5486,15 @@ const useStyles$6 = styles.makeStyles((theme) => ({
|
|
|
5459
5486
|
marginTop: pxToRem(16),
|
|
5460
5487
|
}
|
|
5461
5488
|
}));
|
|
5489
|
+
const MAX_RETRY_COUNT_BUTTON = 1;
|
|
5490
|
+
const RETRY_COUNTDOWN_SECONDS_BUTTON = 15;
|
|
5462
5491
|
const ButtonContent = ({ bodyText, isFormDisabled = false, isStepFormSubmit = false, isFormValid = true }) => {
|
|
5463
5492
|
const classes = useStyles$6();
|
|
5464
5493
|
const dispatch = reactRedux.useDispatch();
|
|
5465
5494
|
const sourceRef = React.useRef(null);
|
|
5495
|
+
const retryCountRef = React.useRef(0);
|
|
5496
|
+
const retryTimerRef = React.useRef(null);
|
|
5497
|
+
const retryPayloadRef = React.useRef(null);
|
|
5466
5498
|
const messageStoreRef = React.useRef({
|
|
5467
5499
|
currentMode: "agent",
|
|
5468
5500
|
chatData: {
|
|
@@ -5506,6 +5538,8 @@ const ButtonContent = ({ bodyText, isFormDisabled = false, isStepFormSubmit = fa
|
|
|
5506
5538
|
sendButton.click();
|
|
5507
5539
|
};
|
|
5508
5540
|
const callInitApiStream = (userInput) => {
|
|
5541
|
+
// Store payload for potential retries
|
|
5542
|
+
retryPayloadRef.current = userInput;
|
|
5509
5543
|
// Prefer module-level values (set synchronously by StreamedContent), fall back to sessionStorage
|
|
5510
5544
|
// If a previous completed/follow-up response updated the state, use those values.
|
|
5511
5545
|
// Read from module-level stepFormStreamControl (survives remounts) instead of local messageStoreRef.
|
|
@@ -5705,12 +5739,65 @@ const ButtonContent = ({ bodyText, isFormDisabled = false, isStepFormSubmit = fa
|
|
|
5705
5739
|
console.error("[ButtonContent] SSE parse error:", e);
|
|
5706
5740
|
}
|
|
5707
5741
|
});
|
|
5708
|
-
sourceRef.current.addEventListener("error", () => {
|
|
5709
|
-
// On error, still dispatch whatever we collected
|
|
5742
|
+
sourceRef.current.addEventListener("error", (event) => {
|
|
5710
5743
|
stepFormStreamControl.isStreaming = false;
|
|
5711
5744
|
stepFormStreamControl.abort = null;
|
|
5712
5745
|
window.dispatchEvent(new CustomEvent("stepFormStreamEnd"));
|
|
5713
|
-
|
|
5746
|
+
// Don't retry if the user manually aborted the request
|
|
5747
|
+
const isUserAbort = event.reason === "Network request aborted";
|
|
5748
|
+
// Check if we can auto-retry (abrupt close or 503, but not user abort)
|
|
5749
|
+
if (!isUserAbort && retryCountRef.current < MAX_RETRY_COUNT_BUTTON) {
|
|
5750
|
+
// Dispatch a "retrying" status so TabularContent can show countdown UI
|
|
5751
|
+
dispatch(smartBotActions.setStepFormStreamData({ status: "retrying", chunks: [...chunksRef], sessionId, countdown: RETRY_COUNTDOWN_SECONDS_BUTTON }));
|
|
5752
|
+
// Start countdown and auto-retry
|
|
5753
|
+
let countdown = RETRY_COUNTDOWN_SECONDS_BUTTON;
|
|
5754
|
+
retryTimerRef.current = setInterval(() => {
|
|
5755
|
+
countdown -= 1;
|
|
5756
|
+
if (countdown <= 0) {
|
|
5757
|
+
clearInterval(retryTimerRef.current);
|
|
5758
|
+
retryTimerRef.current = null;
|
|
5759
|
+
retryCountRef.current += 1;
|
|
5760
|
+
// Re-invoke with same payload
|
|
5761
|
+
callInitApiStream(retryPayloadRef.current);
|
|
5762
|
+
}
|
|
5763
|
+
else {
|
|
5764
|
+
// Update countdown in Redux for UI
|
|
5765
|
+
dispatch(smartBotActions.setStepFormStreamData({ status: "retrying", chunks: [...chunksRef], sessionId, countdown }));
|
|
5766
|
+
}
|
|
5767
|
+
}, 1000);
|
|
5768
|
+
}
|
|
5769
|
+
else {
|
|
5770
|
+
// Retries exhausted or user-aborted — show final message
|
|
5771
|
+
chunksRef.push({ status: "content", message: "Please reach out to IA for this, as this didn't work even after retry" });
|
|
5772
|
+
dispatch(smartBotActions.setStepFormStreamData({ status: "error", chunks: [...chunksRef], sessionId }));
|
|
5773
|
+
}
|
|
5774
|
+
});
|
|
5775
|
+
sourceRef.current.addEventListener("close", () => {
|
|
5776
|
+
if (stepFormStreamControl.isStreaming) {
|
|
5777
|
+
stepFormStreamControl.isStreaming = false;
|
|
5778
|
+
stepFormStreamControl.abort = null;
|
|
5779
|
+
window.dispatchEvent(new CustomEvent("stepFormStreamEnd"));
|
|
5780
|
+
if (retryCountRef.current < MAX_RETRY_COUNT_BUTTON) {
|
|
5781
|
+
dispatch(smartBotActions.setStepFormStreamData({ status: "retrying", chunks: [...chunksRef], sessionId, countdown: RETRY_COUNTDOWN_SECONDS_BUTTON }));
|
|
5782
|
+
let countdown = RETRY_COUNTDOWN_SECONDS_BUTTON;
|
|
5783
|
+
retryTimerRef.current = setInterval(() => {
|
|
5784
|
+
countdown -= 1;
|
|
5785
|
+
if (countdown <= 0) {
|
|
5786
|
+
clearInterval(retryTimerRef.current);
|
|
5787
|
+
retryTimerRef.current = null;
|
|
5788
|
+
retryCountRef.current += 1;
|
|
5789
|
+
callInitApiStream(retryPayloadRef.current);
|
|
5790
|
+
}
|
|
5791
|
+
else {
|
|
5792
|
+
dispatch(smartBotActions.setStepFormStreamData({ status: "retrying", chunks: [...chunksRef], sessionId, countdown }));
|
|
5793
|
+
}
|
|
5794
|
+
}, 1000);
|
|
5795
|
+
}
|
|
5796
|
+
else {
|
|
5797
|
+
chunksRef.push({ status: "content", message: "Please reach out to IA for this, as this didn't work even after retry" });
|
|
5798
|
+
dispatch(smartBotActions.setStepFormStreamData({ status: "error", chunks: [...chunksRef], sessionId }));
|
|
5799
|
+
}
|
|
5800
|
+
}
|
|
5714
5801
|
});
|
|
5715
5802
|
};
|
|
5716
5803
|
const renderButtons = () => {
|
|
@@ -6016,6 +6103,15 @@ const useStyles$5 = styles.makeStyles((theme) => ({
|
|
|
6016
6103
|
position: "relative",
|
|
6017
6104
|
background: colours.white,
|
|
6018
6105
|
},
|
|
6106
|
+
retryContainer: {
|
|
6107
|
+
marginTop: pxToRem(8),
|
|
6108
|
+
},
|
|
6109
|
+
retryMessage: {
|
|
6110
|
+
fontFamily: "Manrope",
|
|
6111
|
+
fontSize: pxToRem(13),
|
|
6112
|
+
fontWeight: 500,
|
|
6113
|
+
color: "#374151",
|
|
6114
|
+
},
|
|
6019
6115
|
}));
|
|
6020
6116
|
|
|
6021
6117
|
var _path$i, _path2$2, _defs$5;
|
|
@@ -7720,8 +7816,20 @@ const AgentResponse$1 = (props) => {
|
|
|
7720
7816
|
};
|
|
7721
7817
|
|
|
7722
7818
|
const StepsResponseTab = (props) => {
|
|
7723
|
-
const { steps, setSteps, stepsDone, setStepsDone, finalStepDone, setFinalStepDone, content, isStreaming, stepChange, currentMode, questions, questionsStepsMap, stepFormDataMap, isFormDisabled, streamingWidgetData, } = props;
|
|
7819
|
+
const { steps, setSteps, stepsDone, setStepsDone, finalStepDone, setFinalStepDone, content, isStreaming, stepChange, currentMode, questions, questionsStepsMap, stepFormDataMap, isFormDisabled, streamingWidgetData, isRetrying, } = props;
|
|
7724
7820
|
const [tabValue, setTabValue] = React.useState("steps");
|
|
7821
|
+
// When retry countdown starts, switch to agent_response to show it.
|
|
7822
|
+
// When retry actually fires (isRetrying goes false + isStreaming goes true), switch back to steps.
|
|
7823
|
+
React.useEffect(() => {
|
|
7824
|
+
if (isRetrying) {
|
|
7825
|
+
setTabValue("agent_response");
|
|
7826
|
+
}
|
|
7827
|
+
}, [isRetrying]);
|
|
7828
|
+
React.useEffect(() => {
|
|
7829
|
+
if (isStreaming && !isRetrying) {
|
|
7830
|
+
setTabValue("steps");
|
|
7831
|
+
}
|
|
7832
|
+
}, [isStreaming, isRetrying]);
|
|
7725
7833
|
const handleChangeTabValue = (_event, newValue) => {
|
|
7726
7834
|
setTabValue(newValue);
|
|
7727
7835
|
};
|
|
@@ -7951,6 +8059,14 @@ const StreamedContent = ({ botData }) => {
|
|
|
7951
8059
|
const [finalStepDone, setFinalStepDone] = React.useState(false);
|
|
7952
8060
|
const [wasStreamingAborted, setWasStreamingAborted] = React.useState(false);
|
|
7953
8061
|
const wasStreamingAbortedRef = React.useRef(false);
|
|
8062
|
+
// Auto-retry state
|
|
8063
|
+
const MAX_RETRY_COUNT = 1; // Number of auto-retries allowed
|
|
8064
|
+
const RETRY_COUNTDOWN_SECONDS = 15; // Countdown duration before auto-retry
|
|
8065
|
+
const [isRetrying, setIsRetrying] = React.useState(false);
|
|
8066
|
+
const [retryCountdown, setRetryCountdown] = React.useState(0);
|
|
8067
|
+
const retryCountRef = React.useRef(0); // How many retries have been attempted
|
|
8068
|
+
const retryTimerRef = React.useRef(null); // Interval ref for countdown
|
|
8069
|
+
const retryCountdownRef = React.useRef(0); // Non-state countdown for interval callback
|
|
7954
8070
|
const thinkingContentRef = React.useRef("");
|
|
7955
8071
|
reactRedux.useSelector((state) => {
|
|
7956
8072
|
return state.smartBotReducer.thinkingContext;
|
|
@@ -8070,6 +8186,94 @@ const StreamedContent = ({ botData }) => {
|
|
|
8070
8186
|
}));
|
|
8071
8187
|
}
|
|
8072
8188
|
}, [stepChange, questions, questionsStepsMap, stepsDone, isStreamingDone]);
|
|
8189
|
+
/**
|
|
8190
|
+
* Starts a countdown for auto-retry. Shows countdown in UI, then calls retryStream.
|
|
8191
|
+
*/
|
|
8192
|
+
const startRetryCountdown = () => {
|
|
8193
|
+
setIsRetrying(true);
|
|
8194
|
+
retryCountdownRef.current = RETRY_COUNTDOWN_SECONDS;
|
|
8195
|
+
setRetryCountdown(RETRY_COUNTDOWN_SECONDS);
|
|
8196
|
+
retryTimerRef.current = setInterval(() => {
|
|
8197
|
+
retryCountdownRef.current -= 1;
|
|
8198
|
+
setRetryCountdown(retryCountdownRef.current);
|
|
8199
|
+
if (retryCountdownRef.current <= 0) {
|
|
8200
|
+
clearInterval(retryTimerRef.current);
|
|
8201
|
+
retryTimerRef.current = null;
|
|
8202
|
+
retryStream();
|
|
8203
|
+
}
|
|
8204
|
+
}, 1000);
|
|
8205
|
+
};
|
|
8206
|
+
/**
|
|
8207
|
+
* Retries the stream by resetting state and calling processStream again.
|
|
8208
|
+
*/
|
|
8209
|
+
const retryStream = () => {
|
|
8210
|
+
// Clear countdown state
|
|
8211
|
+
if (retryTimerRef.current) {
|
|
8212
|
+
clearInterval(retryTimerRef.current);
|
|
8213
|
+
retryTimerRef.current = null;
|
|
8214
|
+
}
|
|
8215
|
+
setIsRetrying(false);
|
|
8216
|
+
setRetryCountdown(0);
|
|
8217
|
+
retryCountRef.current += 1;
|
|
8218
|
+
// Reset streaming state for a fresh start
|
|
8219
|
+
setContent("");
|
|
8220
|
+
setIsStreaming(true);
|
|
8221
|
+
setIsStreamingDone(false);
|
|
8222
|
+
setStepsDone(false);
|
|
8223
|
+
setFinalStepDone(false);
|
|
8224
|
+
setWasStreamingAborted(false);
|
|
8225
|
+
wasStreamingAbortedRef.current = false;
|
|
8226
|
+
setSteps([{ header: "Processing Request", sub_header: "Analyzing the current request", step_status: "not-completed" }]);
|
|
8227
|
+
stepRef.current = [{ header: "Processing Request", sub_header: "Analyzing the current request", step_status: "not-completed" }];
|
|
8228
|
+
setQuestions([]);
|
|
8229
|
+
questionsRef.current = [];
|
|
8230
|
+
setQuestionsStepsMap({});
|
|
8231
|
+
questionsStepsMapRef.current = {};
|
|
8232
|
+
setStepFormDataMap({});
|
|
8233
|
+
stepFormDataMapRef.current = {};
|
|
8234
|
+
setStreamingWidgetData([]);
|
|
8235
|
+
streamingDoneProcessedRef.current = false;
|
|
8236
|
+
// Reset messageToStoreRef for fresh accumulation
|
|
8237
|
+
messageToStoreRef.current = {
|
|
8238
|
+
status: "",
|
|
8239
|
+
currentMode: currentMode,
|
|
8240
|
+
chatData: {
|
|
8241
|
+
response: "",
|
|
8242
|
+
response_heading: "",
|
|
8243
|
+
thinkingResponse: {
|
|
8244
|
+
thinkingContent: jsxRuntime.jsx(ThinkingIndicator, { thinkingContent: thinkingContent }),
|
|
8245
|
+
thinkingStream: "",
|
|
8246
|
+
thinkingTime: 0,
|
|
8247
|
+
thinkingHeading: null,
|
|
8248
|
+
},
|
|
8249
|
+
},
|
|
8250
|
+
appendedData: {},
|
|
8251
|
+
appendedDataFromLastChunk: {},
|
|
8252
|
+
initValue: false,
|
|
8253
|
+
sessionId: "",
|
|
8254
|
+
navSessionId: "",
|
|
8255
|
+
uniqueChatId: "",
|
|
8256
|
+
additionalArgs: {},
|
|
8257
|
+
};
|
|
8258
|
+
// Reset stream state map entry and invalidate old listeners
|
|
8259
|
+
const state = streamStateMap.get(streamKey);
|
|
8260
|
+
if (state) {
|
|
8261
|
+
state.messageStore = messageToStoreRef.current;
|
|
8262
|
+
state.completed = false;
|
|
8263
|
+
// Increment listenerGeneration so any in-flight events from the old source are ignored
|
|
8264
|
+
state.listenerGeneration = (state.listenerGeneration || 0) + 1;
|
|
8265
|
+
// Close the old source if it's still open
|
|
8266
|
+
if (state.source && typeof state.source.close === "function") {
|
|
8267
|
+
try {
|
|
8268
|
+
state.source.close();
|
|
8269
|
+
}
|
|
8270
|
+
catch (e) { /* ignore */ }
|
|
8271
|
+
}
|
|
8272
|
+
state.source = null;
|
|
8273
|
+
}
|
|
8274
|
+
// Re-initiate the stream
|
|
8275
|
+
processStream();
|
|
8276
|
+
};
|
|
8073
8277
|
/**
|
|
8074
8278
|
* Main effect to initialize and handle the streaming connection
|
|
8075
8279
|
* Sets up the SSE connection and event listeners for message processing
|
|
@@ -8175,6 +8379,26 @@ const StreamedContent = ({ botData }) => {
|
|
|
8175
8379
|
}));
|
|
8176
8380
|
return;
|
|
8177
8381
|
}
|
|
8382
|
+
// Skip is_error chunks — already handled in AxiosEventSource sseevent()
|
|
8383
|
+
if (data?.is_error) {
|
|
8384
|
+
// Trigger completion if status is "completed"
|
|
8385
|
+
if (data?.status === "completed") {
|
|
8386
|
+
setStepsDone(true);
|
|
8387
|
+
setIsStreamingDone(true);
|
|
8388
|
+
const doneState = streamStateMap.get(streamKey);
|
|
8389
|
+
if (doneState)
|
|
8390
|
+
doneState.completed = true;
|
|
8391
|
+
dispatch(smartBotActions.setMinimizedStreamData({
|
|
8392
|
+
isStreaming: false,
|
|
8393
|
+
stepHeader: questionsRef.current[questionsRef.current.length - 1] || "Completed",
|
|
8394
|
+
stepSubHeader: "Done",
|
|
8395
|
+
stepStatus: "completed",
|
|
8396
|
+
streamStartTime: streamStartTimeRef.current,
|
|
8397
|
+
actionCount: questionsRef.current.length || 1,
|
|
8398
|
+
}));
|
|
8399
|
+
}
|
|
8400
|
+
return;
|
|
8401
|
+
}
|
|
8178
8402
|
if (data?.message || data?.status === "step" || data?.status === "step_form" || data?.status === "thinking" || data?.status === "questions" || data?.status === "widget") {
|
|
8179
8403
|
if (data.status === "questions") {
|
|
8180
8404
|
const incomingQuestions = data.widget_data?.[0]?.questions || [];
|
|
@@ -8444,64 +8668,36 @@ const StreamedContent = ({ botData }) => {
|
|
|
8444
8668
|
const errState = streamStateMap.get(streamKey);
|
|
8445
8669
|
if (!errState || errState.listenerGeneration !== generation)
|
|
8446
8670
|
return;
|
|
8447
|
-
console.error("Stream error:", event.reason);
|
|
8671
|
+
console.error("Stream error:", event.reason, "statusCode:", event.statusCode);
|
|
8448
8672
|
setIsStreaming(false);
|
|
8449
8673
|
errState.completed = true;
|
|
8674
|
+
// Check if this is a user-initiated abort (should not trigger retry)
|
|
8675
|
+
const isUserAbort = event.reason === "Network request aborted";
|
|
8676
|
+
const is503 = event.statusCode === 503;
|
|
8450
8677
|
if (isThinking) {
|
|
8451
8678
|
setIsThinking(false);
|
|
8452
8679
|
const endTime = Date.now();
|
|
8453
8680
|
const duration = Math.round((endTime - thinkingStartTimeRef.current) / 1000);
|
|
8454
|
-
const finalThinkingTime = Math.max(duration, 1);
|
|
8681
|
+
const finalThinkingTime = Math.max(duration, 1);
|
|
8455
8682
|
setThinkingTime(finalThinkingTime);
|
|
8456
8683
|
setShowThoughtDropdown(true);
|
|
8457
8684
|
thinkingStartTimeRef.current = finalThinkingTime;
|
|
8458
|
-
// Store thinking time in messageToStoreRef for persistence
|
|
8459
8685
|
messageToStoreRef.current.chatData.thinkingResponse.thinkingTime = finalThinkingTime;
|
|
8460
8686
|
}
|
|
8461
|
-
//
|
|
8462
|
-
|
|
8463
|
-
|
|
8464
|
-
|
|
8465
|
-
|
|
8466
|
-
|
|
8467
|
-
|
|
8468
|
-
|
|
8469
|
-
|
|
8470
|
-
|
|
8471
|
-
|
|
8472
|
-
|
|
8473
|
-
}
|
|
8474
|
-
// Set the response message for the agent_response tab
|
|
8475
|
-
const abruptCloseMessage = "The requested Task has ended unexpectedly, please retry again";
|
|
8476
|
-
messageToStoreRef.current.chatData.response = abruptCloseMessage;
|
|
8477
|
-
setContent(abruptCloseMessage);
|
|
8478
|
-
// Dispatch minimized widget data
|
|
8479
|
-
dispatch(smartBotActions.setMinimizedStreamData({
|
|
8480
|
-
isStreaming: false,
|
|
8481
|
-
stepHeader: questionsRef.current[questionsRef.current.length - 1] || "Ended unexpectedly",
|
|
8482
|
-
stepSubHeader: "Connection closed",
|
|
8483
|
-
stepStatus: "completed",
|
|
8484
|
-
streamStartTime: streamStartTimeRef.current,
|
|
8485
|
-
actionCount: questionsRef.current.length || 1,
|
|
8486
|
-
}));
|
|
8487
|
-
// Switch to agent_response tab and trigger completion flow
|
|
8488
|
-
setStepsDone(true);
|
|
8489
|
-
setIsStreamingDone(true);
|
|
8490
|
-
});
|
|
8491
|
-
// Handle stream closure
|
|
8492
|
-
source.addEventListener("close", () => {
|
|
8493
|
-
const closeState = streamStateMap.get(streamKey);
|
|
8494
|
-
if (!closeState || closeState.listenerGeneration !== generation)
|
|
8495
|
-
return;
|
|
8496
|
-
setIsStreaming(false);
|
|
8497
|
-
// If closeState.completed is already true, this is a normal close after [DONE].
|
|
8498
|
-
// If it's still false, the backend closed the connection abruptly.
|
|
8499
|
-
if (!closeState.completed) {
|
|
8500
|
-
closeState.completed = true;
|
|
8501
|
-
// Mark as aborted so the completion effect skips the Submit button
|
|
8687
|
+
// Check if we can auto-retry (abrupt close or 503, but not user abort)
|
|
8688
|
+
if (!isUserAbort && retryCountRef.current < MAX_RETRY_COUNT) {
|
|
8689
|
+
// Still have retries left — start countdown instead of showing final error
|
|
8690
|
+
setStepsDone(true); // Switch to agent_response tab to show countdown
|
|
8691
|
+
const retryMessage = is503
|
|
8692
|
+
? "Service temporarily unavailable (503), auto re-trying..."
|
|
8693
|
+
: "The service is temporarily unavailable because of an unusual load. Please wait a moment and try again.";
|
|
8694
|
+
setContent(retryMessage);
|
|
8695
|
+
startRetryCountdown();
|
|
8696
|
+
}
|
|
8697
|
+
else {
|
|
8698
|
+
// Retries exhausted — show final message
|
|
8502
8699
|
setWasStreamingAborted(true);
|
|
8503
8700
|
wasStreamingAbortedRef.current = true;
|
|
8504
|
-
// Mark the last in-progress step as completed
|
|
8505
8701
|
const currentSteps = stepRef.current;
|
|
8506
8702
|
if (currentSteps.length > 0) {
|
|
8507
8703
|
const lastStep = currentSteps[currentSteps.length - 1];
|
|
@@ -8511,21 +8707,9 @@ const StreamedContent = ({ botData }) => {
|
|
|
8511
8707
|
stepRef.current = [...currentSteps];
|
|
8512
8708
|
setSteps([...currentSteps]);
|
|
8513
8709
|
}
|
|
8514
|
-
|
|
8515
|
-
|
|
8516
|
-
|
|
8517
|
-
setContent(abruptCloseMessage);
|
|
8518
|
-
// Stop thinking if in progress
|
|
8519
|
-
if (isThinking) {
|
|
8520
|
-
setIsThinking(false);
|
|
8521
|
-
const endTime = Date.now();
|
|
8522
|
-
const duration = Math.round((endTime - thinkingStartTimeRef.current) / 1000);
|
|
8523
|
-
const finalThinkingTime = Math.max(duration, 1);
|
|
8524
|
-
setThinkingTime(finalThinkingTime);
|
|
8525
|
-
setShowThoughtDropdown(true);
|
|
8526
|
-
messageToStoreRef.current.chatData.thinkingResponse.thinkingTime = finalThinkingTime;
|
|
8527
|
-
}
|
|
8528
|
-
// Dispatch minimized widget data
|
|
8710
|
+
const finalMessage = "Please reach out to IA for this, as this didn't work even after retry";
|
|
8711
|
+
messageToStoreRef.current.chatData.response = finalMessage;
|
|
8712
|
+
setContent(finalMessage);
|
|
8529
8713
|
dispatch(smartBotActions.setMinimizedStreamData({
|
|
8530
8714
|
isStreaming: false,
|
|
8531
8715
|
stepHeader: questionsRef.current[questionsRef.current.length - 1] || "Ended unexpectedly",
|
|
@@ -8534,10 +8718,65 @@ const StreamedContent = ({ botData }) => {
|
|
|
8534
8718
|
streamStartTime: streamStartTimeRef.current,
|
|
8535
8719
|
actionCount: questionsRef.current.length || 1,
|
|
8536
8720
|
}));
|
|
8537
|
-
// Switch to agent_response tab and trigger completion flow
|
|
8538
8721
|
setStepsDone(true);
|
|
8539
8722
|
setIsStreamingDone(true);
|
|
8540
8723
|
}
|
|
8724
|
+
});
|
|
8725
|
+
// Handle stream closure
|
|
8726
|
+
source.addEventListener("close", () => {
|
|
8727
|
+
const closeState = streamStateMap.get(streamKey);
|
|
8728
|
+
if (!closeState || closeState.listenerGeneration !== generation)
|
|
8729
|
+
return;
|
|
8730
|
+
setIsStreaming(false);
|
|
8731
|
+
// If closeState.completed is already true, this is a normal close after [DONE].
|
|
8732
|
+
// If it's still false, the backend closed the connection abruptly.
|
|
8733
|
+
if (!closeState.completed) {
|
|
8734
|
+
closeState.completed = true;
|
|
8735
|
+
// Stop thinking if in progress
|
|
8736
|
+
if (isThinking) {
|
|
8737
|
+
setIsThinking(false);
|
|
8738
|
+
const endTime = Date.now();
|
|
8739
|
+
const duration = Math.round((endTime - thinkingStartTimeRef.current) / 1000);
|
|
8740
|
+
const finalThinkingTime = Math.max(duration, 1);
|
|
8741
|
+
setThinkingTime(finalThinkingTime);
|
|
8742
|
+
setShowThoughtDropdown(true);
|
|
8743
|
+
messageToStoreRef.current.chatData.thinkingResponse.thinkingTime = finalThinkingTime;
|
|
8744
|
+
}
|
|
8745
|
+
// Check if we can auto-retry
|
|
8746
|
+
if (retryCountRef.current < MAX_RETRY_COUNT) {
|
|
8747
|
+
// Still have retries left — start countdown instead of showing final error
|
|
8748
|
+
setStepsDone(true); // Switch to agent_response tab to show countdown
|
|
8749
|
+
setContent("The service is temporarily unavailable because of an unusual load. Please wait a moment and try again.");
|
|
8750
|
+
startRetryCountdown();
|
|
8751
|
+
}
|
|
8752
|
+
else {
|
|
8753
|
+
// Retries exhausted — show final elegant message
|
|
8754
|
+
setWasStreamingAborted(true);
|
|
8755
|
+
wasStreamingAbortedRef.current = true;
|
|
8756
|
+
const currentSteps = stepRef.current;
|
|
8757
|
+
if (currentSteps.length > 0) {
|
|
8758
|
+
const lastStep = currentSteps[currentSteps.length - 1];
|
|
8759
|
+
if (lastStep.step_status === "not-completed") {
|
|
8760
|
+
lastStep.step_status = "completed";
|
|
8761
|
+
}
|
|
8762
|
+
stepRef.current = [...currentSteps];
|
|
8763
|
+
setSteps([...currentSteps]);
|
|
8764
|
+
}
|
|
8765
|
+
const finalMessage = "Please reach out to IA for this, as this didn't work even after retry";
|
|
8766
|
+
messageToStoreRef.current.chatData.response = finalMessage;
|
|
8767
|
+
setContent(finalMessage);
|
|
8768
|
+
dispatch(smartBotActions.setMinimizedStreamData({
|
|
8769
|
+
isStreaming: false,
|
|
8770
|
+
stepHeader: questionsRef.current[questionsRef.current.length - 1] || "Ended unexpectedly",
|
|
8771
|
+
stepSubHeader: "Connection closed",
|
|
8772
|
+
stepStatus: "completed",
|
|
8773
|
+
streamStartTime: streamStartTimeRef.current,
|
|
8774
|
+
actionCount: questionsRef.current.length || 1,
|
|
8775
|
+
}));
|
|
8776
|
+
setStepsDone(true);
|
|
8777
|
+
setIsStreamingDone(true);
|
|
8778
|
+
}
|
|
8779
|
+
}
|
|
8541
8780
|
// else: closeState.completed is already true — normal close after [DONE], nothing to do.
|
|
8542
8781
|
});
|
|
8543
8782
|
};
|
|
@@ -8841,6 +9080,14 @@ const StreamedContent = ({ botData }) => {
|
|
|
8841
9080
|
* Aborts the current streaming connection
|
|
8842
9081
|
*/
|
|
8843
9082
|
const abortStreaming = () => {
|
|
9083
|
+
// If a retry countdown is running, clear it and prevent further retries
|
|
9084
|
+
if (retryTimerRef.current) {
|
|
9085
|
+
clearInterval(retryTimerRef.current);
|
|
9086
|
+
retryTimerRef.current = null;
|
|
9087
|
+
setIsRetrying(false);
|
|
9088
|
+
setRetryCountdown(0);
|
|
9089
|
+
}
|
|
9090
|
+
retryCountRef.current = MAX_RETRY_COUNT; // Exhaust retries so no further auto-retries happen
|
|
8844
9091
|
if (sourceRef.current && isStreaming) {
|
|
8845
9092
|
setWasStreamingAborted(true);
|
|
8846
9093
|
wasStreamingAbortedRef.current = true;
|
|
@@ -8888,9 +9135,9 @@ const StreamedContent = ({ botData }) => {
|
|
|
8888
9135
|
// If no content yet, show aborted message
|
|
8889
9136
|
// if (isEmpty(messageToStoreRef.current.chatData?.response)) {
|
|
8890
9137
|
messageToStoreRef.current.chatData.response =
|
|
8891
|
-
messageToStoreRef.current.chatData.response + "\n\
|
|
9138
|
+
messageToStoreRef.current.chatData.response + "\n\nYou stopped this response. You can ask a new question whenever you're ready";
|
|
8892
9139
|
setContent((prev) => {
|
|
8893
|
-
return prev + "\n\
|
|
9140
|
+
return prev + "\n\nYou stopped this response. You can ask a new question whenever you're ready";
|
|
8894
9141
|
});
|
|
8895
9142
|
// }
|
|
8896
9143
|
}
|
|
@@ -8912,10 +9159,16 @@ const StreamedContent = ({ botData }) => {
|
|
|
8912
9159
|
* Cleanup on unmount: Do NOT close the stream here.
|
|
8913
9160
|
* The stream connection is kept alive in streamStateMap so it survives
|
|
8914
9161
|
* tab-switch remounts. It is only closed by explicit abortStreaming() or new chat.
|
|
9162
|
+
* Also clean up retry timer if running.
|
|
8915
9163
|
*/
|
|
8916
9164
|
React.useEffect(() => {
|
|
8917
9165
|
return () => {
|
|
8918
9166
|
// Intentionally not closing the stream - it persists in streamStateMap
|
|
9167
|
+
// Clean up retry timer
|
|
9168
|
+
if (retryTimerRef.current) {
|
|
9169
|
+
clearInterval(retryTimerRef.current);
|
|
9170
|
+
retryTimerRef.current = null;
|
|
9171
|
+
}
|
|
8919
9172
|
};
|
|
8920
9173
|
}, []);
|
|
8921
9174
|
/**
|
|
@@ -8967,7 +9220,7 @@ const StreamedContent = ({ botData }) => {
|
|
|
8967
9220
|
* @returns {JSX.Element} Rendered content with optional blinking cursor
|
|
8968
9221
|
*/
|
|
8969
9222
|
const renderContent = () => {
|
|
8970
|
-
return (jsxRuntime.
|
|
9223
|
+
return (jsxRuntime.jsxs("div", { className: classes.streamContainer, children: [jsxRuntime.jsx(StepsResponseTab, { steps: steps, stepChange: stepChange, setSteps: setSteps, stepsDone: stepsDone, setStepsDone: setStepsDone, finalStepDone: finalStepDone, setFinalStepDone: setFinalStepDone, content: content, isStreaming: isStreaming, currentMode: currentMode, questions: questions, questionsStepsMap: questionsStepsMap, stepFormDataMap: stepFormDataMap, isFormDisabled: botData?.isFormDisabled || false, streamingWidgetData: streamingWidgetData, isRetrying: isRetrying }), isRetrying && (jsxRuntime.jsx("div", { className: classes.retryContainer, children: jsxRuntime.jsxs("span", { className: classes.retryMessage, children: ["Auto re-trying in ", retryCountdown, "s"] }) }))] }));
|
|
8971
9224
|
};
|
|
8972
9225
|
if (currentMode === "agent") {
|
|
8973
9226
|
return renderContent();
|
|
@@ -9259,6 +9512,7 @@ const TabularContent = ({ steps: initialSteps, currentTabValue, children, questi
|
|
|
9259
9512
|
const [stepFormSubmitted, setStepFormSubmitted] = React.useState(false);
|
|
9260
9513
|
const [hasNewStepFormFromRestream, setHasNewStepFormFromRestream] = React.useState(false);
|
|
9261
9514
|
const [activeFormIntent, setActiveFormIntent] = React.useState(null);
|
|
9515
|
+
const [retryCountdown, setRetryCountdown] = React.useState(0);
|
|
9262
9516
|
// Stable unique instance ID for this TabularContent mount
|
|
9263
9517
|
const instanceIdRef = React.useRef(null);
|
|
9264
9518
|
if (instanceIdRef.current === null) {
|
|
@@ -9324,10 +9578,15 @@ const TabularContent = ({ steps: initialSteps, currentTabValue, children, questi
|
|
|
9324
9578
|
const payload = stepFormStreamData;
|
|
9325
9579
|
// Clear Redux immediately
|
|
9326
9580
|
dispatch(smartBotActions.setStepFormStreamData(null));
|
|
9581
|
+
if (payload.status === "retrying") {
|
|
9582
|
+
setRetryCountdown(payload.countdown || 0);
|
|
9583
|
+
return;
|
|
9584
|
+
}
|
|
9327
9585
|
if (payload.status === "streaming_start") {
|
|
9328
9586
|
setIsRestreaming(true);
|
|
9329
9587
|
setStepFormSubmitted(true);
|
|
9330
9588
|
setHasNewStepFormFromRestream(false);
|
|
9589
|
+
setRetryCountdown(0);
|
|
9331
9590
|
setTabValue("steps");
|
|
9332
9591
|
return;
|
|
9333
9592
|
}
|
|
@@ -9467,6 +9726,8 @@ const TabularContent = ({ steps: initialSteps, currentTabValue, children, questi
|
|
|
9467
9726
|
}
|
|
9468
9727
|
return;
|
|
9469
9728
|
}
|
|
9729
|
+
// Clear retry countdown when final processing starts
|
|
9730
|
+
setRetryCountdown(0);
|
|
9470
9731
|
// Process all collected chunks at once (done or error)
|
|
9471
9732
|
const chunks = payload.chunks || [];
|
|
9472
9733
|
let newSteps = lodash.cloneDeep(stepsRef.current);
|
|
@@ -9615,7 +9876,7 @@ const TabularContent = ({ steps: initialSteps, currentTabValue, children, questi
|
|
|
9615
9876
|
},
|
|
9616
9877
|
], tabPanels: [
|
|
9617
9878
|
jsxRuntime.jsx(Steps, { steps: stepsState, questions: questionsState, questionsStepsMap: questionsStepsMapState, stepFormDataMap: stepFormDataMapState, isFormDisabled: (isFormDisabled && !isRestreaming) || stepFormSubmitted, activeFormIntent: hasNewStepFormFromRestream ? activeFormIntent : null, isRestreaming: isRestreaming }),
|
|
9618
|
-
jsxRuntime.jsxs(AgentResponse, { children: [children, renderedWidgets.length > 0 && (jsxRuntime.jsx("div", { className: "restream-widget-content", children: renderedWidgets }))] }),
|
|
9879
|
+
jsxRuntime.jsxs(AgentResponse, { children: [children, renderedWidgets.length > 0 && (jsxRuntime.jsx("div", { className: "restream-widget-content", children: renderedWidgets })), retryCountdown > 0 && (jsxRuntime.jsx("div", { style: { marginTop: "8px" }, children: jsxRuntime.jsxs("span", { style: { fontFamily: "Manrope", fontSize: "13px", fontWeight: 500, color: "#374151" }, children: ["Auto re-trying in ", retryCountdown, "s"] }) }))] }),
|
|
9619
9880
|
], value: tabValue }) }));
|
|
9620
9881
|
};
|
|
9621
9882
|
/** Reset the active instance tracker (call when a new conversation starts from the input field) */
|