Feat out of credits msg (#6969)

Co-authored-by: openhands <openhands@all-hands.dev>
This commit is contained in:
tofarr 2025-02-27 09:02:31 -07:00 committed by GitHub
parent f437d06e81
commit be73792230
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 63 additions and 5 deletions

View File

@ -2,6 +2,7 @@ import { render, screen } from "@testing-library/react";
import { describe, it, expect } from "vitest";
import { Messages } from "#/components/features/chat/messages";
import type { Message } from "#/message";
import { renderWithProviders } from "test-utils";
describe("File Operations Messages", () => {
it("should show success indicator for successful file read operation", () => {
@ -16,7 +17,7 @@ describe("File Operations Messages", () => {
},
];
render(<Messages messages={messages} isAwaitingUserConfirmation={false} />);
renderWithProviders(<Messages messages={messages} isAwaitingUserConfirmation={false} />);
const statusIcon = screen.getByTestId("status-icon");
expect(statusIcon).toBeInTheDocument();
@ -35,7 +36,7 @@ describe("File Operations Messages", () => {
},
];
render(<Messages messages={messages} isAwaitingUserConfirmation={false} />);
renderWithProviders(<Messages messages={messages} isAwaitingUserConfirmation={false} />);
const statusIcon = screen.getByTestId("status-icon");
expect(statusIcon).toBeInTheDocument();
@ -54,7 +55,7 @@ describe("File Operations Messages", () => {
},
];
render(<Messages messages={messages} isAwaitingUserConfirmation={false} />);
renderWithProviders(<Messages messages={messages} isAwaitingUserConfirmation={false} />);
const statusIcon = screen.getByTestId("status-icon");
expect(statusIcon).toBeInTheDocument();
@ -73,7 +74,7 @@ describe("File Operations Messages", () => {
},
];
render(<Messages messages={messages} isAwaitingUserConfirmation={false} />);
renderWithProviders(<Messages messages={messages} isAwaitingUserConfirmation={false} />);
const statusIcon = screen.getByTestId("status-icon");
expect(statusIcon).toBeInTheDocument();

View File

@ -2,6 +2,7 @@ import { useState, useEffect } from "react";
import { useTranslation } from "react-i18next";
import Markdown from "react-markdown";
import remarkGfm from "remark-gfm";
import { Link } from "react-router";
import { code } from "../markdown/code";
import { ol, ul } from "../markdown/list";
import ArrowUp from "#/icons/angle-up-solid.svg?react";
@ -9,6 +10,8 @@ import ArrowDown from "#/icons/angle-down-solid.svg?react";
import CheckCircle from "#/icons/check-circle-solid.svg?react";
import XCircle from "#/icons/x-circle-solid.svg?react";
import { cn } from "#/utils/utils";
import { useConfig } from "#/hooks/query/use-config";
import { BILLING_SETTINGS } from "#/utils/feature-flags";
interface ExpandableMessageProps {
id?: string;
@ -23,6 +26,7 @@ export function ExpandableMessage({
type,
success,
}: ExpandableMessageProps) {
const { data: config } = useConfig();
const { t, i18n } = useTranslation();
const [showDetails, setShowDetails] = useState(true);
const [headline, setHeadline] = useState("");
@ -38,6 +42,28 @@ export function ExpandableMessage({
const statusIconClasses = "h-4 w-4 ml-2 inline";
if (
BILLING_SETTINGS() &&
config?.APP_MODE === "saas" &&
id === "STATUS$ERROR_LLM_OUT_OF_CREDITS"
) {
return (
<div className="flex gap-2 items-center justify-start border-l-2 pl-2 my-2 py-2 border-danger">
<div className="text-sm w-full">
<div className="font-bold text-danger">
{t("STATUS$ERROR_LLM_OUT_OF_CREDITS")}
</div>
<Link
className="mt-2 mb-2 w-full h-10 rounded flex items-center justify-center gap-2 bg-primary text-[#0D0F11]"
to="/settings/billing"
>
{t("BILLING$CLICK_TO_TOP_UP")}
</Link>
</div>
</div>
);
}
return (
<div
className={cn(

View File

@ -3833,7 +3833,21 @@
"pt": "Ocorreu um erro ao conectar ao ambiente de execução. Por favor, atualize a página.",
"tr": "Çalışma zamanına bağlanırken bir hata oluştu. Lütfen sayfayı yenileyin."
},
"STATUS$ERROR_LLM_OUT_OF_CREDITS": {
"en": "You're out of OpenHands Credits",
"ja": "OpenHandsクレジットが不足しています",
"zh-CN": "您的OpenHands点数已用完",
"zh-TW": "您的OpenHands點數已用完",
"ko-KR": "OpenHands 크레딧이 소진되었습니다",
"no": "Du er tom for OpenHands-kreditter",
"it": "Hai esaurito i crediti OpenHands",
"pt": "Você está sem créditos OpenHands",
"es": "Te has quedado sin créditos de OpenHands",
"ar": "لقد نفدت رصيدك من OpenHands",
"fr": "Vous n'avez plus de crédits OpenHands",
"tr": "OpenHands kredileriniz tükendi",
"de": "Ihre OpenHands-Guthaben sind aufgebraucht"
},
"STATUS$ERROR_RUNTIME_DISCONNECTED": {
"en": "There was an error while connecting to the runtime. Please refresh the page.",
"zh-CN": "运行时已断开连接",
@ -4558,5 +4572,20 @@
"SETTINGS_FORM$ENABLE_DEFAULT_CONDENSER_SWITCH_LABEL": {
"en": "Enable Memory Condenser",
"zh-TW": "啟用記憶體壓縮器"
},
"BILLING$CLICK_TO_TOP_UP": {
"en": "Add funds to Your Account",
"ja": "アカウントに資金を追加",
"zh-CN": "为您的账户充值",
"zh-TW": "為您的帳戶充值",
"ko-KR": "계정에 자금 추가",
"no": "Legg til midler på kontoen din",
"it": "Aggiungi fondi al tuo account",
"pt": "Adicionar fundos à sua conta",
"es": "Añadir fondos a tu cuenta",
"ar": "إضافة رصيد إلى حسابك",
"fr": "Ajouter des fonds à votre compte",
"tr": "Hesabınıza bakiye ekleyin",
"de": "Guthaben zu Ihrem Konto hinzufügen"
}
}

View File

@ -227,6 +227,8 @@ class AgentController:
err_id = 'STATUS$ERROR_LLM_SERVICE_UNAVAILABLE'
elif isinstance(e, litellm.InternalServerError):
err_id = 'STATUS$ERROR_LLM_INTERNAL_SERVER_ERROR'
elif isinstance(e, litellm.BadRequestError) and 'ExceededBudget' in str(e):
err_id = 'STATUS$ERROR_LLM_OUT_OF_CREDITS'
elif isinstance(e, RateLimitError):
await self.set_agent_state_to(AgentState.RATE_LIMITED)
return