diff --git a/web/app/components/header/account-setting/model-provider-page/provider-added-card/credential-panel.tsx b/web/app/components/header/account-setting/model-provider-page/provider-added-card/credential-panel.tsx
index ba7079ef88..fee8e43423 100644
--- a/web/app/components/header/account-setting/model-provider-page/provider-added-card/credential-panel.tsx
+++ b/web/app/components/header/account-setting/model-provider-page/provider-added-card/credential-panel.tsx
@@ -2,7 +2,6 @@ import type {
ModelProvider,
} from '../declarations'
import { useQueryClient } from '@tanstack/react-query'
-import { useMemo } from 'react'
import { useTranslation } from 'react-i18next'
import { useToastContext } from '@/app/components/base/toast'
import { ConfigProvider } from '@/app/components/header/account-setting/model-provider-page/model-auth'
@@ -25,6 +24,8 @@ import {
import { UPDATE_MODEL_PROVIDER_CUSTOM_MODEL_LIST } from './index'
import PrioritySelector from './priority-selector'
import PriorityUseTip from './priority-use-tip'
+import SystemQuotaCard from './system-quota-card'
+import { useTrialCredits } from './use-trial-credits'
type CredentialPanelProps = {
provider: ModelProvider
@@ -53,6 +54,8 @@ const CredentialPanel = ({
} = useCredentialStatus(provider)
const showPrioritySelector = systemConfig.enabled && isCustomConfigured && IS_CLOUD_EDITION
+ const isUsingSystemQuota = systemConfig.enabled && priorityUseType === PreferredProviderTypeEnum.system && IS_CLOUD_EDITION
+ const { isExhausted } = useTrialCredits()
const handleChangePriority = async (key: PreferredProviderTypeEnum) => {
const res = await changeModelProviderPriority({
@@ -80,24 +83,40 @@ const CredentialPanel = ({
} as any)
}
}
- const credentialLabel = useMemo(() => {
- if (!hasCredential)
- return t('modelProvider.auth.unAuthorized', { ns: 'common' })
- if (authorized)
- return current_credential_name
- if (authRemoved)
- return t('modelProvider.auth.authRemoved', { ns: 'common' })
+ const credentialLabel = !hasCredential
+ ? t('modelProvider.auth.unAuthorized', { ns: 'common' })
+ : authorized
+ ? current_credential_name
+ : authRemoved
+ ? t('modelProvider.auth.authRemoved', { ns: 'common' })
+ : ''
- return ''
- }, [authorized, authRemoved, current_credential_name, hasCredential, t])
+ const color = (authRemoved || !hasCredential)
+ ? 'red'
+ : notAllowedToUse
+ ? 'gray'
+ : 'green'
- const color = useMemo(() => {
- if (authRemoved || !hasCredential)
- return 'red'
- if (notAllowedToUse)
- return 'gray'
- return 'green'
- }, [authRemoved, notAllowedToUse, hasCredential])
+ if (isUsingSystemQuota) {
+ return (
+
+
+ {isExhausted
+ ? t('modelProvider.card.quotaExhausted', { ns: 'common' })
+ : t('modelProvider.card.aiCreditsInUse', { ns: 'common' })}
+
+
+
+ {showPrioritySelector && (
+
+ )}
+
+
+ )
+ }
return (
<>
@@ -108,7 +127,7 @@ const CredentialPanel = ({
authRemoved && 'border-state-destructive-border bg-state-destructive-hover',
)}
>
-
+
> = {
[ModelProviderQuotaGetPaid.OPENAI]: OpenaiSmall,
@@ -50,11 +50,9 @@ const QuotaPanel: FC
= ({
providers,
}) => {
const { t } = useTranslation()
- const { data: currentWorkspace, isPending: isPendingWorkspace } = useCurrentWorkspace()
+ const { credits, isExhausted, isLoading, nextCreditResetDate } = useTrialCredits()
const { data: systemFeatures } = useSystemFeaturesQuery()
const trialModels = systemFeatures?.trial_models ?? []
- const credits = Math.max(((currentWorkspace?.trial_credits ?? 0) - (currentWorkspace?.trial_credits_used ?? 0)) || 0, 0)
- const isLoading = isPendingWorkspace && !currentWorkspace
const providerMap = useMemo(() => new Map(
providers.map(p => [p.provider, p.preferred_provider_type]),
), [providers])
@@ -111,7 +109,7 @@ const QuotaPanel: FC = ({
return (
= ({
{credits > 0
?
{formatNumber(credits)}
:
{t('modelProvider.card.quotaExhausted', { ns: 'common' })}}
- {currentWorkspace?.next_credit_reset_date
+ {nextCreditResetDate
? (
<>
·
{t('modelProvider.resetDate', {
ns: 'common',
- date: formatTime(currentWorkspace.next_credit_reset_date, t('dateFormat', { ns: 'appLog' })),
+ date: formatTime(nextCreditResetDate!, t('dateFormat', { ns: 'appLog' })),
interpolation: { escapeValue: false },
})}
diff --git a/web/app/components/header/account-setting/model-provider-page/provider-added-card/system-quota-card.tsx b/web/app/components/header/account-setting/model-provider-page/provider-added-card/system-quota-card.tsx
new file mode 100644
index 0000000000..3bdc005ec9
--- /dev/null
+++ b/web/app/components/header/account-setting/model-provider-page/provider-added-card/system-quota-card.tsx
@@ -0,0 +1,67 @@
+import type { ReactNode } from 'react'
+import { createContext, useContext } from 'react'
+import { cn } from '@/utils/classnames'
+import styles from './quota-panel.module.css'
+
+type Variant = 'default' | 'destructive'
+
+const VariantContext = createContext
('default')
+
+const containerVariants: Record = {
+ default: 'border-components-panel-border bg-white/[0.18]',
+ destructive: 'border-state-destructive-border bg-state-destructive-hover',
+}
+
+const labelVariants: Record = {
+ default: 'text-text-secondary',
+ destructive: 'text-text-destructive',
+}
+
+type SystemQuotaCardProps = {
+ variant?: Variant
+ children: ReactNode
+}
+
+const SystemQuotaCard = ({
+ variant = 'default',
+ children,
+}: SystemQuotaCardProps) => {
+ return (
+
+
+
+ )
+}
+
+const Label = ({ children }: { children: ReactNode }) => {
+ const variant = useContext(VariantContext)
+ return (
+
+ {children}
+
+ )
+}
+
+const Actions = ({ children }: { children: ReactNode }) => {
+ return (
+
+ {children}
+
+ )
+}
+
+SystemQuotaCard.Label = Label
+SystemQuotaCard.Actions = Actions
+
+export default SystemQuotaCard
diff --git a/web/app/components/header/account-setting/model-provider-page/provider-added-card/use-trial-credits.ts b/web/app/components/header/account-setting/model-provider-page/provider-added-card/use-trial-credits.ts
new file mode 100644
index 0000000000..e92bcd4b21
--- /dev/null
+++ b/web/app/components/header/account-setting/model-provider-page/provider-added-card/use-trial-credits.ts
@@ -0,0 +1,13 @@
+import { useCurrentWorkspace } from '@/service/use-common'
+
+export const useTrialCredits = () => {
+ const { data: currentWorkspace, isPending } = useCurrentWorkspace()
+ const credits = Math.max(((currentWorkspace?.trial_credits ?? 0) - (currentWorkspace?.trial_credits_used ?? 0)) || 0, 0)
+
+ return {
+ credits,
+ isExhausted: credits <= 0,
+ isLoading: isPending && !currentWorkspace,
+ nextCreditResetDate: currentWorkspace?.next_credit_reset_date,
+ }
+}
diff --git a/web/eslint-suppressions.json b/web/eslint-suppressions.json
index 98d6246028..da264df2e5 100644
--- a/web/eslint-suppressions.json
+++ b/web/eslint-suppressions.json
@@ -4858,9 +4858,6 @@
}
},
"app/components/header/account-setting/model-provider-page/provider-added-card/credential-panel.tsx": {
- "tailwindcss/enforce-consistent-class-order": {
- "count": 1
- },
"ts/no-explicit-any": {
"count": 1
}
diff --git a/web/i18n/en-US/common.json b/web/i18n/en-US/common.json
index a38416539a..9062cc77df 100644
--- a/web/i18n/en-US/common.json
+++ b/web/i18n/en-US/common.json
@@ -340,6 +340,7 @@
"modelProvider.auth.unAuthorized": "Unauthorized",
"modelProvider.buyQuota": "Buy Quota",
"modelProvider.callTimes": "Call times",
+ "modelProvider.card.aiCreditsInUse": "AI credits in use",
"modelProvider.card.buyQuota": "Buy Quota",
"modelProvider.card.callTimes": "Call times",
"modelProvider.card.modelAPI": "{{modelName}} models are using the API Key.",
diff --git a/web/i18n/zh-Hans/common.json b/web/i18n/zh-Hans/common.json
index 0168669d34..adf54c1529 100644
--- a/web/i18n/zh-Hans/common.json
+++ b/web/i18n/zh-Hans/common.json
@@ -340,6 +340,7 @@
"modelProvider.auth.unAuthorized": "未授权",
"modelProvider.buyQuota": "购买额度",
"modelProvider.callTimes": "调用次数",
+ "modelProvider.card.aiCreditsInUse": "AI 额度使用中",
"modelProvider.card.buyQuota": "购买额度",
"modelProvider.card.callTimes": "调用次数",
"modelProvider.card.modelAPI": "{{modelName}} 模型正在使用 API Key。",