diff --git a/web/app/components/header/account-setting/model-provider-page/provider-added-card/quota-panel.tsx b/web/app/components/header/account-setting/model-provider-page/provider-added-card/quota-panel.tsx index 069e46be51..9eb5aa79e0 100644 --- a/web/app/components/header/account-setting/model-provider-page/provider-added-card/quota-panel.tsx +++ b/web/app/components/header/account-setting/model-provider-page/provider-added-card/quota-panel.tsx @@ -1,34 +1,43 @@ -import type { FC } from 'react' +import type { ComponentType, FC } from 'react' import type { ModelProvider } from '../declarations' import type { Plugin } from '@/app/components/plugins/types' import { useBoolean } from 'ahooks' import * as React from 'react' import { useCallback, useEffect, useMemo, useRef, useState } from 'react' import { useTranslation } from 'react-i18next' -import { OpenaiSmall } from '@/app/components/base/icons/src/public/llm' +import { AnthropicShortLight, Deepseek, Gemini, Grok, OpenaiSmall, Tongyi } from '@/app/components/base/icons/src/public/llm' import Loading from '@/app/components/base/loading' import Tooltip from '@/app/components/base/tooltip' import InstallFromMarketplace from '@/app/components/plugins/install-plugin/install-from-marketplace' import { useAppContext } from '@/context/app-context' +import { useGlobalPublicStore } from '@/context/global-public-context' import useTimestamp from '@/hooks/use-timestamp' +import { ModelProviderQuotaGetPaid } from '@/types/model-provider' import { cn } from '@/utils/classnames' import { formatNumber } from '@/utils/format' import { PreferredProviderTypeEnum } from '../declarations' import { useMarketplaceAllPlugins } from '../hooks' -import { modelNameMap, ModelProviderQuotaGetPaid } from '../utils' +import { MODEL_PROVIDER_QUOTA_GET_PAID, modelNameMap } from '../utils' -const allProviders = [ - { key: ModelProviderQuotaGetPaid.OPENAI, Icon: OpenaiSmall }, - // { key: ModelProviderQuotaGetPaid.ANTHROPIC, Icon: AnthropicShortLight }, - // { key: ModelProviderQuotaGetPaid.GEMINI, Icon: Gemini }, - // { key: ModelProviderQuotaGetPaid.X, Icon: Grok }, - // { key: ModelProviderQuotaGetPaid.DEEPSEEK, Icon: Deepseek }, - // { key: ModelProviderQuotaGetPaid.TONGYI, Icon: Tongyi }, -] as const +// Icon map for each provider - single source of truth for provider icons +const providerIconMap: Record> = { + [ModelProviderQuotaGetPaid.OPENAI]: OpenaiSmall, + [ModelProviderQuotaGetPaid.ANTHROPIC]: AnthropicShortLight, + [ModelProviderQuotaGetPaid.GEMINI]: Gemini, + [ModelProviderQuotaGetPaid.X]: Grok, + [ModelProviderQuotaGetPaid.DEEPSEEK]: Deepseek, + [ModelProviderQuotaGetPaid.TONGYI]: Tongyi, +} + +// Derive allProviders from the shared constant +const allProviders = MODEL_PROVIDER_QUOTA_GET_PAID.map(key => ({ + key, + Icon: providerIconMap[key], +})) // Map provider key to plugin ID // provider key format: langgenius/provider/model, plugin ID format: langgenius/provider -const providerKeyToPluginId: Record = { +const providerKeyToPluginId: Record = { [ModelProviderQuotaGetPaid.OPENAI]: 'langgenius/openai', [ModelProviderQuotaGetPaid.ANTHROPIC]: 'langgenius/anthropic', [ModelProviderQuotaGetPaid.GEMINI]: 'langgenius/gemini', @@ -47,6 +56,7 @@ const QuotaPanel: FC = ({ }) => { const { t } = useTranslation() const { currentWorkspace } = useAppContext() + const { trial_models } = useGlobalPublicStore(s => s.systemFeatures) const credits = Math.max((currentWorkspace.trial_credits - currentWorkspace.trial_credits_used) || 0, 0) const providerMap = useMemo(() => new Map( providers.map(p => [p.provider, p.preferred_provider_type]), @@ -62,7 +72,7 @@ const QuotaPanel: FC = ({ }] = useBoolean(false) const selectedPluginIdRef = useRef(null) - const handleIconClick = useCallback((key: string) => { + const handleIconClick = useCallback((key: ModelProviderQuotaGetPaid) => { const providerType = providerMap.get(key) if (!providerType && allPlugins) { const pluginId = providerKeyToPluginId[key] @@ -97,7 +107,7 @@ const QuotaPanel: FC = ({
{t('modelProvider.quota', { ns: 'common' })} - + modelNameMap[key as keyof typeof modelNameMap]).filter(Boolean).join(', ') })} />
@@ -119,7 +129,7 @@ const QuotaPanel: FC = ({ : null}
- {allProviders.map(({ key, Icon }) => { + {allProviders.filter(({ key }) => trial_models.includes(key)).map(({ key, Icon }) => { const providerType = providerMap.get(key) const usingQuota = providerType === PreferredProviderTypeEnum.system const getTooltipKey = () => { diff --git a/web/app/components/header/account-setting/model-provider-page/utils.ts b/web/app/components/header/account-setting/model-provider-page/utils.ts index 7cfa7fc654..d9a255575b 100644 --- a/web/app/components/header/account-setting/model-provider-page/utils.ts +++ b/web/app/components/header/account-setting/model-provider-page/utils.ts @@ -1,4 +1,5 @@ import type { + CredentialFormSchemaSelect, CredentialFormSchemaTextInput, FormValue, ModelLoadBalancingConfig, @@ -9,6 +10,7 @@ import { validateModelLoadBalancingCredentials, validateModelProvider, } from '@/service/common' +import { ModelProviderQuotaGetPaid } from '@/types/model-provider' import { ValidatedStatus } from '../key-validator/declarations' import { ConfigurationMethodEnum, @@ -17,15 +19,8 @@ import { ModelTypeEnum, } from './declarations' -export enum ModelProviderQuotaGetPaid { - ANTHROPIC = 'langgenius/anthropic/anthropic', - OPENAI = 'langgenius/openai/openai', - // AZURE_OPENAI = 'langgenius/azure_openai/azure_openai', - GEMINI = 'langgenius/gemini/google', - X = 'langgenius/x/x', - DEEPSEEK = 'langgenius/deepseek/deepseek', - TONGYI = 'langgenius/tongyi/tongyi', -} +export { ModelProviderQuotaGetPaid } from '@/types/model-provider' + export const MODEL_PROVIDER_QUOTA_GET_PAID = [ModelProviderQuotaGetPaid.ANTHROPIC, ModelProviderQuotaGetPaid.OPENAI, ModelProviderQuotaGetPaid.GEMINI, ModelProviderQuotaGetPaid.X, ModelProviderQuotaGetPaid.DEEPSEEK, ModelProviderQuotaGetPaid.TONGYI] export const modelNameMap = { @@ -37,7 +32,7 @@ export const modelNameMap = { [ModelProviderQuotaGetPaid.TONGYI]: 'Tongyi', } -export const isNullOrUndefined = (value: any) => { +export const isNullOrUndefined = (value: unknown): value is null | undefined => { return value === undefined || value === null } @@ -66,8 +61,9 @@ export const validateCredentials = async (predefined: boolean, provider: string, else return Promise.resolve({ status: ValidatedStatus.Error, message: res.error || 'error' }) } - catch (e: any) { - return Promise.resolve({ status: ValidatedStatus.Error, message: e.message }) + catch (e: unknown) { + const message = e instanceof Error ? e.message : 'Unknown error' + return Promise.resolve({ status: ValidatedStatus.Error, message }) } } @@ -90,8 +86,9 @@ export const validateLoadBalancingCredentials = async (predefined: boolean, prov else return Promise.resolve({ status: ValidatedStatus.Error, message: res.error || 'error' }) } - catch (e: any) { - return Promise.resolve({ status: ValidatedStatus.Error, message: e.message }) + catch (e: unknown) { + const message = e instanceof Error ? e.message : 'Unknown error' + return Promise.resolve({ status: ValidatedStatus.Error, message }) } } @@ -177,7 +174,7 @@ export const modelTypeFormat = (modelType: ModelTypeEnum) => { return modelType.toLocaleUpperCase() } -export const genModelTypeFormSchema = (modelTypes: ModelTypeEnum[]) => { +export const genModelTypeFormSchema = (modelTypes: ModelTypeEnum[]): Omit => { return { type: FormTypeEnum.select, label: { @@ -198,10 +195,10 @@ export const genModelTypeFormSchema = (modelTypes: ModelTypeEnum[]) => { show_on: [], } }), - } as any + } } -export const genModelNameFormSchema = (model?: Pick) => { +export const genModelNameFormSchema = (model?: Pick): Omit => { return { type: FormTypeEnum.textInput, label: model?.label || { @@ -215,5 +212,5 @@ export const genModelNameFormSchema = (model?: Pick