Skip to content

Commit d95384f

Browse files
authored
Merge pull request #9082 from uinstinct/retry-overloaded
feat: resubmit overloaded errors
2 parents 8066540 + c391353 commit d95384f

File tree

1 file changed

+50
-30
lines changed

1 file changed

+50
-30
lines changed

gui/src/redux/thunks/streamThunkWrapper.tsx

Lines changed: 50 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -8,43 +8,63 @@ import { ThunkApiType } from "../store";
88
import { cancelStream } from "./cancelStream";
99
import { saveCurrentSession } from "./session";
1010

11+
const OVERLOADED_RETRIES = 3;
12+
const OVERLOADED_DELAY_MS = 1000;
13+
14+
function isOverloadedErrorMessage(message?: string | null): boolean {
15+
if (!message) return false;
16+
const lower = message.toLowerCase();
17+
return lower.includes("overloaded") || lower.includes("malformed json");
18+
}
19+
1120
export const streamThunkWrapper = createAsyncThunk<
1221
void,
1322
() => Promise<void>,
1423
ThunkApiType
15-
>("chat/streamWrapper", async (runStream, { dispatch, extra, getState }) => {
16-
try {
17-
await runStream();
18-
const state = getState();
19-
if (!state.session.isInEdit) {
20-
await dispatch(
21-
saveCurrentSession({
22-
openNewSession: false,
23-
generateTitle: true,
24-
}),
25-
);
26-
}
27-
} catch (e) {
28-
await dispatch(cancelStream());
29-
dispatch(setDialogMessage(<StreamErrorDialog error={e} />));
30-
dispatch(setShowDialog(true));
24+
>("chat/streamWrapper", async (runStream, { dispatch, getState }) => {
25+
for (let attempt = 0; attempt <= OVERLOADED_RETRIES; attempt++) {
26+
try {
27+
await runStream();
28+
const state = getState();
29+
if (!state.session.isInEdit) {
30+
await dispatch(
31+
saveCurrentSession({
32+
openNewSession: false,
33+
generateTitle: true,
34+
}),
35+
);
36+
}
37+
return;
38+
} catch (e) {
39+
// Get the selected model from the state for error analysis
40+
const state = getState();
41+
const selectedModel = selectSelectedChatModel(state);
42+
const { parsedError, statusCode, message, modelTitle, providerName } =
43+
analyzeError(e, selectedModel);
3144

32-
// Get the selected model from the state for error analysis
33-
const state = getState();
34-
const selectedModel = selectSelectedChatModel(state);
45+
const shouldRetry =
46+
isOverloadedErrorMessage(message) && attempt < OVERLOADED_RETRIES;
3547

36-
const { parsedError, statusCode, modelTitle, providerName } = analyzeError(
37-
e,
38-
selectedModel,
39-
);
48+
if (shouldRetry) {
49+
await dispatch(cancelStream());
50+
const delayMs = OVERLOADED_DELAY_MS * 2 ** attempt;
51+
await new Promise((resolve) => setTimeout(resolve, delayMs));
52+
await dispatch(cancelStream());
53+
} else {
54+
await dispatch(cancelStream());
55+
dispatch(setDialogMessage(<StreamErrorDialog error={e} />));
56+
dispatch(setShowDialog(true));
4057

41-
const errorData = {
42-
error_type: statusCode ? `HTTP ${statusCode}` : "Unknown",
43-
error_message: parsedError,
44-
model_provider: providerName,
45-
model_title: modelTitle,
46-
};
58+
const errorData = {
59+
error_type: statusCode ? `HTTP ${statusCode}` : "Unknown",
60+
error_message: parsedError,
61+
model_provider: providerName,
62+
model_title: modelTitle,
63+
};
4764

48-
posthog.capture("gui_stream_error", errorData);
65+
posthog.capture("gui_stream_error", errorData);
66+
return;
67+
}
68+
}
4969
}
5070
});

0 commit comments

Comments
 (0)