Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: support sidebar max rate-limit handling restored #28706

Merged
merged 10 commits into from
Feb 14, 2025
6 changes: 3 additions & 3 deletions ee/support_sidebar_max/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -180,7 +180,7 @@ def _handle_rate_limit(self, retry_after: int) -> Response:
return Response(
{
"error": "rate_limit_exceeded",
"message": "🫣 Uh-oh, I'm really popular today! I've hit my rate limit. I need to catch my breath, please try asking your question again after 30 seconds. 🦔",
"message": "🫣 Uh-oh, I'm really popular today, we've been rate-limited. I just need to catch my breath. Hang on, I'll repeat your question for you and resume searching in less than a minute...",
"retry_after": retry_after,
},
status=status.HTTP_429_TOO_MANY_REQUESTS,
Expand Down Expand Up @@ -305,14 +305,14 @@ def send_message(self, client: anthropic.Anthropic, tools, system_prompt, messag
except (ValueError, TypeError):
continue

retry_seconds = max(reset_times) if reset_times else 15
retry_seconds = max(reset_times) if reset_times else 180

django_logger.warning(f"✨🦔 Rate limit hit - waiting {retry_seconds} seconds before retry")
return self._handle_rate_limit(retry_seconds)

except Exception as header_error:
django_logger.warning(f"✨🦔 Rate limit handling error: {str(header_error)}")
return self._handle_rate_limit(15) # Default to 15 seconds
return self._handle_rate_limit(180) # Default to 3 minutes
except Exception as e:
django_logger.error(f"✨🦔 Request to Anthropic API failed: {str(e)}", exc_info=True)
return Response({"error": str(e)}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { actions, kea, path, reducers } from 'kea'
import { actions, kea, listeners, path, reducers } from 'kea'
import { loaders } from 'kea-loaders'

import type { sidePanelMaxAILogicType } from './sidePanelMaxAILogicType'
Expand Down Expand Up @@ -40,9 +40,16 @@ export const sidePanelMaxAILogic = kea<sidePanelMaxAILogicType>([
setSearchingThinking: (isSearching: boolean) => ({ isSearching }),
setRateLimited: (isLimited: boolean) => ({ isLimited }),
setServerError: (isError: boolean) => ({ isError }),
retryAfter: (message: string, retryAfter: number) => ({ message, retryAfter }),
}),

reducers({
retryAfter: [
null as number | null,
{
retryAfter: (_, { retryAfter }) => retryAfter,
},
],
currentMessages: [
[] as ChatMessage[],
{
Expand Down Expand Up @@ -106,60 +113,50 @@ export const sidePanelMaxAILogic = kea<sidePanelMaxAILogicType>([
const response = (await sidePanelMaxAPI.sendMessage(message)) as MaxResponse
await breakpoint(100)

let messageContent =
const messageContent =
typeof response.content === 'string' ? response.content : response.content.text

// Check rate limits
const { rate_limits } = response
if (rate_limits) {
const isLimited = Object.values(rate_limits).some((limit) => limit.remaining === 0)
if (isLimited) {
actions.setRateLimited(true)
// Find the shortest reset time
const resetTimes = Object.values(rate_limits)
.map((limit) => new Date(limit.reset).getTime())
.filter((time) => !isNaN(time))
if (resetTimes.length > 0) {
const earliestReset = Math.min(...resetTimes)
const waitSeconds = Math.max(0, Math.ceil((earliestReset - Date.now()) / 1000))
messageContent = `🫣 Rate limit hit! Please try again in ${waitSeconds} seconds. 🦔`
}
}
}
actions.appendAssistantMessage(messageContent)

if (response.isError) {
actions.setServerError(true)
} else {
actions.setRateLimited(false)
actions.setServerError(false)
}
await breakpoint(300)
actions.setSearchingThinking(false)

actions.appendAssistantMessage(messageContent)
setTimeout(() => actions.setSearchingThinking(false), 100)
return messageContent
} catch (error: unknown) {
if (
error &&
typeof error === 'object' &&
'message' in error &&
typeof error.message === 'string'
error &&
'status' in error &&
typeof error.status === 'number'
) {
if (error.message.includes('429') || error.message.includes('rate limit')) {
if (error.status === 429) {
actions.setRateLimited(true)
} else if (
error.message.includes('500') ||
error.message.includes('524') ||
error.message.includes('529')
) {
const retryPeriod = (error as any).data?.retry_after || 180
actions.retryAfter(message, retryPeriod)
Copy link
Contributor

Choose a reason for hiding this comment

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

style: Unsafe type assertion to any. Consider adding proper error type handling or interface

await breakpoint(100)
} else if ([500, 524, 529].includes(error.status)) {
actions.setServerError(true)
await breakpoint(100)
actions.setSearchingThinking(false)
}
} else {
await breakpoint(100)
actions.setSearchingThinking(false)
}
setTimeout(() => actions.setSearchingThinking(false), 100)

console.error('Error sending message:', error)
return null
}
},
},
],
})),

listeners(({ actions }) => ({
retryAfter: async ({ retryAfter, message }, breakpoint) => {
await breakpoint(retryAfter * 1000)
actions.setRateLimited(false)
actions.submitMessage(message)
},
})),
])
Loading
Loading