Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
80 changes: 50 additions & 30 deletions gui/src/redux/thunks/streamThunkWrapper.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,43 +8,63 @@ import { ThunkApiType } from "../store";
import { cancelStream } from "./cancelStream";
import { saveCurrentSession } from "./session";

const OVERLOADED_RETRIES = 3;
const OVERLOADED_DELAY_MS = 1000;

function isOverloadedErrorMessage(message?: string | null): boolean {
if (!message) return false;
const lower = message.toLowerCase();
return lower.includes("overloaded") || lower.includes("malformed json");
}

export const streamThunkWrapper = createAsyncThunk<
void,
() => Promise<void>,
ThunkApiType
>("chat/streamWrapper", async (runStream, { dispatch, extra, getState }) => {
try {
await runStream();
const state = getState();
if (!state.session.isInEdit) {
await dispatch(
saveCurrentSession({
openNewSession: false,
generateTitle: true,
}),
);
}
} catch (e) {
await dispatch(cancelStream());
dispatch(setDialogMessage(<StreamErrorDialog error={e} />));
dispatch(setShowDialog(true));
>("chat/streamWrapper", async (runStream, { dispatch, getState }) => {
for (let attempt = 0; attempt <= OVERLOADED_RETRIES; attempt++) {
try {
await runStream();
const state = getState();
if (!state.session.isInEdit) {
await dispatch(
saveCurrentSession({
openNewSession: false,
generateTitle: true,
}),
);
}
return;
} catch (e) {
// Get the selected model from the state for error analysis
const state = getState();
const selectedModel = selectSelectedChatModel(state);
const { parsedError, statusCode, message, modelTitle, providerName } =
analyzeError(e, selectedModel);

// Get the selected model from the state for error analysis
const state = getState();
const selectedModel = selectSelectedChatModel(state);
const shouldRetry =
isOverloadedErrorMessage(message) && attempt < OVERLOADED_RETRIES;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think checking attempt < OVERLOADED_RETRIES here has 2 issues

  • it's duplicate of having the for loop with attempt <= OVERLOADED_RETRIES
  • it wills top at e.g. 2 when OVERLOADED_RETRIES = 3

Let's change to either use recursive stop at depth OVERLOADED_RETRIES approach or just the for loop

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

actually since attempt starts with 0, we go till OVERLOADED_RETRIES which includes (in our case) 4 runs and 3 retries


const { parsedError, statusCode, modelTitle, providerName } = analyzeError(
e,
selectedModel,
);
if (shouldRetry) {
await dispatch(cancelStream());
const delayMs = OVERLOADED_DELAY_MS * 2 ** attempt;
await new Promise((resolve) => setTimeout(resolve, delayMs));
await dispatch(cancelStream());
} else {
await dispatch(cancelStream());
dispatch(setDialogMessage(<StreamErrorDialog error={e} />));
dispatch(setShowDialog(true));

const errorData = {
error_type: statusCode ? `HTTP ${statusCode}` : "Unknown",
error_message: parsedError,
model_provider: providerName,
model_title: modelTitle,
};
const errorData = {
error_type: statusCode ? `HTTP ${statusCode}` : "Unknown",
error_message: parsedError,
model_provider: providerName,
model_title: modelTitle,
};

posthog.capture("gui_stream_error", errorData);
posthog.capture("gui_stream_error", errorData);
return;
}
}
}
});
Loading