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.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,59 @@ 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
|
+
const is503 = 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
|
-
|
|
8508
|
-
|
|
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 = is503
|
|
8670
|
+
? "Service temporarily unavailable (503), auto re-trying..."
|
|
8671
|
+
: "The service is temporarily unavailable because of an unusual load. Please wait a moment and try again.";
|
|
8672
|
+
setContent(retryMessage);
|
|
8673
|
+
startRetryCountdown();
|
|
8529
8674
|
}
|
|
8530
8675
|
else {
|
|
8531
|
-
|
|
8676
|
+
// Retries exhausted — show final message
|
|
8677
|
+
setWasStreamingAborted(true);
|
|
8678
|
+
wasStreamingAbortedRef.current = true;
|
|
8679
|
+
const currentSteps = stepRef.current;
|
|
8680
|
+
if (currentSteps.length > 0) {
|
|
8681
|
+
const lastStep = currentSteps[currentSteps.length - 1];
|
|
8682
|
+
if (lastStep.step_status === "not-completed") {
|
|
8683
|
+
lastStep.step_status = "completed";
|
|
8684
|
+
}
|
|
8685
|
+
stepRef.current = [...currentSteps];
|
|
8686
|
+
setSteps([...currentSteps]);
|
|
8687
|
+
}
|
|
8688
|
+
const finalMessage = "Please reach out to IA for this, as this didn't work even after retry";
|
|
8689
|
+
messageToStoreRef.current.chatData.response = finalMessage;
|
|
8690
|
+
setContent(finalMessage);
|
|
8691
|
+
dispatch(setMinimizedStreamData({
|
|
8692
|
+
isStreaming: false,
|
|
8693
|
+
stepHeader: questionsRef.current[questionsRef.current.length - 1] || "Ended unexpectedly",
|
|
8694
|
+
stepSubHeader: "Connection closed",
|
|
8695
|
+
stepStatus: "completed",
|
|
8696
|
+
streamStartTime: streamStartTimeRef.current,
|
|
8697
|
+
actionCount: questionsRef.current.length || 1,
|
|
8698
|
+
}));
|
|
8699
|
+
setStepsDone(true);
|
|
8700
|
+
setIsStreamingDone(true);
|
|
8532
8701
|
}
|
|
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
8702
|
});
|
|
8547
8703
|
// Handle stream closure
|
|
8548
8704
|
source.addEventListener("close", () => {
|
|
@@ -8554,44 +8710,6 @@ const StreamedContent = ({ botData }) => {
|
|
|
8554
8710
|
// If it's still false, the backend closed the connection abruptly.
|
|
8555
8711
|
if (!closeState.completed) {
|
|
8556
8712
|
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
8713
|
// Stop thinking if in progress
|
|
8596
8714
|
if (isThinking) {
|
|
8597
8715
|
setIsThinking(false);
|
|
@@ -8602,18 +8720,40 @@ const StreamedContent = ({ botData }) => {
|
|
|
8602
8720
|
setShowThoughtDropdown(true);
|
|
8603
8721
|
messageToStoreRef.current.chatData.thinkingResponse.thinkingTime = finalThinkingTime;
|
|
8604
8722
|
}
|
|
8605
|
-
//
|
|
8606
|
-
|
|
8607
|
-
|
|
8608
|
-
|
|
8609
|
-
|
|
8610
|
-
|
|
8611
|
-
|
|
8612
|
-
|
|
8613
|
-
|
|
8614
|
-
|
|
8615
|
-
|
|
8616
|
-
|
|
8723
|
+
// Check if we can auto-retry
|
|
8724
|
+
if (retryCountRef.current < MAX_RETRY_COUNT) {
|
|
8725
|
+
// Still have retries left — start countdown instead of showing final error
|
|
8726
|
+
setStepsDone(true); // Switch to agent_response tab to show countdown
|
|
8727
|
+
setContent("The service is temporarily unavailable because of an unusual load. Please wait a moment and try again.");
|
|
8728
|
+
startRetryCountdown();
|
|
8729
|
+
}
|
|
8730
|
+
else {
|
|
8731
|
+
// Retries exhausted — show final elegant message
|
|
8732
|
+
setWasStreamingAborted(true);
|
|
8733
|
+
wasStreamingAbortedRef.current = true;
|
|
8734
|
+
const currentSteps = stepRef.current;
|
|
8735
|
+
if (currentSteps.length > 0) {
|
|
8736
|
+
const lastStep = currentSteps[currentSteps.length - 1];
|
|
8737
|
+
if (lastStep.step_status === "not-completed") {
|
|
8738
|
+
lastStep.step_status = "completed";
|
|
8739
|
+
}
|
|
8740
|
+
stepRef.current = [...currentSteps];
|
|
8741
|
+
setSteps([...currentSteps]);
|
|
8742
|
+
}
|
|
8743
|
+
const finalMessage = "Please reach out to IA for this, as this didn't work even after retry";
|
|
8744
|
+
messageToStoreRef.current.chatData.response = finalMessage;
|
|
8745
|
+
setContent(finalMessage);
|
|
8746
|
+
dispatch(setMinimizedStreamData({
|
|
8747
|
+
isStreaming: false,
|
|
8748
|
+
stepHeader: questionsRef.current[questionsRef.current.length - 1] || "Ended unexpectedly",
|
|
8749
|
+
stepSubHeader: "Connection closed",
|
|
8750
|
+
stepStatus: "completed",
|
|
8751
|
+
streamStartTime: streamStartTimeRef.current,
|
|
8752
|
+
actionCount: questionsRef.current.length || 1,
|
|
8753
|
+
}));
|
|
8754
|
+
setStepsDone(true);
|
|
8755
|
+
setIsStreamingDone(true);
|
|
8756
|
+
}
|
|
8617
8757
|
}
|
|
8618
8758
|
// else: closeState.completed is already true — normal close after [DONE], nothing to do.
|
|
8619
8759
|
});
|
|
@@ -8918,6 +9058,14 @@ const StreamedContent = ({ botData }) => {
|
|
|
8918
9058
|
* Aborts the current streaming connection
|
|
8919
9059
|
*/
|
|
8920
9060
|
const abortStreaming = () => {
|
|
9061
|
+
// If a retry countdown is running, clear it and prevent further retries
|
|
9062
|
+
if (retryTimerRef.current) {
|
|
9063
|
+
clearInterval(retryTimerRef.current);
|
|
9064
|
+
retryTimerRef.current = null;
|
|
9065
|
+
setIsRetrying(false);
|
|
9066
|
+
setRetryCountdown(0);
|
|
9067
|
+
}
|
|
9068
|
+
retryCountRef.current = MAX_RETRY_COUNT; // Exhaust retries so no further auto-retries happen
|
|
8921
9069
|
if (sourceRef.current && isStreaming) {
|
|
8922
9070
|
setWasStreamingAborted(true);
|
|
8923
9071
|
wasStreamingAbortedRef.current = true;
|
|
@@ -8965,9 +9113,9 @@ const StreamedContent = ({ botData }) => {
|
|
|
8965
9113
|
// If no content yet, show aborted message
|
|
8966
9114
|
// if (isEmpty(messageToStoreRef.current.chatData?.response)) {
|
|
8967
9115
|
messageToStoreRef.current.chatData.response =
|
|
8968
|
-
messageToStoreRef.current.chatData.response + "\n\
|
|
9116
|
+
messageToStoreRef.current.chatData.response + "\n\nYou stopped this response. You can ask a new question whenever you're ready";
|
|
8969
9117
|
setContent((prev) => {
|
|
8970
|
-
return prev + "\n\
|
|
9118
|
+
return prev + "\n\nYou stopped this response. You can ask a new question whenever you're ready";
|
|
8971
9119
|
});
|
|
8972
9120
|
// }
|
|
8973
9121
|
}
|
|
@@ -8989,10 +9137,16 @@ const StreamedContent = ({ botData }) => {
|
|
|
8989
9137
|
* Cleanup on unmount: Do NOT close the stream here.
|
|
8990
9138
|
* The stream connection is kept alive in streamStateMap so it survives
|
|
8991
9139
|
* tab-switch remounts. It is only closed by explicit abortStreaming() or new chat.
|
|
9140
|
+
* Also clean up retry timer if running.
|
|
8992
9141
|
*/
|
|
8993
9142
|
useEffect(() => {
|
|
8994
9143
|
return () => {
|
|
8995
9144
|
// Intentionally not closing the stream - it persists in streamStateMap
|
|
9145
|
+
// Clean up retry timer
|
|
9146
|
+
if (retryTimerRef.current) {
|
|
9147
|
+
clearInterval(retryTimerRef.current);
|
|
9148
|
+
retryTimerRef.current = null;
|
|
9149
|
+
}
|
|
8996
9150
|
};
|
|
8997
9151
|
}, []);
|
|
8998
9152
|
/**
|
|
@@ -9044,7 +9198,7 @@ const StreamedContent = ({ botData }) => {
|
|
|
9044
9198
|
* @returns {JSX.Element} Rendered content with optional blinking cursor
|
|
9045
9199
|
*/
|
|
9046
9200
|
const renderContent = () => {
|
|
9047
|
-
return (
|
|
9201
|
+
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
9202
|
};
|
|
9049
9203
|
if (currentMode === "agent") {
|
|
9050
9204
|
return renderContent();
|
|
@@ -9336,6 +9490,7 @@ const TabularContent = ({ steps: initialSteps, currentTabValue, children, questi
|
|
|
9336
9490
|
const [stepFormSubmitted, setStepFormSubmitted] = useState(false);
|
|
9337
9491
|
const [hasNewStepFormFromRestream, setHasNewStepFormFromRestream] = useState(false);
|
|
9338
9492
|
const [activeFormIntent, setActiveFormIntent] = useState(null);
|
|
9493
|
+
const [retryCountdown, setRetryCountdown] = useState(0);
|
|
9339
9494
|
// Stable unique instance ID for this TabularContent mount
|
|
9340
9495
|
const instanceIdRef = useRef(null);
|
|
9341
9496
|
if (instanceIdRef.current === null) {
|
|
@@ -9401,10 +9556,15 @@ const TabularContent = ({ steps: initialSteps, currentTabValue, children, questi
|
|
|
9401
9556
|
const payload = stepFormStreamData;
|
|
9402
9557
|
// Clear Redux immediately
|
|
9403
9558
|
dispatch(setStepFormStreamData(null));
|
|
9559
|
+
if (payload.status === "retrying") {
|
|
9560
|
+
setRetryCountdown(payload.countdown || 0);
|
|
9561
|
+
return;
|
|
9562
|
+
}
|
|
9404
9563
|
if (payload.status === "streaming_start") {
|
|
9405
9564
|
setIsRestreaming(true);
|
|
9406
9565
|
setStepFormSubmitted(true);
|
|
9407
9566
|
setHasNewStepFormFromRestream(false);
|
|
9567
|
+
setRetryCountdown(0);
|
|
9408
9568
|
setTabValue("steps");
|
|
9409
9569
|
return;
|
|
9410
9570
|
}
|
|
@@ -9544,6 +9704,8 @@ const TabularContent = ({ steps: initialSteps, currentTabValue, children, questi
|
|
|
9544
9704
|
}
|
|
9545
9705
|
return;
|
|
9546
9706
|
}
|
|
9707
|
+
// Clear retry countdown when final processing starts
|
|
9708
|
+
setRetryCountdown(0);
|
|
9547
9709
|
// Process all collected chunks at once (done or error)
|
|
9548
9710
|
const chunks = payload.chunks || [];
|
|
9549
9711
|
let newSteps = cloneDeep(stepsRef.current);
|
|
@@ -9692,7 +9854,7 @@ const TabularContent = ({ steps: initialSteps, currentTabValue, children, questi
|
|
|
9692
9854
|
},
|
|
9693
9855
|
], tabPanels: [
|
|
9694
9856
|
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 }))] }),
|
|
9857
|
+
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
9858
|
], value: tabValue }) }));
|
|
9697
9859
|
};
|
|
9698
9860
|
/** Reset the active instance tracker (call when a new conversation starts from the input field) */
|