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.esm.js
CHANGED
|
@@ -5406,6 +5406,7 @@ const AxiosSource = (url, opts, messageToStoreRef) => {
|
|
|
5406
5406
|
})
|
|
5407
5407
|
.catch((error) => {
|
|
5408
5408
|
let reason = "Network request failed";
|
|
5409
|
+
let statusCode = 0;
|
|
5409
5410
|
console.error("[AxiosSource] Request failed:", error);
|
|
5410
5411
|
// Determine specific error reasons
|
|
5411
5412
|
if (axios.isCancel(error)) {
|
|
@@ -5414,8 +5415,15 @@ const AxiosSource = (url, opts, messageToStoreRef) => {
|
|
|
5414
5415
|
else if (error.code === "ECONNABORTED") {
|
|
5415
5416
|
reason = "Network request timed out";
|
|
5416
5417
|
}
|
|
5417
|
-
//
|
|
5418
|
-
|
|
5418
|
+
// Capture HTTP status code from the error response (e.g. 503)
|
|
5419
|
+
if (error.response && error.response.status) {
|
|
5420
|
+
statusCode = error.response.status;
|
|
5421
|
+
reason = `HTTP ${statusCode}: ${reason}`;
|
|
5422
|
+
}
|
|
5423
|
+
// Emit error event with the specific reason and status code
|
|
5424
|
+
const errorEvent = new CloseEvent("error", { reason });
|
|
5425
|
+
errorEvent.statusCode = statusCode;
|
|
5426
|
+
eventTarget.dispatchEvent(errorEvent);
|
|
5419
5427
|
});
|
|
5420
5428
|
// Add method to manually close the connection
|
|
5421
5429
|
eventTarget.close = () => {
|
|
@@ -5456,10 +5464,15 @@ const useStyles$6 = makeStyles((theme) => ({
|
|
|
5456
5464
|
marginTop: pxToRem(16),
|
|
5457
5465
|
}
|
|
5458
5466
|
}));
|
|
5467
|
+
const MAX_RETRY_COUNT_BUTTON = 1;
|
|
5468
|
+
const RETRY_COUNTDOWN_SECONDS_BUTTON = 15;
|
|
5459
5469
|
const ButtonContent = ({ bodyText, isFormDisabled = false, isStepFormSubmit = false, isFormValid = true }) => {
|
|
5460
5470
|
const classes = useStyles$6();
|
|
5461
5471
|
const dispatch = useDispatch();
|
|
5462
5472
|
const sourceRef = useRef(null);
|
|
5473
|
+
const retryCountRef = useRef(0);
|
|
5474
|
+
const retryTimerRef = useRef(null);
|
|
5475
|
+
const retryPayloadRef = useRef(null);
|
|
5463
5476
|
const messageStoreRef = useRef({
|
|
5464
5477
|
currentMode: "agent",
|
|
5465
5478
|
chatData: {
|
|
@@ -5503,6 +5516,8 @@ const ButtonContent = ({ bodyText, isFormDisabled = false, isStepFormSubmit = fa
|
|
|
5503
5516
|
sendButton.click();
|
|
5504
5517
|
};
|
|
5505
5518
|
const callInitApiStream = (userInput) => {
|
|
5519
|
+
// Store payload for potential retries
|
|
5520
|
+
retryPayloadRef.current = userInput;
|
|
5506
5521
|
// Prefer module-level values (set synchronously by StreamedContent), fall back to sessionStorage
|
|
5507
5522
|
// If a previous completed/follow-up response updated the state, use those values.
|
|
5508
5523
|
// Read from module-level stepFormStreamControl (survives remounts) instead of local messageStoreRef.
|
|
@@ -5702,33 +5717,64 @@ const ButtonContent = ({ bodyText, isFormDisabled = false, isStepFormSubmit = fa
|
|
|
5702
5717
|
console.error("[ButtonContent] SSE parse error:", e);
|
|
5703
5718
|
}
|
|
5704
5719
|
});
|
|
5705
|
-
sourceRef.current.addEventListener("error", () => {
|
|
5706
|
-
// On error, still dispatch whatever we collected
|
|
5720
|
+
sourceRef.current.addEventListener("error", (event) => {
|
|
5707
5721
|
stepFormStreamControl.isStreaming = false;
|
|
5708
5722
|
stepFormStreamControl.abort = null;
|
|
5709
5723
|
window.dispatchEvent(new CustomEvent("stepFormStreamEnd"));
|
|
5710
|
-
|
|
5724
|
+
// Don't retry if the user manually aborted the request
|
|
5725
|
+
const isUserAbort = event.reason === "Network request aborted";
|
|
5726
|
+
// Check if we can auto-retry (abrupt close or 503, but not user abort)
|
|
5727
|
+
if (!isUserAbort && retryCountRef.current < MAX_RETRY_COUNT_BUTTON) {
|
|
5728
|
+
// Dispatch a "retrying" status so TabularContent can show countdown UI
|
|
5729
|
+
dispatch(setStepFormStreamData({ status: "retrying", chunks: [...chunksRef], sessionId, countdown: RETRY_COUNTDOWN_SECONDS_BUTTON }));
|
|
5730
|
+
// Start countdown and auto-retry
|
|
5731
|
+
let countdown = RETRY_COUNTDOWN_SECONDS_BUTTON;
|
|
5732
|
+
retryTimerRef.current = setInterval(() => {
|
|
5733
|
+
countdown -= 1;
|
|
5734
|
+
if (countdown <= 0) {
|
|
5735
|
+
clearInterval(retryTimerRef.current);
|
|
5736
|
+
retryTimerRef.current = null;
|
|
5737
|
+
retryCountRef.current += 1;
|
|
5738
|
+
// Re-invoke with same payload
|
|
5739
|
+
callInitApiStream(retryPayloadRef.current);
|
|
5740
|
+
}
|
|
5741
|
+
else {
|
|
5742
|
+
// Update countdown in Redux for UI
|
|
5743
|
+
dispatch(setStepFormStreamData({ status: "retrying", chunks: [...chunksRef], sessionId, countdown }));
|
|
5744
|
+
}
|
|
5745
|
+
}, 1000);
|
|
5746
|
+
}
|
|
5747
|
+
else {
|
|
5748
|
+
// Retries exhausted or user-aborted — show final message
|
|
5749
|
+
chunksRef.push({ status: "content", message: "Please reach out to IA for this, as this didn't work even after retry" });
|
|
5750
|
+
dispatch(setStepFormStreamData({ status: "error", chunks: [...chunksRef], sessionId }));
|
|
5751
|
+
}
|
|
5711
5752
|
});
|
|
5712
|
-
// Handle stream closure — fires when the HTTP request completes successfully.
|
|
5713
|
-
// If [DONE] was received, the "message" handler already dispatched status:"done"
|
|
5714
|
-
// and set stepFormStreamControl.isStreaming = false.
|
|
5715
|
-
// If [DONE] was NOT received, the connection closed abruptly mid-processing.
|
|
5716
5753
|
sourceRef.current.addEventListener("close", () => {
|
|
5717
5754
|
if (stepFormStreamControl.isStreaming) {
|
|
5718
5755
|
stepFormStreamControl.isStreaming = false;
|
|
5719
5756
|
stepFormStreamControl.abort = null;
|
|
5720
5757
|
window.dispatchEvent(new CustomEvent("stepFormStreamEnd"));
|
|
5721
|
-
|
|
5722
|
-
|
|
5723
|
-
|
|
5724
|
-
|
|
5725
|
-
|
|
5726
|
-
|
|
5727
|
-
|
|
5728
|
-
|
|
5729
|
-
|
|
5730
|
-
|
|
5731
|
-
|
|
5758
|
+
if (retryCountRef.current < MAX_RETRY_COUNT_BUTTON) {
|
|
5759
|
+
dispatch(setStepFormStreamData({ status: "retrying", chunks: [...chunksRef], sessionId, countdown: RETRY_COUNTDOWN_SECONDS_BUTTON }));
|
|
5760
|
+
let countdown = RETRY_COUNTDOWN_SECONDS_BUTTON;
|
|
5761
|
+
retryTimerRef.current = setInterval(() => {
|
|
5762
|
+
countdown -= 1;
|
|
5763
|
+
if (countdown <= 0) {
|
|
5764
|
+
clearInterval(retryTimerRef.current);
|
|
5765
|
+
retryTimerRef.current = null;
|
|
5766
|
+
retryCountRef.current += 1;
|
|
5767
|
+
callInitApiStream(retryPayloadRef.current);
|
|
5768
|
+
}
|
|
5769
|
+
else {
|
|
5770
|
+
dispatch(setStepFormStreamData({ status: "retrying", chunks: [...chunksRef], sessionId, countdown }));
|
|
5771
|
+
}
|
|
5772
|
+
}, 1000);
|
|
5773
|
+
}
|
|
5774
|
+
else {
|
|
5775
|
+
chunksRef.push({ status: "content", message: "Please reach out to IA for this, as this didn't work even after retry" });
|
|
5776
|
+
dispatch(setStepFormStreamData({ status: "error", chunks: [...chunksRef], sessionId }));
|
|
5777
|
+
}
|
|
5732
5778
|
}
|
|
5733
5779
|
});
|
|
5734
5780
|
};
|
|
@@ -6035,6 +6081,15 @@ const useStyles$5 = makeStyles((theme) => ({
|
|
|
6035
6081
|
position: "relative",
|
|
6036
6082
|
background: colours.white,
|
|
6037
6083
|
},
|
|
6084
|
+
retryContainer: {
|
|
6085
|
+
marginTop: pxToRem(8),
|
|
6086
|
+
},
|
|
6087
|
+
retryMessage: {
|
|
6088
|
+
fontFamily: "Manrope",
|
|
6089
|
+
fontSize: pxToRem(13),
|
|
6090
|
+
fontWeight: 500,
|
|
6091
|
+
color: "#374151",
|
|
6092
|
+
},
|
|
6038
6093
|
}));
|
|
6039
6094
|
|
|
6040
6095
|
var _path$i, _path2$2, _defs$5;
|
|
@@ -7739,8 +7794,20 @@ const AgentResponse$1 = (props) => {
|
|
|
7739
7794
|
};
|
|
7740
7795
|
|
|
7741
7796
|
const StepsResponseTab = (props) => {
|
|
7742
|
-
const { steps, setSteps, stepsDone, setStepsDone, finalStepDone, setFinalStepDone, content, isStreaming, stepChange, currentMode, questions, questionsStepsMap, stepFormDataMap, isFormDisabled, streamingWidgetData, } = props;
|
|
7797
|
+
const { steps, setSteps, stepsDone, setStepsDone, finalStepDone, setFinalStepDone, content, isStreaming, stepChange, currentMode, questions, questionsStepsMap, stepFormDataMap, isFormDisabled, streamingWidgetData, isRetrying, } = props;
|
|
7743
7798
|
const [tabValue, setTabValue] = useState("steps");
|
|
7799
|
+
// When retry countdown starts, switch to agent_response to show it.
|
|
7800
|
+
// When retry actually fires (isRetrying goes false + isStreaming goes true), switch back to steps.
|
|
7801
|
+
useEffect(() => {
|
|
7802
|
+
if (isRetrying) {
|
|
7803
|
+
setTabValue("agent_response");
|
|
7804
|
+
}
|
|
7805
|
+
}, [isRetrying]);
|
|
7806
|
+
useEffect(() => {
|
|
7807
|
+
if (isStreaming && !isRetrying) {
|
|
7808
|
+
setTabValue("steps");
|
|
7809
|
+
}
|
|
7810
|
+
}, [isStreaming, isRetrying]);
|
|
7744
7811
|
const handleChangeTabValue = (_event, newValue) => {
|
|
7745
7812
|
setTabValue(newValue);
|
|
7746
7813
|
};
|
|
@@ -7970,6 +8037,14 @@ const StreamedContent = ({ botData }) => {
|
|
|
7970
8037
|
const [finalStepDone, setFinalStepDone] = useState(false);
|
|
7971
8038
|
const [wasStreamingAborted, setWasStreamingAborted] = useState(false);
|
|
7972
8039
|
const wasStreamingAbortedRef = useRef(false);
|
|
8040
|
+
// Auto-retry state
|
|
8041
|
+
const MAX_RETRY_COUNT = 1; // Number of auto-retries allowed
|
|
8042
|
+
const RETRY_COUNTDOWN_SECONDS = 15; // Countdown duration before auto-retry
|
|
8043
|
+
const [isRetrying, setIsRetrying] = useState(false);
|
|
8044
|
+
const [retryCountdown, setRetryCountdown] = useState(0);
|
|
8045
|
+
const retryCountRef = useRef(0); // How many retries have been attempted
|
|
8046
|
+
const retryTimerRef = useRef(null); // Interval ref for countdown
|
|
8047
|
+
const retryCountdownRef = useRef(0); // Non-state countdown for interval callback
|
|
7973
8048
|
const thinkingContentRef = useRef("");
|
|
7974
8049
|
useSelector((state) => {
|
|
7975
8050
|
return state.smartBotReducer.thinkingContext;
|
|
@@ -8089,6 +8164,94 @@ const StreamedContent = ({ botData }) => {
|
|
|
8089
8164
|
}));
|
|
8090
8165
|
}
|
|
8091
8166
|
}, [stepChange, questions, questionsStepsMap, stepsDone, isStreamingDone]);
|
|
8167
|
+
/**
|
|
8168
|
+
* Starts a countdown for auto-retry. Shows countdown in UI, then calls retryStream.
|
|
8169
|
+
*/
|
|
8170
|
+
const startRetryCountdown = () => {
|
|
8171
|
+
setIsRetrying(true);
|
|
8172
|
+
retryCountdownRef.current = RETRY_COUNTDOWN_SECONDS;
|
|
8173
|
+
setRetryCountdown(RETRY_COUNTDOWN_SECONDS);
|
|
8174
|
+
retryTimerRef.current = setInterval(() => {
|
|
8175
|
+
retryCountdownRef.current -= 1;
|
|
8176
|
+
setRetryCountdown(retryCountdownRef.current);
|
|
8177
|
+
if (retryCountdownRef.current <= 0) {
|
|
8178
|
+
clearInterval(retryTimerRef.current);
|
|
8179
|
+
retryTimerRef.current = null;
|
|
8180
|
+
retryStream();
|
|
8181
|
+
}
|
|
8182
|
+
}, 1000);
|
|
8183
|
+
};
|
|
8184
|
+
/**
|
|
8185
|
+
* Retries the stream by resetting state and calling processStream again.
|
|
8186
|
+
*/
|
|
8187
|
+
const retryStream = () => {
|
|
8188
|
+
// Clear countdown state
|
|
8189
|
+
if (retryTimerRef.current) {
|
|
8190
|
+
clearInterval(retryTimerRef.current);
|
|
8191
|
+
retryTimerRef.current = null;
|
|
8192
|
+
}
|
|
8193
|
+
setIsRetrying(false);
|
|
8194
|
+
setRetryCountdown(0);
|
|
8195
|
+
retryCountRef.current += 1;
|
|
8196
|
+
// Reset streaming state for a fresh start
|
|
8197
|
+
setContent("");
|
|
8198
|
+
setIsStreaming(true);
|
|
8199
|
+
setIsStreamingDone(false);
|
|
8200
|
+
setStepsDone(false);
|
|
8201
|
+
setFinalStepDone(false);
|
|
8202
|
+
setWasStreamingAborted(false);
|
|
8203
|
+
wasStreamingAbortedRef.current = false;
|
|
8204
|
+
setSteps([{ header: "Processing Request", sub_header: "Analyzing the current request", step_status: "not-completed" }]);
|
|
8205
|
+
stepRef.current = [{ header: "Processing Request", sub_header: "Analyzing the current request", step_status: "not-completed" }];
|
|
8206
|
+
setQuestions([]);
|
|
8207
|
+
questionsRef.current = [];
|
|
8208
|
+
setQuestionsStepsMap({});
|
|
8209
|
+
questionsStepsMapRef.current = {};
|
|
8210
|
+
setStepFormDataMap({});
|
|
8211
|
+
stepFormDataMapRef.current = {};
|
|
8212
|
+
setStreamingWidgetData([]);
|
|
8213
|
+
streamingDoneProcessedRef.current = false;
|
|
8214
|
+
// Reset messageToStoreRef for fresh accumulation
|
|
8215
|
+
messageToStoreRef.current = {
|
|
8216
|
+
status: "",
|
|
8217
|
+
currentMode: currentMode,
|
|
8218
|
+
chatData: {
|
|
8219
|
+
response: "",
|
|
8220
|
+
response_heading: "",
|
|
8221
|
+
thinkingResponse: {
|
|
8222
|
+
thinkingContent: jsx(ThinkingIndicator, { thinkingContent: thinkingContent }),
|
|
8223
|
+
thinkingStream: "",
|
|
8224
|
+
thinkingTime: 0,
|
|
8225
|
+
thinkingHeading: null,
|
|
8226
|
+
},
|
|
8227
|
+
},
|
|
8228
|
+
appendedData: {},
|
|
8229
|
+
appendedDataFromLastChunk: {},
|
|
8230
|
+
initValue: false,
|
|
8231
|
+
sessionId: "",
|
|
8232
|
+
navSessionId: "",
|
|
8233
|
+
uniqueChatId: "",
|
|
8234
|
+
additionalArgs: {},
|
|
8235
|
+
};
|
|
8236
|
+
// Reset stream state map entry and invalidate old listeners
|
|
8237
|
+
const state = streamStateMap.get(streamKey);
|
|
8238
|
+
if (state) {
|
|
8239
|
+
state.messageStore = messageToStoreRef.current;
|
|
8240
|
+
state.completed = false;
|
|
8241
|
+
// Increment listenerGeneration so any in-flight events from the old source are ignored
|
|
8242
|
+
state.listenerGeneration = (state.listenerGeneration || 0) + 1;
|
|
8243
|
+
// Close the old source if it's still open
|
|
8244
|
+
if (state.source && typeof state.source.close === "function") {
|
|
8245
|
+
try {
|
|
8246
|
+
state.source.close();
|
|
8247
|
+
}
|
|
8248
|
+
catch (e) { /* ignore */ }
|
|
8249
|
+
}
|
|
8250
|
+
state.source = null;
|
|
8251
|
+
}
|
|
8252
|
+
// Re-initiate the stream
|
|
8253
|
+
processStream();
|
|
8254
|
+
};
|
|
8092
8255
|
/**
|
|
8093
8256
|
* Main effect to initialize and handle the streaming connection
|
|
8094
8257
|
* Sets up the SSE connection and event listeners for message processing
|
|
@@ -8483,66 +8646,57 @@ const StreamedContent = ({ botData }) => {
|
|
|
8483
8646
|
const errState = streamStateMap.get(streamKey);
|
|
8484
8647
|
if (!errState || errState.listenerGeneration !== generation)
|
|
8485
8648
|
return;
|
|
8486
|
-
console.error("Stream error:", event.reason);
|
|
8649
|
+
console.error("Stream error:", event.reason, "statusCode:", event.statusCode);
|
|
8487
8650
|
setIsStreaming(false);
|
|
8488
8651
|
errState.completed = true;
|
|
8652
|
+
// Check if this is a user-initiated abort (should not trigger retry)
|
|
8653
|
+
const isUserAbort = event.reason === "Network request aborted";
|
|
8654
|
+
event.statusCode === 503;
|
|
8489
8655
|
if (isThinking) {
|
|
8490
8656
|
setIsThinking(false);
|
|
8491
8657
|
const endTime = Date.now();
|
|
8492
8658
|
const duration = Math.round((endTime - thinkingStartTimeRef.current) / 1000);
|
|
8493
|
-
const finalThinkingTime = Math.max(duration, 1);
|
|
8659
|
+
const finalThinkingTime = Math.max(duration, 1);
|
|
8494
8660
|
setThinkingTime(finalThinkingTime);
|
|
8495
8661
|
setShowThoughtDropdown(true);
|
|
8496
8662
|
thinkingStartTimeRef.current = finalThinkingTime;
|
|
8497
|
-
// Store thinking time in messageToStoreRef for persistence
|
|
8498
8663
|
messageToStoreRef.current.chatData.thinkingResponse.thinkingTime = finalThinkingTime;
|
|
8499
8664
|
}
|
|
8500
|
-
//
|
|
8501
|
-
|
|
8502
|
-
|
|
8503
|
-
|
|
8504
|
-
|
|
8505
|
-
|
|
8506
|
-
|
|
8507
|
-
if (lastStep.step_status === "not-completed") {
|
|
8508
|
-
lastStep.step_status = "completed";
|
|
8509
|
-
}
|
|
8510
|
-
stepRef.current = [...currentSteps];
|
|
8511
|
-
setSteps([...currentSteps]);
|
|
8512
|
-
}
|
|
8513
|
-
// Show the abrupt close message at the bottom of any existing content.
|
|
8514
|
-
// If widgets already exist in appendedData, append the error as the last widget
|
|
8515
|
-
// so it renders AFTER the already-received content (not at the top).
|
|
8516
|
-
const abruptCloseMessage = "The requested Task has ended unexpectedly, please retry again";
|
|
8517
|
-
const hasExistingWidgetsOnErr = messageToStoreRef.current.appendedData &&
|
|
8518
|
-
(isArray(messageToStoreRef.current.appendedData)
|
|
8519
|
-
? messageToStoreRef.current.appendedData.length > 0
|
|
8520
|
-
: Object.keys(messageToStoreRef.current.appendedData).length > 0);
|
|
8521
|
-
if (hasExistingWidgetsOnErr) {
|
|
8522
|
-
const errorWidget = { type: "text", response: abruptCloseMessage };
|
|
8523
|
-
if (isArray(messageToStoreRef.current.appendedData)) {
|
|
8524
|
-
messageToStoreRef.current.appendedData.push(errorWidget);
|
|
8525
|
-
}
|
|
8526
|
-
else {
|
|
8527
|
-
messageToStoreRef.current.appendedData = [messageToStoreRef.current.appendedData, errorWidget];
|
|
8528
|
-
}
|
|
8665
|
+
// Check if we can auto-retry (abrupt close or 503, but not user abort)
|
|
8666
|
+
if (!isUserAbort && retryCountRef.current < MAX_RETRY_COUNT) {
|
|
8667
|
+
// Still have retries left — start countdown instead of showing final error
|
|
8668
|
+
setStepsDone(true); // Switch to agent_response tab to show countdown
|
|
8669
|
+
const retryMessage = "The service is temporarily unavailable because of an unusual load. Please wait a moment and try again.";
|
|
8670
|
+
setContent(retryMessage);
|
|
8671
|
+
startRetryCountdown();
|
|
8529
8672
|
}
|
|
8530
8673
|
else {
|
|
8531
|
-
|
|
8674
|
+
// Retries exhausted — show final message
|
|
8675
|
+
setWasStreamingAborted(true);
|
|
8676
|
+
wasStreamingAbortedRef.current = true;
|
|
8677
|
+
const currentSteps = stepRef.current;
|
|
8678
|
+
if (currentSteps.length > 0) {
|
|
8679
|
+
const lastStep = currentSteps[currentSteps.length - 1];
|
|
8680
|
+
if (lastStep.step_status === "not-completed") {
|
|
8681
|
+
lastStep.step_status = "completed";
|
|
8682
|
+
}
|
|
8683
|
+
stepRef.current = [...currentSteps];
|
|
8684
|
+
setSteps([...currentSteps]);
|
|
8685
|
+
}
|
|
8686
|
+
const finalMessage = "Please reach out to IA for this, as this didn't work even after retry";
|
|
8687
|
+
messageToStoreRef.current.chatData.response = finalMessage;
|
|
8688
|
+
setContent(finalMessage);
|
|
8689
|
+
dispatch(setMinimizedStreamData({
|
|
8690
|
+
isStreaming: false,
|
|
8691
|
+
stepHeader: questionsRef.current[questionsRef.current.length - 1] || "Ended unexpectedly",
|
|
8692
|
+
stepSubHeader: "Connection closed",
|
|
8693
|
+
stepStatus: "completed",
|
|
8694
|
+
streamStartTime: streamStartTimeRef.current,
|
|
8695
|
+
actionCount: questionsRef.current.length || 1,
|
|
8696
|
+
}));
|
|
8697
|
+
setStepsDone(true);
|
|
8698
|
+
setIsStreamingDone(true);
|
|
8532
8699
|
}
|
|
8533
|
-
setContent(abruptCloseMessage);
|
|
8534
|
-
// Dispatch minimized widget data
|
|
8535
|
-
dispatch(setMinimizedStreamData({
|
|
8536
|
-
isStreaming: false,
|
|
8537
|
-
stepHeader: questionsRef.current[questionsRef.current.length - 1] || "Ended unexpectedly",
|
|
8538
|
-
stepSubHeader: "Connection closed",
|
|
8539
|
-
stepStatus: "completed",
|
|
8540
|
-
streamStartTime: streamStartTimeRef.current,
|
|
8541
|
-
actionCount: questionsRef.current.length || 1,
|
|
8542
|
-
}));
|
|
8543
|
-
// Switch to agent_response tab and trigger completion flow
|
|
8544
|
-
setStepsDone(true);
|
|
8545
|
-
setIsStreamingDone(true);
|
|
8546
8700
|
});
|
|
8547
8701
|
// Handle stream closure
|
|
8548
8702
|
source.addEventListener("close", () => {
|
|
@@ -8554,44 +8708,6 @@ const StreamedContent = ({ botData }) => {
|
|
|
8554
8708
|
// If it's still false, the backend closed the connection abruptly.
|
|
8555
8709
|
if (!closeState.completed) {
|
|
8556
8710
|
closeState.completed = true;
|
|
8557
|
-
// Mark as aborted so the completion effect skips the Submit button
|
|
8558
|
-
setWasStreamingAborted(true);
|
|
8559
|
-
wasStreamingAbortedRef.current = true;
|
|
8560
|
-
// Mark the last in-progress step as completed
|
|
8561
|
-
const currentSteps = stepRef.current;
|
|
8562
|
-
if (currentSteps.length > 0) {
|
|
8563
|
-
const lastStep = currentSteps[currentSteps.length - 1];
|
|
8564
|
-
if (lastStep.step_status === "not-completed") {
|
|
8565
|
-
lastStep.step_status = "completed";
|
|
8566
|
-
}
|
|
8567
|
-
stepRef.current = [...currentSteps];
|
|
8568
|
-
setSteps([...currentSteps]);
|
|
8569
|
-
}
|
|
8570
|
-
// Show the abrupt close message. The final response array is structured as:
|
|
8571
|
-
// [textResponseTobeParsed, ...appendedData] — so chatData.response renders FIRST,
|
|
8572
|
-
// then widget items from appendedData render below it.
|
|
8573
|
-
// To ensure the error message appears AFTER already-rendered widgets, we append it
|
|
8574
|
-
// as a text widget item to appendedData instead of setting it on chatData.response.
|
|
8575
|
-
const abruptCloseMessage = "The requested Task has ended unexpectedly, please retry again";
|
|
8576
|
-
const hasExistingWidgets = messageToStoreRef.current.appendedData &&
|
|
8577
|
-
(isArray(messageToStoreRef.current.appendedData)
|
|
8578
|
-
? messageToStoreRef.current.appendedData.length > 0
|
|
8579
|
-
: Object.keys(messageToStoreRef.current.appendedData).length > 0);
|
|
8580
|
-
if (hasExistingWidgets) {
|
|
8581
|
-
// Widgets already exist — append error as the last widget so it renders at the bottom
|
|
8582
|
-
const errorWidget = { type: "text", response: abruptCloseMessage };
|
|
8583
|
-
if (isArray(messageToStoreRef.current.appendedData)) {
|
|
8584
|
-
messageToStoreRef.current.appendedData.push(errorWidget);
|
|
8585
|
-
}
|
|
8586
|
-
else {
|
|
8587
|
-
messageToStoreRef.current.appendedData = [messageToStoreRef.current.appendedData, errorWidget];
|
|
8588
|
-
}
|
|
8589
|
-
}
|
|
8590
|
-
else {
|
|
8591
|
-
// No widgets — set on chatData.response (will be the only rendered text)
|
|
8592
|
-
messageToStoreRef.current.chatData.response = abruptCloseMessage;
|
|
8593
|
-
}
|
|
8594
|
-
setContent(abruptCloseMessage);
|
|
8595
8711
|
// Stop thinking if in progress
|
|
8596
8712
|
if (isThinking) {
|
|
8597
8713
|
setIsThinking(false);
|
|
@@ -8602,18 +8718,40 @@ const StreamedContent = ({ botData }) => {
|
|
|
8602
8718
|
setShowThoughtDropdown(true);
|
|
8603
8719
|
messageToStoreRef.current.chatData.thinkingResponse.thinkingTime = finalThinkingTime;
|
|
8604
8720
|
}
|
|
8605
|
-
//
|
|
8606
|
-
|
|
8607
|
-
|
|
8608
|
-
|
|
8609
|
-
|
|
8610
|
-
|
|
8611
|
-
|
|
8612
|
-
|
|
8613
|
-
|
|
8614
|
-
|
|
8615
|
-
|
|
8616
|
-
|
|
8721
|
+
// Check if we can auto-retry
|
|
8722
|
+
if (retryCountRef.current < MAX_RETRY_COUNT) {
|
|
8723
|
+
// Still have retries left — start countdown instead of showing final error
|
|
8724
|
+
setStepsDone(true); // Switch to agent_response tab to show countdown
|
|
8725
|
+
setContent("The service is temporarily unavailable because of an unusual load. Please wait a moment and try again.");
|
|
8726
|
+
startRetryCountdown();
|
|
8727
|
+
}
|
|
8728
|
+
else {
|
|
8729
|
+
// Retries exhausted — show final elegant message
|
|
8730
|
+
setWasStreamingAborted(true);
|
|
8731
|
+
wasStreamingAbortedRef.current = true;
|
|
8732
|
+
const currentSteps = stepRef.current;
|
|
8733
|
+
if (currentSteps.length > 0) {
|
|
8734
|
+
const lastStep = currentSteps[currentSteps.length - 1];
|
|
8735
|
+
if (lastStep.step_status === "not-completed") {
|
|
8736
|
+
lastStep.step_status = "completed";
|
|
8737
|
+
}
|
|
8738
|
+
stepRef.current = [...currentSteps];
|
|
8739
|
+
setSteps([...currentSteps]);
|
|
8740
|
+
}
|
|
8741
|
+
const finalMessage = "Please reach out to IA for this, as this didn't work even after retry";
|
|
8742
|
+
messageToStoreRef.current.chatData.response = finalMessage;
|
|
8743
|
+
setContent(finalMessage);
|
|
8744
|
+
dispatch(setMinimizedStreamData({
|
|
8745
|
+
isStreaming: false,
|
|
8746
|
+
stepHeader: questionsRef.current[questionsRef.current.length - 1] || "Ended unexpectedly",
|
|
8747
|
+
stepSubHeader: "Connection closed",
|
|
8748
|
+
stepStatus: "completed",
|
|
8749
|
+
streamStartTime: streamStartTimeRef.current,
|
|
8750
|
+
actionCount: questionsRef.current.length || 1,
|
|
8751
|
+
}));
|
|
8752
|
+
setStepsDone(true);
|
|
8753
|
+
setIsStreamingDone(true);
|
|
8754
|
+
}
|
|
8617
8755
|
}
|
|
8618
8756
|
// else: closeState.completed is already true — normal close after [DONE], nothing to do.
|
|
8619
8757
|
});
|
|
@@ -8918,6 +9056,14 @@ const StreamedContent = ({ botData }) => {
|
|
|
8918
9056
|
* Aborts the current streaming connection
|
|
8919
9057
|
*/
|
|
8920
9058
|
const abortStreaming = () => {
|
|
9059
|
+
// If a retry countdown is running, clear it and prevent further retries
|
|
9060
|
+
if (retryTimerRef.current) {
|
|
9061
|
+
clearInterval(retryTimerRef.current);
|
|
9062
|
+
retryTimerRef.current = null;
|
|
9063
|
+
setIsRetrying(false);
|
|
9064
|
+
setRetryCountdown(0);
|
|
9065
|
+
}
|
|
9066
|
+
retryCountRef.current = MAX_RETRY_COUNT; // Exhaust retries so no further auto-retries happen
|
|
8921
9067
|
if (sourceRef.current && isStreaming) {
|
|
8922
9068
|
setWasStreamingAborted(true);
|
|
8923
9069
|
wasStreamingAbortedRef.current = true;
|
|
@@ -8965,9 +9111,9 @@ const StreamedContent = ({ botData }) => {
|
|
|
8965
9111
|
// If no content yet, show aborted message
|
|
8966
9112
|
// if (isEmpty(messageToStoreRef.current.chatData?.response)) {
|
|
8967
9113
|
messageToStoreRef.current.chatData.response =
|
|
8968
|
-
messageToStoreRef.current.chatData.response + "\n\
|
|
9114
|
+
messageToStoreRef.current.chatData.response + "\n\nYou stopped this response. You can ask a new question whenever you're ready";
|
|
8969
9115
|
setContent((prev) => {
|
|
8970
|
-
return prev + "\n\
|
|
9116
|
+
return prev + "\n\nYou stopped this response. You can ask a new question whenever you're ready";
|
|
8971
9117
|
});
|
|
8972
9118
|
// }
|
|
8973
9119
|
}
|
|
@@ -8989,10 +9135,16 @@ const StreamedContent = ({ botData }) => {
|
|
|
8989
9135
|
* Cleanup on unmount: Do NOT close the stream here.
|
|
8990
9136
|
* The stream connection is kept alive in streamStateMap so it survives
|
|
8991
9137
|
* tab-switch remounts. It is only closed by explicit abortStreaming() or new chat.
|
|
9138
|
+
* Also clean up retry timer if running.
|
|
8992
9139
|
*/
|
|
8993
9140
|
useEffect(() => {
|
|
8994
9141
|
return () => {
|
|
8995
9142
|
// Intentionally not closing the stream - it persists in streamStateMap
|
|
9143
|
+
// Clean up retry timer
|
|
9144
|
+
if (retryTimerRef.current) {
|
|
9145
|
+
clearInterval(retryTimerRef.current);
|
|
9146
|
+
retryTimerRef.current = null;
|
|
9147
|
+
}
|
|
8996
9148
|
};
|
|
8997
9149
|
}, []);
|
|
8998
9150
|
/**
|
|
@@ -9044,7 +9196,7 @@ const StreamedContent = ({ botData }) => {
|
|
|
9044
9196
|
* @returns {JSX.Element} Rendered content with optional blinking cursor
|
|
9045
9197
|
*/
|
|
9046
9198
|
const renderContent = () => {
|
|
9047
|
-
return (
|
|
9199
|
+
return (jsxs("div", { className: classes.streamContainer, children: [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 && (jsx("div", { className: classes.retryContainer, children: jsxs("span", { className: classes.retryMessage, children: ["Auto re-trying in ", retryCountdown, "s"] }) }))] }));
|
|
9048
9200
|
};
|
|
9049
9201
|
if (currentMode === "agent") {
|
|
9050
9202
|
return renderContent();
|
|
@@ -9336,6 +9488,7 @@ const TabularContent = ({ steps: initialSteps, currentTabValue, children, questi
|
|
|
9336
9488
|
const [stepFormSubmitted, setStepFormSubmitted] = useState(false);
|
|
9337
9489
|
const [hasNewStepFormFromRestream, setHasNewStepFormFromRestream] = useState(false);
|
|
9338
9490
|
const [activeFormIntent, setActiveFormIntent] = useState(null);
|
|
9491
|
+
const [retryCountdown, setRetryCountdown] = useState(0);
|
|
9339
9492
|
// Stable unique instance ID for this TabularContent mount
|
|
9340
9493
|
const instanceIdRef = useRef(null);
|
|
9341
9494
|
if (instanceIdRef.current === null) {
|
|
@@ -9401,10 +9554,15 @@ const TabularContent = ({ steps: initialSteps, currentTabValue, children, questi
|
|
|
9401
9554
|
const payload = stepFormStreamData;
|
|
9402
9555
|
// Clear Redux immediately
|
|
9403
9556
|
dispatch(setStepFormStreamData(null));
|
|
9557
|
+
if (payload.status === "retrying") {
|
|
9558
|
+
setRetryCountdown(payload.countdown || 0);
|
|
9559
|
+
return;
|
|
9560
|
+
}
|
|
9404
9561
|
if (payload.status === "streaming_start") {
|
|
9405
9562
|
setIsRestreaming(true);
|
|
9406
9563
|
setStepFormSubmitted(true);
|
|
9407
9564
|
setHasNewStepFormFromRestream(false);
|
|
9565
|
+
setRetryCountdown(0);
|
|
9408
9566
|
setTabValue("steps");
|
|
9409
9567
|
return;
|
|
9410
9568
|
}
|
|
@@ -9544,6 +9702,8 @@ const TabularContent = ({ steps: initialSteps, currentTabValue, children, questi
|
|
|
9544
9702
|
}
|
|
9545
9703
|
return;
|
|
9546
9704
|
}
|
|
9705
|
+
// Clear retry countdown when final processing starts
|
|
9706
|
+
setRetryCountdown(0);
|
|
9547
9707
|
// Process all collected chunks at once (done or error)
|
|
9548
9708
|
const chunks = payload.chunks || [];
|
|
9549
9709
|
let newSteps = cloneDeep(stepsRef.current);
|
|
@@ -9692,7 +9852,7 @@ const TabularContent = ({ steps: initialSteps, currentTabValue, children, questi
|
|
|
9692
9852
|
},
|
|
9693
9853
|
], tabPanels: [
|
|
9694
9854
|
jsx(Steps, { steps: stepsState, questions: questionsState, questionsStepsMap: questionsStepsMapState, stepFormDataMap: stepFormDataMapState, isFormDisabled: (isFormDisabled && !isRestreaming) || stepFormSubmitted, activeFormIntent: hasNewStepFormFromRestream ? activeFormIntent : null, isRestreaming: isRestreaming }),
|
|
9695
|
-
jsxs(AgentResponse, { children: [children, renderedWidgets.length > 0 && (jsx("div", { className: "restream-widget-content", children: renderedWidgets }))] }),
|
|
9855
|
+
jsxs(AgentResponse, { children: [children, renderedWidgets.length > 0 && (jsx("div", { className: "restream-widget-content", children: renderedWidgets })), retryCountdown > 0 && (jsx("div", { style: { marginTop: "8px" }, children: jsxs("span", { style: { fontFamily: "Manrope", fontSize: "13px", fontWeight: 500, color: "#374151" }, children: ["Auto re-trying in ", retryCountdown, "s"] }) }))] }),
|
|
9696
9856
|
], value: tabValue }) }));
|
|
9697
9857
|
};
|
|
9698
9858
|
/** Reset the active instance tracker (call when a new conversation starts from the input field) */
|