impact-chatbot 2.3.62 → 2.3.64
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 +281 -121
- package/dist/index.cjs.js.map +1 -1
- package/dist/index.esm.js +281 -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,57 @@ 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
|
+
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
|
-
if (lastStep.step_status === "not-completed") {
|
|
8530
|
-
lastStep.step_status = "completed";
|
|
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 = "The service is temporarily unavailable because of an unusual load. Please wait a moment and try again.";
|
|
8692
|
+
setContent(retryMessage);
|
|
8693
|
+
startRetryCountdown();
|
|
8551
8694
|
}
|
|
8552
8695
|
else {
|
|
8553
|
-
|
|
8696
|
+
// Retries exhausted — show final message
|
|
8697
|
+
setWasStreamingAborted(true);
|
|
8698
|
+
wasStreamingAbortedRef.current = true;
|
|
8699
|
+
const currentSteps = stepRef.current;
|
|
8700
|
+
if (currentSteps.length > 0) {
|
|
8701
|
+
const lastStep = currentSteps[currentSteps.length - 1];
|
|
8702
|
+
if (lastStep.step_status === "not-completed") {
|
|
8703
|
+
lastStep.step_status = "completed";
|
|
8704
|
+
}
|
|
8705
|
+
stepRef.current = [...currentSteps];
|
|
8706
|
+
setSteps([...currentSteps]);
|
|
8707
|
+
}
|
|
8708
|
+
const finalMessage = "Please reach out to IA for this, as this didn't work even after retry";
|
|
8709
|
+
messageToStoreRef.current.chatData.response = finalMessage;
|
|
8710
|
+
setContent(finalMessage);
|
|
8711
|
+
dispatch(smartBotActions.setMinimizedStreamData({
|
|
8712
|
+
isStreaming: false,
|
|
8713
|
+
stepHeader: questionsRef.current[questionsRef.current.length - 1] || "Ended unexpectedly",
|
|
8714
|
+
stepSubHeader: "Connection closed",
|
|
8715
|
+
stepStatus: "completed",
|
|
8716
|
+
streamStartTime: streamStartTimeRef.current,
|
|
8717
|
+
actionCount: questionsRef.current.length || 1,
|
|
8718
|
+
}));
|
|
8719
|
+
setStepsDone(true);
|
|
8720
|
+
setIsStreamingDone(true);
|
|
8554
8721
|
}
|
|
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
8722
|
});
|
|
8569
8723
|
// Handle stream closure
|
|
8570
8724
|
source.addEventListener("close", () => {
|
|
@@ -8576,44 +8730,6 @@ const StreamedContent = ({ botData }) => {
|
|
|
8576
8730
|
// If it's still false, the backend closed the connection abruptly.
|
|
8577
8731
|
if (!closeState.completed) {
|
|
8578
8732
|
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
8733
|
// Stop thinking if in progress
|
|
8618
8734
|
if (isThinking) {
|
|
8619
8735
|
setIsThinking(false);
|
|
@@ -8624,18 +8740,40 @@ const StreamedContent = ({ botData }) => {
|
|
|
8624
8740
|
setShowThoughtDropdown(true);
|
|
8625
8741
|
messageToStoreRef.current.chatData.thinkingResponse.thinkingTime = finalThinkingTime;
|
|
8626
8742
|
}
|
|
8627
|
-
//
|
|
8628
|
-
|
|
8629
|
-
|
|
8630
|
-
|
|
8631
|
-
|
|
8632
|
-
|
|
8633
|
-
|
|
8634
|
-
|
|
8635
|
-
|
|
8636
|
-
|
|
8637
|
-
|
|
8638
|
-
|
|
8743
|
+
// Check if we can auto-retry
|
|
8744
|
+
if (retryCountRef.current < MAX_RETRY_COUNT) {
|
|
8745
|
+
// Still have retries left — start countdown instead of showing final error
|
|
8746
|
+
setStepsDone(true); // Switch to agent_response tab to show countdown
|
|
8747
|
+
setContent("The service is temporarily unavailable because of an unusual load. Please wait a moment and try again.");
|
|
8748
|
+
startRetryCountdown();
|
|
8749
|
+
}
|
|
8750
|
+
else {
|
|
8751
|
+
// Retries exhausted — show final elegant message
|
|
8752
|
+
setWasStreamingAborted(true);
|
|
8753
|
+
wasStreamingAbortedRef.current = true;
|
|
8754
|
+
const currentSteps = stepRef.current;
|
|
8755
|
+
if (currentSteps.length > 0) {
|
|
8756
|
+
const lastStep = currentSteps[currentSteps.length - 1];
|
|
8757
|
+
if (lastStep.step_status === "not-completed") {
|
|
8758
|
+
lastStep.step_status = "completed";
|
|
8759
|
+
}
|
|
8760
|
+
stepRef.current = [...currentSteps];
|
|
8761
|
+
setSteps([...currentSteps]);
|
|
8762
|
+
}
|
|
8763
|
+
const finalMessage = "Please reach out to IA for this, as this didn't work even after retry";
|
|
8764
|
+
messageToStoreRef.current.chatData.response = finalMessage;
|
|
8765
|
+
setContent(finalMessage);
|
|
8766
|
+
dispatch(smartBotActions.setMinimizedStreamData({
|
|
8767
|
+
isStreaming: false,
|
|
8768
|
+
stepHeader: questionsRef.current[questionsRef.current.length - 1] || "Ended unexpectedly",
|
|
8769
|
+
stepSubHeader: "Connection closed",
|
|
8770
|
+
stepStatus: "completed",
|
|
8771
|
+
streamStartTime: streamStartTimeRef.current,
|
|
8772
|
+
actionCount: questionsRef.current.length || 1,
|
|
8773
|
+
}));
|
|
8774
|
+
setStepsDone(true);
|
|
8775
|
+
setIsStreamingDone(true);
|
|
8776
|
+
}
|
|
8639
8777
|
}
|
|
8640
8778
|
// else: closeState.completed is already true — normal close after [DONE], nothing to do.
|
|
8641
8779
|
});
|
|
@@ -8940,6 +9078,14 @@ const StreamedContent = ({ botData }) => {
|
|
|
8940
9078
|
* Aborts the current streaming connection
|
|
8941
9079
|
*/
|
|
8942
9080
|
const abortStreaming = () => {
|
|
9081
|
+
// If a retry countdown is running, clear it and prevent further retries
|
|
9082
|
+
if (retryTimerRef.current) {
|
|
9083
|
+
clearInterval(retryTimerRef.current);
|
|
9084
|
+
retryTimerRef.current = null;
|
|
9085
|
+
setIsRetrying(false);
|
|
9086
|
+
setRetryCountdown(0);
|
|
9087
|
+
}
|
|
9088
|
+
retryCountRef.current = MAX_RETRY_COUNT; // Exhaust retries so no further auto-retries happen
|
|
8943
9089
|
if (sourceRef.current && isStreaming) {
|
|
8944
9090
|
setWasStreamingAborted(true);
|
|
8945
9091
|
wasStreamingAbortedRef.current = true;
|
|
@@ -8987,9 +9133,9 @@ const StreamedContent = ({ botData }) => {
|
|
|
8987
9133
|
// If no content yet, show aborted message
|
|
8988
9134
|
// if (isEmpty(messageToStoreRef.current.chatData?.response)) {
|
|
8989
9135
|
messageToStoreRef.current.chatData.response =
|
|
8990
|
-
messageToStoreRef.current.chatData.response + "\n\
|
|
9136
|
+
messageToStoreRef.current.chatData.response + "\n\nYou stopped this response. You can ask a new question whenever you're ready";
|
|
8991
9137
|
setContent((prev) => {
|
|
8992
|
-
return prev + "\n\
|
|
9138
|
+
return prev + "\n\nYou stopped this response. You can ask a new question whenever you're ready";
|
|
8993
9139
|
});
|
|
8994
9140
|
// }
|
|
8995
9141
|
}
|
|
@@ -9011,10 +9157,16 @@ const StreamedContent = ({ botData }) => {
|
|
|
9011
9157
|
* Cleanup on unmount: Do NOT close the stream here.
|
|
9012
9158
|
* The stream connection is kept alive in streamStateMap so it survives
|
|
9013
9159
|
* tab-switch remounts. It is only closed by explicit abortStreaming() or new chat.
|
|
9160
|
+
* Also clean up retry timer if running.
|
|
9014
9161
|
*/
|
|
9015
9162
|
React.useEffect(() => {
|
|
9016
9163
|
return () => {
|
|
9017
9164
|
// Intentionally not closing the stream - it persists in streamStateMap
|
|
9165
|
+
// Clean up retry timer
|
|
9166
|
+
if (retryTimerRef.current) {
|
|
9167
|
+
clearInterval(retryTimerRef.current);
|
|
9168
|
+
retryTimerRef.current = null;
|
|
9169
|
+
}
|
|
9018
9170
|
};
|
|
9019
9171
|
}, []);
|
|
9020
9172
|
/**
|
|
@@ -9066,7 +9218,7 @@ const StreamedContent = ({ botData }) => {
|
|
|
9066
9218
|
* @returns {JSX.Element} Rendered content with optional blinking cursor
|
|
9067
9219
|
*/
|
|
9068
9220
|
const renderContent = () => {
|
|
9069
|
-
return (jsxRuntime.
|
|
9221
|
+
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
9222
|
};
|
|
9071
9223
|
if (currentMode === "agent") {
|
|
9072
9224
|
return renderContent();
|
|
@@ -9358,6 +9510,7 @@ const TabularContent = ({ steps: initialSteps, currentTabValue, children, questi
|
|
|
9358
9510
|
const [stepFormSubmitted, setStepFormSubmitted] = React.useState(false);
|
|
9359
9511
|
const [hasNewStepFormFromRestream, setHasNewStepFormFromRestream] = React.useState(false);
|
|
9360
9512
|
const [activeFormIntent, setActiveFormIntent] = React.useState(null);
|
|
9513
|
+
const [retryCountdown, setRetryCountdown] = React.useState(0);
|
|
9361
9514
|
// Stable unique instance ID for this TabularContent mount
|
|
9362
9515
|
const instanceIdRef = React.useRef(null);
|
|
9363
9516
|
if (instanceIdRef.current === null) {
|
|
@@ -9423,10 +9576,15 @@ const TabularContent = ({ steps: initialSteps, currentTabValue, children, questi
|
|
|
9423
9576
|
const payload = stepFormStreamData;
|
|
9424
9577
|
// Clear Redux immediately
|
|
9425
9578
|
dispatch(smartBotActions.setStepFormStreamData(null));
|
|
9579
|
+
if (payload.status === "retrying") {
|
|
9580
|
+
setRetryCountdown(payload.countdown || 0);
|
|
9581
|
+
return;
|
|
9582
|
+
}
|
|
9426
9583
|
if (payload.status === "streaming_start") {
|
|
9427
9584
|
setIsRestreaming(true);
|
|
9428
9585
|
setStepFormSubmitted(true);
|
|
9429
9586
|
setHasNewStepFormFromRestream(false);
|
|
9587
|
+
setRetryCountdown(0);
|
|
9430
9588
|
setTabValue("steps");
|
|
9431
9589
|
return;
|
|
9432
9590
|
}
|
|
@@ -9566,6 +9724,8 @@ const TabularContent = ({ steps: initialSteps, currentTabValue, children, questi
|
|
|
9566
9724
|
}
|
|
9567
9725
|
return;
|
|
9568
9726
|
}
|
|
9727
|
+
// Clear retry countdown when final processing starts
|
|
9728
|
+
setRetryCountdown(0);
|
|
9569
9729
|
// Process all collected chunks at once (done or error)
|
|
9570
9730
|
const chunks = payload.chunks || [];
|
|
9571
9731
|
let newSteps = lodash.cloneDeep(stepsRef.current);
|
|
@@ -9714,7 +9874,7 @@ const TabularContent = ({ steps: initialSteps, currentTabValue, children, questi
|
|
|
9714
9874
|
},
|
|
9715
9875
|
], tabPanels: [
|
|
9716
9876
|
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 }))] }),
|
|
9877
|
+
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
9878
|
], value: tabValue }) }));
|
|
9719
9879
|
};
|
|
9720
9880
|
/** Reset the active instance tracker (call when a new conversation starts from the input field) */
|