impact-chatbot 2.3.62 → 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 +283 -121
- package/dist/index.cjs.js.map +1 -1
- package/dist/index.esm.js +283 -121
- package/dist/index.esm.js.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs.js
CHANGED
|
@@ -5428,6 +5428,7 @@ const AxiosSource = (url, opts, messageToStoreRef) => {
|
|
|
5428
5428
|
})
|
|
5429
5429
|
.catch((error) => {
|
|
5430
5430
|
let reason = "Network request failed";
|
|
5431
|
+
let statusCode = 0;
|
|
5431
5432
|
console.error("[AxiosSource] Request failed:", error);
|
|
5432
5433
|
// Determine specific error reasons
|
|
5433
5434
|
if (axios.isCancel(error)) {
|
|
@@ -5436,8 +5437,15 @@ const AxiosSource = (url, opts, messageToStoreRef) => {
|
|
|
5436
5437
|
else if (error.code === "ECONNABORTED") {
|
|
5437
5438
|
reason = "Network request timed out";
|
|
5438
5439
|
}
|
|
5439
|
-
//
|
|
5440
|
-
|
|
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);
|
|
5441
5449
|
});
|
|
5442
5450
|
// Add method to manually close the connection
|
|
5443
5451
|
eventTarget.close = () => {
|
|
@@ -5478,10 +5486,15 @@ const useStyles$6 = styles.makeStyles((theme) => ({
|
|
|
5478
5486
|
marginTop: pxToRem(16),
|
|
5479
5487
|
}
|
|
5480
5488
|
}));
|
|
5489
|
+
const MAX_RETRY_COUNT_BUTTON = 1;
|
|
5490
|
+
const RETRY_COUNTDOWN_SECONDS_BUTTON = 15;
|
|
5481
5491
|
const ButtonContent = ({ bodyText, isFormDisabled = false, isStepFormSubmit = false, isFormValid = true }) => {
|
|
5482
5492
|
const classes = useStyles$6();
|
|
5483
5493
|
const dispatch = reactRedux.useDispatch();
|
|
5484
5494
|
const sourceRef = React.useRef(null);
|
|
5495
|
+
const retryCountRef = React.useRef(0);
|
|
5496
|
+
const retryTimerRef = React.useRef(null);
|
|
5497
|
+
const retryPayloadRef = React.useRef(null);
|
|
5485
5498
|
const messageStoreRef = React.useRef({
|
|
5486
5499
|
currentMode: "agent",
|
|
5487
5500
|
chatData: {
|
|
@@ -5525,6 +5538,8 @@ const ButtonContent = ({ bodyText, isFormDisabled = false, isStepFormSubmit = fa
|
|
|
5525
5538
|
sendButton.click();
|
|
5526
5539
|
};
|
|
5527
5540
|
const callInitApiStream = (userInput) => {
|
|
5541
|
+
// Store payload for potential retries
|
|
5542
|
+
retryPayloadRef.current = userInput;
|
|
5528
5543
|
// Prefer module-level values (set synchronously by StreamedContent), fall back to sessionStorage
|
|
5529
5544
|
// If a previous completed/follow-up response updated the state, use those values.
|
|
5530
5545
|
// Read from module-level stepFormStreamControl (survives remounts) instead of local messageStoreRef.
|
|
@@ -5724,33 +5739,64 @@ const ButtonContent = ({ bodyText, isFormDisabled = false, isStepFormSubmit = fa
|
|
|
5724
5739
|
console.error("[ButtonContent] SSE parse error:", e);
|
|
5725
5740
|
}
|
|
5726
5741
|
});
|
|
5727
|
-
sourceRef.current.addEventListener("error", () => {
|
|
5728
|
-
// On error, still dispatch whatever we collected
|
|
5742
|
+
sourceRef.current.addEventListener("error", (event) => {
|
|
5729
5743
|
stepFormStreamControl.isStreaming = false;
|
|
5730
5744
|
stepFormStreamControl.abort = null;
|
|
5731
5745
|
window.dispatchEvent(new CustomEvent("stepFormStreamEnd"));
|
|
5732
|
-
|
|
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
|
+
}
|
|
5733
5774
|
});
|
|
5734
|
-
// Handle stream closure — fires when the HTTP request completes successfully.
|
|
5735
|
-
// If [DONE] was received, the "message" handler already dispatched status:"done"
|
|
5736
|
-
// and set stepFormStreamControl.isStreaming = false.
|
|
5737
|
-
// If [DONE] was NOT received, the connection closed abruptly mid-processing.
|
|
5738
5775
|
sourceRef.current.addEventListener("close", () => {
|
|
5739
5776
|
if (stepFormStreamControl.isStreaming) {
|
|
5740
5777
|
stepFormStreamControl.isStreaming = false;
|
|
5741
5778
|
stepFormStreamControl.abort = null;
|
|
5742
5779
|
window.dispatchEvent(new CustomEvent("stepFormStreamEnd"));
|
|
5743
|
-
|
|
5744
|
-
|
|
5745
|
-
|
|
5746
|
-
|
|
5747
|
-
|
|
5748
|
-
|
|
5749
|
-
|
|
5750
|
-
|
|
5751
|
-
|
|
5752
|
-
|
|
5753
|
-
|
|
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
|
+
}
|
|
5754
5800
|
}
|
|
5755
5801
|
});
|
|
5756
5802
|
};
|
|
@@ -6057,6 +6103,15 @@ const useStyles$5 = styles.makeStyles((theme) => ({
|
|
|
6057
6103
|
position: "relative",
|
|
6058
6104
|
background: colours.white,
|
|
6059
6105
|
},
|
|
6106
|
+
retryContainer: {
|
|
6107
|
+
marginTop: pxToRem(8),
|
|
6108
|
+
},
|
|
6109
|
+
retryMessage: {
|
|
6110
|
+
fontFamily: "Manrope",
|
|
6111
|
+
fontSize: pxToRem(13),
|
|
6112
|
+
fontWeight: 500,
|
|
6113
|
+
color: "#374151",
|
|
6114
|
+
},
|
|
6060
6115
|
}));
|
|
6061
6116
|
|
|
6062
6117
|
var _path$i, _path2$2, _defs$5;
|
|
@@ -7761,8 +7816,20 @@ const AgentResponse$1 = (props) => {
|
|
|
7761
7816
|
};
|
|
7762
7817
|
|
|
7763
7818
|
const StepsResponseTab = (props) => {
|
|
7764
|
-
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;
|
|
7765
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]);
|
|
7766
7833
|
const handleChangeTabValue = (_event, newValue) => {
|
|
7767
7834
|
setTabValue(newValue);
|
|
7768
7835
|
};
|
|
@@ -7992,6 +8059,14 @@ const StreamedContent = ({ botData }) => {
|
|
|
7992
8059
|
const [finalStepDone, setFinalStepDone] = React.useState(false);
|
|
7993
8060
|
const [wasStreamingAborted, setWasStreamingAborted] = React.useState(false);
|
|
7994
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
|
|
7995
8070
|
const thinkingContentRef = React.useRef("");
|
|
7996
8071
|
reactRedux.useSelector((state) => {
|
|
7997
8072
|
return state.smartBotReducer.thinkingContext;
|
|
@@ -8111,6 +8186,94 @@ const StreamedContent = ({ botData }) => {
|
|
|
8111
8186
|
}));
|
|
8112
8187
|
}
|
|
8113
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
|
+
};
|
|
8114
8277
|
/**
|
|
8115
8278
|
* Main effect to initialize and handle the streaming connection
|
|
8116
8279
|
* Sets up the SSE connection and event listeners for message processing
|
|
@@ -8505,66 +8668,59 @@ const StreamedContent = ({ botData }) => {
|
|
|
8505
8668
|
const errState = streamStateMap.get(streamKey);
|
|
8506
8669
|
if (!errState || errState.listenerGeneration !== generation)
|
|
8507
8670
|
return;
|
|
8508
|
-
console.error("Stream error:", event.reason);
|
|
8671
|
+
console.error("Stream error:", event.reason, "statusCode:", event.statusCode);
|
|
8509
8672
|
setIsStreaming(false);
|
|
8510
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;
|
|
8511
8677
|
if (isThinking) {
|
|
8512
8678
|
setIsThinking(false);
|
|
8513
8679
|
const endTime = Date.now();
|
|
8514
8680
|
const duration = Math.round((endTime - thinkingStartTimeRef.current) / 1000);
|
|
8515
|
-
const finalThinkingTime = Math.max(duration, 1);
|
|
8681
|
+
const finalThinkingTime = Math.max(duration, 1);
|
|
8516
8682
|
setThinkingTime(finalThinkingTime);
|
|
8517
8683
|
setShowThoughtDropdown(true);
|
|
8518
8684
|
thinkingStartTimeRef.current = finalThinkingTime;
|
|
8519
|
-
// Store thinking time in messageToStoreRef for persistence
|
|
8520
8685
|
messageToStoreRef.current.chatData.thinkingResponse.thinkingTime = finalThinkingTime;
|
|
8521
8686
|
}
|
|
8522
|
-
//
|
|
8523
|
-
|
|
8524
|
-
|
|
8525
|
-
|
|
8526
|
-
|
|
8527
|
-
|
|
8528
|
-
|
|
8529
|
-
|
|
8530
|
-
|
|
8531
|
-
}
|
|
8532
|
-
stepRef.current = [...currentSteps];
|
|
8533
|
-
setSteps([...currentSteps]);
|
|
8534
|
-
}
|
|
8535
|
-
// Show the abrupt close message at the bottom of any existing content.
|
|
8536
|
-
// If widgets already exist in appendedData, append the error as the last widget
|
|
8537
|
-
// so it renders AFTER the already-received content (not at the top).
|
|
8538
|
-
const abruptCloseMessage = "The requested Task has ended unexpectedly, please retry again";
|
|
8539
|
-
const hasExistingWidgetsOnErr = messageToStoreRef.current.appendedData &&
|
|
8540
|
-
(isArray(messageToStoreRef.current.appendedData)
|
|
8541
|
-
? messageToStoreRef.current.appendedData.length > 0
|
|
8542
|
-
: Object.keys(messageToStoreRef.current.appendedData).length > 0);
|
|
8543
|
-
if (hasExistingWidgetsOnErr) {
|
|
8544
|
-
const errorWidget = { type: "text", response: abruptCloseMessage };
|
|
8545
|
-
if (isArray(messageToStoreRef.current.appendedData)) {
|
|
8546
|
-
messageToStoreRef.current.appendedData.push(errorWidget);
|
|
8547
|
-
}
|
|
8548
|
-
else {
|
|
8549
|
-
messageToStoreRef.current.appendedData = [messageToStoreRef.current.appendedData, errorWidget];
|
|
8550
|
-
}
|
|
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();
|
|
8551
8696
|
}
|
|
8552
8697
|
else {
|
|
8553
|
-
|
|
8698
|
+
// Retries exhausted — show final message
|
|
8699
|
+
setWasStreamingAborted(true);
|
|
8700
|
+
wasStreamingAbortedRef.current = true;
|
|
8701
|
+
const currentSteps = stepRef.current;
|
|
8702
|
+
if (currentSteps.length > 0) {
|
|
8703
|
+
const lastStep = currentSteps[currentSteps.length - 1];
|
|
8704
|
+
if (lastStep.step_status === "not-completed") {
|
|
8705
|
+
lastStep.step_status = "completed";
|
|
8706
|
+
}
|
|
8707
|
+
stepRef.current = [...currentSteps];
|
|
8708
|
+
setSteps([...currentSteps]);
|
|
8709
|
+
}
|
|
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);
|
|
8713
|
+
dispatch(smartBotActions.setMinimizedStreamData({
|
|
8714
|
+
isStreaming: false,
|
|
8715
|
+
stepHeader: questionsRef.current[questionsRef.current.length - 1] || "Ended unexpectedly",
|
|
8716
|
+
stepSubHeader: "Connection closed",
|
|
8717
|
+
stepStatus: "completed",
|
|
8718
|
+
streamStartTime: streamStartTimeRef.current,
|
|
8719
|
+
actionCount: questionsRef.current.length || 1,
|
|
8720
|
+
}));
|
|
8721
|
+
setStepsDone(true);
|
|
8722
|
+
setIsStreamingDone(true);
|
|
8554
8723
|
}
|
|
8555
|
-
setContent(abruptCloseMessage);
|
|
8556
|
-
// Dispatch minimized widget data
|
|
8557
|
-
dispatch(smartBotActions.setMinimizedStreamData({
|
|
8558
|
-
isStreaming: false,
|
|
8559
|
-
stepHeader: questionsRef.current[questionsRef.current.length - 1] || "Ended unexpectedly",
|
|
8560
|
-
stepSubHeader: "Connection closed",
|
|
8561
|
-
stepStatus: "completed",
|
|
8562
|
-
streamStartTime: streamStartTimeRef.current,
|
|
8563
|
-
actionCount: questionsRef.current.length || 1,
|
|
8564
|
-
}));
|
|
8565
|
-
// Switch to agent_response tab and trigger completion flow
|
|
8566
|
-
setStepsDone(true);
|
|
8567
|
-
setIsStreamingDone(true);
|
|
8568
8724
|
});
|
|
8569
8725
|
// Handle stream closure
|
|
8570
8726
|
source.addEventListener("close", () => {
|
|
@@ -8576,44 +8732,6 @@ const StreamedContent = ({ botData }) => {
|
|
|
8576
8732
|
// If it's still false, the backend closed the connection abruptly.
|
|
8577
8733
|
if (!closeState.completed) {
|
|
8578
8734
|
closeState.completed = true;
|
|
8579
|
-
// Mark as aborted so the completion effect skips the Submit button
|
|
8580
|
-
setWasStreamingAborted(true);
|
|
8581
|
-
wasStreamingAbortedRef.current = true;
|
|
8582
|
-
// Mark the last in-progress step as completed
|
|
8583
|
-
const currentSteps = stepRef.current;
|
|
8584
|
-
if (currentSteps.length > 0) {
|
|
8585
|
-
const lastStep = currentSteps[currentSteps.length - 1];
|
|
8586
|
-
if (lastStep.step_status === "not-completed") {
|
|
8587
|
-
lastStep.step_status = "completed";
|
|
8588
|
-
}
|
|
8589
|
-
stepRef.current = [...currentSteps];
|
|
8590
|
-
setSteps([...currentSteps]);
|
|
8591
|
-
}
|
|
8592
|
-
// Show the abrupt close message. The final response array is structured as:
|
|
8593
|
-
// [textResponseTobeParsed, ...appendedData] — so chatData.response renders FIRST,
|
|
8594
|
-
// then widget items from appendedData render below it.
|
|
8595
|
-
// To ensure the error message appears AFTER already-rendered widgets, we append it
|
|
8596
|
-
// as a text widget item to appendedData instead of setting it on chatData.response.
|
|
8597
|
-
const abruptCloseMessage = "The requested Task has ended unexpectedly, please retry again";
|
|
8598
|
-
const hasExistingWidgets = messageToStoreRef.current.appendedData &&
|
|
8599
|
-
(isArray(messageToStoreRef.current.appendedData)
|
|
8600
|
-
? messageToStoreRef.current.appendedData.length > 0
|
|
8601
|
-
: Object.keys(messageToStoreRef.current.appendedData).length > 0);
|
|
8602
|
-
if (hasExistingWidgets) {
|
|
8603
|
-
// Widgets already exist — append error as the last widget so it renders at the bottom
|
|
8604
|
-
const errorWidget = { type: "text", response: abruptCloseMessage };
|
|
8605
|
-
if (isArray(messageToStoreRef.current.appendedData)) {
|
|
8606
|
-
messageToStoreRef.current.appendedData.push(errorWidget);
|
|
8607
|
-
}
|
|
8608
|
-
else {
|
|
8609
|
-
messageToStoreRef.current.appendedData = [messageToStoreRef.current.appendedData, errorWidget];
|
|
8610
|
-
}
|
|
8611
|
-
}
|
|
8612
|
-
else {
|
|
8613
|
-
// No widgets — set on chatData.response (will be the only rendered text)
|
|
8614
|
-
messageToStoreRef.current.chatData.response = abruptCloseMessage;
|
|
8615
|
-
}
|
|
8616
|
-
setContent(abruptCloseMessage);
|
|
8617
8735
|
// Stop thinking if in progress
|
|
8618
8736
|
if (isThinking) {
|
|
8619
8737
|
setIsThinking(false);
|
|
@@ -8624,18 +8742,40 @@ const StreamedContent = ({ botData }) => {
|
|
|
8624
8742
|
setShowThoughtDropdown(true);
|
|
8625
8743
|
messageToStoreRef.current.chatData.thinkingResponse.thinkingTime = finalThinkingTime;
|
|
8626
8744
|
}
|
|
8627
|
-
//
|
|
8628
|
-
|
|
8629
|
-
|
|
8630
|
-
|
|
8631
|
-
|
|
8632
|
-
|
|
8633
|
-
|
|
8634
|
-
|
|
8635
|
-
|
|
8636
|
-
|
|
8637
|
-
|
|
8638
|
-
|
|
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
|
+
}
|
|
8639
8779
|
}
|
|
8640
8780
|
// else: closeState.completed is already true — normal close after [DONE], nothing to do.
|
|
8641
8781
|
});
|
|
@@ -8940,6 +9080,14 @@ const StreamedContent = ({ botData }) => {
|
|
|
8940
9080
|
* Aborts the current streaming connection
|
|
8941
9081
|
*/
|
|
8942
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
|
|
8943
9091
|
if (sourceRef.current && isStreaming) {
|
|
8944
9092
|
setWasStreamingAborted(true);
|
|
8945
9093
|
wasStreamingAbortedRef.current = true;
|
|
@@ -8987,9 +9135,9 @@ const StreamedContent = ({ botData }) => {
|
|
|
8987
9135
|
// If no content yet, show aborted message
|
|
8988
9136
|
// if (isEmpty(messageToStoreRef.current.chatData?.response)) {
|
|
8989
9137
|
messageToStoreRef.current.chatData.response =
|
|
8990
|
-
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";
|
|
8991
9139
|
setContent((prev) => {
|
|
8992
|
-
return prev + "\n\
|
|
9140
|
+
return prev + "\n\nYou stopped this response. You can ask a new question whenever you're ready";
|
|
8993
9141
|
});
|
|
8994
9142
|
// }
|
|
8995
9143
|
}
|
|
@@ -9011,10 +9159,16 @@ const StreamedContent = ({ botData }) => {
|
|
|
9011
9159
|
* Cleanup on unmount: Do NOT close the stream here.
|
|
9012
9160
|
* The stream connection is kept alive in streamStateMap so it survives
|
|
9013
9161
|
* tab-switch remounts. It is only closed by explicit abortStreaming() or new chat.
|
|
9162
|
+
* Also clean up retry timer if running.
|
|
9014
9163
|
*/
|
|
9015
9164
|
React.useEffect(() => {
|
|
9016
9165
|
return () => {
|
|
9017
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
|
+
}
|
|
9018
9172
|
};
|
|
9019
9173
|
}, []);
|
|
9020
9174
|
/**
|
|
@@ -9066,7 +9220,7 @@ const StreamedContent = ({ botData }) => {
|
|
|
9066
9220
|
* @returns {JSX.Element} Rendered content with optional blinking cursor
|
|
9067
9221
|
*/
|
|
9068
9222
|
const renderContent = () => {
|
|
9069
|
-
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"] }) }))] }));
|
|
9070
9224
|
};
|
|
9071
9225
|
if (currentMode === "agent") {
|
|
9072
9226
|
return renderContent();
|
|
@@ -9358,6 +9512,7 @@ const TabularContent = ({ steps: initialSteps, currentTabValue, children, questi
|
|
|
9358
9512
|
const [stepFormSubmitted, setStepFormSubmitted] = React.useState(false);
|
|
9359
9513
|
const [hasNewStepFormFromRestream, setHasNewStepFormFromRestream] = React.useState(false);
|
|
9360
9514
|
const [activeFormIntent, setActiveFormIntent] = React.useState(null);
|
|
9515
|
+
const [retryCountdown, setRetryCountdown] = React.useState(0);
|
|
9361
9516
|
// Stable unique instance ID for this TabularContent mount
|
|
9362
9517
|
const instanceIdRef = React.useRef(null);
|
|
9363
9518
|
if (instanceIdRef.current === null) {
|
|
@@ -9423,10 +9578,15 @@ const TabularContent = ({ steps: initialSteps, currentTabValue, children, questi
|
|
|
9423
9578
|
const payload = stepFormStreamData;
|
|
9424
9579
|
// Clear Redux immediately
|
|
9425
9580
|
dispatch(smartBotActions.setStepFormStreamData(null));
|
|
9581
|
+
if (payload.status === "retrying") {
|
|
9582
|
+
setRetryCountdown(payload.countdown || 0);
|
|
9583
|
+
return;
|
|
9584
|
+
}
|
|
9426
9585
|
if (payload.status === "streaming_start") {
|
|
9427
9586
|
setIsRestreaming(true);
|
|
9428
9587
|
setStepFormSubmitted(true);
|
|
9429
9588
|
setHasNewStepFormFromRestream(false);
|
|
9589
|
+
setRetryCountdown(0);
|
|
9430
9590
|
setTabValue("steps");
|
|
9431
9591
|
return;
|
|
9432
9592
|
}
|
|
@@ -9566,6 +9726,8 @@ const TabularContent = ({ steps: initialSteps, currentTabValue, children, questi
|
|
|
9566
9726
|
}
|
|
9567
9727
|
return;
|
|
9568
9728
|
}
|
|
9729
|
+
// Clear retry countdown when final processing starts
|
|
9730
|
+
setRetryCountdown(0);
|
|
9569
9731
|
// Process all collected chunks at once (done or error)
|
|
9570
9732
|
const chunks = payload.chunks || [];
|
|
9571
9733
|
let newSteps = lodash.cloneDeep(stepsRef.current);
|
|
@@ -9714,7 +9876,7 @@ const TabularContent = ({ steps: initialSteps, currentTabValue, children, questi
|
|
|
9714
9876
|
},
|
|
9715
9877
|
], tabPanels: [
|
|
9716
9878
|
jsxRuntime.jsx(Steps, { steps: stepsState, questions: questionsState, questionsStepsMap: questionsStepsMapState, stepFormDataMap: stepFormDataMapState, isFormDisabled: (isFormDisabled && !isRestreaming) || stepFormSubmitted, activeFormIntent: hasNewStepFormFromRestream ? activeFormIntent : null, isRestreaming: isRestreaming }),
|
|
9717
|
-
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"] }) }))] }),
|
|
9718
9880
|
], value: tabValue }) }));
|
|
9719
9881
|
};
|
|
9720
9882
|
/** Reset the active instance tracker (call when a new conversation starts from the input field) */
|