From 9a5a06bb40cc4bbab3b1fc16a267b38c608b4e67 Mon Sep 17 00:00:00 2001 From: CodingOnStar Date: Thu, 25 Dec 2025 13:58:08 +0800 Subject: [PATCH] feat: add TONGYI model support and update related translations --- .../provider-added-card/quota-panel.tsx | 39 ++++++++++++------- .../model-provider-page/utils.ts | 6 ++- web/i18n/en-US/billing.ts | 2 +- web/i18n/en-US/common.ts | 3 +- web/i18n/ja-JP/billing.ts | 2 +- web/i18n/ja-JP/common.ts | 3 +- web/i18n/zh-Hans/billing.ts | 2 +- web/i18n/zh-Hans/common.ts | 3 +- 8 files changed, 39 insertions(+), 21 deletions(-) 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 106ab54901..90cf1f363c 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 @@ -2,9 +2,9 @@ import type { FC } from 'react' import type { ModelProvider } from '../declarations' import { useBoolean } from 'ahooks' import * as React from 'react' -import { useCallback, useEffect, useRef, useState } from 'react' +import { useCallback, useEffect, useMemo, useRef, useState } from 'react' import { useTranslation } from 'react-i18next' -import { AnthropicShortLight, Deepseek, Gemini, Grok, 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' @@ -12,6 +12,7 @@ import { useAppContext } from '@/context/app-context' import useTimestamp from '@/hooks/use-timestamp' import { cn } from '@/utils/classnames' import { formatNumber } from '@/utils/format' +import { PreferredProviderTypeEnum } from '../declarations' import { useMarketplaceAllPlugins } from '../hooks' import { modelNameMap, ModelProviderQuotaGetPaid } from '../utils' @@ -21,6 +22,7 @@ const allProviders = [ { key: ModelProviderQuotaGetPaid.GEMINI, Icon: Gemini }, { key: ModelProviderQuotaGetPaid.X, Icon: Grok }, { key: ModelProviderQuotaGetPaid.DEEPSEEK, Icon: Deepseek }, + { key: ModelProviderQuotaGetPaid.TONGYI, Icon: Tongyi }, ] as const // Map provider key to plugin ID @@ -31,6 +33,7 @@ const providerKeyToPluginId: Record = { [ModelProviderQuotaGetPaid.GEMINI]: 'langgenius/gemini', [ModelProviderQuotaGetPaid.X]: 'langgenius/x', [ModelProviderQuotaGetPaid.DEEPSEEK]: 'langgenius/deepseek', + [ModelProviderQuotaGetPaid.TONGYI]: 'langgenius/tongyi', } type QuotaPanelProps = { @@ -43,8 +46,10 @@ const QuotaPanel: FC = ({ }) => { const { t } = useTranslation() const { currentWorkspace } = useAppContext() - const credits = Math.max(currentWorkspace.trial_credits - currentWorkspace.trial_credits_used, 0) - const providerSet = new Set(providers.map(p => p.provider)) + 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]), + ), [providers]) const { formatTime } = useTimestamp() const { plugins: allPlugins, @@ -56,8 +61,9 @@ const QuotaPanel: FC = ({ }] = useBoolean(false) const selectedPluginIdRef = useRef(null) - const handleIconClick = useCallback((key: string, isAvailable: boolean) => { - if (!isAvailable && allPlugins) { + const handleIconClick = useCallback((key: string) => { + const providerType = providerMap.get(key) + if (!providerType && allPlugins) { const pluginId = providerKeyToPluginId[key] const plugin = allPlugins.find(p => p.plugin_id === pluginId) if (plugin) { @@ -66,9 +72,8 @@ const QuotaPanel: FC = ({ showInstallFromMarketplace() } } - }, [allPlugins, showInstallFromMarketplace]) + }, [allPlugins, providerMap, showInstallFromMarketplace]) - // Listen to providers changes and auto-close modal if installation succeeds useEffect(() => { if (isShowInstallModal && selectedPluginIdRef.current) { const isInstalled = providers.some(p => p.provider.startsWith(selectedPluginIdRef.current!)) @@ -113,18 +118,26 @@ const QuotaPanel: FC = ({
{allProviders.map(({ key, Icon }) => { - const isAvailable = providerSet.has(key) + const providerType = providerMap.get(key) + const usingQuota = providerType === PreferredProviderTypeEnum.system + const getTooltipKey = () => { + if (usingQuota) + return 'common.modelProvider.card.modelSupported' + if (providerType === PreferredProviderTypeEnum.custom) + return 'common.modelProvider.card.modelAPI' + return 'common.modelProvider.card.modelNotSupported' + } return (
handleIconClick(key, isAvailable)} + className={cn('relative h-6 w-6', !providerType && 'cursor-pointer hover:opacity-80')} + onClick={() => handleIconClick(key)} > - {!isAvailable && ( + {!usingQuota && (
)}
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 c81c90aa24..d958f3eef3 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 @@ -24,15 +24,17 @@ export enum ModelProviderQuotaGetPaid { GEMINI = 'langgenius/gemini/google', X = 'langgenius/x/x', DEEPSEEK = 'langgenius/deepseek/deepseek', + TONGYI = 'langgenius/tongyi/tongyi', } -export const MODEL_PROVIDER_QUOTA_GET_PAID = [ModelProviderQuotaGetPaid.ANTHROPIC, ModelProviderQuotaGetPaid.OPENAI, ModelProviderQuotaGetPaid.GEMINI, ModelProviderQuotaGetPaid.X, ModelProviderQuotaGetPaid.DEEPSEEK] +export const MODEL_PROVIDER_QUOTA_GET_PAID = [ModelProviderQuotaGetPaid.ANTHROPIC, ModelProviderQuotaGetPaid.OPENAI, ModelProviderQuotaGetPaid.GEMINI, ModelProviderQuotaGetPaid.X, ModelProviderQuotaGetPaid.DEEPSEEK, ModelProviderQuotaGetPaid.TONGYI] export const modelNameMap = { [ModelProviderQuotaGetPaid.OPENAI]: 'OpenAI', [ModelProviderQuotaGetPaid.ANTHROPIC]: 'Anthropic', [ModelProviderQuotaGetPaid.GEMINI]: 'Gemini', - [ModelProviderQuotaGetPaid.X]: 'Grok', + [ModelProviderQuotaGetPaid.X]: 'xAI', [ModelProviderQuotaGetPaid.DEEPSEEK]: 'DeepSeek', + [ModelProviderQuotaGetPaid.TONGYI]: 'TONGYI', } export const isNullOrUndefined = (value: any) => { diff --git a/web/i18n/en-US/billing.ts b/web/i18n/en-US/billing.ts index f1975a8f79..3a18eebb4e 100644 --- a/web/i18n/en-US/billing.ts +++ b/web/i18n/en-US/billing.ts @@ -130,7 +130,7 @@ const translation = { messageRequest: { title: '{{count,number}} message credits', titlePerMonth: '{{count,number}} message credits/month', - tooltip: 'Message credits are provided to help you easily try out different models from OpenAI, Anthropic, Gemini, Grok, and DeepSeek in Dify. Credits are consumed based on the model type. Once they\'re used up, you can switch to your own API key.', + tooltip: 'Message credits are provided to help you easily try out different models from OpenAI, Anthropic, Gemini, Grok, DeepSeek and TONGYI in Dify. Credits are consumed based on the model type. Once they\'re used up, you can switch to your own API key.', }, annotatedResponse: { title: '{{count,number}} Annotation Quota Limits', diff --git a/web/i18n/en-US/common.ts b/web/i18n/en-US/common.ts index 520f5cd607..c3c6581047 100644 --- a/web/i18n/en-US/common.ts +++ b/web/i18n/en-US/common.ts @@ -462,8 +462,9 @@ const translation = { buyQuota: 'Buy Quota', priorityUse: 'Priority use', removeKey: 'Remove API Key', - tip: 'Message Credits supports models from OpenAI, Anthropic, Gemini, Grok, and DeepSeek. Priority will be given to the paid quota. The free quota will be used after the paid quota is exhausted.', + tip: 'Message Credits supports models from OpenAI, Anthropic, Gemini, Grok, DeepSeek and TONGYI. Priority will be given to the paid quota. The free quota will be used after the paid quota is exhausted.', modelSupported: '{{modelName}} models are using this quota.', + modelAPI: '{{modelName}} models are using the API Key.', modelNotSupported: '{{modelName}} models are not installed.', }, item: { diff --git a/web/i18n/ja-JP/billing.ts b/web/i18n/ja-JP/billing.ts index 775cf4c958..5dae80f40c 100644 --- a/web/i18n/ja-JP/billing.ts +++ b/web/i18n/ja-JP/billing.ts @@ -128,7 +128,7 @@ const translation = { messageRequest: { title: '{{count,number}}メッセージクレジット', titlePerMonth: '{{count,number}}メッセージクレジット/月', - tooltip: 'メッセージクレジットは、DifyでOpenAI、Anthropic、Gemini、Grok、DeepSeekなどのさまざまなモデルを簡単に試すために提供されています。クレジットはモデルの種類に基づいて消費されます。使い切ったら、独自のAPIキーに切り替えることができます。', + tooltip: 'メッセージクレジットは、DifyでOpenAI、Anthropic、Gemini、Grok、DeepSeek、TONGYIなどのさまざまなモデルを簡単に試すために提供されています。クレジットはモデルの種類に基づいて消費されます。使い切ったら、独自のAPIキーに切り替えることができます。', }, annotatedResponse: { title: '{{count,number}}の注釈クォータ制限', diff --git a/web/i18n/ja-JP/common.ts b/web/i18n/ja-JP/common.ts index 6e5a97a994..1669597d5c 100644 --- a/web/i18n/ja-JP/common.ts +++ b/web/i18n/ja-JP/common.ts @@ -451,8 +451,9 @@ const translation = { buyQuota: 'クォータを購入', priorityUse: '優先利用', removeKey: 'API キーを削除', - tip: 'メッセージ枠はOpen AI、Anthropic、Gemini、Grok、DeepSeekのモデルを使用することをサポートしています。無料枠は有料枠が使い果たされた後に消費されます。', + tip: 'メッセージ枠はOpen AI、Anthropic、Gemini、Grok、DeepSeek、TONGYIのモデルを使用することをサポートしています。無料枠は有料枠が使い果たされた後に消費されます。', modelSupported: 'このクォータは現在{{modelName}}に使用されでいます。', + modelAPI: '{{modelName}} は現在 APIキーを使用しています。', modelNotSupported: '{{modelName}} 未インストール。', }, item: { diff --git a/web/i18n/zh-Hans/billing.ts b/web/i18n/zh-Hans/billing.ts index d40fd231e1..dd69e7ae4a 100644 --- a/web/i18n/zh-Hans/billing.ts +++ b/web/i18n/zh-Hans/billing.ts @@ -129,7 +129,7 @@ const translation = { messageRequest: { title: '{{count,number}} 条消息额度', titlePerMonth: '{{count,number}} 条消息额度/月', - tooltip: '消息额度旨在帮助您便捷地试用 Dify 中来自 OpenAI、Anthropic、Gemini、Grok、DeepSeek 的模型的的不同模型。不同模型会消耗不同额度。额度用尽后,您可以切换为使用自己的 API 密钥。', + tooltip: '消息额度旨在帮助您便捷地试用 Dify 中来自 OpenAI、Anthropic、Gemini、Grok、DeepSeek、TONGYI 的模型的的不同模型。不同模型会消耗不同额度。额度用尽后,您可以切换为使用自己的 API 密钥。', }, annotatedResponse: { title: '{{count,number}} 个标注回复数', diff --git a/web/i18n/zh-Hans/common.ts b/web/i18n/zh-Hans/common.ts index 1475369132..70e33f45bc 100644 --- a/web/i18n/zh-Hans/common.ts +++ b/web/i18n/zh-Hans/common.ts @@ -454,8 +454,9 @@ const translation = { buyQuota: '购买额度', priorityUse: '优先使用', removeKey: '删除 API 密钥', - tip: '消息额度支持使用 OpenAI、Anthropic、Gemini、Grok、DeepSeek 的模型;免费额度会在付费额度用尽后才会消耗。', + tip: '消息额度支持使用 OpenAI、Anthropic、Gemini、Grok、DeepSeek、TONGYI 的模型;免费额度会在付费额度用尽后才会消耗。', modelSupported: '{{modelName}} 模型正在使用此额度。', + modelAPI: '{{modelName}} 模型正在使用 API Key。', modelNotSupported: '{{modelName}} 模型未安装。', }, item: {