- {icon && (
-
- {icon}
+>(
+ (
+ {
+ className,
+ icon,
+ title,
+ description,
+ action,
+ variant = "green",
+ ...props
+ },
+ ref,
+ ) => {
+ const CardVariant =
+ variant === "blue"
+ ? CardBlue
+ : variant === "destructive"
+ ? CardRed
+ : CardGreen;
+ const iconColor =
+ variant === "blue"
+ ? "text-blue-600 dark:text-blue-400"
+ : variant === "destructive"
+ ? "text-red-600 dark:text-red-400"
+ : "text-green-600 dark:text-green-400";
+
+ return (
+
+
+
+ {icon && (
+
+ {icon}
+
+ )}
+
+
{title}
+
+ {description}
+
+
- )}
-
-
{title}
-
{description}
+ {action &&
{action}
}
-
- {action && {action}
}
-
-
-));
+
+ );
+ },
+);
ActionCard.displayName = "ActionCard";
export {
@@ -143,5 +204,7 @@ export {
CardContent,
CardBasic,
CardGreen,
+ CardBlue,
+ CardRed,
ActionCard,
};
diff --git a/apps/web/utils/actions/settings.ts b/apps/web/utils/actions/settings.ts
index 8f319f099d..08bfe1f621 100644
--- a/apps/web/utils/actions/settings.ts
+++ b/apps/web/utils/actions/settings.ts
@@ -57,7 +57,7 @@ export const updateAiSettingsAction = actionClientUser
ErrorType.INCORRECT_OPENAI_API_KEY,
ErrorType.INVALID_OPENAI_MODEL,
ErrorType.OPENAI_API_KEY_DEACTIVATED,
- ErrorType.OPENAI_RETRY_ERROR,
+ ErrorType.AI_QUOTA_ERROR,
ErrorType.ANTHROPIC_INSUFFICIENT_BALANCE,
],
logger,
diff --git a/apps/web/utils/error-messages/index.ts b/apps/web/utils/error-messages/index.ts
index a857b18d32..0bb5ffc755 100644
--- a/apps/web/utils/error-messages/index.ts
+++ b/apps/web/utils/error-messages/index.ts
@@ -113,7 +113,7 @@ export const ErrorType = {
INCORRECT_OPENAI_API_KEY: "Incorrect OpenAI API key",
INVALID_OPENAI_MODEL: "Invalid OpenAI model",
OPENAI_API_KEY_DEACTIVATED: "OpenAI API key deactivated",
- OPENAI_RETRY_ERROR: "OpenAI retry error",
+ AI_QUOTA_ERROR: "AI quota error",
ANTHROPIC_INSUFFICIENT_BALANCE: "Anthropic insufficient balance",
ACCOUNT_DISCONNECTED: "Account disconnected",
};
@@ -137,8 +137,8 @@ const errorTypeConfig: Record<
actionUrl: "/settings",
actionLabel: "Update API Key",
},
- [ErrorType.OPENAI_RETRY_ERROR]: {
- label: "API Quota Exceeded",
+ [ErrorType.AI_QUOTA_ERROR]: {
+ label: "AI Rate Limited",
actionUrl: "/settings",
actionLabel: "Update Settings",
},
diff --git a/apps/web/utils/error.ts b/apps/web/utils/error.ts
index d2c40cb84d..07144ec26b 100644
--- a/apps/web/utils/error.ts
+++ b/apps/web/utils/error.ts
@@ -130,10 +130,18 @@ export function isAnthropicInsufficientBalanceError(
);
}
-// Handling OpenAI retry errors on their own because this will be related to the user's own API quota,
-// rather than an error on our side (as we default to Anthropic atm).
-export function isOpenAIRetryError(error: RetryError): boolean {
- return error.message.includes("You exceeded your current quota");
+// Handling AI quota/retry errors. This can be related to the user's own API quota or the system's quota.
+export function isAiQuotaExceededError(error: RetryError): boolean {
+ const message = error.message.toLowerCase();
+ const quotaErrorMessages = [
+ "exceeded your current quota",
+ "quota exceeded",
+ "rate limit reached",
+ "rate_limit_reached",
+ "too many requests",
+ "hit a rate limit",
+ ];
+ return quotaErrorMessages.some((substr) => message.includes(substr));
}
export function isAWSThrottlingError(error: unknown): error is Error {
@@ -178,7 +186,7 @@ export function isKnownApiError(error: unknown): boolean {
isInvalidOpenAIModelError(error) ||
isOpenAIAPIKeyDeactivatedError(error) ||
isAnthropicInsufficientBalanceError(error))) ||
- (RetryError.isInstance(error) && isOpenAIRetryError(error))
+ (RetryError.isInstance(error) && isAiQuotaExceededError(error))
);
}
@@ -227,11 +235,11 @@ export function checkCommonErrors(
};
}
- if (RetryError.isInstance(error) && isOpenAIRetryError(error)) {
- logger.warn("OpenAI quota exceeded for url", { url });
+ if (RetryError.isInstance(error) && isAiQuotaExceededError(error)) {
+ logger.warn("AI quota exceeded for url", { url });
return {
- type: "OpenAI Quota Exceeded",
- message: `OpenAI error: ${error.message}`,
+ type: "AI Quota Exceeded",
+ message: `AI error: ${error.message}`,
code: 429,
};
}
diff --git a/apps/web/utils/llms/index.ts b/apps/web/utils/llms/index.ts
index c963266533..40bbd52966 100644
--- a/apps/web/utils/llms/index.ts
+++ b/apps/web/utils/llms/index.ts
@@ -29,7 +29,7 @@ import {
isIncorrectOpenAIAPIKeyError,
isInvalidOpenAIModelError,
isOpenAIAPIKeyDeactivatedError,
- isOpenAIRetryError,
+ isAiQuotaExceededError,
isServiceUnavailableError,
} from "@/utils/error";
import { getModel, type ModelType } from "@/utils/llms/model";
@@ -316,14 +316,14 @@ async function handleError(
modelName,
});
- if (RetryError.isInstance(error) && isOpenAIRetryError(error)) {
+ if (RetryError.isInstance(error) && isAiQuotaExceededError(error)) {
return await addUserErrorMessageWithNotification({
userId,
userEmail,
emailAccountId,
- errorType: ErrorType.OPENAI_RETRY_ERROR,
+ errorType: ErrorType.AI_QUOTA_ERROR,
errorMessage:
- "You have exceeded your OpenAI API quota. Please check your OpenAI account.",
+ "Your AI provider has rejected requests due to rate limits or quota. Please check your provider account if this persists.",
logger,
});
}