diff --git a/controller/option.go b/controller/option.go index f827e0e4a..ecb1e25e8 100644 --- a/controller/option.go +++ b/controller/option.go @@ -1,7 +1,6 @@ package controller import ( - "encoding/json" "fmt" "net/http" "strings" @@ -17,10 +16,56 @@ import ( "github.com/gin-gonic/gin" ) +var completionRatioMetaOptionKeys = []string{ + "ModelPrice", + "ModelRatio", + "CompletionRatio", + "CacheRatio", + "CreateCacheRatio", + "ImageRatio", + "AudioRatio", + "AudioCompletionRatio", +} + +func collectModelNamesFromOptionValue(raw string, modelNames map[string]struct{}) { + if strings.TrimSpace(raw) == "" { + return + } + + var parsed map[string]any + if err := common.UnmarshalJsonStr(raw, &parsed); err != nil { + return + } + + for modelName := range parsed { + modelNames[modelName] = struct{}{} + } +} + +func buildCompletionRatioMetaValue(optionValues map[string]string) string { + modelNames := make(map[string]struct{}) + for _, key := range completionRatioMetaOptionKeys { + collectModelNamesFromOptionValue(optionValues[key], modelNames) + } + + meta := make(map[string]ratio_setting.CompletionRatioInfo, len(modelNames)) + for modelName := range modelNames { + meta[modelName] = ratio_setting.GetCompletionRatioInfo(modelName) + } + + jsonBytes, err := common.Marshal(meta) + if err != nil { + return "{}" + } + return string(jsonBytes) +} + func GetOptions(c *gin.Context) { var options []*model.Option + optionValues := make(map[string]string) common.OptionMapRWMutex.Lock() for k, v := range common.OptionMap { + value := common.Interface2String(v) if strings.HasSuffix(k, "Token") || strings.HasSuffix(k, "Secret") || strings.HasSuffix(k, "Key") || @@ -30,10 +75,20 @@ func GetOptions(c *gin.Context) { } options = append(options, &model.Option{ Key: k, - Value: common.Interface2String(v), + Value: value, }) + for _, optionKey := range completionRatioMetaOptionKeys { + if optionKey == k { + optionValues[k] = value + break + } + } } common.OptionMapRWMutex.Unlock() + options = append(options, &model.Option{ + Key: "CompletionRatioMeta", + Value: buildCompletionRatioMetaValue(optionValues), + }) c.JSON(http.StatusOK, gin.H{ "success": true, "message": "", @@ -49,7 +104,7 @@ type OptionUpdateRequest struct { func UpdateOption(c *gin.Context) { var option OptionUpdateRequest - err := json.NewDecoder(c.Request.Body).Decode(&option) + err := common.DecodeJson(c.Request.Body, &option) if err != nil { c.JSON(http.StatusBadRequest, gin.H{ "success": false, diff --git a/setting/ratio_setting/model_ratio.go b/setting/ratio_setting/model_ratio.go index a40fb35f0..c20508713 100644 --- a/setting/ratio_setting/model_ratio.go +++ b/setting/ratio_setting/model_ratio.go @@ -452,6 +452,44 @@ func GetCompletionRatio(name string) float64 { return hardCodedRatio } +type CompletionRatioInfo struct { + Ratio float64 `json:"ratio"` + Locked bool `json:"locked"` +} + +func GetCompletionRatioInfo(name string) CompletionRatioInfo { + name = FormatMatchingModelName(name) + + if strings.Contains(name, "/") { + if ratio, ok := completionRatioMap.Get(name); ok { + return CompletionRatioInfo{ + Ratio: ratio, + Locked: false, + } + } + } + + hardCodedRatio, locked := getHardcodedCompletionModelRatio(name) + if locked { + return CompletionRatioInfo{ + Ratio: hardCodedRatio, + Locked: true, + } + } + + if ratio, ok := completionRatioMap.Get(name); ok { + return CompletionRatioInfo{ + Ratio: ratio, + Locked: false, + } + } + + return CompletionRatioInfo{ + Ratio: hardCodedRatio, + Locked: false, + } +} + func getHardcodedCompletionModelRatio(name string) (float64, bool) { isReservedModel := strings.HasSuffix(name, "-all") || strings.HasSuffix(name, "-gizmo-*") diff --git a/web/i18next.config.js b/web/i18next.config.js index 40808629b..fc4767ee6 100644 --- a/web/i18next.config.js +++ b/web/i18next.config.js @@ -21,7 +21,7 @@ import { defineConfig } from 'i18next-cli'; /** @type {import('i18next-cli').I18nextToolkitConfig} */ export default defineConfig({ - locales: ['zh', 'en', 'fr', 'ru', 'ja', 'vi'], + locales: ['zh-CN', 'zh-TW', 'en', 'fr', 'ru', 'ja', 'vi'], extract: { input: ['src/**/*.{js,jsx,ts,tsx}'], ignore: ['src/i18n/**/*'], diff --git a/web/src/components/layout/PageLayout.jsx b/web/src/components/layout/PageLayout.jsx index 49c47e690..0799838eb 100644 --- a/web/src/components/layout/PageLayout.jsx +++ b/web/src/components/layout/PageLayout.jsx @@ -37,6 +37,7 @@ import { import { UserContext } from '../../context/User'; import { StatusContext } from '../../context/Status'; import { useLocation } from 'react-router-dom'; +import { normalizeLanguage } from '../../i18n/language'; const { Sider, Content, Header } = Layout; const PageLayout = () => { @@ -115,7 +116,11 @@ const PageLayout = () => { } const savedLang = localStorage.getItem('i18nextLng'); if (savedLang) { - i18n.changeLanguage(savedLang); + const normalizedLang = normalizeLanguage(savedLang); + if (normalizedLang !== savedLang) { + localStorage.setItem('i18nextLng', normalizedLang); + } + i18n.changeLanguage(normalizedLang); } }, [i18n]); diff --git a/web/src/components/settings/RatioSetting.jsx b/web/src/components/settings/RatioSetting.jsx index 170413058..90858bf81 100644 --- a/web/src/components/settings/RatioSetting.jsx +++ b/web/src/components/settings/RatioSetting.jsx @@ -95,19 +95,19 @@ const RatioSetting = () => { return ( - {/* 模型倍率设置以及可视化编辑器 */} + {/* 模型倍率设置以及价格编辑器 */} - + - + - + - + diff --git a/web/src/components/settings/personal/cards/PreferencesSettings.jsx b/web/src/components/settings/personal/cards/PreferencesSettings.jsx index 3aa333162..6f36951e8 100644 --- a/web/src/components/settings/personal/cards/PreferencesSettings.jsx +++ b/web/src/components/settings/personal/cards/PreferencesSettings.jsx @@ -23,6 +23,7 @@ import { Languages } from "lucide-react"; import { useTranslation } from "react-i18next"; import { API, showSuccess, showError } from "../../../../helpers"; import { UserContext } from "../../../../context/User"; +import { normalizeLanguage } from "../../../../i18n/language"; // Language options with native names const languageOptions = [ @@ -39,7 +40,7 @@ const PreferencesSettings = ({ t }) => { const { i18n } = useTranslation(); const [userState, userDispatch] = useContext(UserContext); const [currentLanguage, setCurrentLanguage] = useState( - i18n.language || "zh-CN", + normalizeLanguage(i18n.language) || "zh-CN", ); const [loading, setLoading] = useState(false); @@ -49,8 +50,7 @@ const PreferencesSettings = ({ t }) => { try { const settings = JSON.parse(userState.user.setting); if (settings.language) { - // Normalize legacy "zh" to "zh-CN" for backward compatibility - const lang = settings.language === "zh" ? "zh-CN" : settings.language; + const lang = normalizeLanguage(settings.language); setCurrentLanguage(lang); // Sync i18n with saved preference if (i18n.language !== lang) { diff --git a/web/src/context/User/index.jsx b/web/src/context/User/index.jsx index 2dd910035..175409c6f 100644 --- a/web/src/context/User/index.jsx +++ b/web/src/context/User/index.jsx @@ -20,6 +20,7 @@ For commercial licensing, please contact support@quantumnous.com import React, { useEffect } from 'react'; import { useTranslation } from 'react-i18next'; import { reducer, initialState } from './reducer'; +import { normalizeLanguage } from '../../i18n/language'; export const UserContext = React.createContext({ state: initialState, @@ -35,8 +36,9 @@ export const UserProvider = ({ children }) => { if (state.user?.setting) { try { const settings = JSON.parse(state.user.setting); - if (settings.language && settings.language !== i18n.language) { - i18n.changeLanguage(settings.language); + const normalizedLanguage = normalizeLanguage(settings.language); + if (normalizedLanguage && normalizedLanguage !== i18n.language) { + i18n.changeLanguage(normalizedLanguage); } } catch (e) { // Ignore parse errors diff --git a/web/src/hooks/common/useHeaderBar.js b/web/src/hooks/common/useHeaderBar.js index 02b005e56..ad95f8775 100644 --- a/web/src/hooks/common/useHeaderBar.js +++ b/web/src/hooks/common/useHeaderBar.js @@ -24,6 +24,7 @@ import { UserContext } from '../../context/User'; import { StatusContext } from '../../context/Status'; import { useSetTheme, useTheme, useActualTheme } from '../../context/Theme'; import { getLogo, getSystemName, API, showSuccess } from '../../helpers'; +import { normalizeLanguage } from '../../i18n/language'; import { useIsMobile } from './useIsMobile'; import { useSidebarCollapsed } from './useSidebarCollapsed'; import { useMinimumLoadingTime } from './useMinimumLoadingTime'; @@ -36,7 +37,7 @@ export const useHeaderBar = ({ onMobileMenuToggle, drawerOpen }) => { const [collapsed, toggleCollapsed] = useSidebarCollapsed(); const [logoLoaded, setLogoLoaded] = useState(false); const navigate = useNavigate(); - const [currentLang, setCurrentLang] = useState(i18n.language); + const [currentLang, setCurrentLang] = useState(normalizeLanguage(i18n.language)); const location = useLocation(); const loading = statusState?.status === undefined; @@ -118,12 +119,13 @@ export const useHeaderBar = ({ onMobileMenuToggle, drawerOpen }) => { // Language change effect useEffect(() => { const handleLanguageChanged = (lng) => { - setCurrentLang(lng); + const normalizedLang = normalizeLanguage(lng); + setCurrentLang(normalizedLang); try { const iframe = document.querySelector('iframe'); const cw = iframe && iframe.contentWindow; if (cw) { - cw.postMessage({ lang: lng }, '*'); + cw.postMessage({ lang: normalizedLang }, '*'); } } catch (e) { // Silently ignore cross-origin or access errors diff --git a/web/src/i18n/i18n.js b/web/src/i18n/i18n.js index 1c34a9d9e..904e65a5b 100644 --- a/web/src/i18n/i18n.js +++ b/web/src/i18n/i18n.js @@ -28,12 +28,14 @@ import zhTWTranslation from './locales/zh-TW.json'; import ruTranslation from './locales/ru.json'; import jaTranslation from './locales/ja.json'; import viTranslation from './locales/vi.json'; +import { supportedLanguages } from './language'; i18n .use(LanguageDetector) .use(initReactI18next) .init({ load: 'currentOnly', + supportedLngs: supportedLanguages, resources: { en: enTranslation, 'zh-CN': zhCNTranslation, diff --git a/web/src/i18n/language.js b/web/src/i18n/language.js new file mode 100644 index 000000000..088da5328 --- /dev/null +++ b/web/src/i18n/language.js @@ -0,0 +1,61 @@ +/* +Copyright (C) 2025 QuantumNous + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 of the +License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . + +For commercial licensing, please contact support@quantumnous.com +*/ + +export const supportedLanguages = [ + 'zh-CN', + 'zh-TW', + 'en', + 'fr', + 'ru', + 'ja', + 'vi', +]; + +export const normalizeLanguage = (language) => { + if (!language) { + return language; + } + + const normalized = language.trim().replace(/_/g, '-'); + const lower = normalized.toLowerCase(); + + if ( + lower === 'zh' || + lower === 'zh-cn' || + lower === 'zh-sg' || + lower.startsWith('zh-hans') + ) { + return 'zh-CN'; + } + + if ( + lower === 'zh-tw' || + lower === 'zh-hk' || + lower === 'zh-mo' || + lower.startsWith('zh-hant') + ) { + return 'zh-TW'; + } + + const matchedLanguage = supportedLanguages.find( + (supportedLanguage) => supportedLanguage.toLowerCase() === lower, + ); + + return matchedLanguage || normalized; +}; diff --git a/web/src/i18n/locales/en.json b/web/src/i18n/locales/en.json index 867087b36..948ae78a9 100644 --- a/web/src/i18n/locales/en.json +++ b/web/src/i18n/locales/en.json @@ -1718,6 +1718,7 @@ "未获取到授权码": "Authorization code not obtained", "未设置": "Not set", "未设置倍率模型": "Models without ratio settings", + "未设置价格模型": "Models without price settings", "未设置路径": "", "未配置模型": "No model configured", "未配置的模型列表": "Models not configured", @@ -2850,8 +2851,8 @@ "输入 OIDC 的 Userinfo Endpoint": "Enter OIDC Userinfo Endpoint", "输入IP地址后回车,如:8.8.8.8": "Enter IP address and press Enter, e.g.: 8.8.8.8", "输入JSON对象": "Enter JSON Object", - "输入价格": "Enter Price", - "输入价格:{{symbol}}{{price}} / 1M tokens{{audioPrice}}": "Enter price: {{symbol}}{{price}} / 1M tokens{{audioPrice}}", + "输入价格": "Input Price", + "输入价格:{{symbol}}{{price}} / 1M tokens{{audioPrice}}": "Input Price: {{symbol}}{{price}} / 1M tokens{{audioPrice}}", "输入你注册的 LinuxDO OAuth APP 的 ID": "Enter the ID of your registered LinuxDO OAuth APP", "输入你的账户名{{username}}以确认删除": "Enter your account name{{username}} to confirm deletion", "输入域名后回车": "Enter domain and press Enter", @@ -3183,6 +3184,52 @@ "默认折叠侧边栏": "Default collapse sidebar", "默认测试模型": "Default Test Model", "默认用户消息": "Default User Message", - "默认补全倍率": "Default completion ratio" + "默认补全倍率": "Default completion ratio", + "分组相关设置": "Group Related Settings", + "保存分组相关设置": "Save Group Related Settings", + "此页面仅显示未设置价格或基础倍率的模型,设置后会自动从列表中移出": "This page only shows models without base pricing. After saving, configured models will be removed from this list automatically.", + "没有未设置定价的模型": "No unpriced models", + "当前没有未设置定价的模型": "There are currently no models without pricing", + "模型计费编辑器": "Model Pricing Editor", + "价格摘要": "Price Summary", + "当前提示": "Current Notes", + "这个界面默认按价格填写,保存时会自动换算回后端需要的倍率 JSON。": "This editor uses prices by default and converts them back into the ratio JSON required by the backend when saved.", + "当前未启用,需要时再打开即可。": "This field is currently disabled. Enable it when needed.", + "下面展示这个模型保存后会写入哪些后端字段,便于和原始 JSON 编辑框保持一致。": "The fields below show which backend values will be written after saving, so you can keep them aligned with the raw JSON editors.", + "补全价格已锁定": "Completion price is locked", + "后端固定倍率:{{ratio}}。该字段仅展示换算后的价格。": "Backend fixed ratio: {{ratio}}. This field only displays the converted price.", + "这些价格都是可选项,不填也可以。": "All of these prices are optional and can be left empty.", + "请先开启并填写音频输入价格。": "Enable and fill in the audio input price first.", + "输入模型名称,例如 gpt-4.1": "Enter a model name, for example gpt-4.1", + "当前模型同时存在按次价格和倍率配置,保存时会按当前计费方式覆盖。": "This model currently has both per-request pricing and ratio-based pricing. Saving will overwrite them according to the current billing mode.", + "当前模型存在未显式设置输入倍率的扩展倍率;填写输入价格后会自动换算为价格字段。": "This model has derived ratios without an explicit input ratio. Once you fill in the input price, they will be converted into price fields automatically.", + "按量计费下需要先填写输入价格,才能保存其它价格项。": "For per-token billing, fill in the input price before saving other price fields.", + "填写音频补全价格前,需要先填写音频输入价格。": "Fill in the audio input price before setting the audio completion price.", + "模型 {{name}} 缺少输入价格,无法计算补全/缓存/图片/音频价格对应的倍率": "Model {{name}} is missing an input price, so the ratios for completion, cache, image, and audio pricing cannot be calculated.", + "模型 {{name}} 缺少音频输入价格,无法计算音频补全倍率": "Model {{name}} is missing an audio input price, so the audio completion ratio cannot be calculated.", + "批量应用当前模型价格": "Batch Apply Current Model Pricing", + "请先选择一个作为模板的模型": "Please select a model to use as the template first", + "请先勾选需要批量设置的模型": "Please select the models you want to update in batch first", + "已将模型 {{name}} 的价格配置批量应用到 {{count}} 个模型": "Applied the pricing configuration of model {{name}} to {{count}} models in batch", + "将把当前编辑中的模型 {{name}} 的价格配置,批量应用到已勾选的 {{count}} 个模型。": "The pricing configuration of the currently edited model {{name}} will be applied to the {{count}} selected models.", + "适合同系列模型一起定价,例如把 gpt-5.1 的价格批量同步到 gpt-5.1-high、gpt-5.1-low 等模型。": "Useful for pricing model variants together, for example syncing the pricing of gpt-5.1 to gpt-5.1-high, gpt-5.1-low, and similar models.", + "已勾选": "Selected", + "当前编辑": "Editing", + "已勾选 {{count}} 个模型": "{{count}} models selected", + "计费方式": "Billing Mode", + "未设置价格": "Price not set", + "保存预览": "Save Preview", + "基础价格": "Base Pricing", + "扩展价格": "Additional Pricing", + "额外价格项": "Additional price items", + "补全价格": "Completion Price", + "提示缓存价格": "Input Cache Read Price", + "缓存创建价格": "Input Cache Creation Price", + "图片输入价格": "Image Input Price", + "音频输入价格": "Audio Input Price", + "音频补全价格": "Audio Completion Price", + "适合 MJ / 任务类等按次收费模型。": "Suitable for MJ and other task-based models billed per request.", + "该模型补全倍率由后端固定为 {{ratio}}。补全价格不能在这里修改。": "This model's completion ratio is fixed to {{ratio}} by the backend. The completion price cannot be changed here.", + "空": "Empty" } } diff --git a/web/src/i18n/locales/fr.json b/web/src/i18n/locales/fr.json index e2854c994..891a70ba3 100644 --- a/web/src/i18n/locales/fr.json +++ b/web/src/i18n/locales/fr.json @@ -1709,6 +1709,7 @@ "未获取到授权码": "Code d'autorisation non obtenu", "未设置": "Non défini", "未设置倍率模型": "Modèles sans ratio", + "未设置价格模型": "Modèles sans prix", "未设置路径": "", "未配置模型": "Aucun modèle configuré", "未配置的模型列表": "Modèles non configurés", @@ -2832,8 +2833,8 @@ "输入 OIDC 的 Userinfo Endpoint": "Saisir le point de terminaison des informations utilisateur OIDC", "输入IP地址后回车,如:8.8.8.8": "Saisissez l'adresse IP et appuyez sur Entrée, par exemple : 8.8.8.8", "输入JSON对象": "Saisir l'objet JSON", - "输入价格": "Saisir le prix", - "输入价格:{{symbol}}{{price}} / 1M tokens{{audioPrice}}": "Saisir le prix : {{symbol}}{{price}} / 1M tokens{{audioPrice}}", + "输入价格": "Prix d'entrée", + "输入价格:{{symbol}}{{price}} / 1M tokens{{audioPrice}}": "Prix d'entrée : {{symbol}}{{price}} / 1M tokens{{audioPrice}}", "输入你注册的 LinuxDO OAuth APP 的 ID": "Saisir l'ID de votre application OAuth LinuxDO enregistrée", "输入你的账户名{{username}}以确认删除": "Saisissez votre nom de compte{{username}}pour confirmer la suppression", "输入域名后回车": "Saisissez le domaine et appuyez sur Entrée", @@ -3152,6 +3153,52 @@ "默认折叠侧边栏": "Réduire la barre latérale par défaut", "默认测试模型": "Modèle de test par défaut", "默认用户消息": "Bonjour", - "默认补全倍率": "Taux de complétion par défaut" + "默认补全倍率": "Taux de complétion par défaut", + "分组相关设置": "Paramètres liés aux groupes", + "保存分组相关设置": "Enregistrer les paramètres liés aux groupes", + "此页面仅显示未设置价格或基础倍率的模型,设置后会自动从列表中移出": "Cette page n'affiche que les modèles sans prix ou ratio de base. Après enregistrement, ils seront retirés automatiquement de cette liste.", + "没有未设置定价的模型": "Aucun modèle sans prix", + "当前没有未设置定价的模型": "Il n'y a actuellement aucun modèle sans prix", + "模型计费编辑器": "Éditeur de tarification des modèles", + "价格摘要": "Résumé des prix", + "当前提示": "Informations actuelles", + "这个界面默认按价格填写,保存时会自动换算回后端需要的倍率 JSON。": "Cette interface utilise les prix par défaut et les reconvertit automatiquement en JSON de ratios requis par le backend lors de l'enregistrement.", + "当前未启用,需要时再打开即可。": "Ce champ est actuellement désactivé. Activez-le si nécessaire.", + "下面展示这个模型保存后会写入哪些后端字段,便于和原始 JSON 编辑框保持一致。": "Les champs backend écrits après l'enregistrement sont affichés ci-dessous afin de rester cohérents avec les éditeurs JSON bruts.", + "补全价格已锁定": "Le prix de complétion est verrouillé", + "后端固定倍率:{{ratio}}。该字段仅展示换算后的价格。": "Ratio fixé par le backend : {{ratio}}. Ce champ affiche uniquement le prix converti.", + "这些价格都是可选项,不填也可以。": "Tous ces prix sont optionnels et peuvent être laissés vides.", + "请先开启并填写音频输入价格。": "Activez et renseignez d'abord le prix d'entrée audio.", + "输入模型名称,例如 gpt-4.1": "Saisissez un nom de modèle, par exemple gpt-4.1", + "当前模型同时存在按次价格和倍率配置,保存时会按当前计费方式覆盖。": "Ce modèle possède actuellement à la fois une tarification par requête et une configuration par ratio. L'enregistrement écrasera selon le mode de facturation actuel.", + "当前模型存在未显式设置输入倍率的扩展倍率;填写输入价格后会自动换算为价格字段。": "Ce modèle contient des ratios étendus sans ratio d'entrée explicite. Après saisie du prix d'entrée, ils seront convertis automatiquement en champs de prix.", + "按量计费下需要先填写输入价格,才能保存其它价格项。": "En facturation au volume, il faut d'abord renseigner le prix d'entrée avant d'enregistrer les autres prix.", + "填写音频补全价格前,需要先填写音频输入价格。": "Renseignez d'abord le prix d'entrée audio avant de définir le prix de complétion audio.", + "模型 {{name}} 缺少输入价格,无法计算补全/缓存/图片/音频价格对应的倍率": "Le modèle {{name}} n'a pas de prix d'entrée, impossible de calculer les ratios correspondants pour la complétion, le cache, les images et l'audio.", + "模型 {{name}} 缺少音频输入价格,无法计算音频补全倍率": "Le modèle {{name}} n'a pas de prix d'entrée audio, impossible de calculer le ratio de complétion audio.", + "批量应用当前模型价格": "Appliquer en lot le prix du modèle actuel", + "请先选择一个作为模板的模型": "Veuillez d'abord choisir un modèle comme modèle de référence", + "请先勾选需要批量设置的模型": "Veuillez d'abord sélectionner les modèles à configurer en lot", + "已将模型 {{name}} 的价格配置批量应用到 {{count}} 个模型": "La configuration tarifaire du modèle {{name}} a été appliquée à {{count}} modèles en lot", + "将把当前编辑中的模型 {{name}} 的价格配置,批量应用到已勾选的 {{count}} 个模型。": "La configuration tarifaire du modèle actuellement édité {{name}} sera appliquée aux {{count}} modèles sélectionnés.", + "适合同系列模型一起定价,例如把 gpt-5.1 的价格批量同步到 gpt-5.1-high、gpt-5.1-low 等模型。": "Pratique pour tarifer ensemble des variantes d'un même modèle, par exemple synchroniser le prix de gpt-5.1 vers gpt-5.1-high, gpt-5.1-low et autres variantes similaires.", + "已勾选": "Sélectionné", + "当前编辑": "En cours d'édition", + "已勾选 {{count}} 个模型": "{{count}} modèles sélectionnés", + "计费方式": "Mode de facturation", + "未设置价格": "Prix non défini", + "保存预览": "Aperçu avant enregistrement", + "基础价格": "Prix de base", + "扩展价格": "Prix supplémentaires", + "额外价格项": "Éléments de prix supplémentaires", + "补全价格": "Prix de complétion", + "提示缓存价格": "Prix de lecture du cache d'entrée", + "缓存创建价格": "Prix de création du cache d'entrée", + "图片输入价格": "Prix d'entrée image", + "音频输入价格": "Prix d'entrée audio", + "音频补全价格": "Prix de complétion audio", + "适合 MJ / 任务类等按次收费模型。": "Convient aux modèles MJ et autres modèles facturés à la requête.", + "该模型补全倍率由后端固定为 {{ratio}}。补全价格不能在这里修改。": "Le ratio de complétion de ce modèle est fixé à {{ratio}} par le backend. Le prix de complétion ne peut pas être modifié ici.", + "空": "Vide" } } diff --git a/web/src/i18n/locales/ja.json b/web/src/i18n/locales/ja.json index f09cc2da1..6aa49d928 100644 --- a/web/src/i18n/locales/ja.json +++ b/web/src/i18n/locales/ja.json @@ -1692,6 +1692,7 @@ "未获取到授权码": "認可コードの取得に失敗しました", "未设置": "未設定", "未设置倍率模型": "倍率が未設定のモデル", + "未设置价格模型": "価格が未設定のモデル", "未设置路径": "", "未配置模型": "未設定モデル", "未配置的模型列表": "未設定のモデルリスト", @@ -2813,7 +2814,7 @@ "输入 OIDC 的 Userinfo Endpoint": "OIDCのUserinfo Endpointを入力してください", "输入IP地址后回车,如:8.8.8.8": "IPアドレスを入力してEnter(例:8.8.8.8)", "输入JSON对象": "JSONオブジェクトを入力してください", - "输入价格": "料金を入力してください", + "输入价格": "入力価格", "输入价格:{{symbol}}{{price}} / 1M tokens{{audioPrice}}": "入力料金:{{symbol}}{{price}} / 1M tokens{{audioPrice}}", "输入你注册的 LinuxDO OAuth APP 的 ID": "登録したLinuxDO OAuth APPのIDを入力してください", "输入你的账户名{{username}}以确认删除": "削除確認: アカウント名{{username}}を入力してください", @@ -3133,6 +3134,52 @@ "默认折叠侧边栏": "サイドバーをデフォルトで折りたたむ", "默认测试模型": "デフォルトテストモデル", "默认用户消息": "こんにちは", - "默认补全倍率": "デフォルト補完倍率" + "默认补全倍率": "デフォルト補完倍率", + "分组相关设置": "グループ関連設定", + "保存分组相关设置": "グループ関連設定を保存", + "此页面仅显示未设置价格或基础倍率的模型,设置后会自动从列表中移出": "このページには価格または基本倍率が未設定のモデルのみ表示され、設定後は一覧から自動的に消えます。", + "没有未设置定价的模型": "価格未設定のモデルはありません", + "当前没有未设置定价的模型": "現在、価格未設定のモデルはありません", + "模型计费编辑器": "モデル料金エディタ", + "价格摘要": "価格概要", + "当前提示": "現在のヒント", + "这个界面默认按价格填写,保存时会自动换算回后端需要的倍率 JSON。": "この画面では価格を基準に入力し、保存時にバックエンドが必要とする倍率 JSON に自動変換されます。", + "当前未启用,需要时再打开即可。": "この項目は現在無効です。必要なときに有効にしてください。", + "下面展示这个模型保存后会写入哪些后端字段,便于和原始 JSON 编辑框保持一致。": "保存後にこのモデルでどのバックエンド項目に書き込まれるかを以下に表示します。元の JSON エディタとの整合確認に便利です。", + "补全价格已锁定": "補完価格はロックされています", + "后端固定倍率:{{ratio}}。该字段仅展示换算后的价格。": "バックエンド固定倍率: {{ratio}}。この項目は変換後の価格表示のみです。", + "这些价格都是可选项,不填也可以。": "これらの価格はすべて任意項目で、未入力でも構いません。", + "请先开启并填写音频输入价格。": "先に音声入力価格を有効にして入力してください。", + "输入模型名称,例如 gpt-4.1": "モデル名を入力してください。例: gpt-4.1", + "当前模型同时存在按次价格和倍率配置,保存时会按当前计费方式覆盖。": "このモデルには従量価格と倍率設定が同時に存在しています。保存すると現在の課金方式に従って上書きされます。", + "当前模型存在未显式设置输入倍率的扩展倍率;填写输入价格后会自动换算为价格字段。": "このモデルには入力倍率が明示されていない拡張倍率があります。入力価格を設定すると価格項目へ自動換算されます。", + "按量计费下需要先填写输入价格,才能保存其它价格项。": "従量課金では、他の価格項目を保存する前に入力価格を設定する必要があります。", + "填写音频补全价格前,需要先填写音频输入价格。": "音声補完価格を入力する前に、先に音声入力価格を入力してください。", + "模型 {{name}} 缺少输入价格,无法计算补全/缓存/图片/音频价格对应的倍率": "モデル {{name}} に入力価格がないため、補完・キャッシュ・画像・音声価格に対応する倍率を計算できません。", + "模型 {{name}} 缺少音频输入价格,无法计算音频补全倍率": "モデル {{name}} に音声入力価格がないため、音声補完倍率を計算できません。", + "批量应用当前模型价格": "現在のモデル価格を一括適用", + "请先选择一个作为模板的模型": "まずテンプレートとして使うモデルを選択してください", + "请先勾选需要批量设置的模型": "一括設定したいモデルを先に選択してください", + "已将模型 {{name}} 的价格配置批量应用到 {{count}} 个模型": "モデル {{name}} の価格設定を {{count}} 個のモデルに一括適用しました", + "将把当前编辑中的模型 {{name}} 的价格配置,批量应用到已勾选的 {{count}} 个模型。": "現在編集中のモデル {{name}} の価格設定を、選択済みの {{count}} 個のモデルに一括適用します。", + "适合同系列模型一起定价,例如把 gpt-5.1 的价格批量同步到 gpt-5.1-high、gpt-5.1-low 等模型。": "同系列モデルをまとめて価格設定するのに適しています。例えば gpt-5.1 の価格を gpt-5.1-high、gpt-5.1-low などへ一括同期できます。", + "已勾选": "選択済み", + "当前编辑": "編集中", + "已勾选 {{count}} 个模型": "{{count}} 個のモデルを選択済み", + "计费方式": "課金方式", + "未设置价格": "価格未設定", + "保存预览": "保存プレビュー", + "基础价格": "基本価格", + "扩展价格": "追加価格", + "额外价格项": "追加価格項目", + "补全价格": "補完価格", + "提示缓存价格": "入力キャッシュ読み取り価格", + "缓存创建价格": "入力キャッシュ作成価格", + "图片输入价格": "画像入力価格", + "音频输入价格": "音声入力価格", + "音频补全价格": "音声補完価格", + "适合 MJ / 任务类等按次收费模型。": "MJ やその他のリクエスト単位課金モデルに適しています。", + "该模型补全倍率由后端固定为 {{ratio}}。补全价格不能在这里修改。": "このモデルの補完倍率はバックエンドで {{ratio}} に固定されています。ここでは補完価格を変更できません。", + "空": "空" } } diff --git a/web/src/i18n/locales/ru.json b/web/src/i18n/locales/ru.json index bfc9f4640..f003d0a98 100644 --- a/web/src/i18n/locales/ru.json +++ b/web/src/i18n/locales/ru.json @@ -1721,6 +1721,7 @@ "未获取到授权码": "Код авторизации не получен", "未设置": "Не настроено", "未设置倍率模型": "Модели с неустановленным множителем", + "未设置价格模型": "Модели с неустановленной ценой", "未设置路径": "", "未配置模型": "Ненастроенные модели", "未配置的模型列表": "Список ненастроенных моделей", @@ -2846,7 +2847,7 @@ "输入 OIDC 的 Userinfo Endpoint": "Введите Userinfo Endpoint OIDC", "输入IP地址后回车,如:8.8.8.8": "Введите IP-адрес и нажмите Enter, например: 8.8.8.8", "输入JSON对象": "Введите JSON-объект", - "输入价格": "Введите цену", + "输入价格": "Цена ввода", "输入价格:{{symbol}}{{price}} / 1M tokens{{audioPrice}}": "Цена ввода: {{symbol}}{{price}} / 1M tokens{{audioPrice}}", "输入你注册的 LinuxDO OAuth APP 的 ID": "Введите ID вашего зарегистрированного LinuxDO OAuth APP", "输入你的账户名{{username}}以确认删除": "Введите имя вашей учётной записи {{username}} для подтверждения удаления", @@ -3166,6 +3167,52 @@ "默认折叠侧边栏": "Сворачивать боковую панель по умолчанию", "默认测试模型": "Модель для тестирования по умолчанию", "默认用户消息": "Здравствуйте", - "默认补全倍率": "Default completion ratio" + "默认补全倍率": "Default completion ratio", + "分组相关设置": "Настройки, связанные с группами", + "保存分组相关设置": "Сохранить настройки, связанные с группами", + "此页面仅显示未设置价格或基础倍率的模型,设置后会自动从列表中移出": "На этой странице показаны только модели без цены или базового коэффициента. После сохранения они будут автоматически удалены из списка.", + "没有未设置定价的模型": "Нет моделей без цены", + "当前没有未设置定价的模型": "Сейчас нет моделей без цены", + "模型计费编辑器": "Редактор тарификации моделей", + "价格摘要": "Сводка цен", + "当前提示": "Текущие подсказки", + "这个界面默认按价格填写,保存时会自动换算回后端需要的倍率 JSON。": "В этом интерфейсе значения по умолчанию задаются через цены, а при сохранении они автоматически преобразуются в JSON коэффициентов, требуемый backend.", + "当前未启用,需要时再打开即可。": "Это поле сейчас отключено. Включите его при необходимости.", + "下面展示这个模型保存后会写入哪些后端字段,便于和原始 JSON 编辑框保持一致。": "Ниже показано, какие backend-поля будут записаны после сохранения, чтобы их было удобно сверять с редакторами исходного JSON.", + "补全价格已锁定": "Цена завершения заблокирована", + "后端固定倍率:{{ratio}}。该字段仅展示换算后的价格。": "Фиксированный backend-коэффициент: {{ratio}}. Это поле только показывает вычисленную цену.", + "这些价格都是可选项,不填也可以。": "Все эти цены необязательны и могут быть оставлены пустыми.", + "请先开启并填写音频输入价格。": "Сначала включите и заполните цену аудио-ввода.", + "输入模型名称,例如 gpt-4.1": "Введите имя модели, например gpt-4.1", + "当前模型同时存在按次价格和倍率配置,保存时会按当前计费方式覆盖。": "У этой модели одновременно задана цена за запрос и конфигурация коэффициентов. При сохранении данные будут перезаписаны согласно текущему режиму тарификации.", + "当前模型存在未显式设置输入倍率的扩展倍率;填写输入价格后会自动换算为价格字段。": "У этой модели есть дополнительные коэффициенты без явно заданного входного коэффициента; после ввода входной цены они будут автоматически преобразованы в ценовые поля.", + "按量计费下需要先填写输入价格,才能保存其它价格项。": "При тарификации по объему сначала нужно указать входную цену, чтобы сохранить остальные ценовые поля.", + "填写音频补全价格前,需要先填写音频输入价格。": "Перед указанием цены аудио-завершения сначала задайте цену аудио-ввода.", + "模型 {{name}} 缺少输入价格,无法计算补全/缓存/图片/音频价格对应的倍率": "У модели {{name}} отсутствует входная цена, поэтому невозможно вычислить коэффициенты для завершения, кэша, изображений и аудио.", + "模型 {{name}} 缺少音频输入价格,无法计算音频补全倍率": "У модели {{name}} отсутствует цена аудио-ввода, поэтому невозможно вычислить коэффициент аудио-завершения.", + "批量应用当前模型价格": "Массово применить цену текущей модели", + "请先选择一个作为模板的模型": "Сначала выберите модель-шаблон", + "请先勾选需要批量设置的模型": "Сначала отметьте модели для массовой настройки", + "已将模型 {{name}} 的价格配置批量应用到 {{count}} 个模型": "Ценовая конфигурация модели {{name}} массово применена к {{count}} моделям", + "将把当前编辑中的模型 {{name}} 的价格配置,批量应用到已勾选的 {{count}} 个模型。": "Ценовая конфигурация редактируемой модели {{name}} будет применена к {{count}} выбранным моделям.", + "适合同系列模型一起定价,例如把 gpt-5.1 的价格批量同步到 gpt-5.1-high、gpt-5.1-low 等模型。": "Подходит для совместной настройки цен вариантов одной модели, например синхронизации цены gpt-5.1 с gpt-5.1-high, gpt-5.1-low и похожими моделями.", + "已勾选": "Выбрано", + "当前编辑": "Текущее редактирование", + "已勾选 {{count}} 个模型": "Выбрано моделей: {{count}}", + "计费方式": "Режим тарификации", + "未设置价格": "Цена не задана", + "保存预览": "Предпросмотр сохранения", + "基础价格": "Базовые цены", + "扩展价格": "Дополнительные цены", + "额外价格项": "Дополнительные ценовые позиции", + "补全价格": "Цена завершения", + "提示缓存价格": "Цена чтения входного кеша", + "缓存创建价格": "Цена создания входного кеша", + "图片输入价格": "Цена входного изображения", + "音频输入价格": "Цена входного аудио", + "音频补全价格": "Цена завершения аудио", + "适合 MJ / 任务类等按次收费模型。": "Подходит для MJ и других моделей с тарификацией за запрос.", + "该模型补全倍率由后端固定为 {{ratio}}。补全价格不能在这里修改。": "Коэффициент завершения для этой модели зафиксирован на уровне {{ratio}} на бэкенде. Цену завершения нельзя изменить здесь.", + "空": "Пусто" } } diff --git a/web/src/i18n/locales/vi.json b/web/src/i18n/locales/vi.json index b38596569..207064264 100644 --- a/web/src/i18n/locales/vi.json +++ b/web/src/i18n/locales/vi.json @@ -1693,6 +1693,7 @@ "未获取到授权码": "Không lấy được mã ủy quyền", "未设置": "Chưa thiết lập", "未设置倍率模型": "Mô hình không có cài đặt tỷ lệ", + "未设置价格模型": "Mô hình chưa thiết lập giá", "未设置路径": "", "未配置模型": "Không có mô hình được cấu hình", "未配置的模型列表": "Mô hình chưa được cấu hình", @@ -3291,9 +3292,9 @@ "输入 OIDC 的 Userinfo Endpoint": "Nhập Userinfo Endpoint của OIDC", "输入IP地址后回车,如:8.8.8.8": "Nhập địa chỉ IP và nhấn Enter, ví dụ: 8.8.8.8", "输入JSON对象": "Nhập đối tượng JSON", - "输入价格": "Nhập giá", + "输入价格": "Giá đầu vào", "输入价格:{{symbol}}{{price}} / 1M tokens": "Giá đầu vào: {{symbol}}{{price}} / 1M tokens", - "输入价格:{{symbol}}{{price}} / 1M tokens{{audioPrice}}": "Nhập giá: {{symbol}}{{price}} / 1M tokens{{audioPrice}}", + "输入价格:{{symbol}}{{price}} / 1M tokens{{audioPrice}}": "Giá đầu vào: {{symbol}}{{price}} / 1M tokens{{audioPrice}}", "输入你注册的 LinuxDO OAuth APP 的 ID": "Nhập ID của LinuxDO OAuth APP bạn đã đăng ký", "输入你的账户名{{username}}以确认删除": "Nhập tên tài khoản của bạn {{username}} để xác nhận xóa", "输入倍率": "Tỷ lệ đầu vào", @@ -3705,6 +3706,52 @@ "默认折叠侧边栏": "Mặc định thu gọn thanh bên", "默认测试模型": "Mô hình kiểm tra mặc định", "默认用户消息": "Xin chào", - "默认补全倍率": "Tỷ lệ hoàn thành mặc định" + "默认补全倍率": "Tỷ lệ hoàn thành mặc định", + "分组相关设置": "Cài đặt liên quan đến nhóm", + "保存分组相关设置": "Lưu cài đặt liên quan đến nhóm", + "此页面仅显示未设置价格或基础倍率的模型,设置后会自动从列表中移出": "Trang này chỉ hiển thị các mô hình chưa thiết lập giá hoặc tỷ lệ cơ bản. Sau khi lưu, chúng sẽ tự động biến mất khỏi danh sách.", + "没有未设置定价的模型": "Không có mô hình chưa thiết lập giá", + "当前没有未设置定价的模型": "Hiện không có mô hình nào chưa thiết lập giá", + "模型计费编辑器": "Trình chỉnh sửa giá mô hình", + "价格摘要": "Tóm tắt giá", + "当前提示": "Gợi ý hiện tại", + "这个界面默认按价格填写,保存时会自动换算回后端需要的倍率 JSON。": "Giao diện này mặc định nhập theo giá, khi lưu sẽ tự động quy đổi lại thành JSON tỷ lệ mà backend yêu cầu.", + "当前未启用,需要时再打开即可。": "Trường này hiện đang tắt. Hãy bật khi cần.", + "下面展示这个模型保存后会写入哪些后端字段,便于和原始 JSON 编辑框保持一致。": "Bên dưới hiển thị các trường backend sẽ được ghi sau khi lưu, giúp bạn dễ đối chiếu với ô chỉnh sửa JSON gốc.", + "补全价格已锁定": "Giá hoàn thành đã bị khóa", + "后端固定倍率:{{ratio}}。该字段仅展示换算后的价格。": "Tỷ lệ cố định từ backend: {{ratio}}. Trường này chỉ hiển thị giá sau khi quy đổi.", + "这些价格都是可选项,不填也可以。": "Tất cả các mức giá này đều là tùy chọn và có thể để trống.", + "请先开启并填写音频输入价格。": "Hãy bật và điền giá đầu vào âm thanh trước.", + "输入模型名称,例如 gpt-4.1": "Nhập tên mô hình, ví dụ gpt-4.1", + "当前模型同时存在按次价格和倍率配置,保存时会按当前计费方式覆盖。": "Mô hình này hiện đồng thời có giá theo lượt gọi và cấu hình tỷ lệ. Khi lưu, dữ liệu sẽ bị ghi đè theo chế độ tính phí hiện tại.", + "当前模型存在未显式设置输入倍率的扩展倍率;填写输入价格后会自动换算为价格字段。": "Mô hình này có các tỷ lệ mở rộng mà chưa đặt rõ tỷ lệ đầu vào; sau khi điền giá đầu vào, chúng sẽ tự động được quy đổi thành trường giá.", + "按量计费下需要先填写输入价格,才能保存其它价格项。": "Ở chế độ tính phí theo lượng, cần điền giá đầu vào trước thì mới lưu được các mục giá khác.", + "填写音频补全价格前,需要先填写音频输入价格。": "Trước khi nhập giá hoàn thành âm thanh, hãy nhập giá đầu vào âm thanh trước.", + "模型 {{name}} 缺少输入价格,无法计算补全/缓存/图片/音频价格对应的倍率": "Mô hình {{name}} thiếu giá đầu vào, nên không thể tính tỷ lệ tương ứng cho giá hoàn thành, bộ nhớ đệm, hình ảnh và âm thanh.", + "模型 {{name}} 缺少音频输入价格,无法计算音频补全倍率": "Mô hình {{name}} thiếu giá đầu vào âm thanh, nên không thể tính tỷ lệ hoàn thành âm thanh.", + "批量应用当前模型价格": "Áp dụng hàng loạt giá của mô hình hiện tại", + "请先选择一个作为模板的模型": "Vui lòng chọn trước một mô hình làm mẫu", + "请先勾选需要批量设置的模型": "Vui lòng chọn các mô hình cần thiết lập hàng loạt trước", + "已将模型 {{name}} 的价格配置批量应用到 {{count}} 个模型": "Đã áp dụng hàng loạt cấu hình giá của mô hình {{name}} cho {{count}} mô hình", + "将把当前编辑中的模型 {{name}} 的价格配置,批量应用到已勾选的 {{count}} 个模型。": "Cấu hình giá của mô hình đang chỉnh sửa {{name}} sẽ được áp dụng hàng loạt cho {{count}} mô hình đã chọn.", + "适合同系列模型一起定价,例如把 gpt-5.1 的价格批量同步到 gpt-5.1-high、gpt-5.1-low 等模型。": "Phù hợp để định giá cùng lúc các biến thể cùng dòng, ví dụ đồng bộ giá của gpt-5.1 sang gpt-5.1-high, gpt-5.1-low và các mô hình tương tự.", + "已勾选": "Đã chọn", + "当前编辑": "Đang chỉnh sửa", + "已勾选 {{count}} 个模型": "Đã chọn {{count}} mô hình", + "计费方式": "Chế độ tính phí", + "未设置价格": "Chưa thiết lập giá", + "保存预览": "Xem trước khi lưu", + "基础价格": "Giá cơ bản", + "扩展价格": "Giá mở rộng", + "额外价格项": "Mục giá bổ sung", + "补全价格": "Giá hoàn thành", + "提示缓存价格": "Giá đọc bộ nhớ đệm đầu vào", + "缓存创建价格": "Giá tạo bộ nhớ đệm đầu vào", + "图片输入价格": "Giá đầu vào hình ảnh", + "音频输入价格": "Giá đầu vào âm thanh", + "音频补全价格": "Giá hoàn thành âm thanh", + "适合 MJ / 任务类等按次收费模型。": "Phù hợp cho MJ và các mô hình tính phí theo lượt gọi tương tự.", + "该模型补全倍率由后端固定为 {{ratio}}。补全价格不能在这里修改。": "Tỷ lệ hoàn thành của mô hình này được backend cố định ở {{ratio}}. Không thể chỉnh giá hoàn thành tại đây.", + "空": "Trống" } } diff --git a/web/src/i18n/locales/zh-CN.json b/web/src/i18n/locales/zh-CN.json index fb135f6fb..7e5cec50f 100644 --- a/web/src/i18n/locales/zh-CN.json +++ b/web/src/i18n/locales/zh-CN.json @@ -1368,6 +1368,7 @@ "未获取到授权码": "未获取到授权码", "未设置": "未设置", "未设置倍率模型": "未设置倍率模型", + "未设置价格模型": "未设置价格模型", "未配置模型": "未配置模型", "未配置的模型列表": "未配置的模型列表", "本地": "本地", @@ -2813,6 +2814,49 @@ "缓存写": "缓存写", "写": "写", "根据 Anthropic 协定,/v1/messages 的输入 tokens 仅统计非缓存输入,不包含缓存读取与缓存写入 tokens。": "根据 Anthropic 协定,/v1/messages 的输入 tokens 仅统计非缓存输入,不包含缓存读取与缓存写入 tokens。", - "未匹配到模型,按回车键可将「{{name}}」作为自定义模型名添加": "未匹配到模型,按回车键可将「{{name}}」作为自定义模型名添加" + "未匹配到模型,按回车键可将「{{name}}」作为自定义模型名添加": "未匹配到模型,按回车键可将「{{name}}」作为自定义模型名添加", + "分组相关设置": "分组相关设置", + "保存分组相关设置": "保存分组相关设置", + "此页面仅显示未设置价格或基础倍率的模型,设置后会自动从列表中移出": "此页面仅显示未设置价格或基础倍率的模型,设置后会自动从列表中移出", + "没有未设置定价的模型": "没有未设置定价的模型", + "当前没有未设置定价的模型": "当前没有未设置定价的模型", + "模型计费编辑器": "模型计费编辑器", + "价格摘要": "价格摘要", + "当前提示": "当前提示", + "这个界面默认按价格填写,保存时会自动换算回后端需要的倍率 JSON。": "这个界面默认按价格填写,保存时会自动换算回后端需要的倍率 JSON。", + "当前未启用,需要时再打开即可。": "当前未启用,需要时再打开即可。", + "下面展示这个模型保存后会写入哪些后端字段,便于和原始 JSON 编辑框保持一致。": "下面展示这个模型保存后会写入哪些后端字段,便于和原始 JSON 编辑框保持一致。", + "补全价格已锁定": "补全价格已锁定", + "后端固定倍率:{{ratio}}。该字段仅展示换算后的价格。": "后端固定倍率:{{ratio}}。该字段仅展示换算后的价格。", + "这些价格都是可选项,不填也可以。": "这些价格都是可选项,不填也可以。", + "请先开启并填写音频输入价格。": "请先开启并填写音频输入价格。", + "输入模型名称,例如 gpt-4.1": "输入模型名称,例如 gpt-4.1", + "当前模型同时存在按次价格和倍率配置,保存时会按当前计费方式覆盖。": "当前模型同时存在按次价格和倍率配置,保存时会按当前计费方式覆盖。", + "当前模型存在未显式设置输入倍率的扩展倍率;填写输入价格后会自动换算为价格字段。": "当前模型存在未显式设置输入倍率的扩展倍率;填写输入价格后会自动换算为价格字段。", + "按量计费下需要先填写输入价格,才能保存其它价格项。": "按量计费下需要先填写输入价格,才能保存其它价格项。", + "填写音频补全价格前,需要先填写音频输入价格。": "填写音频补全价格前,需要先填写音频输入价格。", + "模型 {{name}} 缺少输入价格,无法计算补全/缓存/图片/音频价格对应的倍率": "模型 {{name}} 缺少输入价格,无法计算补全/缓存/图片/音频价格对应的倍率", + "模型 {{name}} 缺少音频输入价格,无法计算音频补全倍率": "模型 {{name}} 缺少音频输入价格,无法计算音频补全倍率", + "批量应用当前模型价格": "批量应用当前模型价格", + "请先选择一个作为模板的模型": "请先选择一个作为模板的模型", + "请先勾选需要批量设置的模型": "请先勾选需要批量设置的模型", + "已将模型 {{name}} 的价格配置批量应用到 {{count}} 个模型": "已将模型 {{name}} 的价格配置批量应用到 {{count}} 个模型", + "将把当前编辑中的模型 {{name}} 的价格配置,批量应用到已勾选的 {{count}} 个模型。": "将把当前编辑中的模型 {{name}} 的价格配置,批量应用到已勾选的 {{count}} 个模型。", + "适合同系列模型一起定价,例如把 gpt-5.1 的价格批量同步到 gpt-5.1-high、gpt-5.1-low 等模型。": "适合同系列模型一起定价,例如把 gpt-5.1 的价格批量同步到 gpt-5.1-high、gpt-5.1-low 等模型。", + "已勾选": "已勾选", + "当前编辑": "当前编辑", + "已勾选 {{count}} 个模型": "已勾选 {{count}} 个模型", + "基础价格": "基础价格", + "扩展价格": "扩展价格", + "额外价格项": "额外价格项", + "补全价格": "补全价格", + "提示缓存价格": "提示缓存价格", + "缓存创建价格": "缓存创建价格", + "图片输入价格": "图片输入价格", + "音频输入价格": "音频输入价格", + "音频补全价格": "音频补全价格", + "适合 MJ / 任务类等按次收费模型。": "适合 MJ / 任务类等按次收费模型。", + "该模型补全倍率由后端固定为 {{ratio}}。补全价格不能在这里修改。": "该模型补全倍率由后端固定为 {{ratio}}。补全价格不能在这里修改。", + "空": "空" } } diff --git a/web/src/i18n/locales/zh-TW.json b/web/src/i18n/locales/zh-TW.json index 85be3f9f7..a709bcf96 100644 --- a/web/src/i18n/locales/zh-TW.json +++ b/web/src/i18n/locales/zh-TW.json @@ -1372,6 +1372,7 @@ "未获取到授权码": "未獲取到授權碼", "未设置": "未設定", "未设置倍率模型": "未設定倍率模型", + "未设置价格模型": "未設定價格模型", "未配置模型": "未設定模型", "未配置的模型列表": "未設定的模型列表", "本地": "本地", @@ -2806,6 +2807,49 @@ "自动生成:": "自動生成:", "请先填写服务器地址,以自动生成完整的端点 URL": "請先填寫伺服器位址,以自動生成完整的端點 URL", "端点 URL 必须是完整地址(以 http:// 或 https:// 开头)": "端點 URL 必須是完整位址(以 http:// 或 https:// 開頭)", - "未匹配到模型,按回车键可将「{{name}}」作为自定义模型名添加": "未匹配到模型,按下 Enter 鍵可將「{{name}}」作為自訂模型名稱新增" + "未匹配到模型,按回车键可将「{{name}}」作为自定义模型名添加": "未匹配到模型,按下 Enter 鍵可將「{{name}}」作為自訂模型名稱新增", + "分组相关设置": "分組相關設定", + "保存分组相关设置": "保存分組相關設定", + "此页面仅显示未设置价格或基础倍率的模型,设置后会自动从列表中移出": "此頁面僅顯示未設定價格或基礎倍率的模型,設定後會自動從列表中移出", + "没有未设置定价的模型": "沒有未設定定價的模型", + "当前没有未设置定价的模型": "目前沒有未設定定價的模型", + "模型计费编辑器": "模型計費編輯器", + "价格摘要": "價格摘要", + "当前提示": "目前提示", + "这个界面默认按价格填写,保存时会自动换算回后端需要的倍率 JSON。": "這個介面預設按價格填寫,儲存時會自動換算回後端需要的倍率 JSON。", + "当前未启用,需要时再打开即可。": "目前未啟用,需要時再開啟即可。", + "下面展示这个模型保存后会写入哪些后端字段,便于和原始 JSON 编辑框保持一致。": "下方會顯示此模型儲存後將寫入哪些後端欄位,方便與原始 JSON 編輯框保持一致。", + "补全价格已锁定": "補全價格已鎖定", + "后端固定倍率:{{ratio}}。该字段仅展示换算后的价格。": "後端固定倍率:{{ratio}}。此欄位僅展示換算後的價格。", + "这些价格都是可选项,不填也可以。": "這些價格都是可選項,不填也可以。", + "请先开启并填写音频输入价格。": "請先開啟並填寫音訊輸入價格。", + "输入模型名称,例如 gpt-4.1": "輸入模型名稱,例如 gpt-4.1", + "当前模型同时存在按次价格和倍率配置,保存时会按当前计费方式覆盖。": "目前模型同時存在按次價格與倍率配置,儲存時會依目前計費方式覆蓋。", + "当前模型存在未显式设置输入倍率的扩展倍率;填写输入价格后会自动换算为价格字段。": "目前模型存在未明確設定輸入倍率的擴展倍率;填寫輸入價格後會自動換算為價格欄位。", + "按量计费下需要先填写输入价格,才能保存其它价格项。": "按量計費下需要先填寫輸入價格,才能儲存其它價格項。", + "填写音频补全价格前,需要先填写音频输入价格。": "填寫音訊補全價格前,需要先填寫音訊輸入價格。", + "模型 {{name}} 缺少输入价格,无法计算补全/缓存/图片/音频价格对应的倍率": "模型 {{name}} 缺少輸入價格,無法計算補全、快取、圖片與音訊價格對應的倍率", + "模型 {{name}} 缺少音频输入价格,无法计算音频补全倍率": "模型 {{name}} 缺少音訊輸入價格,無法計算音訊補全倍率", + "批量应用当前模型价格": "批量套用目前模型價格", + "请先选择一个作为模板的模型": "請先選擇一個作為範本的模型", + "请先勾选需要批量设置的模型": "請先勾選需要批量設定的模型", + "已将模型 {{name}} 的价格配置批量应用到 {{count}} 个模型": "已將模型 {{name}} 的價格配置批量套用到 {{count}} 個模型", + "将把当前编辑中的模型 {{name}} 的价格配置,批量应用到已勾选的 {{count}} 个模型。": "會把目前編輯中的模型 {{name}} 的價格配置,批量套用到已勾選的 {{count}} 個模型。", + "适合同系列模型一起定价,例如把 gpt-5.1 的价格批量同步到 gpt-5.1-high、gpt-5.1-low 等模型。": "適合同系列模型一起定價,例如把 gpt-5.1 的價格批量同步到 gpt-5.1-high、gpt-5.1-low 等模型。", + "已勾选": "已勾選", + "当前编辑": "目前編輯", + "已勾选 {{count}} 个模型": "已勾選 {{count}} 個模型", + "基础价格": "基礎價格", + "扩展价格": "擴展價格", + "额外价格项": "額外價格項", + "补全价格": "補全價格", + "提示缓存价格": "提示快取價格", + "缓存创建价格": "快取建立價格", + "图片输入价格": "圖片輸入價格", + "音频输入价格": "音訊輸入價格", + "音频补全价格": "音訊補全價格", + "适合 MJ / 任务类等按次收费模型。": "適合 MJ / 任務類等按次收費模型。", + "该模型补全倍率由后端固定为 {{ratio}}。补全价格不能在这里修改。": "該模型補全倍率由後端固定為 {{ratio}}。補全價格不能在這裡修改。", + "空": "空" } } diff --git a/web/src/i18n/locales/zh.json b/web/src/i18n/locales/zh.json deleted file mode 100644 index 7fa537acb..000000000 --- a/web/src/i18n/locales/zh.json +++ /dev/null @@ -1,3083 +0,0 @@ -{ - "translation": { - " + Web搜索 {{count}}次 / 1K 次 * {{symbol}}{{price}} * {{ratioType}} {{ratio}}_other": " + Web搜索 {{count}}次 / 1K 次 * {{symbol}}{{price}} * {{ratioType}} {{ratio}}", - " + 图片生成调用 {{symbol}}{{price}} / 1次 * {{ratioType}} {{ratio}}": " + 图片生成调用 {{symbol}}{{price}} / 1次 * {{ratioType}} {{ratio}}", - " + 文件搜索 {{count}}次 / 1K 次 * {{symbol}}{{price}} * {{ratioType}} {{ratio}}_other": " + 文件搜索 {{count}}次 / 1K 次 * {{symbol}}{{price}} * {{ratioType}} {{ratio}}", - " 个模型设置相同的值": " 个模型设置相同的值", - " 吗?": " 吗?", - " 秒": " 秒", - " 秒。": " 秒。", - ",当前无生效订阅,将自动使用钱包": ",当前无生效订阅,将自动使用钱包", - ",时间:": ",时间:", - ",点击更新": ",点击更新", - "(当前仅支持易支付接口,默认使用上方服务器地址作为回调地址!)": "(当前仅支持易支付接口,默认使用上方服务器地址作为回调地址!)", - "(筛选后显示 {{count}} 条)_other": "(筛选后显示 {{count}} 条)", - "(输入 {{input}} tokens / 1M tokens * {{symbol}}{{price}}": "(输入 {{input}} tokens / 1M tokens * {{symbol}}{{price}}", - "(输入 {{nonAudioInput}} tokens / 1M tokens * {{symbol}}{{price}} + 音频输入 {{audioInput}} tokens / 1M tokens * {{symbol}}{{audioPrice}}": "(输入 {{nonAudioInput}} tokens / 1M tokens * {{symbol}}{{price}} + 音频输入 {{audioInput}} tokens / 1M tokens * {{symbol}}{{audioPrice}}", - "(输入 {{nonCacheInput}} tokens / 1M tokens * {{symbol}}{{price}} + 缓存 {{cacheInput}} tokens / 1M tokens * {{symbol}}{{cachePrice}}": "(输入 {{nonCacheInput}} tokens / 1M tokens * {{symbol}}{{price}} + 缓存 {{cacheInput}} tokens / 1M tokens * {{symbol}}{{cachePrice}}", - "(输入 {{nonImageInput}} tokens + 图片输入 {{imageInput}} tokens * {{imageRatio}} / 1M tokens * {{symbol}}{{price}}": "(输入 {{nonImageInput}} tokens + 图片输入 {{imageInput}} tokens * {{imageRatio}} / 1M tokens * {{symbol}}{{price}}", - "[最多请求次数]和[最多请求完成次数]的最大值为2147483647。": "[最多请求次数]和[最多请求完成次数]的最大值为2147483647。", - "[最多请求次数]必须大于等于0,[最多请求完成次数]必须大于等于1。": "[最多请求次数]必须大于等于0,[最多请求完成次数]必须大于等于1。", - "{\n \"default\": [200, 100],\n \"vip\": [0, 1000]\n}": "{\n \"default\": [200, 100],\n \"vip\": [0, 1000]\n}", - "{{breakdown}} * {{ratioType}} {{ratio}} = {{symbol}}{{total}}": "{{breakdown}} * {{ratioType}} {{ratio}} = {{symbol}}{{total}}", - "{{inputDesc}} + {{outputDesc}}{{extraServices}} = {{symbol}}{{total}}": "{{inputDesc}} + {{outputDesc}}{{extraServices}} = {{symbol}}{{total}}", - "{{name}} ID": "{{name}} ID", - "{{ratioType}} {{ratio}}": "{{ratioType}} {{ratio}}", - "• 视频服务商的跨域限制": "• 视频服务商的跨域限制", - "• 防盗链保护机制": "• 防盗链保护机制", - "• 需要特定的请求头或认证": "• 需要特定的请求头或认证", - "© {{currentYear}}": "© {{currentYear}}", - "| 基于": "| 基于", - "$/1M tokens": "$/1M tokens", - "0 - 最低": "0 - 最低", - "0 表示不限": "0 表示不限", - "0.002-1之间的小数": "0.002-1之间的小数", - "0.1以上的小数": "0.1以上的小数", - "1) 点击「打开授权页面」完成登录;2) 浏览器会跳转到 localhost(页面打不开也没关系);3) 复制地址栏完整 URL 粘贴到下方;4) 点击「生成并填入」。": "1) 点击「打开授权页面」完成登录;2) 浏览器会跳转到 localhost(页面打不开也没关系);3) 复制地址栏完整 URL 粘贴到下方;4) 点击「生成并填入」。", - "10 - 最高": "10 - 最高", - "1h缓存创建 {{tokens}} tokens / 1M tokens * {{symbol}}{{price}} (倍率: {{ratio}})": "1h缓存创建 {{tokens}} tokens / 1M tokens * {{symbol}}{{price}} (倍率: {{ratio}})", - "1h缓存创建价格:{{symbol}}{{price}} * {{ratio}} = {{symbol}}{{total}} / 1M tokens (1h缓存创建倍率: {{cacheCreationRatio1h}})": "1h缓存创建价格:{{symbol}}{{price}} * {{ratio}} = {{symbol}}{{total}} / 1M tokens (1h缓存创建倍率: {{cacheCreationRatio1h}})", - "2 - 低": "2 - 低", - "2025年5月10日后添加的渠道,不需要再在部署的时候移除模型名称中的\".\"": "2025年5月10日后添加的渠道,不需要再在部署的时候移除模型名称中的\".\"", - "360智脑": "360智脑", - "5 - 正常(默认)": "5 - 正常(默认)", - "5m缓存创建 {{tokens}} tokens / 1M tokens * {{symbol}}{{price}} (倍率: {{ratio}})": "5m缓存创建 {{tokens}} tokens / 1M tokens * {{symbol}}{{price}} (倍率: {{ratio}})", - "5m缓存创建价格:{{symbol}}{{price}} * {{ratio}} = {{symbol}}{{total}} / 1M tokens (5m缓存创建倍率: {{cacheCreationRatio5m}})": "5m缓存创建价格:{{symbol}}{{price}} * {{ratio}} = {{symbol}}{{total}} / 1M tokens (5m缓存创建倍率: {{cacheCreationRatio5m}})", - "8 - 高": "8 - 高", - "AGPL v3.0协议": "AGPL v3.0协议", - "AI 对话": "AI 对话", - "AI模型测试环境": "AI模型测试环境", - "AI模型配置": "AI模型配置", - "AK/SK 模式:使用 AccessKey 和 SecretAccessKey;API Key 模式:使用 API Key": "AK/SK 模式:使用 AccessKey 和 SecretAccessKey;API Key 模式:使用 API Key", - "API Key": "API Key", - "API Key 模式下不支持批量创建": "API Key 模式下不支持批量创建", - "API Key 验证失败": "API Key 验证失败", - "API Key 验证成功!连接到 io.net 服务正常": "API Key 验证成功!连接到 io.net 服务正常", - "API 地址和相关配置": "API 地址和相关配置", - "API 密钥": "API 密钥", - "API 文档": "API 文档", - "API 配置": "API 配置", - "API令牌管理": "API令牌管理", - "API使用记录": "API使用记录", - "API信息": "API信息", - "API信息管理,可以配置多个API地址用于状态展示和负载均衡(最多50个)": "API信息管理,可以配置多个API地址用于状态展示和负载均衡(最多50个)", - "API地址": "API地址", - "API渠道配置": "API渠道配置", - "API端点": "API端点", - "Authorization callback URL 填": "Authorization callback URL 填", - "Authorization Endpoint": "Authorization Endpoint", - "auto分组调用链路": "auto分组调用链路", - "Bark推送URL": "Bark推送URL", - "Bark推送URL必须以http://或https://开头": "Bark推送URL必须以http://或https://开头", - "Bark通知": "Bark通知", - "Basic Auth 头": "Basic Auth 头", - "Cached tokens": "Cached tokens", - "Cached tokens 占比口径由后端返回:Claude 语义按 cached/(prompt+cached),其余按 cached/prompt。": "Cached tokens 占比口径由后端返回:Claude 语义按 cached/(prompt+cached),其余按 cached/prompt。", - "Changing batch type to:": "Changing batch type to:", - "ChatCompletions→Responses 兼容配置": "ChatCompletions→Responses 兼容配置", - "Claude 强制 beta=true": "Claude 强制 beta=true", - "Claude思考适配 BudgetTokens = MaxTokens * BudgetTokens 百分比": "Claude思考适配 BudgetTokens = MaxTokens * BudgetTokens 百分比", - "Claude设置": "Claude设置", - "Claude请求头覆盖": "Claude请求头覆盖", - "Client ID": "Client ID", - "Client Secret": "Client Secret", - "Codex 授权": "Codex 授权", - "Codex 渠道不支持批量创建": "Codex 渠道不支持批量创建", - "common.changeLanguage": "common.changeLanguage", - "Completion tokens": "Completion tokens", - "Configuration": "Configuration", - "context_int/context_string 从请求上下文读取;gjson 从入口请求的 JSON body 按 gjson path 读取。": "context_int/context_string 从请求上下文读取;gjson 从入口请求的 JSON body 按 gjson path 读取。", - "CPU 使用率超过此值时拒绝请求": "CPU 使用率超过此值时拒绝请求", - "CPU 阈值 (%)": "CPU 阈值 (%)", - "Creem API 密钥,敏感信息不显示": "Creem API 密钥,敏感信息不显示", - "Creem Setting Tips": "Creem Setting Tips", - "Creem 介绍": "Creem 介绍", - "Creem 充值": "Creem 充值", - "Creem 设置": "Creem 设置", - "default为默认设置,可单独设置每个分类的安全等级": "default为默认设置,可单独设置每个分类的安全等级", - "default为默认设置,可单独设置每个模型的版本": "default为默认设置,可单独设置每个模型的版本", - "Dify渠道只适配chatflow和agent,并且agent不支持图片!": "Dify渠道只适配chatflow和agent,并且agent不支持图片!", - "Discord": "Discord", - "Discord Client ID": "Discord Client ID", - "Discord Client Secret": "Discord Client Secret", - "Discord ID": "Discord ID", - "Discovery claims": "Discovery claims", - "Discovery scopes": "Discovery scopes", - "Discovery 建议 scopes:": "Discovery 建议 scopes:", - "EUR (欧元)": "EUR (欧元)", - "false": "false", - "GC 已执行": "GC 已执行", - "GC 执行失败": "GC 执行失败", - "GC 次数": "GC 次数", - "Gemini安全设置": "Gemini安全设置", - "Gemini思考适配 BudgetTokens = MaxTokens * BudgetTokens 百分比": "Gemini思考适配 BudgetTokens = MaxTokens * BudgetTokens 百分比", - "Gemini思考适配设置": "Gemini思考适配设置", - "Gemini版本设置": "Gemini版本设置", - "Gemini设置": "Gemini设置", - "GitHub": "GitHub", - "GitHub Client ID": "GitHub Client ID", - "GitHub Client Secret": "GitHub Client Secret", - "GitHub ID": "GitHub ID", - "Goroutine 数": "Goroutine 数", - "Gotify应用令牌": "Gotify应用令牌", - "Gotify服务器地址": "Gotify服务器地址", - "Gotify服务器地址必须以http://或https://开头": "Gotify服务器地址必须以http://或https://开头", - "Gotify通知": "Gotify通知", - "GPU/容器": "GPU/容器", - "GPU数量": "GPU数量", - "Grok设置": "Grok设置", - "Homepage URL 填": "Homepage URL 填", - "ID": "ID", - "include_obfuscation 用于控制 Responses 流混淆字段。默认关闭以避免客户端关闭该安全保护": "include_obfuscation 用于控制 Responses 流混淆字段。默认关闭以避免客户端关闭该安全保护", - "inference_geo 字段用于控制 Claude 数据驻留推理区域。默认关闭以避免未经授权透传地域信息": "inference_geo 字段用于控制 Claude 数据驻留推理区域。默认关闭以避免未经授权透传地域信息", - "IP": "IP", - "IP白名单": "IP白名单", - "IP白名单(支持CIDR表达式)": "IP白名单(支持CIDR表达式)", - "IP限制": "IP限制", - "IP黑名单": "IP黑名单", - "JSON": "JSON", - "JSON 已格式化": "JSON 已格式化", - "JSON 文本": "JSON 文本", - "JSON 无效": "JSON 无效", - "JSON 模式": "JSON 模式", - "JSON 模式支持手动输入或上传服务账号 JSON": "JSON 模式支持手动输入或上传服务账号 JSON", - "JSON格式密钥,请确保格式正确": "JSON格式密钥,请确保格式正确", - "JSON格式错误": "JSON格式错误", - "JSON编辑": "JSON编辑", - "JSON解析错误:": "JSON解析错误:", - "Key": "Key", - "Key 或 Path": "Key 或 Path", - "Key 指纹": "Key 指纹", - "Key 摘要": "Key 摘要", - "Key 来源": "Key 来源", - "Key 来源类型": "Key 来源类型", - "Linux DO Client ID": "Linux DO Client ID", - "Linux DO Client Secret": "Linux DO Client Secret", - "LinuxDO": "LinuxDO", - "LinuxDO ID": "LinuxDO ID", - "Logo 图片地址": "Logo 图片地址", - "Midjourney 任务记录": "Midjourney 任务记录", - "MIT许可证": "MIT许可证", - "New API项目仓库地址:": "New API项目仓库地址:", - "NewAPI 默认不会将入口请求的 User-Agent 透传到上游渠道;该条件仅用于识别访问本站点的客户端。": "NewAPI 默认不会将入口请求的 User-Agent 透传到上游渠道;该条件仅用于识别访问本站点的客户端。", - "OAuth Client ID": "OAuth Client ID", - "OAuth Client Secret": "OAuth Client Secret", - "OAuth 端点": "OAuth 端点", - "OIDC": "OIDC", - "OIDC ID": "OIDC ID", - "Ollama 模型管理": "Ollama 模型管理", - "Ollama 版本信息": "Ollama 版本信息", - "Passkey": "Passkey", - "Passkey 已解绑": "Passkey 已解绑", - "Passkey 已重置": "Passkey 已重置", - "Passkey 是基于 WebAuthn 标准的无密码身份验证方法,支持指纹、面容、硬件密钥等认证方式": "Passkey 是基于 WebAuthn 标准的无密码身份验证方法,支持指纹、面容、硬件密钥等认证方式", - "Passkey 注册失败,请重试": "Passkey 注册失败,请重试", - "Passkey 注册成功": "Passkey 注册成功", - "Passkey 登录": "Passkey 登录", - "Ping间隔(秒)": "Ping间隔(秒)", - "POST 参数": "POST 参数", - "price_xxx 的商品价格 ID,新建产品后可获得": "price_xxx 的商品价格 ID,新建产品后可获得", - "Prompt cache hit tokens": "Prompt cache hit tokens", - "Prompt tokens": "Prompt tokens", - "Reasoning Effort": "Reasoning Effort", - "Request ID": "Request ID", - "safety_identifier 字段用于帮助 OpenAI 识别可能违反使用政策的应用程序用户。默认关闭以保护用户隐私": "safety_identifier 字段用于帮助 OpenAI 识别可能违反使用政策的应用程序用户。默认关闭以保护用户隐私", - "Scopes(可选)": "Scopes(可选)", - "service_tier 字段用于指定服务层级,允许透传可能导致实际计费高于预期。默认关闭以避免额外费用": "service_tier 字段用于指定服务层级,允许透传可能导致实际计费高于预期。默认关闭以避免额外费用", - "sk_xxx 或 rk_xxx 的 Stripe 密钥,敏感信息不显示": "sk_xxx 或 rk_xxx 的 Stripe 密钥,敏感信息不显示", - "SMTP 发送者邮箱": "SMTP 发送者邮箱", - "SMTP 服务器地址": "SMTP 服务器地址", - "SMTP 端口": "SMTP 端口", - "SMTP 访问凭证": "SMTP 访问凭证", - "SMTP 账户": "SMTP 账户", - "SSE 事件": "SSE 事件", - "SSE数据流": "SSE数据流", - "SSRF防护开关详细说明": "SSRF防护开关详细说明", - "SSRF防护设置": "SSRF防护设置", - "SSRF防护详细说明": "SSRF防护详细说明", - "store 字段用于授权 OpenAI 存储请求数据以评估和优化产品。默认关闭,开启后可能导致 Codex 无法正常使用": "store 字段用于授权 OpenAI 存储请求数据以评估和优化产品。默认关闭,开启后可能导致 Codex 无法正常使用", - "Stripe 设置": "Stripe 设置", - "Stripe/Creem 商品ID(可选)": "Stripe/Creem 商品ID(可选)", - "Stripe/Creem 需在第三方平台创建商品并填入 ID": "Stripe/Creem 需在第三方平台创建商品并填入 ID", - "Telegram": "Telegram", - "Telegram Bot Token": "Telegram Bot Token", - "Telegram Bot 名称": "Telegram Bot 名称", - "Telegram ID": "Telegram ID", - "Token Endpoint": "Token Endpoint", - "token 会按倍率换算成“额度/次数”,请求结束后再做差额结算(补扣/返还)。": "token 会按倍率换算成“额度/次数”,请求结束后再做差额结算(补扣/返还)。", - "Total tokens": "Total tokens", - "true": "true", - "TTL(秒,0 表示默认)": "TTL(秒,0 表示默认)", - "TTL(秒)": "TTL(秒)", - "Turnstile Secret Key": "Turnstile Secret Key", - "Turnstile Site Key": "Turnstile Site Key", - "Unix时间戳": "Unix时间戳", - "Uptime Kuma地址": "Uptime Kuma地址", - "Uptime Kuma监控分类管理,可以配置多个监控分类用于服务状态展示(最多20个)": "Uptime Kuma监控分类管理,可以配置多个监控分类用于服务状态展示(最多20个)", - "URL 标识,只能包含小写字母、数字和连字符": "URL 标识,只能包含小写字母、数字和连字符", - "URL链接": "URL链接", - "USD (美元)": "USD (美元)", - "User Info Endpoint": "User Info Endpoint", - "User-Agent include(每行一个,可不写)": "User-Agent include(每行一个,可不写)", - "Value 正则": "Value 正则", - "Vertex AI 不支持 functionResponse.id 字段,开启后将自动移除该字段": "Vertex AI 不支持 functionResponse.id 字段,开启后将自动移除该字段", - "Webhook 密钥": "Webhook 密钥", - "Webhook 签名密钥": "Webhook 签名密钥", - "Webhook地址": "Webhook地址", - "Webhook地址必须以https://开头": "Webhook地址必须以https://开头", - "Webhook请求结构说明": "Webhook请求结构说明", - "Webhook通知": "Webhook通知", - "Web搜索价格:{{symbol}}{{price}} / 1K 次": "Web搜索价格:{{symbol}}{{price}} / 1K 次", - "WeChat Server 服务器地址": "WeChat Server 服务器地址", - "WeChat Server 访问凭证": "WeChat Server 访问凭证", - "Well-Known URL": "Well-Known URL", - "Well-Known URL 必须以 http:// 或 https:// 开头": "Well-Known URL 必须以 http:// 或 https:// 开头", - "whsec_xxx 的 Webhook 签名密钥,敏感信息不显示": "whsec_xxx 的 Webhook 签名密钥,敏感信息不显示", - "Worker地址": "Worker地址", - "Worker密钥": "Worker密钥", - "一个月": "一个月", - "一天": "一天", - "一小时": "一小时", - "一次调用消耗多少刀,优先级大于模型倍率": "一次调用消耗多少刀,优先级大于模型倍率", - "一行一个,不区分大小写": "一行一个,不区分大小写", - "一行一个屏蔽词,不需要符号分割": "一行一个屏蔽词,不需要符号分割", - "一键填充到 FluentRead": "一键填充到 FluentRead", - "上一个表单块": "上一个表单块", - "上一步": "上一步", - "上次保存: ": "上次保存: ", - "上游倍率同步": "上游倍率同步", - "上游返回": "上游返回", - "下一个表单块": "下一个表单块", - "下一步": "下一步", - "下午好": "下午好", - "下载日志": "下载日志", - "不再提醒": "不再提醒", - "不升级": "不升级", - "不同用户分组的价格信息": "不同用户分组的价格信息", - "不填则为模型列表第一个": "不填则为模型列表第一个", - "不建议使用": "不建议使用", - "不支持": "不支持", - "不是合法的 JSON 字符串": "不是合法的 JSON 字符串", - "不更改": "不更改", - "不重置": "不重置", - "不限": "不限", - "不限制": "不限制", - "与本地相同": "与本地相同", - "专属倍率": "专属倍率", - "两次输入的密码不一致": "两次输入的密码不一致", - "两次输入的密码不一致!": "两次输入的密码不一致!", - "两步验证": "两步验证", - "两步验证(2FA)为您的账户提供额外的安全保护。启用后,登录时需要输入密码和验证器应用生成的验证码。": "两步验证(2FA)为您的账户提供额外的安全保护。启用后,登录时需要输入密码和验证器应用生成的验证码。", - "两步验证启用成功!": "两步验证启用成功!", - "两步验证已禁用": "两步验证已禁用", - "两步验证设置": "两步验证设置", - "个": "个", - "个GPU": "个GPU", - "个人中心": "个人中心", - "个人中心区域": "个人中心区域", - "个人信息设置": "个人信息设置", - "个人设置": "个人设置", - "个字段": "个字段", - "个实例": "个实例", - "个已过期": "个已过期", - "个性化设置": "个性化设置", - "个月": "个月", - "个未配置模型": "个未配置模型", - "个模型": "个模型", - "个生效中": "个生效中", - "个部署吗?此操作不可逆。": "个部署吗?此操作不可逆。", - "中午好": "中午好", - "为一个 JSON 对象,例如:{\"100\": 0.95, \"200\": 0.9, \"500\": 0.85}": "为一个 JSON 对象,例如:{\"100\": 0.95, \"200\": 0.9, \"500\": 0.85}", - "为一个 JSON 数组,例如:[10, 20, 50, 100, 200, 500]": "为一个 JSON 数组,例如:[10, 20, 50, 100, 200, 500]", - "为一个 JSON 文本": "为一个 JSON 文本", - "为一个 JSON 文本,例如:": "为一个 JSON 文本,例如:", - "为一个 JSON 文本,键为分组名称,值为倍率": "为一个 JSON 文本,键为分组名称,值为倍率", - "为一个 JSON 文本,键为分组名称,值为分组描述": "为一个 JSON 文本,键为分组名称,值为分组描述", - "为一个 JSON 文本,键为模型名称,值为一次调用消耗多少刀,比如 \"gpt-4-gizmo-*\": 0.1,一次消耗0.1刀": "为一个 JSON 文本,键为模型名称,值为一次调用消耗多少刀,比如 \"gpt-4-gizmo-*\": 0.1,一次消耗0.1刀", - "为一个 JSON 文本,键为模型名称,值为倍率": "为一个 JSON 文本,键为模型名称,值为倍率", - "为一个 JSON 文本,键为模型名称,值为倍率,例如:{\"gpt-4o-audio-preview\": 16}": "为一个 JSON 文本,键为模型名称,值为倍率,例如:{\"gpt-4o-audio-preview\": 16}", - "为一个 JSON 文本,键为模型名称,值为倍率,例如:{\"gpt-4o-realtime\": 2}": "为一个 JSON 文本,键为模型名称,值为倍率,例如:{\"gpt-4o-realtime\": 2}", - "为一个 JSON 文本,键为模型名称,值为倍率,例如:{\"gpt-image-1\": 2}": "为一个 JSON 文本,键为模型名称,值为倍率,例如:{\"gpt-image-1\": 2}", - "为一个 JSON 文本,键为组名称,值为倍率": "为一个 JSON 文本,键为组名称,值为倍率", - "为了保护账户安全,请验证您的身份。": "为了保护账户安全,请验证您的身份。", - "为保证匹配准确,请确保客户端直连本站点(避免反向代理/网关改写 User-Agent)。": "为保证匹配准确,请确保客户端直连本站点(避免反向代理/网关改写 User-Agent)。", - "为空则默认使用服务器地址,多个 Origin 用逗号分隔,例如 https://newapi.pro,https://newapi.com ,注意不能携带[],需使用https": "为空则默认使用服务器地址,多个 Origin 用逗号分隔,例如 https://newapi.pro,https://newapi.com ,注意不能携带[],需使用https", - "主页链接填": "主页链接填", - "之前的所有日志": "之前的所有日志", - "二步验证已重置": "二步验证已重置", - "产品ID": "产品ID", - "产品ID已存在": "产品ID已存在", - "产品名称": "产品名称", - "产品配置": "产品配置", - "产品配置错误,请联系管理员": "产品配置错误,请联系管理员", - "仅为使用OpenAI格式的Gemini/Vertex渠道填充thoughtSignature": "仅为使用OpenAI格式的Gemini/Vertex渠道填充thoughtSignature", - "仅会覆盖你勾选的字段,未勾选的字段保持本地不变。": "仅会覆盖你勾选的字段,未勾选的字段保持本地不变。", - "仅供参考,以实际扣费为准": "仅供参考,以实际扣费为准", - "仅保存": "仅保存", - "仅修改展示粒度,统计精确到小时": "仅修改展示粒度,统计精确到小时", - "仅密钥": "仅密钥", - "仅对自定义模型有效": "仅对自定义模型有效", - "仅当前层": "仅当前层", - "仅当自动禁用开启时有效,关闭后不会自动禁用该渠道": "仅当自动禁用开启时有效,关闭后不会自动禁用该渠道", - "仅支持": "仅支持", - "仅支持 JSON 对象,必须包含 access_token 与 account_id": "仅支持 JSON 对象,必须包含 access_token 与 account_id", - "仅支持 JSON 文件": "仅支持 JSON 文件", - "仅支持 JSON 文件,支持多文件": "仅支持 JSON 文件,支持多文件", - "仅支持 OpenAI 接口格式": "仅支持 OpenAI 接口格式", - "仅显示已绑定": "仅显示已绑定", - "仅显示矛盾倍率": "仅显示矛盾倍率", - "仅用于开发环境,生产环境应使用 HTTPS": "仅用于开发环境,生产环境应使用 HTTPS", - "仅用于换算,实际保存的是额度": "仅用于换算,实际保存的是额度", - "仅用订阅": "仅用订阅", - "仅用钱包": "仅用钱包", - "仅重置配置": "仅重置配置", - "今日关闭": "今日关闭", - "今日已签到": "今日已签到", - "今日已签到,累计签到": "今日已签到,累计签到", - "从官方模型库同步": "从官方模型库同步", - "从认证器应用中获取验证码,或使用备用码": "从认证器应用中获取验证码,或使用备用码", - "从配置文件同步": "从配置文件同步", - "代理地址": "代理地址", - "代理设置": "代理设置", - "代码已复制到剪贴板": "代码已复制到剪贴板", - "令牌": "令牌", - "令牌分组": "令牌分组", - "令牌分组,默认为用户的分组": "令牌分组,默认为用户的分组", - "令牌创建成功,请在列表页面点击复制获取令牌!": "令牌创建成功,请在列表页面点击复制获取令牌!", - "令牌名称": "令牌名称", - "令牌已重置并已复制到剪贴板": "令牌已重置并已复制到剪贴板", - "令牌更新成功!": "令牌更新成功!", - "令牌的额度仅用于限制令牌本身的最大额度使用量,实际的使用受到账户的剩余额度限制": "令牌的额度仅用于限制令牌本身的最大额度使用量,实际的使用受到账户的剩余额度限制", - "令牌管理": "令牌管理", - "以下上游数据可能不可信:": "以下上游数据可能不可信:", - "以下文件解析失败,已忽略:{{list}}": "以下文件解析失败,已忽略:{{list}}", - "以及": "以及", - "仪表盘设置": "仪表盘设置", - "价格": "价格", - "价格:{{symbol}}{{price}} * {{ratioType}}:{{ratio}}": "价格:{{symbol}}{{price}} * {{ratioType}}:{{ratio}}", - "价格暂时不可用,请稍后重试": "价格暂时不可用,请稍后重试", - "价格计算中...": "价格计算中...", - "价格计算失败": "价格计算失败", - "价格计算失败: ": "价格计算失败: ", - "价格设置": "价格设置", - "价格设置方式": "价格设置方式", - "价格重新计算中...": "价格重新计算中...", - "价格预估": "价格预估", - "任一满足(OR)": "任一满足(OR)", - "任务 ID": "任务 ID", - "任务ID": "任务ID", - "任务日志": "任务日志", - "任务状态": "任务状态", - "任务记录": "任务记录", - "企业账户为特殊返回格式,需要特殊处理,如果非企业账户,请勿勾选": "企业账户为特殊返回格式,需要特殊处理,如果非企业账户,请勿勾选", - "优先级": "优先级", - "优先订阅": "优先订阅", - "优先钱包": "优先钱包", - "优惠": "优惠", - "低于此额度时将发送邮件提醒用户": "低于此额度时将发送邮件提醒用户", - "余额": "余额", - "余额充值管理": "余额充值管理", - "作废": "作废", - "作废于": "作废于", - "作废后该订阅将立即失效,历史记录不受影响。是否继续?": "作废后该订阅将立即失效,历史记录不受影响。是否继续?", - "作用域": "作用域", - "作用域:包含分组": "作用域:包含分组", - "作用域:包含规则名称": "作用域:包含规则名称", - "你似乎并没有修改什么": "你似乎并没有修改什么", - "你可以在“自定义模型名称”处手动添加它们,然后点击填入后再提交,或者直接使用下方操作自动处理。": "你可以在“自定义模型名称”处手动添加它们,然后点击填入后再提交,或者直接使用下方操作自动处理。", - "使用 {{name}} 继续": "使用 {{name}} 继续", - "使用 Discord 继续": "使用 Discord 继续", - "使用 JSON 对象格式,格式为:{\"组名\": [最多请求次数, 最多请求完成次数]}": "使用 JSON 对象格式,格式为:{\"组名\": [最多请求次数, 最多请求完成次数]}", - "使用 LinuxDO 继续": "使用 LinuxDO 继续", - "使用 OIDC 继续": "使用 OIDC 继续", - "使用 Passkey 实现免密且更安全的登录体验": "使用 Passkey 实现免密且更安全的登录体验", - "使用 Passkey 登录": "使用 Passkey 登录", - "使用 Passkey 验证": "使用 Passkey 验证", - "使用 微信 继续": "使用 微信 继续", - "使用 用户名 注册": "使用 用户名 注册", - "使用 邮箱或用户名 登录": "使用 邮箱或用户名 登录", - "使用ID排序": "使用ID排序", - "使用日志": "使用日志", - "使用模式": "使用模式", - "使用统计": "使用统计", - "使用认证器应用(如 Google Authenticator、Microsoft Authenticator)扫描下方二维码:": "使用认证器应用(如 Google Authenticator、Microsoft Authenticator)扫描下方二维码:", - "使用认证器应用扫描二维码": "使用认证器应用扫描二维码", - "例如 /var/cache/new-api": "例如 /var/cache/new-api", - "例如 €, £, Rp, ₩, ₹...": "例如 €, £, Rp, ₩, ₹...", - "例如 https://docs.newapi.pro": "例如 https://docs.newapi.pro", - "例如:": "例如:", - "例如: /bin/bash -c \"python app.py\"": "例如: /bin/bash -c \"python app.py\"", - "例如: nginx:latest": "例如: nginx:latest", - "例如: socks5://user:pass@host:port": "例如: socks5://user:pass@host:port", - "例如:-c": "例如:-c", - "例如:/bin/bash": "例如:/bin/bash", - "例如:0001": "例如:0001", - "例如:1000": "例如:1000", - "例如:100000": "例如:100000", - "例如:2,就是最低充值2$": "例如:2,就是最低充值2$", - "例如:2000": "例如:2000", - "例如:4.99": "例如:4.99", - "例如:401, 403, 429, 500-599": "例如:401, 403, 429, 500-599", - "例如:7,就是7元/美金": "例如:7,就是7元/美金", - "例如:email": "例如:email", - "例如:example.com": "例如:example.com", - "例如:github / si:google / https://example.com/logo.png / 🐱": "例如:github / si:google / https://example.com/logo.png / 🐱", - "例如:GitHub Enterprise": "例如:GitHub Enterprise", - "例如:github-enterprise": "例如:github-enterprise", - "例如:https://example.com/.well-known/openid-configuration": "例如:https://example.com/.well-known/openid-configuration", - "例如:https://gitea.example.com": "例如:https://gitea.example.com", - "例如:https://yourdomain.com": "例如:https://yourdomain.com", - "例如:name、full_name": "例如:name、full_name", - "例如:nginx:latest": "例如:nginx:latest", - "例如:preferred_username、login": "例如:preferred_username、login", - "例如:preview": "例如:preview", - "例如:prod_6I8rBerHpPxyoiU9WK4kot": "例如:prod_6I8rBerHpPxyoiU9WK4kot", - "例如:sub、id、data.user.id": "例如:sub、id、data.user.id", - "例如:基础套餐": "例如:基础套餐", - "例如:该请求不满足准入策略": "例如:该请求不满足准入策略", - "例如:适合轻度使用": "例如:适合轻度使用", - "例如:需要等级 {{required}},你当前等级 {{current}}": "例如:需要等级 {{required}},你当前等级 {{current}}", - "例如(全渠道):": "例如(全渠道):", - "例如(指定渠道):": "例如(指定渠道):", - "例如发卡网站的购买链接": "例如发卡网站的购买链接", - "供应商": "供应商", - "供应商介绍": "供应商介绍", - "供应商信息:": "供应商信息:", - "供应商创建成功!": "供应商创建成功!", - "供应商删除成功": "供应商删除成功", - "供应商名称": "供应商名称", - "供应商图标": "供应商图标", - "供应商更新成功!": "供应商更新成功!", - "侧边栏管理(全局控制)": "侧边栏管理(全局控制)", - "侧边栏设置保存成功": "侧边栏设置保存成功", - "保存": "保存", - "保存 Discord OAuth 设置": "保存 Discord OAuth 设置", - "保存 GitHub OAuth 设置": "保存 GitHub OAuth 设置", - "保存 Linux DO OAuth 设置": "保存 Linux DO OAuth 设置", - "保存 OIDC 设置": "保存 OIDC 设置", - "保存 Passkey 设置": "保存 Passkey 设置", - "保存 SMTP 设置": "保存 SMTP 设置", - "保存 Telegram 登录设置": "保存 Telegram 登录设置", - "保存 Turnstile 设置": "保存 Turnstile 设置", - "保存 WeChat Server 设置": "保存 WeChat Server 设置", - "保存分组倍率设置": "保存分组倍率设置", - "保存备用码": "保存备用码", - "保存备用码以备不时之需": "保存备用码以备不时之需", - "保存失败": "保存失败", - "保存失败,请重试": "保存失败,请重试", - "保存失败:": "保存失败:", - "保存屏蔽词过滤设置": "保存屏蔽词过滤设置", - "保存性能设置": "保存性能设置", - "保存成功": "保存成功", - "保存数据看板设置": "保存数据看板设置", - "保存日志设置": "保存日志设置", - "保存模型倍率设置": "保存模型倍率设置", - "保存模型速率限制": "保存模型速率限制", - "保存监控设置": "保存监控设置", - "保存签到设置": "保存签到设置", - "保存绘图设置": "保存绘图设置", - "保存聊天设置": "保存聊天设置", - "保存设置": "保存设置", - "保存通用设置": "保存通用设置", - "保存邮箱域名白名单设置": "保存邮箱域名白名单设置", - "保存额度设置": "保存额度设置", - "保留原值(目标已有值时不覆盖)": "保留原值(目标已有值时不覆盖)", - "修复数据库一致性": "修复数据库一致性", - "修改为": "修改为", - "修改子渠道优先级": "修改子渠道优先级", - "修改子渠道权重": "修改子渠道权重", - "修改密码": "修改密码", - "修改绑定": "修改绑定", - "修改部署名称": "修改部署名称", - "倍率": "倍率", - "倍率信息": "倍率信息", - "倍率是为了方便换算不同价格的模型": "倍率是为了方便换算不同价格的模型", - "倍率模式": "倍率模式", - "倍率类型": "倍率类型", - "偏好设置": "偏好设置", - "停止测试": "停止测试", - "停止重试": "停止重试", - "停用": "停用", - "允许 AccountFilter 参数": "允许 AccountFilter 参数", - "允许 HTTP 协议图片请求(适用于自部署代理)": "允许 HTTP 协议图片请求(适用于自部署代理)", - "允许 inference_geo 透传": "允许 inference_geo 透传", - "允许 safety_identifier 透传": "允许 safety_identifier 透传", - "允许 service_tier 透传": "允许 service_tier 透传", - "允许 stream_options.include_obfuscation 透传": "允许 stream_options.include_obfuscation 透传", - "允许 Turnstile 用户校验": "允许 Turnstile 用户校验", - "允许不安全的 Origin(HTTP)": "允许不安全的 Origin(HTTP)", - "允许回调(会泄露服务器 IP 地址)": "允许回调(会泄露服务器 IP 地址)", - "允许在 Stripe 支付中输入促销码": "允许在 Stripe 支付中输入促销码", - "允许新用户注册": "允许新用户注册", - "允许的 Origins": "允许的 Origins", - "允许的IP,一行一个,不填写则不限制": "允许的IP,一行一个,不填写则不限制", - "允许的端口": "允许的端口", - "允许访问私有IP地址(127.0.0.1、192.168.x.x等内网地址)": "允许访问私有IP地址(127.0.0.1、192.168.x.x等内网地址)", - "允许通过 Discord 账户登录 & 注册": "允许通过 Discord 账户登录 & 注册", - "允许通过 GitHub 账户登录 & 注册": "允许通过 GitHub 账户登录 & 注册", - "允许通过 Linux DO 账户登录 & 注册": "允许通过 Linux DO 账户登录 & 注册", - "允许通过 OIDC 进行登录": "允许通过 OIDC 进行登录", - "允许通过 Passkey 登录 & 认证": "允许通过 Passkey 登录 & 认证", - "允许通过 Telegram 进行登录": "允许通过 Telegram 进行登录", - "允许通过密码进行注册": "允许通过密码进行注册", - "允许通过密码进行登录": "允许通过密码进行登录", - "允许通过微信登录 & 注册": "允许通过微信登录 & 注册", - "允许重试": "允许重试", - "元": "元", - "充值": "充值", - "充值价格(x元/美金)": "充值价格(x元/美金)", - "充值价格显示": "充值价格显示", - "充值分组倍率": "充值分组倍率", - "充值分组倍率不是合法的 JSON 字符串": "充值分组倍率不是合法的 JSON 字符串", - "充值数量": "充值数量", - "充值数量,最低 ": "充值数量,最低 ", - "充值数量不能小于": "充值数量不能小于", - "充值方式设置": "充值方式设置", - "充值方式设置不是合法的 JSON 字符串": "充值方式设置不是合法的 JSON 字符串", - "充值确认": "充值确认", - "充值账单": "充值账单", - "充值金额折扣配置": "充值金额折扣配置", - "充值金额折扣配置不是合法的 JSON 对象": "充值金额折扣配置不是合法的 JSON 对象", - "充值链接": "充值链接", - "充值额度": "充值额度", - "先填写配置,再自动填充 OAuth 端点,能显著减少手工输入": "先填写配置,再自动填充 OAuth 端点,能显著减少手工输入", - "先搜索,再一键复制字段名或填入当前规则。字段名为系统内部路径,可直接用于路径 / 来源 / 目标。": "先搜索,再一键复制字段名或填入当前规则。字段名为系统内部路径,可直接用于路径 / 来源 / 目标。", - "免责声明:仅限个人使用,请勿分发或共享任何凭证。该渠道存在前置条件与使用门槛,请在充分了解流程与风险后使用,并遵守 OpenAI 的相关条款与政策。相关凭证与配置仅限接入 Codex CLI 使用,不适用于其他客户端、平台或渠道。": "免责声明:仅限个人使用,请勿分发或共享任何凭证。该渠道存在前置条件与使用门槛,请在充分了解流程与风险后使用,并遵守 OpenAI 的相关条款与政策。相关凭证与配置仅限接入 Codex CLI 使用,不适用于其他客户端、平台或渠道。", - "兑换人ID": "兑换人ID", - "兑换成功!": "兑换成功!", - "兑换码充值": "兑换码充值", - "兑换码创建成功": "兑换码创建成功", - "兑换码创建成功,是否下载兑换码?": "兑换码创建成功,是否下载兑换码?", - "兑换码创建成功!": "兑换码创建成功!", - "兑换码将以文本文件的形式下载,文件名为兑换码的名称。": "兑换码将以文本文件的形式下载,文件名为兑换码的名称。", - "兑换码更新成功!": "兑换码更新成功!", - "兑换码生成管理": "兑换码生成管理", - "兑换码管理": "兑换码管理", - "兑换额度": "兑换额度", - "全局控制侧边栏区域和功能显示,管理员隐藏的功能用户无法启用": "全局控制侧边栏区域和功能显示,管理员隐藏的功能用户无法启用", - "全局设置": "全局设置", - "全选": "全选", - "全部": "全部", - "全部供应商": "全部供应商", - "全部分组": "全部分组", - "全部地区总可用资源": "全部地区总可用资源", - "全部填入": "全部填入", - "全部容器": "全部容器", - "全部展开": "全部展开", - "全部收起": "全部收起", - "全部标签": "全部标签", - "全部模型": "全部模型", - "全部满足(AND)": "全部满足(AND)", - "全部状态": "全部状态", - "全部硬件总可用资源": "全部硬件总可用资源", - "全部端点": "全部端点", - "全部类型": "全部类型", - "公告": "公告", - "公告内容": "公告内容", - "公告已更新": "公告已更新", - "公告更新失败": "公告更新失败", - "公告类型": "公告类型", - "共": "共", - "共 {{count}} 个密钥_other": "共 {{count}} 个密钥", - "共 {{count}} 个模型_other": "共 {{count}} 个模型", - "共 {{count}} 条日志_other": "共 {{count}} 条日志", - "共 {{total}} 项,当前显示 {{start}}-{{end}} 项": "共 {{total}} 项,当前显示 {{start}}-{{end}} 项", - "关": "关", - "关于": "关于", - "关于我们": "关于我们", - "关于系统的详细信息": "关于系统的详细信息", - "关于项目": "关于项目", - "关键字(id或者名称)": "关键字(id或者名称)", - "关闭": "关闭", - "关闭侧边栏": "关闭侧边栏", - "关闭公告": "关闭公告", - "关闭后,此模型将不会被“同步官方”自动覆盖或创建": "关闭后,此模型将不会被“同步官方”自动覆盖或创建", - "关闭后将不再显示此提示(仅对当前浏览器生效)。确定要关闭吗?": "关闭后将不再显示此提示(仅对当前浏览器生效)。确定要关闭吗?", - "关闭弹窗,已停止批量测试": "关闭弹窗,已停止批量测试", - "关闭提示": "关闭提示", - "其他": "其他", - "其他注册选项": "其他注册选项", - "其他登录选项": "其他登录选项", - "其他设置": "其他设置", - "其他详情": "其他详情", - "内存 阈值 (%)": "内存 阈值 (%)", - "内存使用率超过此值时拒绝请求": "内存使用率超过此值时拒绝请求", - "内存命中": "内存命中", - "内存缓存最大条目数。0 表示使用后端默认容量:100000。": "内存缓存最大条目数。0 表示使用后端默认容量:100000。", - "内容": "内容", - "内容较大,已启用性能优化模式": "内容较大,已启用性能优化模式", - "内容较大,部分功能可能受限": "内容较大,部分功能可能受限", - "内置": "内置", - "内置 Ollama 镜像": "内置 Ollama 镜像", - "再次输入部署名称": "再次输入部署名称", - "最低": "最低", - "最低充值美元数量": "最低充值美元数量", - "最后使用时间": "最后使用时间", - "最后更新": "最后更新", - "最后请求": "最后请求", - "最大GPU数量": "最大GPU数量", - "最大可用": "最大可用", - "最大条目数": "最大条目数", - "最终抵扣": "最终抵扣", - "最近一次": "最近一次", - "最近事件": "最近事件", - "写": "写", - "准入策略": "准入策略", - "准入策略 JSON(可选)": "准入策略 JSON(可选)", - "准备中...": "准备中...", - "准备完成初始化": "准备完成初始化", - "凭证已刷新": "凭证已刷新", - "分类名称": "分类名称", - "分组": "分组", - "分组与模型定价设置": "分组与模型定价设置", - "分组价格": "分组价格", - "分组倍率": "分组倍率", - "分组倍率设置": "分组倍率设置", - "分组倍率设置,可以在此处新增分组或修改现有分组的倍率,格式为 JSON 字符串,例如:{\"vip\": 0.5, \"test\": 1},表示 vip 分组的倍率为 0.5,test 分组的倍率为 1": "分组倍率设置,可以在此处新增分组或修改现有分组的倍率,格式为 JSON 字符串,例如:{\"vip\": 0.5, \"test\": 1},表示 vip 分组的倍率为 0.5,test 分组的倍率为 1", - "分组特殊倍率": "分组特殊倍率", - "分组特殊可用分组": "分组特殊可用分组", - "分组设置": "分组设置", - "分组速率配置优先级高于全局速率限制。": "分组速率配置优先级高于全局速率限制。", - "分组速率限制": "分组速率限制", - "分钟": "分钟", - "切换为Assistant角色": "切换为Assistant角色", - "切换为System角色": "切换为System角色", - "切换为单密钥模式": "切换为单密钥模式", - "切换主题": "切换主题", - "划转到余额": "划转到余额", - "划转邀请额度": "划转邀请额度", - "划转金额最低为": "划转金额最低为", - "划转额度": "划转额度", - "列出的模型将不会自动添加或移除-thinking/-nothinking 后缀": "列出的模型将不会自动添加或移除-thinking/-nothinking 后缀", - "列设置": "列设置", - "创建": "创建", - "创建令牌默认选择auto分组,初始令牌也将设为auto(否则留空,为用户默认分组)": "创建令牌默认选择auto分组,初始令牌也将设为auto(否则留空,为用户默认分组)", - "创建失败": "创建失败", - "创建成功": "创建成功", - "创建或选择密钥时,将 Project 设置为 io.cloud": "创建或选择密钥时,将 Project 设置为 io.cloud", - "创建新用户账户": "创建新用户账户", - "创建新的令牌": "创建新的令牌", - "创建新的兑换码": "创建新的兑换码", - "创建新的模型": "创建新的模型", - "创建新的渠道": "创建新的渠道", - "创建新的订阅套餐": "创建新的订阅套餐", - "创建新的预填组": "创建新的预填组", - "创建时间": "创建时间", - "创建用户": "创建用户", - "初始化失败,请重试": "初始化失败,请重试", - "初始化系统": "初始化系统", - "删除": "删除", - "删除 Key 来源": "删除 Key 来源", - "删除会彻底移除该订阅记录(含权益明细)。是否继续?": "删除会彻底移除该订阅记录(含权益明细)。是否继续?", - "删除后无法恢复,确定要删除模型 \"{{name}}\" 吗?": "删除后无法恢复,确定要删除模型 \"{{name}}\" 吗?", - "删除失败": "删除失败", - "删除密钥失败": "删除密钥失败", - "删除成功": "删除成功", - "删除所选": "删除所选", - "删除所选令牌": "删除所选令牌", - "删除所选通道": "删除所选通道", - "删除条件": "删除条件", - "删除禁用密钥失败": "删除禁用密钥失败", - "删除禁用通道": "删除禁用通道", - "删除自动禁用密钥": "删除自动禁用密钥", - "删除规则": "删除规则", - "删除账户": "删除账户", - "删除账户确认": "删除账户确认", - "删除部署失败": "删除部署失败", - "刷新": "刷新", - "刷新凭证": "刷新凭证", - "刷新失败": "刷新失败", - "刷新容器信息": "刷新容器信息", - "刷新日志": "刷新日志", - "刷新统计": "刷新统计", - "刷新缓存统计": "刷新缓存统计", - "刷新缓存统计失败": "刷新缓存统计失败", - "前往 io.net API Keys": "前往 io.net API Keys", - "前往设置": "前往设置", - "前往设置页面": "前往设置页面", - "前缀": "前缀", - "副本数量": "副本数量", - "剩余": "剩余", - "剩余备用码:": "剩余备用码:", - "剩余时间": "剩余时间", - "剩余额度": "剩余额度", - "剩余额度/总额度": "剩余额度/总额度", - "剩余额度$": "剩余额度$", - "功能特性": "功能特性", - "加入渠道": "加入渠道", - "加入预填组": "加入预填组", - "加密存储": "加密存储", - "加载中...": "加载中...", - "加载供应商信息失败": "加载供应商信息失败", - "加载关于内容失败...": "加载关于内容失败...", - "加载分组失败": "加载分组失败", - "加载失败": "加载失败", - "加载容器信息中...": "加载容器信息中...", - "加载容器详情中...": "加载容器详情中...", - "加载日志中...": "加载日志中...", - "加载模型信息失败": "加载模型信息失败", - "加载模型列表失败": "加载模型列表失败", - "加载模型失败": "加载模型失败", - "加载用户协议内容失败...": "加载用户协议内容失败...", - "加载设置中...": "加载设置中...", - "加载详情中...": "加载详情中...", - "加载账单失败": "加载账单失败", - "加载隐私政策内容失败...": "加载隐私政策内容失败...", - "包含": "包含", - "包含来自未知或未标明供应商的AI模型,这些模型可能来自小型供应商或开源项目。": "包含来自未知或未标明供应商的AI模型,这些模型可能来自小型供应商或开源项目。", - "包括失败请求的次数,0代表不限制": "包括失败请求的次数,0代表不限制", - "匹配值": "匹配值", - "匹配值(可选)": "匹配值(可选)", - "匹配方式": "匹配方式", - "匹配类型": "匹配类型", - "区域": "区域", - "升级分组": "升级分组", - "单GPU小时费率": "单GPU小时费率", - "历史消耗": "历史消耗", - "原价": "原价", - "原因:": "原因:", - "原密码": "原密码", - "原生格式": "原生格式", - "原生额度": "原生额度", - "去重完成:去重前 {{before}} 个密钥,去重后 {{after}} 个密钥": "去重完成:去重前 {{before}} 个密钥,去重后 {{after}} 个密钥", - "参与官方同步": "参与官方同步", - "参数": "参数", - "参数值": "参数值", - "参数覆盖": "参数覆盖", - "参数覆盖 JSON 已复制": "参数覆盖 JSON 已复制", - "参数覆盖必须是合法的 JSON 对象": "参数覆盖必须是合法的 JSON 对象", - "参数覆盖必须是合法的 JSON 格式!": "参数覆盖必须是合法的 JSON 格式!", - "参数覆盖模板": "参数覆盖模板", - "参数覆盖模板 JSON 格式不正确": "参数覆盖模板 JSON 格式不正确", - "参数覆盖模板预览": "参数覆盖模板预览", - "参数配置": "参数配置", - "参数配置有误": "参数配置有误", - "参数错误": "参数错误", - "参照生视频": "参照生视频", - "友情链接": "友情链接", - "发布日期": "发布日期", - "发布时间": "发布时间", - "发现文档地址(Discovery URL,可选)": "发现文档地址(Discovery URL,可选)", - "发行者 URL(Issuer URL)": "发行者 URL(Issuer URL)", - "取消": "取消", - "取消全选": "取消全选", - "取消选择": "取消选择", - "变换": "变换", - "变焦": "变焦", - "变量值": "变量值", - "变量名": "变量名", - "只包括请求成功的次数": "只包括请求成功的次数", - "只支持HTTPS,系统将以POST方式发送通知,请确保地址可以接收POST请求": "只支持HTTPS,系统将以POST方式发送通知,请确保地址可以接收POST请求", - "只有当用户设置开启IP记录时,才会进行请求和错误类型日志的IP记录": "只有当用户设置开启IP记录时,才会进行请求和错误类型日志的IP记录", - "可信": "可信", - "可在设置页面设置关于内容,支持 HTML & Markdown": "可在设置页面设置关于内容,支持 HTML & Markdown", - "可手动填写,多个 scope 用空格分隔": "可手动填写,多个 scope 用空格分隔", - "可用": "可用", - "可用令牌分组": "可用令牌分组", - "可用分组": "可用分组", - "可用变量:{{provider}} {{field}} {{op}} {{required}} {{current}} 以及 {{current.path}}": "可用变量:{{provider}} {{field}} {{op}} {{required}} {{current}} 以及 {{current.path}}", - "可用数量": "可用数量", - "可用模型": "可用模型", - "可用空间: {{free}} / 总空间: {{total}}": "可用空间: {{free}} / 总空间: {{total}}", - "可用端点类型": "可用端点类型", - "可用邀请额度": "可用邀请额度", - "可留空;留空时会尝试使用 Issuer URL + /.well-known/openid-configuration": "可留空;留空时会尝试使用 Issuer URL + /.well-known/openid-configuration", - "可视化": "可视化", - "可视化倍率设置": "可视化倍率设置", - "可视化编辑": "可视化编辑", - "可选,公告的补充说明": "可选,公告的补充说明", - "可选,用于复现结果": "可选,用于复现结果", - "可选:基于用户信息 JSON 做组合条件准入,条件不满足时返回自定义提示": "可选:基于用户信息 JSON 做组合条件准入,条件不满足时返回自定义提示", - "可选:用于自动生成端点或 Discovery URL": "可选:用于自动生成端点或 Discovery URL", - "可选。匹配入口请求的 User-Agent;任意一行作为子串匹配(忽略大小写)即命中。": "可选。匹配入口请求的 User-Agent;任意一行作为子串匹配(忽略大小写)即命中。", - "可选。对提取到的亲和 Key 做正则校验;不填表示不校验。": "可选。对提取到的亲和 Key 做正则校验;不填表示不校验。", - "可选。对请求路径进行匹配;不填表示匹配所有路径。": "可选。对请求路径进行匹配;不填表示匹配所有路径。", - "可选值": "可选值", - "同时重置消息": "同时重置消息", - "同步": "同步", - "同步到渠道": "同步到渠道", - "同步向导": "同步向导", - "同步失败": "同步失败", - "同步成功": "同步成功", - "同步接口": "同步接口", - "同步渠道失败": "同步渠道失败", - "同步渠道失败:缺少部署信息": "同步渠道失败:缺少部署信息", - "同步端点": "同步端点", - "名称": "名称", - "名称+密钥": "名称+密钥", - "名称不能为空": "名称不能为空", - "名称匹配类型": "名称匹配类型", - "后端请求失败": "后端请求失败", - "后缀": "后缀", - "否": "否", - "启动": "启动", - "启动参数 (Args)": "启动参数 (Args)", - "启动命令": "启动命令", - "启动命令 (Entrypoint)": "启动命令 (Entrypoint)", - "启动授权失败": "启动授权失败", - "启动时间": "启动时间", - "启动部署失败": "启动部署失败", - "启动配置": "启动配置", - "启用": "启用", - "启用 io.net 部署": "启用 io.net 部署", - "启用 io.net 部署开关": "启用 io.net 部署开关", - "启用 Prompt 检查": "启用 Prompt 检查", - "启用2FA失败": "启用2FA失败", - "启用Claude思考适配(-thinking后缀)": "启用Claude思考适配(-thinking后缀)", - "启用FunctionCall思维签名填充": "启用FunctionCall思维签名填充", - "启用Gemini思考后缀适配": "启用Gemini思考后缀适配", - "启用Ping间隔": "启用Ping间隔", - "启用SMTP SSL": "启用SMTP SSL", - "启用SSRF防护(推荐开启以保护服务器安全)": "启用SSRF防护(推荐开启以保护服务器安全)", - "启用供应商": "启用供应商", - "启用全部": "启用全部", - "启用后可接入 io.net GPU 资源": "启用后可接入 io.net GPU 资源", - "启用后可添加图片URL进行多模态对话": "启用后可添加图片URL进行多模态对话", - "启用后套餐将在用户端展示。是否继续?": "启用后套餐将在用户端展示。是否继续?", - "启用后将优先复用上一次成功的渠道(粘滞选路)。": "启用后将优先复用上一次成功的渠道(粘滞选路)。", - "启用后将使用 Creem Test Mode": "启用后将使用 Creem Test Mode", - "启用密钥失败": "启用密钥失败", - "启用屏蔽词过滤功能": "启用屏蔽词过滤功能", - "启用性能监控": "启用性能监控", - "启用性能监控后,当系统资源使用率超过设定阈值时,将拒绝新的 Relay 请求 (/v1, /v1beta 等),以保护系统稳定性。": "启用性能监控后,当系统资源使用率超过设定阈值时,将拒绝新的 Relay 请求 (/v1, /v1beta 等),以保护系统稳定性。", - "启用所有密钥失败": "启用所有密钥失败", - "启用数据看板(实验性)": "启用数据看板(实验性)", - "启用此模式后,将使用您自定义的请求体发送API请求,模型配置面板的参数设置将被忽略。": "启用此模式后,将使用您自定义的请求体发送API请求,模型配置面板的参数设置将被忽略。", - "启用状态": "启用状态", - "启用用户模型请求速率限制(可能会影响高并发性能)": "启用用户模型请求速率限制(可能会影响高并发性能)", - "启用磁盘缓存": "启用磁盘缓存", - "启用磁盘缓存后,大请求体将临时存储到磁盘而非内存,可显著降低内存占用,适用于处理包含大量图片/文件的请求。建议在 SSD 环境下使用。": "启用磁盘缓存后,大请求体将临时存储到磁盘而非内存,可显著降低内存占用,适用于处理包含大量图片/文件的请求。建议在 SSD 环境下使用。", - "启用签到功能": "启用签到功能", - "启用绘图功能": "启用绘图功能", - "启用请求体透传功能": "启用请求体透传功能", - "启用请求透传": "启用请求透传", - "启用违规扣费": "启用违规扣费", - "启用额度消费日志记录": "启用额度消费日志记录", - "启用验证": "启用验证", - "周": "周", - "命中判定:usage 中存在 cached tokens(例如 cached_tokens/prompt_cache_hit_tokens)即视为命中。": "命中判定:usage 中存在 cached tokens(例如 cached_tokens/prompt_cache_hit_tokens)即视为命中。", - "命中率": "命中率", - "命中该亲和规则后,会把此模板合并到渠道参数覆盖中(同名键由模板覆盖)。": "命中该亲和规则后,会把此模板合并到渠道参数覆盖中(同名键由模板覆盖)。", - "和": "和", - "响应": "响应", - "响应时间": "响应时间", - "响应缺少凭据": "响应缺少凭据", - "响应缺少授权链接": "响应缺少授权链接", - "商品价格 ID": "商品价格 ID", - "回答内容": "回答内容", - "回调 URL 填": "回调 URL 填", - "回调 URL 格式": "回调 URL 格式", - "回调地址": "回调地址", - "固定价格": "固定价格", - "固定价格(每次)": "固定价格(每次)", - "固定价格值": "固定价格值", - "图像生成": "图像生成", - "图标": "图标", - "图标使用 react-icons(Simple Icons)或 URL/emoji,例如:github、gitlab、si:google": "图标使用 react-icons(Simple Icons)或 URL/emoji,例如:github、gitlab、si:google", - "图标使用@lobehub/icons库,如:OpenAI、Claude.Color,支持链式参数:OpenAI.Avatar.type={'platform'}、OpenRouter.Avatar.shape={'square'},查询所有可用图标请 ": "图标使用@lobehub/icons库,如:OpenAI、Claude.Color,支持链式参数:OpenAI.Avatar.type={'platform'}、OpenRouter.Avatar.shape={'square'},查询所有可用图标请 ", - "图混合": "图混合", - "图片功能在自定义请求体模式下不可用": "图片功能在自定义请求体模式下不可用", - "图片地址": "图片地址", - "图片已添加": "图片已添加", - "图片生成调用:{{symbol}}{{price}} / 1次": "图片生成调用:{{symbol}}{{price}} / 1次", - "图片输入: {{imageRatio}}": "图片输入: {{imageRatio}}", - "图片输入价格:{{symbol}}{{price}} * {{ratio}} = {{symbol}}{{total}} / 1M tokens (图片倍率: {{imageRatio}})": "图片输入价格:{{symbol}}{{price}} * {{ratio}} = {{symbol}}{{total}} / 1M tokens (图片倍率: {{imageRatio}})", - "图片输入倍率(仅部分模型支持该计费)": "图片输入倍率(仅部分模型支持该计费)", - "图片输入相关的倍率设置,键为模型名称,值为倍率,仅部分模型支持该计费": "图片输入相关的倍率设置,键为模型名称,值为倍率,仅部分模型支持该计费", - "图生文": "图生文", - "图生视频": "图生视频", - "在Gotify服务器创建应用后获得的令牌,用于发送通知": "在Gotify服务器创建应用后获得的令牌,用于发送通知", - "在Gotify服务器的应用管理中创建新应用": "在Gotify服务器的应用管理中创建新应用", - "在找兑换码?": "在找兑换码?", - "在新标签页中打开": "在新标签页中打开", - "在模型广场向用户展示的端点": "在模型广场向用户展示的端点", - "在此输入 Logo 图片地址": "在此输入 Logo 图片地址", - "在此输入新的公告内容,支持 Markdown & HTML 代码": "在此输入新的公告内容,支持 Markdown & HTML 代码", - "在此输入新的关于内容,支持 Markdown & HTML 代码。如果输入的是一个链接,则会使用该链接作为 iframe 的 src 属性,这允许你设置任意网页作为关于页面": "在此输入新的关于内容,支持 Markdown & HTML 代码。如果输入的是一个链接,则会使用该链接作为 iframe 的 src 属性,这允许你设置任意网页作为关于页面", - "在此输入新的页脚,留空则使用默认页脚,支持 HTML 代码": "在此输入新的页脚,留空则使用默认页脚,支持 HTML 代码", - "在此输入用户协议内容,支持 Markdown & HTML 代码": "在此输入用户协议内容,支持 Markdown & HTML 代码", - "在此输入系统名称": "在此输入系统名称", - "在此输入隐私政策内容,支持 Markdown & HTML 代码": "在此输入隐私政策内容,支持 Markdown & HTML 代码", - "在此输入首页内容,支持 Markdown & HTML 代码,设置后首页的状态信息将不再显示。如果输入的是一个链接,则会使用该链接作为 iframe 的 src 属性,这允许你设置任意网页作为首页": "在此输入首页内容,支持 Markdown & HTML 代码,设置后首页的状态信息将不再显示。如果输入的是一个链接,则会使用该链接作为 iframe 的 src 属性,这允许你设置任意网页作为首页", - "域名IP过滤详细说明": "域名IP过滤详细说明", - "域名白名单": "域名白名单", - "域名黑名单": "域名黑名单", - "基本信息": "基本信息", - "填充 Codex CLI / Claude CLI 模版": "填充 Codex CLI / Claude CLI 模版", - "填充新模板": "填充新模板", - "填充旧模板": "填充旧模板", - "填充模板": "填充模板", - "填充模板:等级+激活": "填充模板:等级+激活", - "填充模板:等级提示": "填充模板:等级提示", - "填充模板:组织或角色": "填充模板:组织或角色", - "填充模板:组织提示": "填充模板:组织提示", - "填充模板(全渠道)": "填充模板(全渠道)", - "填充模板(指定渠道)": "填充模板(指定渠道)", - "填入": "填入", - "填入 CC Switch": "填入 CC Switch", - "填入所有模型": "填入所有模型", - "填入来源": "填入来源", - "填入模板": "填入模板", - "填入目标": "填入目标", - "填入相关模型": "填入相关模型", - "填入路径": "填入路径", - "填入透传模版": "填入透传模版", - "填写 Issuer URL 后自动生成:": "填写 Issuer URL 后自动生成:", - "填写Gotify服务器的完整URL地址": "填写Gotify服务器的完整URL地址", - "填写后会自动拼接预设端点": "填写后会自动拼接预设端点", - "填写带https的域名,逗号分隔": "填写带https的域名,逗号分隔", - "填写用户协议内容后,用户注册时将被要求勾选已阅读用户协议": "填写用户协议内容后,用户注册时将被要求勾选已阅读用户协议", - "填写隐私政策内容后,用户注册时将被要求勾选已阅读隐私政策": "填写隐私政策内容后,用户注册时将被要求勾选已阅读隐私政策", - "处理中": "处理中", - "备份支持": "备份支持", - "备份状态": "备份状态", - "备注": "备注", - "备用恢复代码": "备用恢复代码", - "备用码已复制到剪贴板": "备用码已复制到剪贴板", - "备用码重新生成成功": "备用码重新生成成功", - "复制": "复制", - "复制代码": "复制代码", - "复制令牌": "复制令牌", - "复制全部": "复制全部", - "复制名称": "复制名称", - "复制失败": "复制失败", - "复制失败,请手动复制": "复制失败,请手动复制", - "复制失败,请手动选择文本复制": "复制失败,请手动选择文本复制", - "复制已选": "复制已选", - "复制应用的令牌(Token)并填写到上方的应用令牌字段": "复制应用的令牌(Token)并填写到上方的应用令牌字段", - "复制成功": "复制成功", - "复制所有代码": "复制所有代码", - "复制所有模型": "复制所有模型", - "复制所选令牌": "复制所选令牌", - "复制所选兑换码到剪贴板": "复制所选兑换码到剪贴板", - "复制授权链接": "复制授权链接", - "复制日志": "复制日志", - "复制渠道的所有信息": "复制渠道的所有信息", - "复制版本号": "复制版本号", - "复制生成的密钥并粘贴到此处": "复制生成的密钥并粘贴到此处", - "复制链接": "复制链接", - "外接设备": "外接设备", - "多个命令用空格分隔": "多个命令用空格分隔", - "多密钥渠道操作项目组": "多密钥渠道操作项目组", - "多密钥管理": "多密钥管理", - "多种充值方式,安全便捷": "多种充值方式,安全便捷", - "大模型接口网关": "大模型接口网关", - "天": "天", - "天前": "天前", - "失败": "失败", - "失败原因": "失败原因", - "失败后不重试": "失败后不重试", - "失败时自动禁用通道": "失败时自动禁用通道", - "失败重试次数": "失败重试次数", - "奖励说明": "奖励说明", - "套餐": "套餐", - "套餐副标题": "套餐副标题", - "套餐名称": "套餐名称", - "套餐标题": "套餐标题", - "套餐标题不能为空": "套餐标题不能为空", - "套餐的基本信息和定价": "套餐的基本信息和定价", - "如:大带宽批量分析图片推荐": "如:大带宽批量分析图片推荐", - "如:香港线路": "如:香港线路", - "如果亲和到的渠道失败,重试到其他渠道成功后,将亲和更新到成功的渠道。": "如果亲和到的渠道失败,重试到其他渠道成功后,将亲和更新到成功的渠道。", - "如果你对接的是上游One API或者New API等转发项目,请使用OpenAI类型,不要使用此类型,除非你知道你在做什么。": "如果你对接的是上游One API或者New API等转发项目,请使用OpenAI类型,不要使用此类型,除非你知道你在做什么。", - "如果用户请求中包含系统提示词,则使用此设置拼接到用户的系统提示词前面": "如果用户请求中包含系统提示词,则使用此设置拼接到用户的系统提示词前面", - "如果镜像为私有,请填写密码或Token": "如果镜像为私有,请填写密码或Token", - "如果镜像为私有,请填写用户名": "如果镜像为私有,请填写用户名", - "始终使用浅色主题": "始终使用浅色主题", - "始终使用深色主题": "始终使用深色主题", - "字段映射": "字段映射", - "字段缺失视为命中": "字段缺失视为命中", - "字段路径": "字段路径", - "字段透传控制": "字段透传控制", - "字段速查": "字段速查", - "存在惩罚,鼓励讨论新话题": "存在惩罚,鼓励讨论新话题", - "存在重复的键名:": "存在重复的键名:", - "安全提醒": "安全提醒", - "安全设置": "安全设置", - "安全验证": "安全验证", - "安全验证级别": "安全验证级别", - "安装指南": "安装指南", - "完成": "完成", - "完成初始化": "完成初始化", - "完成硬件类型、部署位置、副本数量等配置后,将自动计算价格": "完成硬件类型、部署位置、副本数量等配置后,将自动计算价格", - "完成设置并启用两步验证": "完成设置并启用两步验证", - "完成进度": "完成进度", - "完整的 Base URL,支持变量{model}": "完整的 Base URL,支持变量{model}", - "官方": "官方", - "官方文档": "官方文档", - "官方模型同步": "官方模型同步", - "官方说明": "官方说明", - "定价模式": "定价模式", - "定时测试所有通道": "定时测试所有通道", - "定期更改密码可以提高账户安全性": "定期更改密码可以提高账户安全性", - "实付": "实付", - "实付金额": "实付金额", - "实付金额:": "实付金额:", - "实际模型": "实际模型", - "实际请求体": "实际请求体", - "容器": "容器", - "容器ID": "容器ID", - "容器创建失败: ": "容器创建失败: ", - "容器创建成功": "容器创建成功", - "容器名称": "容器名称", - "容器启动后执行的命令": "容器启动后执行的命令", - "容器启动配置": "容器启动配置", - "容器实例": "容器实例", - "容器对外暴露的端口": "容器对外暴露的端口", - "容器对外服务的端口号,可选": "容器对外服务的端口号,可选", - "容器总数": "容器总数", - "容器数量": "容器数量", - "容器日志": "容器日志", - "容器时长延长成功": "容器时长延长成功", - "容器访问地址无效": "容器访问地址无效", - "容器详情": "容器详情", - "容器配置": "容器配置", - "容器配置更新成功": "容器配置更新成功", - "密码": "密码", - "密码修改成功!": "密码修改成功!", - "密码已复制到剪贴板:": "密码已复制到剪贴板:", - "密码已重置并已复制到剪贴板:": "密码已重置并已复制到剪贴板:", - "密码管理": "密码管理", - "密码重置": "密码重置", - "密码重置完成": "密码重置完成", - "密码重置确认": "密码重置确认", - "密码长度至少为8个字符": "密码长度至少为8个字符", - "密钥": "密钥", - "密钥 JSON 必须包含 access_token": "密钥 JSON 必须包含 access_token", - "密钥 JSON 必须包含 account_id": "密钥 JSON 必须包含 account_id", - "密钥(编辑模式下,保存的密钥不会显示)": "密钥(编辑模式下,保存的密钥不会显示)", - "密钥去重": "密钥去重", - "密钥将以Bearer方式添加到请求头中,用于验证webhook请求的合法性": "密钥将以Bearer方式添加到请求头中,用于验证webhook请求的合法性", - "密钥已删除": "密钥已删除", - "密钥已启用": "密钥已启用", - "密钥已复制到剪贴板": "密钥已复制到剪贴板", - "密钥已禁用": "密钥已禁用", - "密钥必须是 JSON 对象": "密钥必须是 JSON 对象", - "密钥必须是合法的 JSON 格式!": "密钥必须是合法的 JSON 格式!", - "密钥文件 (.json)": "密钥文件 (.json)", - "密钥更新模式": "密钥更新模式", - "密钥格式": "密钥格式", - "密钥格式无效,请输入有效的 JSON 格式密钥": "密钥格式无效,请输入有效的 JSON 格式密钥", - "密钥环境变量": "密钥环境变量", - "密钥聚合模式": "密钥聚合模式", - "密钥获取成功": "密钥获取成功", - "密钥输入方式": "密钥输入方式", - "密钥预览": "密钥预览", - "对于官方渠道,new-api已经内置地址,除非是第三方代理站点或者Azure的特殊接入地址,否则不需要填写": "对于官方渠道,new-api已经内置地址,除非是第三方代理站点或者Azure的特殊接入地址,否则不需要填写", - "对免费模型启用预消耗": "对免费模型启用预消耗", - "对域名启用 IP 过滤(实验性)": "对域名启用 IP 过滤(实验性)", - "对外运营模式": "对外运营模式", - "对象清理规则": "对象清理规则", - "导入": "导入", - "导入的配置将覆盖当前设置,是否继续?": "导入的配置将覆盖当前设置,是否继续?", - "导入配置": "导入配置", - "导入配置失败: ": "导入配置失败: ", - "导出": "导出", - "导出配置": "导出配置", - "导出配置失败: ": "导出配置失败: ", - "将 reasoning_content 转换为 标签拼接到内容中": "将 reasoning_content 转换为 标签拼接到内容中", - "将为选中的 ": "将为选中的 ", - "将仅保留第一个密钥文件,其余文件将被移除,是否继续?": "将仅保留第一个密钥文件,其余文件将被移除,是否继续?", - "将删除": "将删除", - "将删除已使用、已禁用及过期的兑换码,此操作不可撤销。": "将删除已使用、已禁用及过期的兑换码,此操作不可撤销。", - "将删除所有仍在内存中的渠道亲和性缓存条目。": "将删除所有仍在内存中的渠道亲和性缓存条目。", - "将大请求体临时存储到磁盘": "将大请求体临时存储到磁盘", - "将清除所有保存的配置并恢复默认设置,此操作不可撤销。是否继续?": "将清除所有保存的配置并恢复默认设置,此操作不可撤销。是否继续?", - "将清除选定时间之前的所有日志": "将清除选定时间之前的所有日志", - "将追加 2 条规则到现有规则列表。": "将追加 2 条规则到现有规则列表。", - "小时": "小时", - "小时费率": "小时费率", - "尚未使用": "尚未使用", - "局部重绘-提交": "局部重绘-提交", - "屏蔽词列表": "屏蔽词列表", - "屏蔽词过滤设置": "屏蔽词过滤设置", - "展开": "展开", - "展开更多": "展开更多", - "展示价格": "展示价格", - "已为 {{count}} 个模型设置{{type}}_other": "已为 {{count}} 个模型设置{{type}}", - "已为 ${count} 个渠道设置标签!": "已为 ${count} 个渠道设置标签!", - "已从 Discovery 自动填充配置": "已从 Discovery 自动填充配置", - "已从 Discovery 获取配置,可继续手动修改所有字段。": "已从 Discovery 获取配置,可继续手动修改所有字段。", - "已作废": "已作废", - "已保存偏好为": "已保存偏好为", - "已修复 ${success} 个通道,失败 ${fails} 个通道。": "已修复 ${success} 个通道,失败 ${fails} 个通道。", - "已停止": "已停止", - "已停止批量测试": "已停止批量测试", - "已关闭后续提醒": "已关闭后续提醒", - "已分配内存": "已分配内存", - "已切换为Assistant角色": "已切换为Assistant角色", - "已切换为System角色": "已切换为System角色", - "已切换至最优倍率视图,每个模型使用其最低倍率分组": "已切换至最优倍率视图,每个模型使用其最低倍率分组", - "已初始化": "已初始化", - "已删除": "已删除", - "已删除 {{count}} 个令牌!_other": "已删除 {{count}} 个令牌!", - "已删除 {{count}} 条失效兑换码_other": "已删除 {{count}} 条失效兑换码", - "已删除 ${data} 个通道!": "已删除 ${data} 个通道!", - "已删除所有禁用渠道,共计 ${data} 个": "已删除所有禁用渠道,共计 ${data} 个", - "已删除消息及其回复": "已删除消息及其回复", - "已发起支付": "已发起支付", - "已发送到 Fluent": "已发送到 Fluent", - "已取消 Passkey 注册": "已取消 Passkey 注册", - "已同步到渠道": "已同步到渠道", - "已启用": "已启用", - "已启用 Passkey,无需密码即可登录": "已启用 Passkey,无需密码即可登录", - "已启用所有密钥": "已启用所有密钥", - "已在自定义模式中忽略": "已在自定义模式中忽略", - "已填充提示模板": "已填充提示模板", - "已填充模版": "已填充模版", - "已填充策略模板": "已填充策略模板", - "已备份": "已备份", - "已复制": "已复制", - "已复制 ${count} 个模型": "已复制 ${count} 个模型", - "已复制 ID 到剪贴板": "已复制 ID 到剪贴板", - "已复制:": "已复制:", - "已复制:{{name}}": "已复制:{{name}}", - "已复制全部数据": "已复制全部数据", - "已复制到剪切板": "已复制到剪切板", - "已复制到剪贴板": "已复制到剪贴板", - "已复制到剪贴板!": "已复制到剪贴板!", - "已复制字段:{{name}}": "已复制字段:{{name}}", - "已复制模型名称": "已复制模型名称", - "已复制版本号": "已复制版本号", - "已复制自动生成的 API Key": "已复制自动生成的 API Key", - "已完成": "已完成", - "已开启全局请求透传:参数覆写、模型重定向、渠道适配等 NewAPI 内置功能将失效,非最佳实践;如因此产生问题,请勿提交 issue 反馈。": "已开启全局请求透传:参数覆写、模型重定向、渠道适配等 NewAPI 内置功能将失效,非最佳实践;如因此产生问题,请勿提交 issue 反馈。", - "已成功开始测试所有已启用通道,请刷新页面查看结果。": "已成功开始测试所有已启用通道,请刷新页面查看结果。", - "已打开授权页面": "已打开授权页面", - "已打开支付页面": "已打开支付页面", - "已提交": "已提交", - "已支付金额": "已支付金额", - "已新增 {{count}} 个模型:{{list}}_other": "已新增 {{count}} 个模型:{{list}}", - "已更新完毕所有已启用通道余额!": "已更新完毕所有已启用通道余额!", - "已有保存的配置": "已有保存的配置", - "已有模型": "已有模型", - "已有的模型": "已有的模型", - "已有账户?": "已有账户?", - "已服务": "已服务", - "已注销": "已注销", - "已添加": "已添加", - "已添加 {{count}} 个模板_other": "已添加 {{count}} 个模板", - "已添加到白名单": "已添加到白名单", - "已清空": "已清空", - "已清空测试结果": "已清空测试结果", - "已生成授权凭据": "已生成授权凭据", - "已用": "已用", - "已用/剩余": "已用/剩余", - "已用额度": "已用额度", - "已禁用": "已禁用", - "已禁用所有密钥": "已禁用所有密钥", - "已绑定": "已绑定", - "已绑定渠道": "已绑定渠道", - "已结束": "已结束", - "已耗尽": "已耗尽", - "已解锁豆包自定义 API 地址编辑": "已解锁豆包自定义 API 地址编辑", - "已设置": "已设置", - "已达上限": "已达上限", - "已达到购买上限": "已达到购买上限", - "已过期": "已过期", - "已运行时间": "已运行时间", - "已选择 {{count}} 个模型_other": "已选择 {{count}} 个模型", - "已选择 {{selected}} / {{total}}": "已选择 {{selected}} / {{total}}", - "已选择 ${count} 个渠道": "已选择 ${count} 个渠道", - "已重置为默认配置": "已重置为默认配置", - "已销毁": "已销毁", - "币种": "币种", - "常用上下文 Key(用于 context_*)": "常用上下文 Key(用于 context_*)", - "常见问答": "常见问答", - "常见问答管理,为用户提供常见问题的答案(最多50个,前端显示最新20条)": "常见问答管理,为用户提供常见问题的答案(最多50个,前端显示最新20条)", - "平台": "平台", - "平均RPM": "平均RPM", - "平均TPM": "平均TPM", - "平移": "平移", - "年": "年", - "应付金额": "应付金额", - "应用": "应用", - "应用同步": "应用同步", - "应用更改": "应用更改", - "应用覆盖": "应用覆盖", - "延长后总时长": "延长后总时长", - "延长容器时长": "延长容器时长", - "延长容器时长将会产生额外费用,请确认您有足够的账户余额。": "延长容器时长将会产生额外费用,请确认您有足够的账户余额。", - "延长操作一旦确认无法撤销,费用将立即扣除。": "延长操作一旦确认无法撤销,费用将立即扣除。", - "延长时长": "延长时长", - "延长时长(小时)": "延长时长(小时)", - "延长时长不能超过720小时(30天)": "延长时长不能超过720小时(30天)", - "延长时长失败": "延长时长失败", - "延长时长至少为1小时": "延长时长至少为1小时", - "建立连接时发生错误": "建立连接时发生错误", - "建议在生产环境中使用 MySQL 或 PostgreSQL 数据库,或确保 SQLite 数据库文件已映射到宿主机的持久化存储。": "建议在生产环境中使用 MySQL 或 PostgreSQL 数据库,或确保 SQLite 数据库文件已映射到宿主机的持久化存储。", - "开": "开", - "开启之后会清除用户提示词中的": "开启之后会清除用户提示词中的", - "开启之后将上游地址替换为服务器地址": "开启之后将上游地址替换为服务器地址", - "开启后,using_group 会参与 cache key(不同分组隔离)。": "开启后,using_group 会参与 cache key(不同分组隔离)。", - "开启后,仅\"消费\"和\"错误\"日志将记录您的客户端IP地址": "开启后,仅\"消费\"和\"错误\"日志将记录您的客户端IP地址", - "开启后,对免费模型(倍率为0,或者价格为0)的模型也会预消耗额度": "开启后,对免费模型(倍率为0,或者价格为0)的模型也会预消耗额度", - "开启后,将定期发送ping数据保持连接活跃": "开启后,将定期发送ping数据保持连接活跃", - "开启后,当前分组渠道失败时会按顺序尝试下一个分组的渠道": "开启后,当前分组渠道失败时会按顺序尝试下一个分组的渠道", - "开启后,所有请求将直接透传给上游,不会进行任何处理(重定向和渠道适配也将失效),请谨慎开启": "开启后,所有请求将直接透传给上游,不会进行任何处理(重定向和渠道适配也将失效),请谨慎开启", - "开启后,若该规则命中且请求失败,将不会切换渠道重试。": "开启后,若该规则命中且请求失败,将不会切换渠道重试。", - "开启后,规则名称会参与 cache key(不同规则隔离)。": "开启后,规则名称会参与 cache key(不同规则隔离)。", - "开启后,该渠道请求 Claude 时将强制追加 ?beta=true(无需客户端手动传参)": "开启后,该渠道请求 Claude 时将强制追加 ?beta=true(无需客户端手动传参)", - "开启后,违规请求将额外扣费。": "开启后,违规请求将额外扣费。", - "开启后不限制:必须设置模型倍率": "开启后不限制:必须设置模型倍率", - "开启后未登录用户无法访问模型广场": "开启后未登录用户无法访问模型广场", - "开启批量操作": "开启批量操作", - "开始": "开始", - "开始同步": "开始同步", - "开始批量测试 ${count} 个模型,已清空上次结果...": "开始批量测试 ${count} 个模型,已清空上次结果...", - "开始时间": "开始时间", - "异步任务退款": "异步任务退款", - "张图片": "张图片", - "弱变换": "弱变换", - "强制将响应格式化为 OpenAI 标准格式(只适用于OpenAI渠道类型)": "强制将响应格式化为 OpenAI 标准格式(只适用于OpenAI渠道类型)", - "强制格式化": "强制格式化", - "强制要求": "强制要求", - "强变换": "强变换", - "当上游通道返回错误中包含这些关键词时(不区分大小写),自动禁用通道": "当上游通道返回错误中包含这些关键词时(不区分大小写),自动禁用通道", - "当前 API 密钥已过期,请在设置中更新。": "当前 API 密钥已过期,请在设置中更新。", - "当前 Ollama 版本为 ${version}": "当前 Ollama 版本为 ${version}", - "当前仅 OpenAI / Claude 语义支持缓存 token 统计,其他通道将隐藏 token 相关字段。": "当前仅 OpenAI / Claude 语义支持缓存 token 统计,其他通道将隐藏 token 相关字段。", - "当前余额": "当前余额", - "当前值": "当前值", - "当前值不是合法 JSON,无法格式化": "当前值不是合法 JSON,无法格式化", - "当前分组为 auto,会自动选择最优分组,当一个组不可用时自动降级到下一个组(熔断机制)": "当前分组为 auto,会自动选择最优分组,当一个组不可用时自动降级到下一个组(熔断机制)", - "当前剩余": "当前剩余", - "当前参数覆盖不是合法的 JSON": "当前参数覆盖不是合法的 JSON", - "当前旧格式 JSON 不合法,无法追加模板": "当前旧格式 JSON 不合法,无法追加模板", - "当前旧格式不是 JSON 对象,无法追加模板": "当前旧格式不是 JSON 对象,无法追加模板", - "当前时间": "当前时间", - "当前未开启Midjourney回调,部分项目可能无法获得绘图结果,可在运营设置中开启。": "当前未开启Midjourney回调,部分项目可能无法获得绘图结果,可在运营设置中开启。", - "当前查看的分组为:{{group}},倍率为:{{ratio}}": "当前查看的分组为:{{group}},倍率为:{{ratio}}", - "当前模型列表为该标签下所有渠道模型列表最长的一个,并非所有渠道的并集,请注意可能导致某些渠道模型丢失。": "当前模型列表为该标签下所有渠道模型列表最长的一个,并非所有渠道的并集,请注意可能导致某些渠道模型丢失。", - "当前版本": "当前版本", - "当前状态": "当前状态", - "当前缓存大小": "当前缓存大小", - "当前规则不支持写入到该位置": "当前规则不支持写入到该位置", - "当前规则未设置参数覆盖模板": "当前规则未设置参数覆盖模板", - "当前计费": "当前计费", - "当前设备不支持 Passkey": "当前设备不支持 Passkey", - "当前设置类型: ": "当前设置类型: ", - "当前跟随系统": "当前跟随系统", - "当前配置无法连接到 io.net。": "当前配置无法连接到 io.net。", - "当模型没有设置价格时仍接受调用,仅当您信任该网站时使用,可能会产生高额费用": "当模型没有设置价格时仍接受调用,仅当您信任该网站时使用,可能会产生高额费用", - "当运行通道全部测试时,超过此时间将自动禁用通道": "当运行通道全部测试时,超过此时间将自动禁用通道", - "当钱包或订阅剩余额度低于此数值时,系统将通过选择的方式发送通知": "当钱包或订阅剩余额度低于此数值时,系统将通过选择的方式发送通知", - "待使用收益": "待使用收益", - "待部署": "待部署", - "微信": "微信", - "微信公众号二维码图片链接": "微信公众号二维码图片链接", - "微信扫码关注公众号,输入「验证码」获取验证码(三分钟内有效)": "微信扫码关注公众号,输入「验证码」获取验证码(三分钟内有效)", - "微信扫码登录": "微信扫码登录", - "微信账户绑定成功!": "微信账户绑定成功!", - "必填。对请求的 model 名称进行匹配,任意一条匹配即命中该规则。": "必填。对请求的 model 名称进行匹配,任意一条匹配即命中该规则。", - "必须全部满足(AND)": "必须全部满足(AND)", - "必须是有效的 JSON 字符串数组,例如:[\"g1\",\"g2\"]": "必须是有效的 JSON 字符串数组,例如:[\"g1\",\"g2\"]", - "忘记密码?": "忘记密码?", - "快速开始": "快速开始", - "快速选择": "快速选择", - "思考中...": "思考中...", - "思考内容转换": "思考内容转换", - "思考过程": "思考过程", - "思考适配 BudgetTokens 百分比": "思考适配 BudgetTokens 百分比", - "思考预算占比": "思考预算占比", - "性能指标": "性能指标", - "性能监控": "性能监控", - "性能设置": "性能设置", - "总 GPU 小时": "总 GPU 小时", - "总价:文字价格 {{textPrice}} + 音频价格 {{audioPrice}} = {{symbol}}{{total}}": "总价:文字价格 {{textPrice}} + 音频价格 {{audioPrice}} = {{symbol}}{{total}}", - "总分配内存": "总分配内存", - "总密钥数": "总密钥数", - "总收益": "总收益", - "总计": "总计", - "总额度": "总额度", - "您可以个性化设置侧边栏的要显示功能": "您可以个性化设置侧边栏的要显示功能", - "您可以在上方拉取需要的模型": "您可以在上方拉取需要的模型", - "您无权访问此页面,请联系管理员": "您无权访问此页面,请联系管理员", - "您正在使用 MySQL 数据库。MySQL 是一个可靠的关系型数据库管理系统,适合生产环境使用。": "您正在使用 MySQL 数据库。MySQL 是一个可靠的关系型数据库管理系统,适合生产环境使用。", - "您正在使用 PostgreSQL 数据库。PostgreSQL 是一个功能强大的开源关系型数据库系统,提供了出色的可靠性和数据完整性,适合生产环境使用。": "您正在使用 PostgreSQL 数据库。PostgreSQL 是一个功能强大的开源关系型数据库系统,提供了出色的可靠性和数据完整性,适合生产环境使用。", - "您正在使用 SQLite 数据库。如果您在容器环境中运行,请确保已正确设置数据库文件的持久化映射,否则容器重启后所有数据将丢失!": "您正在使用 SQLite 数据库。如果您在容器环境中运行,请确保已正确设置数据库文件的持久化映射,否则容器重启后所有数据将丢失!", - "您正在删除自己的帐户,将清空所有数据且不可恢复": "您正在删除自己的帐户,将清空所有数据且不可恢复", - "您的数据将安全地存储在本地计算机上。所有配置、用户信息和使用记录都会自动保存,关闭应用后不会丢失。": "您的数据将安全地存储在本地计算机上。所有配置、用户信息和使用记录都会自动保存,关闭应用后不会丢失。", - "您确定要取消密码登录功能吗?这可能会影响用户的登录方式。": "您确定要取消密码登录功能吗?这可能会影响用户的登录方式。", - "您需要先启用两步验证或 Passkey 才能执行此操作": "您需要先启用两步验证或 Passkey 才能执行此操作", - "您需要先启用两步验证或 Passkey 才能查看敏感信息。": "您需要先启用两步验证或 Passkey 才能查看敏感信息。", - "想起来了?": "想起来了?", - "成功": "成功", - "成功兑换额度:": "成功兑换额度:", - "成功后切换亲和": "成功后切换亲和", - "成功时自动启用通道": "成功时自动启用通道", - "我已了解禁用两步验证将永久删除所有相关设置和备用码,此操作不可撤销": "我已了解禁用两步验证将永久删除所有相关设置和备用码,此操作不可撤销", - "我已阅读并同意": "我已阅读并同意", - "我的订阅": "我的订阅", - "或": "或", - "或其兼容new-api-worker格式的其他版本": "或其兼容new-api-worker格式的其他版本", - "或手动输入密钥:": "或手动输入密钥:", - "所有上游数据均可信": "所有上游数据均可信", - "所有密钥已复制到剪贴板": "所有密钥已复制到剪贴板", - "所有编辑均为覆盖操作,留空则不更改": "所有编辑均为覆盖操作,留空则不更改", - "所选模板已存在": "所选模板已存在", - "手动禁用": "手动禁用", - "手动编辑": "手动编辑", - "手动输入": "手动输入", - "打开 CC Switch": "打开 CC Switch", - "打开侧边栏": "打开侧边栏", - "打开授权页面": "打开授权页面", - "扣费": "扣费", - "执行 GC": "执行 GC", - "执行中": "执行中", - "扫描二维码": "扫描二维码", - "批量创建": "批量创建", - "批量创建时会在名称后自动添加随机后缀": "批量创建时会在名称后自动添加随机后缀", - "批量创建模式下仅支持文件上传,不支持手动输入": "批量创建模式下仅支持文件上传,不支持手动输入", - "批量删除": "批量删除", - "批量删除令牌": "批量删除令牌", - "批量删除失败": "批量删除失败", - "批量删除成功": "批量删除成功", - "批量删除模型": "批量删除模型", - "批量操作": "批量操作", - "批量测试${count}个模型": "批量测试${count}个模型", - "批量测试完成!成功: ${success}, 失败: ${fail}, 总计: ${total}": "批量测试完成!成功: ${success}, 失败: ${fail}, 总计: ${total}", - "批量测试已停止": "批量测试已停止", - "批量测试过程中发生错误: ": "批量测试过程中发生错误: ", - "批量设置": "批量设置", - "批量设置成功": "批量设置成功", - "批量设置标签": "批量设置标签", - "批量设置模型参数": "批量设置模型参数", - "折": "折", - "拉取中...": "拉取中...", - "拉取新模型": "拉取新模型", - "拉取模型": "拉取模型", - "拉取进度": "拉取进度", - "拒绝提示模板(可选)": "拒绝提示模板(可选)", - "拦截原因": "拦截原因", - "按K显示单位": "按K显示单位", - "按价格设置": "按价格设置", - "按倍率类型筛选": "按倍率类型筛选", - "按倍率设置": "按倍率设置", - "按次计费": "按次计费", - "按照如下格式输入:AccessKey|SecretAccessKey|Region": "按照如下格式输入:AccessKey|SecretAccessKey|Region", - "按量计费": "按量计费", - "按顺序替换content中的变量占位符": "按顺序替换content中的变量占位符", - "换脸": "换脸", - "授权,需在遵守": "授权,需在遵守", - "授权失败": "授权失败", - "排序": "排序", - "排队中": "排队中", - "接受未设置价格模型": "接受未设置价格模型", - "接口凭证": "接口凭证", - "接口密钥已过期": "接口密钥已过期", - "控制台": "控制台", - "控制台区域": "控制台区域", - "控制输出的随机性和创造性": "控制输出的随机性和创造性", - "控制顶栏模块显示状态,全局生效": "控制顶栏模块显示状态,全局生效", - "推荐": "推荐", - "推荐:用户可以选择是否使用指纹等验证": "推荐:用户可以选择是否使用指纹等验证", - "推荐使用(用户可选)": "推荐使用(用户可选)", - "描述": "描述", - "提交": "提交", - "提交时间": "提交时间", - "提交结果": "提交结果", - "提升": "提升", - "提示": "提示", - "提示 {{input}} tokens / 1M tokens * {{symbol}}{{price}}": "提示 {{input}} tokens / 1M tokens * {{symbol}}{{price}}", - "提示:如需备份数据,只需复制上述目录即可": "提示:如需备份数据,只需复制上述目录即可", - "提示:此处配置仅用于控制「模型广场」对用户的展示效果,不会影响模型的实际调用与路由。若需配置真实调用行为,请前往「渠道管理」进行设置。": "提示:此处配置仅用于控制「模型广场」对用户的展示效果,不会影响模型的实际调用与路由。若需配置真实调用行为,请前往「渠道管理」进行设置。", - "提示:该功能为测试版,未来配置结构与功能行为可能发生变更,请勿在生产环境使用。": "提示:该功能为测试版,未来配置结构与功能行为可能发生变更,请勿在生产环境使用。", - "提示:语言偏好会同步到您登录的所有设备,并影响API返回的错误消息语言。": "提示:语言偏好会同步到您登录的所有设备,并影响API返回的错误消息语言。", - "提示:链接中的{key}将被替换为API密钥,{address}将被替换为服务器地址": "提示:链接中的{key}将被替换为API密钥,{address}将被替换为服务器地址", - "提示价格:{{symbol}}{{price}} / 1M tokens": "提示价格:{{symbol}}{{price}} / 1M tokens", - "提示缓存倍率": "提示缓存倍率", - "搜索供应商": "搜索供应商", - "搜索关键字": "搜索关键字", - "搜索失败": "搜索失败", - "搜索字段名 / 中文说明": "搜索字段名 / 中文说明", - "搜索无结果": "搜索无结果", - "搜索日志内容": "搜索日志内容", - "搜索条件": "搜索条件", - "搜索模型": "搜索模型", - "搜索模型...": "搜索模型...", - "搜索模型名称": "搜索模型名称", - "搜索模型失败": "搜索模型失败", - "搜索渠道名称或地址": "搜索渠道名称或地址", - "搜索聊天应用名称": "搜索聊天应用名称", - "搜索规则(类型 / 路径 / 来源 / 目标)": "搜索规则(类型 / 路径 / 来源 / 目标)", - "搜索部署名称": "搜索部署名称", - "操作": "操作", - "操作失败": "操作失败", - "操作失败,请重试": "操作失败,请重试", - "操作成功完成!": "操作成功完成!", - "操作暂时被禁用": "操作暂时被禁用", - "操作类型": "操作类型", - "操练场": "操练场", - "操练场和聊天功能": "操练场和聊天功能", - "支付": "支付", - "支付地址": "支付地址", - "支付失败": "支付失败", - "支付宝": "支付宝", - "支付方式": "支付方式", - "支付渠道": "支付渠道", - "支付设置": "支付设置", - "支付请求失败": "支付请求失败", - "支付金额": "支付金额", - "支持 Ctrl+V 粘贴图片": "支持 Ctrl+V 粘贴图片", - "支持CIDR格式,如:8.8.8.8, 192.168.1.0/24": "支持CIDR格式,如:8.8.8.8, 192.168.1.0/24", - "支持HTTP和HTTPS,填写Gotify服务器的完整URL地址": "支持HTTP和HTTPS,填写Gotify服务器的完整URL地址", - "支持HTTP和HTTPS,模板变量: {{title}} (通知标题), {{content}} (通知内容)": "支持HTTP和HTTPS,模板变量: {{title}} (通知标题), {{content}} (通知内容)", - "支持众多的大模型供应商": "支持众多的大模型供应商", - "支持单个端口和端口范围,如:80, 443, 8000-8999": "支持单个端口和端口范围,如:80, 443, 8000-8999", - "支持变量:": "支持变量:", - "支持周期性重置套餐权益额度": "支持周期性重置套餐权益额度", - "支持填写单个状态码或范围(含首尾),使用逗号分隔": "支持填写单个状态码或范围(含首尾),使用逗号分隔", - "支持填写单个状态码或范围(含首尾),使用逗号分隔;504 和 524 始终不重试,不受此处配置影响": "支持填写单个状态码或范围(含首尾),使用逗号分隔;504 和 524 始终不重试,不受此处配置影响", - "支持备份": "支持备份", - "支持拉取 Ollama 官方模型库中的所有模型,拉取过程可能需要几分钟时间": "支持拉取 Ollama 官方模型库中的所有模型,拉取过程可能需要几分钟时间", - "支持搜索用户的 ID、用户名、显示名称和邮箱地址": "支持搜索用户的 ID、用户名、显示名称和邮箱地址", - "支持的图像模型": "支持的图像模型", - "支持通配符格式,如:example.com, *.api.example.com": "支持通配符格式,如:example.com, *.api.example.com", - "支持逻辑 and/or 与嵌套 groups;操作符支持 eq/ne/gt/gte/lt/lte/in/not_in/contains/exists": "支持逻辑 and/or 与嵌套 groups;操作符支持 eq/ne/gt/gte/lt/lte/in/not_in/contains/exists", - "收益": "收益", - "收益统计": "收益统计", - "收起": "收起", - "收起侧边栏": "收起侧边栏", - "收起内容": "收起内容", - "放大": "放大", - "放大编辑": "放大编辑", - "敏感信息不会发送到前端显示": "敏感信息不会发送到前端显示", - "数据传输中断": "数据传输中断", - "数据存储位置:": "数据存储位置:", - "数据库信息": "数据库信息", - "数据库检查": "数据库检查", - "数据库类型": "数据库类型", - "数据库警告": "数据库警告", - "数据格式错误": "数据格式错误", - "数据看板": "数据看板", - "数据看板更新间隔": "数据看板更新间隔", - "数据看板设置": "数据看板设置", - "数据看板默认时间粒度": "数据看板默认时间粒度", - "数据管理和日志查看": "数据管理和日志查看", - "文件上传": "文件上传", - "文件搜索价格:{{symbol}}{{price}} / 1K 次": "文件搜索价格:{{symbol}}{{price}} / 1K 次", - "文字提示 {{input}} tokens / 1M tokens * {{symbol}}{{price}} + 文字补全 {{completion}} tokens / 1M tokens * {{symbol}}{{compPrice}} = {{symbol}}{{total}}": "文字提示 {{input}} tokens / 1M tokens * {{symbol}}{{price}} + 文字补全 {{completion}} tokens / 1M tokens * {{symbol}}{{compPrice}} = {{symbol}}{{total}}", - "文字提示 {{nonCacheInput}} tokens / 1M tokens * {{symbol}}{{price}} + 缓存 {{cacheInput}} tokens / 1M tokens * {{symbol}}{{cachePrice}} + 文字补全 {{completion}} tokens / 1M tokens * {{symbol}}{{compPrice}} = {{symbol}}{{total}}": "文字提示 {{nonCacheInput}} tokens / 1M tokens * {{symbol}}{{price}} + 缓存 {{cacheInput}} tokens / 1M tokens * {{symbol}}{{cachePrice}} + 文字补全 {{completion}} tokens / 1M tokens * {{symbol}}{{compPrice}} = {{symbol}}{{total}}", - "文字输入": "文字输入", - "文字输出": "文字输出", - "文心一言": "文心一言", - "文档": "文档", - "文档地址": "文档地址", - "文生视频": "文生视频", - "新增 Key 来源": "新增 Key 来源", - "新增供应商": "新增供应商", - "新增失败": "新增失败", - "新增成功": "新增成功", - "新增条件": "新增条件", - "新增规则": "新增规则", - "新增订阅": "新增订阅", - "新密码": "新密码", - "新密码需要和原密码不一致!": "新密码需要和原密码不一致!", - "新建": "新建", - "新建套餐": "新建套餐", - "新建容器": "新建容器", - "新建容器部署": "新建容器部署", - "新建数量": "新建数量", - "新建组": "新建组", - "新格式(支持条件判断与json自定义):": "新格式(支持条件判断与json自定义):", - "新格式(规则 + 条件)": "新格式(规则 + 条件)", - "新格式模板": "新格式模板", - "新版本": "新版本", - "新用户使用邀请码奖励额度": "新用户使用邀请码奖励额度", - "新用户初始额度": "新用户初始额度", - "新的备用恢复代码": "新的备用恢复代码", - "新的备用码已生成": "新的备用码已生成", - "新获取的模型": "新获取的模型", - "新额度:": "新额度:", - "无": "无", - "无GPU": "无GPU", - "无冲突项": "无冲突项", - "无效的部署信息": "无效的部署信息", - "无效的重置链接,请重新发起密码重置请求": "无效的重置链接,请重新发起密码重置请求", - "无法发起 Passkey 注册": "无法发起 Passkey 注册", - "无法复制到剪贴板,请手动复制": "无法复制到剪贴板,请手动复制", - "无法添加图片": "无法添加图片", - "无法获取容器详情": "无法获取容器详情", - "无法连接 io.net": "无法连接 io.net", - "无生效": "无生效", - "无邀请人": "无邀请人", - "无限制": "无限制", - "无限额度": "无限额度", - "日": "日", - "日志已下载": "日志已下载", - "日志已加载": "日志已加载", - "日志已复制到剪贴板": "日志已复制到剪贴板", - "日志流": "日志流", - "日志清理失败:": "日志清理失败:", - "日志类型": "日志类型", - "日志设置": "日志设置", - "日志详情": "日志详情", - "旧格式(JSON 对象)": "旧格式(JSON 对象)", - "旧格式(直接覆盖):": "旧格式(直接覆盖):", - "旧格式必须是 JSON 对象": "旧格式必须是 JSON 对象", - "旧格式模板": "旧格式模板", - "旧的备用码已失效,请保存新的备用码": "旧的备用码已失效,请保存新的备用码", - "早上好": "早上好", - "时间": "时间", - "时间信息": "时间信息", - "时间粒度": "时间粒度", - "易支付": "易支付", - "易支付商户ID": "易支付商户ID", - "易支付商户密钥": "易支付商户密钥", - "是": "是", - "是否为企业账户": "是否为企业账户", - "是否同时重置对话消息?选择\"是\"将清空所有对话记录并恢复默认示例;选择\"否\"将保留当前对话记录。": "是否同时重置对话消息?选择\"是\"将清空所有对话记录并恢复默认示例;选择\"否\"将保留当前对话记录。", - "是否将该订单标记为成功并为用户入账?": "是否将该订单标记为成功并为用户入账?", - "是否确认充值?": "是否确认充值?", - "是否自动禁用": "是否自动禁用", - "是否要求指纹/面容等生物识别": "是否要求指纹/面容等生物识别", - "显示倍率": "显示倍率", - "显示最新20条": "显示最新20条", - "显示名称": "显示名称", - "显示名称字段(可选)": "显示名称字段(可选)", - "显示完整内容": "显示完整内容", - "显示操作项": "显示操作项", - "显示更多": "显示更多", - "显示第": "显示第", - "显示设置": "显示设置", - "显示调试": "显示调试", - "晚上好": "晚上好", - "普通环境变量": "普通环境变量", - "普通用户": "普通用户", - "智能体ID": "智能体ID", - "智能熔断": "智能熔断", - "智谱": "智谱", - "暂存错误": "暂存错误", - "暂无": "暂无", - "暂无API信息": "暂无API信息", - "暂无SSE响应数据": "暂无SSE响应数据", - "暂无产品配置": "暂无产品配置", - "暂无保存的配置": "暂无保存的配置", - "暂无充值记录": "暂无充值记录", - "暂无公告": "暂无公告", - "暂无匹配模型": "暂无匹配模型", - "暂无可复制 JSON": "暂无可复制 JSON", - "暂无可复制的版本信息": "暂无可复制的版本信息", - "暂无可展示数据": "暂无可展示数据", - "暂无可用的支付方式,请联系管理员配置": "暂无可用的支付方式,请联系管理员配置", - "暂无可购买套餐": "暂无可购买套餐", - "暂无响应数据": "暂无响应数据", - "暂无容器信息": "暂无容器信息", - "暂无容器详情": "暂无容器详情", - "暂无密钥数据": "暂无密钥数据", - "暂无差异化倍率显示": "暂无差异化倍率显示", - "暂无已绑定项": "暂无已绑定项", - "暂无常见问答": "暂无常见问答", - "暂无成功模型": "暂无成功模型", - "暂无数据": "暂无数据", - "暂无数据,点击下方按钮添加键值对": "暂无数据,点击下方按钮添加键值对", - "暂无日志": "暂无日志", - "暂无日志可下载": "暂无日志可下载", - "暂无日志可复制": "暂无日志可复制", - "暂无机密环境变量": "暂无机密环境变量", - "暂无模型": "暂无模型", - "暂无模型描述": "暂无模型描述", - "暂无环境变量": "暂无环境变量", - "暂无监控数据": "暂无监控数据", - "暂无系统公告": "暂无系统公告", - "暂无缺失模型": "暂无缺失模型", - "暂无自定义 OAuth 提供商": "暂无自定义 OAuth 提供商", - "暂无订阅套餐": "暂无订阅套餐", - "暂无订阅记录": "暂无订阅记录", - "暂无请求数据": "暂无请求数据", - "暂无项目": "暂无项目", - "暂无预填组": "暂无预填组", - "暴露倍率接口": "暴露倍率接口", - "更多信息请参考": "更多信息请参考", - "更多参数请参考": "更多参数请参考", - "更好的价格,更好的稳定性,只需要将模型基址替换为:": "更好的价格,更好的稳定性,只需要将模型基址替换为:", - "更新": "更新", - "更新 Creem 设置": "更新 Creem 设置", - "更新 Stripe 设置": "更新 Stripe 设置", - "更新SSRF防护设置": "更新SSRF防护设置", - "更新Worker设置": "更新Worker设置", - "更新令牌信息": "更新令牌信息", - "更新兑换码信息": "更新兑换码信息", - "更新失败": "更新失败", - "更新失败,请检查输入信息": "更新失败,请检查输入信息", - "更新套餐信息": "更新套餐信息", - "更新容器配置": "更新容器配置", - "更新容器配置可能会导致容器重启,请确保在合适的时间进行此操作。": "更新容器配置可能会导致容器重启,请确保在合适的时间进行此操作。", - "更新成功": "更新成功", - "更新所有已启用通道余额": "更新所有已启用通道余额", - "更新支付设置": "更新支付设置", - "更新时间": "更新时间", - "更新服务器地址": "更新服务器地址", - "更新模型信息": "更新模型信息", - "更新渠道信息": "更新渠道信息", - "更新部署名称失败": "更新部署名称失败", - "更新配置": "更新配置", - "更新配置后,容器可能需要重启以应用新的设置。请确保您了解这些更改的影响。": "更新配置后,容器可能需要重启以应用新的设置。请确保您了解这些更改的影响。", - "更新配置失败": "更新配置失败", - "更新预填组": "更新预填组", - "月": "月", - "有 Reasoning": "有 Reasoning", - "有效期": "有效期", - "有效期单位": "有效期单位", - "有效期数值": "有效期数值", - "有效期设置": "有效期设置", - "服务可用性": "服务可用性", - "服务商": "服务商", - "服务器地址": "服务器地址", - "服务显示名称": "服务显示名称", - "未匹配到模型,按回车键可将「{{name}}」作为自定义模型名添加": "未匹配到模型,按回车键可将「{{name}}」作为自定义模型名添加", - "未发现新增模型": "未发现新增模型", - "未发现重复密钥": "未发现重复密钥", - "未启动": "未启动", - "未启用": "未启用", - "未命名": "未命名", - "未在 Discovery 响应中找到可用的 OAuth 端点": "未在 Discovery 响应中找到可用的 OAuth 端点", - "未备份": "未备份", - "未开始": "未开始", - "未找到匹配的模型": "未找到匹配的模型", - "未找到可用的容器访问地址": "未找到可用的容器访问地址", - "未找到差异化倍率,无需同步": "未找到差异化倍率,无需同步", - "未授权": "未授权", - "未提交": "未提交", - "未检测到 Fluent 容器": "未检测到 Fluent 容器", - "未检测到 FluentRead(流畅阅读),请确认扩展已启用": "未检测到 FluentRead(流畅阅读),请确认扩展已启用", - "未测试": "未测试", - "未添加附加条件时,仅使用上方 type 进行清理。": "未添加附加条件时,仅使用上方 type 进行清理。", - "未登录或登录已过期,请重新登录": "未登录或登录已过期,请重新登录", - "未知": "未知", - "未知供应商": "未知供应商", - "未知品牌": "未知品牌", - "未知模型": "未知模型", - "未知渠道": "未知渠道", - "未知状态": "未知状态", - "未知类型": "未知类型", - "未知身份": "未知身份", - "未知部署": "未知部署", - "未知错误": "未知错误", - "未绑定": "未绑定", - "未获取到授权码": "未获取到授权码", - "未设置": "未设置", - "未设置倍率模型": "未设置倍率模型", - "未设置路径": "未设置路径", - "未配置模型": "未配置模型", - "未配置的模型列表": "未配置的模型列表", - "本地": "本地", - "本地数据存储": "本地数据存储", - "本地计费": "本地计费", - "本月获得": "本月获得", - "本设备:手机指纹/面容,外接:USB安全密钥": "本设备:手机指纹/面容,外接:USB安全密钥", - "本设备内置": "本设备内置", - "本项目根据": "本项目根据", - "机密环境变量": "机密环境变量", - "机密环境变量将被加密存储,适用于存储密码、API密钥等敏感信息。": "机密环境变量将被加密存储,适用于存储密码、API密钥等敏感信息。", - "机密环境变量说明": "机密环境变量说明", - "权重": "权重", - "权限设置": "权限设置", - "条": "条", - "条 - 第": "条 - 第", - "条,共": "条,共", - "条件取反": "条件取反", - "条件数": "条件数", - "条件规则": "条件规则", - "条件项设置": "条件项设置", - "条日志已清理!": "条日志已清理!", - "来源": "来源", - "来源于 IO.NET 部署": "来源于 IO.NET 部署", - "来源端点": "来源端点", - "来自模型重定向,尚未加入模型列表": "来自模型重定向,尚未加入模型列表", - "某些配置更改可能需要几分钟才能生效。": "某些配置更改可能需要几分钟才能生效。", - "查看": "查看", - "查看关联部署": "查看关联部署", - "查看图片": "查看图片", - "查看密钥": "查看密钥", - "查看所有可用的AI模型供应商,包括众多知名供应商的模型。": "查看所有可用的AI模型供应商,包括众多知名供应商的模型。", - "查看日志": "查看日志", - "查看渠道密钥": "查看渠道密钥", - "查看详情": "查看详情", - "查询": "查询", - "标签": "标签", - "标签不能为空!": "标签不能为空!", - "标签信息": "标签信息", - "标签名称": "标签名称", - "标签的基本配置": "标签的基本配置", - "标签组": "标签组", - "标签聚合": "标签聚合", - "标签聚合模式": "标签聚合模式", - "标识颜色": "标识颜色", - "核采样,控制词汇选择的多样性": "核采样,控制词汇选择的多样性", - "根据 Anthropic 协定,/v1/messages 的输入 tokens 仅统计非缓存输入,不包含缓存读取与缓存写入 tokens。": "根据 Anthropic 协定,/v1/messages 的输入 tokens 仅统计非缓存输入,不包含缓存读取与缓存写入 tokens。", - "根据模型名称和匹配规则查找模型元数据,优先级:精确 > 前缀 > 后缀 > 包含": "根据模型名称和匹配规则查找模型元数据,优先级:精确 > 前缀 > 后缀 > 包含", - "格式化": "格式化", - "格式化 JSON": "格式化 JSON", - "格式正确": "格式正确", - "格式示例:": "格式示例:", - "格式错误": "格式错误", - "检查更新": "检查更新", - "检测到 FluentRead(流畅阅读)": "检测到 FluentRead(流畅阅读)", - "检测到多个密钥,您可以单独复制每个密钥,或点击复制全部获取完整内容。": "检测到多个密钥,您可以单独复制每个密钥,或点击复制全部获取完整内容。", - "检测到该消息后有AI回复,是否删除后续回复并重新生成?": "检测到该消息后有AI回复,是否删除后续回复并重新生成?", - "检测必须等待绘图成功才能进行放大等操作": "检测必须等待绘图成功才能进行放大等操作", - "模型": "模型", - "模型: {{ratio}}": "模型: {{ratio}}", - "模型专用区域": "模型专用区域", - "模型价格": "模型价格", - "模型价格 {{symbol}}{{price}},{{ratioType}} {{ratio}}": "模型价格 {{symbol}}{{price}},{{ratioType}} {{ratio}}", - "模型价格:{{symbol}}{{price}} * {{ratioType}}:{{ratio}} = {{symbol}}{{total}}": "模型价格:{{symbol}}{{price}} * {{ratioType}}:{{ratio}} = {{symbol}}{{total}}", - "模型倍率": "模型倍率", - "模型倍率 {{modelRatio}}": "模型倍率 {{modelRatio}}", - "模型倍率 {{modelRatio}},缓存倍率 {{cacheRatio}},输出倍率 {{completionRatio}},{{ratioType}} {{ratio}}": "模型倍率 {{modelRatio}},缓存倍率 {{cacheRatio}},输出倍率 {{completionRatio}},{{ratioType}} {{ratio}}", - "模型倍率 {{modelRatio}},缓存倍率 {{cacheRatio}},输出倍率 {{completionRatio}},{{ratioType}} {{ratio}},Web 搜索调用 {{webSearchCallCount}} 次": "模型倍率 {{modelRatio}},缓存倍率 {{cacheRatio}},输出倍率 {{completionRatio}},{{ratioType}} {{ratio}},Web 搜索调用 {{webSearchCallCount}} 次", - "模型倍率 {{modelRatio}},缓存倍率 {{cacheRatio}},输出倍率 {{completionRatio}},图片输入倍率 {{imageRatio}},{{ratioType}} {{ratio}}": "模型倍率 {{modelRatio}},缓存倍率 {{cacheRatio}},输出倍率 {{completionRatio}},图片输入倍率 {{imageRatio}},{{ratioType}} {{ratio}}", - "模型倍率值": "模型倍率值", - "模型倍率和补全倍率": "模型倍率和补全倍率", - "模型倍率和补全倍率同时设置": "模型倍率和补全倍率同时设置", - "模型倍率设置": "模型倍率设置", - "模型关键字": "模型关键字", - "模型列表已复制到剪贴板": "模型列表已复制到剪贴板", - "模型列表已更新": "模型列表已更新", - "模型列表已追加更新": "模型列表已追加更新", - "模型创建成功!": "模型创建成功!", - "模型删除失败": "模型删除失败", - "模型删除失败: {{error}}": "模型删除失败: {{error}}", - "模型删除成功": "模型删除成功", - "模型名称": "模型名称", - "模型名称已存在": "模型名称已存在", - "模型固定价格": "模型固定价格", - "模型图标": "模型图标", - "模型定价,需要登录访问": "模型定价,需要登录访问", - "模型广场": "模型广场", - "模型拉取失败: {{error}}": "模型拉取失败: {{error}}", - "模型支持的接口端点信息": "模型支持的接口端点信息", - "模型数据分析": "模型数据分析", - "模型映射必须是合法的 JSON 格式!": "模型映射必须是合法的 JSON 格式!", - "模型更新成功!": "模型更新成功!", - "模型未加入列表,可能无法调用": "模型未加入列表,可能无法调用", - "模型正则": "模型正则", - "模型正则(每行一个)": "模型正则(每行一个)", - "模型正则不能为空": "模型正则不能为空", - "模型消耗分布": "模型消耗分布", - "模型消耗趋势": "模型消耗趋势", - "模型版本": "模型版本", - "模型的详细描述和基本特性": "模型的详细描述和基本特性", - "模型相关设置": "模型相关设置", - "模型社区需要大家的共同维护,如发现数据有误或想贡献新的模型数据,请访问:": "模型社区需要大家的共同维护,如发现数据有误或想贡献新的模型数据,请访问:", - "模型管理": "模型管理", - "模型组": "模型组", - "模型补全倍率(仅对自定义模型有效)": "模型补全倍率(仅对自定义模型有效)", - "模型请求速率限制": "模型请求速率限制", - "模型调用次数占比": "模型调用次数占比", - "模型调用次数排行": "模型调用次数排行", - "模型选择和映射设置": "模型选择和映射设置", - "模型部署": "模型部署", - "模型部署服务未启用": "模型部署服务未启用", - "模型部署管理": "模型部署管理", - "模型部署设置": "模型部署设置", - "模型配置": "模型配置", - "模型重定向": "模型重定向", - "模型重定向里的下列模型尚未添加到“模型”列表,调用时会因为缺少可用模型而失败:": "模型重定向里的下列模型尚未添加到“模型”列表,调用时会因为缺少可用模型而失败:", - "模型限制列表": "模型限制列表", - "模式": "模式", - "模板": "模板", - "模板应用失败": "模板应用失败", - "模板示例": "模板示例", - "模糊搜索模型名称": "模糊搜索模型名称", - "次": "次", - "欢迎使用,请完成以下设置以开始使用系统": "欢迎使用,请完成以下设置以开始使用系统", - "欧元": "欧元", - "正在加载可用部署位置...": "正在加载可用部署位置...", - "正在加载签到状态...": "正在加载签到状态...", - "正在处理大内容...": "正在处理大内容...", - "正在提交": "正在提交", - "正在构造请求体预览...": "正在构造请求体预览...", - "正在检查 io.net 连接...": "正在检查 io.net 连接...", - "正在测试第 ${current} - ${end} 个模型 (共 ${total} 个)": "正在测试第 ${current} - ${end} 个模型 (共 ${total} 个)", - "正在跟随最新日志": "正在跟随最新日志", - "正在跳转...": "正在跳转...", - "此代理仅用于图片请求转发,Webhook通知发送等,AI API请求仍然由服务器直接发出,可在渠道设置中单独配置代理": "此代理仅用于图片请求转发,Webhook通知发送等,AI API请求仍然由服务器直接发出,可在渠道设置中单独配置代理", - "此修改将不可逆": "此修改将不可逆", - "此操作不可恢复,请仔细确认时间后再操作!": "此操作不可恢复,请仔细确认时间后再操作!", - "此操作不可撤销,将永久删除已自动禁用的密钥": "此操作不可撤销,将永久删除已自动禁用的密钥", - "此操作不可撤销,将永久删除该密钥": "此操作不可撤销,将永久删除该密钥", - "此操作不可逆,所有数据将被永久删除": "此操作不可逆,所有数据将被永久删除", - "此操作具有风险,请确认要继续执行": "此操作具有风险,请确认要继续执行", - "此操作将启用用户账户": "此操作将启用用户账户", - "此操作将提升用户的权限级别": "此操作将提升用户的权限级别", - "此操作将禁用用户账户": "此操作将禁用用户账户", - "此操作将禁用该用户当前的两步验证配置,下次登录将不再强制输入验证码,直到用户重新启用。": "此操作将禁用该用户当前的两步验证配置,下次登录将不再强制输入验证码,直到用户重新启用。", - "此操作将解绑用户当前的 Passkey,下次登录需要重新注册。": "此操作将解绑用户当前的 Passkey,下次登录需要重新注册。", - "此操作将降低用户的权限级别": "此操作将降低用户的权限级别", - "此支付方式最低充值金额为": "此支付方式最低充值金额为", - "此渠道由 IO.NET 自动同步,类型、密钥和 API 地址已锁定。": "此渠道由 IO.NET 自动同步,类型、密钥和 API 地址已锁定。", - "此设置用于系统内部计算,默认值500000是为了精确到6位小数点设计,不推荐修改。": "此设置用于系统内部计算,默认值500000是为了精确到6位小数点设计,不推荐修改。", - "此页面仅显示未设置价格或倍率的模型,设置后将自动从列表中移除": "此页面仅显示未设置价格或倍率的模型,设置后将自动从列表中移除", - "此项可选,用于修改请求体中的模型名称,为一个 JSON 字符串,键为请求中模型名称,值为要替换的模型名称,例如:": "此项可选,用于修改请求体中的模型名称,为一个 JSON 字符串,键为请求中模型名称,值为要替换的模型名称,例如:", - "此项可选,用于修改请求体中的模型名称,为一个 JSON 字符串,键为请求中模型名称,值为要替换的模型名称,留空则不更改": "此项可选,用于修改请求体中的模型名称,为一个 JSON 字符串,键为请求中模型名称,值为要替换的模型名称,留空则不更改", - "此项可选,用于复写返回的状态码,仅影响本地判断,不修改返回到上游的状态码,比如将claude渠道的400错误复写为500(用于重试),请勿滥用该功能,例如:": "此项可选,用于复写返回的状态码,仅影响本地判断,不修改返回到上游的状态码,比如将claude渠道的400错误复写为500(用于重试),请勿滥用该功能,例如:", - "此项可选,用于覆盖请求参数。不支持覆盖 stream 参数": "此项可选,用于覆盖请求参数。不支持覆盖 stream 参数", - "此项可选,用于覆盖请求头参数": "此项可选,用于覆盖请求头参数", - "此项可选,用于通过自定义API地址来进行 API 调用,末尾不要带/v1和/": "此项可选,用于通过自定义API地址来进行 API 调用,末尾不要带/v1和/", - "每个用户最多可创建的令牌数量,默认 1000,设置过大可能会影响性能": "每个用户最多可创建的令牌数量,默认 1000,设置过大可能会影响性能", - "每周": "每周", - "每天": "每天", - "每容器GPU数": "每容器GPU数", - "每日仅可签到一次,请勿重复签到": "每日仅可签到一次,请勿重复签到", - "每日签到": "每日签到", - "每日签到可获得随机额度奖励": "每日签到可获得随机额度奖励", - "每月": "每月", - "每隔多少分钟测试一次所有通道": "每隔多少分钟测试一次所有通道", - "永不过期": "永不过期", - "永久删除您的两步验证设置": "永久删除您的两步验证设置", - "永久删除所有备用码(包括未使用的)": "永久删除所有备用码(包括未使用的)", - "没有匹配的字段": "没有匹配的字段", - "没有匹配的日志条目": "没有匹配的日志条目", - "没有匹配的规则": "没有匹配的规则", - "没有可用令牌用于填充": "没有可用令牌用于填充", - "没有找到匹配的模型": "没有找到匹配的模型", - "没有未设置的模型": "没有未设置的模型", - "没有条件时,默认总是执行该操作。": "没有条件时,默认总是执行该操作。", - "没有模型可以复制": "没有模型可以复制", - "没有账户?": "没有账户?", - "注 册": "注 册", - "注册": "注册", - "注册 Passkey": "注册 Passkey", - "注意": "注意", - "注意:JSON中重复的键只会保留最后一个同名键的值": "注意:JSON中重复的键只会保留最后一个同名键的值", - "注意非Chat API,请务必填写正确的API地址,否则可能导致无法使用": "注意非Chat API,请务必填写正确的API地址,否则可能导致无法使用", - "注销": "注销", - "注销成功!": "注销成功!", - "活跃文件": "活跃文件", - "活跃缓存数": "活跃缓存数", - "流": "流", - "流式": "流式", - "流式响应完成": "流式响应完成", - "流式输出": "流式输出", - "流量端口": "流量端口", - "浅色": "浅色", - "浅色模式": "浅色模式", - "测活": "测活", - "测试": "测试", - "测试中": "测试中", - "测试中...": "测试中...", - "测试单个渠道操作项目组": "测试单个渠道操作项目组", - "测试失败": "测试失败", - "测试失败:": "测试失败:", - "测试所有未手动禁用渠道": "测试所有未手动禁用渠道", - "测试所有渠道的最长响应时间": "测试所有渠道的最长响应时间", - "测试模式": "测试模式", - "测试连接": "测试连接", - "测速": "测速", - "消息优先级": "消息优先级", - "消息优先级,范围0-10,默认为5": "消息优先级,范围0-10,默认为5", - "消息已删除": "消息已删除", - "消息已复制到剪贴板": "消息已复制到剪贴板", - "消息已更新": "消息已更新", - "消息已编辑": "消息已编辑", - "消耗分布": "消耗分布", - "消耗趋势": "消耗趋势", - "消耗额度": "消耗额度", - "消费": "消费", - "深色": "深色", - "深色模式": "深色模式", - "添加": "添加", - "添加 OAuth 提供商": "添加 OAuth 提供商", - "添加API": "添加API", - "添加产品": "添加产品", - "添加令牌": "添加令牌", - "添加兑换码": "添加兑换码", - "添加公告": "添加公告", - "添加分类": "添加分类", - "添加后提交": "添加后提交", - "添加启动参数": "添加启动参数", - "添加启动命令": "添加启动命令", - "添加密钥环境变量": "添加密钥环境变量", - "添加成功": "添加成功", - "添加模型": "添加模型", - "添加模型区域": "添加模型区域", - "添加渠道": "添加渠道", - "添加环境变量": "添加环境变量", - "添加用户": "添加用户", - "添加聊天配置": "添加聊天配置", - "添加键值对": "添加键值对", - "添加问答": "添加问答", - "添加额度": "添加额度", - "清理不活跃缓存": "清理不活跃缓存", - "清理失败": "清理失败", - "清空": "清空", - "清空全部缓存": "清空全部缓存", - "清空该规则缓存": "清空该规则缓存", - "清空重定向": "清空重定向", - "清除历史日志": "清除历史日志", - "清除失效兑换码": "清除失效兑换码", - "清除所有模型": "清除所有模型", - "渠道": "渠道", - "渠道 ID": "渠道 ID", - "渠道ID,名称,密钥,API地址": "渠道ID,名称,密钥,API地址", - "渠道亲和性": "渠道亲和性", - "渠道亲和性:上游缓存命中": "渠道亲和性:上游缓存命中", - "渠道亲和性会基于从请求上下文或 JSON Body 提取的 Key,优先复用上一次成功的渠道。": "渠道亲和性会基于从请求上下文或 JSON Body 提取的 Key,优先复用上一次成功的渠道。", - "渠道优先级": "渠道优先级", - "渠道信息": "渠道信息", - "渠道创建成功!": "渠道创建成功!", - "渠道复制失败": "渠道复制失败", - "渠道复制失败: ": "渠道复制失败: ", - "渠道复制成功": "渠道复制成功", - "渠道密钥": "渠道密钥", - "渠道密钥信息": "渠道密钥信息", - "渠道密钥列表": "渠道密钥列表", - "渠道更新成功!": "渠道更新成功!", - "渠道权重": "渠道权重", - "渠道标签": "渠道标签", - "渠道模型信息不完整": "渠道模型信息不完整", - "渠道的基本配置信息": "渠道的基本配置信息", - "渠道的模型测试": "渠道的模型测试", - "渠道的高级配置选项": "渠道的高级配置选项", - "渠道管理": "渠道管理", - "渠道额外设置": "渠道额外设置", - "源地址": "源地址", - "满足任一条件(OR)": "满足任一条件(OR)", - "演示站点": "演示站点", - "演示站点模式": "演示站点模式", - "点击 + 按钮添加图片URL进行多模态对话": "点击 + 按钮添加图片URL进行多模态对话", - "点击\"确认延长\"后将立即扣除费用并延长容器运行时间": "点击\"确认延长\"后将立即扣除费用并延长容器运行时间", - "点击上传文件或拖拽文件到这里": "点击上传文件或拖拽文件到这里", - "点击下方按钮通过 Telegram 完成绑定": "点击下方按钮通过 Telegram 完成绑定", - "点击复制ID": "点击复制ID", - "点击复制模型名称": "点击复制模型名称", - "点击查看差异": "点击查看差异", - "点击此处": "点击此处", - "点击预览视频": "点击预览视频", - "点击预览音乐": "点击预览音乐", - "点击验证按钮,使用您的生物特征或安全密钥": "点击验证按钮,使用您的生物特征或安全密钥", - "版权所有": "版权所有", - "状态": "状态", - "状态码": "状态码", - "状态码复写": "状态码复写", - "状态码复写包含无效的状态码": "状态码复写包含无效的状态码", - "状态筛选": "状态筛选", - "状态页面Slug": "状态页面Slug", - "环境变量": "环境变量", - "生成令牌": "生成令牌", - "生成并填入": "生成并填入", - "生成数量": "生成数量", - "生成数量必须大于0": "生成数量必须大于0", - "生成新的备用码": "生成新的备用码", - "生成歌词": "生成歌词", - "生成音乐": "生成音乐", - "生效": "生效", - "用于API调用的身份验证令牌,请妥善保管": "用于API调用的身份验证令牌,请妥善保管", - "用于唯一标识用户的字段路径": "用于唯一标识用户的字段路径", - "用于配置网络代理,支持 socks5 协议": "用于配置网络代理,支持 socks5 协议", - "用于验证回调 new-api 的 webhook 请求的密钥,敏感信息不显示": "用于验证回调 new-api 的 webhook 请求的密钥,敏感信息不显示", - "用以支持基于 WebAuthn 的无密码登录注册": "用以支持基于 WebAuthn 的无密码登录注册", - "用以支持用户校验": "用以支持用户校验", - "用以支持系统的邮件发送": "用以支持系统的邮件发送", - "用以支持通过 Discord 进行登录注册": "用以支持通过 Discord 进行登录注册", - "用以支持通过 GitHub 进行登录注册": "用以支持通过 GitHub 进行登录注册", - "用以支持通过 Linux DO 进行登录注册": "用以支持通过 Linux DO 进行登录注册", - "用以支持通过 OIDC 登录,例如 Okta、Auth0 等兼容 OIDC 协议的 IdP": "用以支持通过 OIDC 登录,例如 Okta、Auth0 等兼容 OIDC 协议的 IdP", - "用以支持通过 Telegram 进行登录注册": "用以支持通过 Telegram 进行登录注册", - "用以支持通过微信进行登录注册": "用以支持通过微信进行登录注册", - "用以防止恶意用户利用临时邮箱批量注册": "用以防止恶意用户利用临时邮箱批量注册", - "用户": "用户", - "用户 ID 字段(可选)": "用户 ID 字段(可选)", - "用户个人功能": "用户个人功能", - "用户主页,展示系统信息": "用户主页,展示系统信息", - "用户优先:如果用户在请求中指定了系统提示词,将优先使用用户的设置": "用户优先:如果用户在请求中指定了系统提示词,将优先使用用户的设置", - "用户信息": "用户信息", - "用户信息更新成功!": "用户信息更新成功!", - "用户信息缺失": "用户信息缺失", - "用户最大令牌数量": "用户最大令牌数量", - "用户分组": "用户分组", - "用户分组和额度管理": "用户分组和额度管理", - "用户分组配置": "用户分组配置", - "用户协议": "用户协议", - "用户协议已更新": "用户协议已更新", - "用户协议更新失败": "用户协议更新失败", - "用户可选分组": "用户可选分组", - "用户名": "用户名", - "用户名字段(可选)": "用户名字段(可选)", - "用户名或邮箱": "用户名或邮箱", - "用户名称": "用户名称", - "用户控制面板,管理账户": "用户控制面板,管理账户", - "用户新建令牌时可选的分组,格式为 JSON 字符串,例如:{\"vip\": \"VIP 用户\", \"test\": \"测试\"},表示用户可以选择 vip 分组和 test 分组": "用户新建令牌时可选的分组,格式为 JSON 字符串,例如:{\"vip\": \"VIP 用户\", \"test\": \"测试\"},表示用户可以选择 vip 分组和 test 分组", - "用户每周期最多请求完成次数": "用户每周期最多请求完成次数", - "用户每周期最多请求次数": "用户每周期最多请求次数", - "用户注册时看到的网站名称,比如'我的网站'": "用户注册时看到的网站名称,比如'我的网站'", - "用户的基本账户信息": "用户的基本账户信息", - "用户管理": "用户管理", - "用户组": "用户组", - "用户订阅管理": "用户订阅管理", - "用户账户创建成功!": "用户账户创建成功!", - "用户账户管理": "用户账户管理", - "用时/首字": "用时/首字", - "由全站货币展示设置统一控制": "由全站货币展示设置统一控制", - "由订阅抵扣": "由订阅抵扣", - "界面语言和其他个人偏好": "界面语言和其他个人偏好", - "留空使用系统临时目录": "留空使用系统临时目录", - "留空则使用账号绑定的邮箱": "留空则使用账号绑定的邮箱", - "留空则使用默认端点;支持 {path, method}": "留空则使用默认端点;支持 {path, method}", - "留空则保持原有密钥": "留空则保持原有密钥", - "留空则默认使用服务器地址,注意不能携带http://或者https://": "留空则默认使用服务器地址,注意不能携带http://或者https://", - "登 录": "登 录", - "登录": "登录", - "登录成功!": "登录成功!", - "登录过期,请重新登录!": "登录过期,请重新登录!", - "白名单": "白名单", - "的前提下使用。": "的前提下使用。", - "监控设置": "监控设置", - "目录总大小": "目录总大小", - "目录文件数": "目录文件数", - "目标用户:{{username}}": "目标用户:{{username}}", - "目标端点": "目标端点", - "目标路径(可选)": "目标路径(可选)", - "直接提交": "直接提交", - "直接编辑 JSON 文本,保存时会校验格式。": "直接编辑 JSON 文本,保存时会校验格式。", - "相关项目": "相关项目", - "相当于删除用户,此修改将不可逆": "相当于删除用户,此修改将不可逆", - "矛盾": "矛盾", - "知识库 ID": "知识库 ID", - "硬件": "硬件", - "硬件与性能": "硬件与性能", - "硬件类型": "硬件类型", - "硬件配置": "硬件配置", - "确定": "确定", - "确定?": "确定?", - "确定删除此组?": "确定删除此组?", - "确定导入": "确定导入", - "确定是否要修复数据库一致性?": "确定是否要修复数据库一致性?", - "确定是否要删除所选通道?": "确定是否要删除所选通道?", - "确定是否要删除此令牌?": "确定是否要删除此令牌?", - "确定是否要删除此兑换码?": "确定是否要删除此兑换码?", - "确定是否要删除此模型?": "确定是否要删除此模型?", - "确定是否要删除此渠道?": "确定是否要删除此渠道?", - "确定是否要删除禁用通道?": "确定是否要删除禁用通道?", - "确定是否要复制此渠道?": "确定是否要复制此渠道?", - "确定是否要注销此用户?": "确定是否要注销此用户?", - "确定清除所有失效兑换码?": "确定清除所有失效兑换码?", - "确定要修改所有子渠道优先级为 ": "确定要修改所有子渠道优先级为 ", - "确定要修改所有子渠道权重为 ": "确定要修改所有子渠道权重为 ", - "确定要充值 $": "确定要充值 $", - "确定要删除供应商 \"{{name}}\" 吗?此操作不可撤销。": "确定要删除供应商 \"{{name}}\" 吗?此操作不可撤销。", - "确定要删除所有已自动禁用的密钥吗?": "确定要删除所有已自动禁用的密钥吗?", - "确定要删除所选的 {{count}} 个令牌吗?_other": "确定要删除所选的 {{count}} 个令牌吗?", - "确定要删除所选的 {{count}} 个模型吗?_other": "确定要删除所选的 {{count}} 个模型吗?", - "确定要删除此 OAuth 提供商吗?": "确定要删除此 OAuth 提供商吗?", - "确定要删除此API信息吗?": "确定要删除此API信息吗?", - "确定要删除此公告吗?": "确定要删除此公告吗?", - "确定要删除此分类吗?": "确定要删除此分类吗?", - "确定要删除此密钥吗?": "确定要删除此密钥吗?", - "确定要删除此问答吗?": "确定要删除此问答吗?", - "确定要删除这条消息吗?": "确定要删除这条消息吗?", - "确定要删除选中的": "确定要删除选中的", - "确定要启用所有密钥吗?": "确定要启用所有密钥吗?", - "确定要启用此用户吗?": "确定要启用此用户吗?", - "确定要提升此用户吗?": "确定要提升此用户吗?", - "确定要更新所有已启用通道余额吗?": "确定要更新所有已启用通道余额吗?", - "确定要测试所有未手动禁用渠道吗?": "确定要测试所有未手动禁用渠道吗?", - "确定要禁用所有的密钥吗?": "确定要禁用所有的密钥吗?", - "确定要禁用此用户吗?": "确定要禁用此用户吗?", - "确定要解绑 {{name}} 吗?": "确定要解绑 {{name}} 吗?", - "确定要降级此用户吗?": "确定要降级此用户吗?", - "确定重置": "确定重置", - "确定重置模型倍率吗?": "确定重置模型倍率吗?", - "确认": "确认", - "确认作废": "确认作废", - "确认关闭提示": "确认关闭提示", - "确认冲突项修改": "确认冲突项修改", - "确认删除": "确认删除", - "确认删除模型": "确认删除模型", - "确认取消密码登录": "确认取消密码登录", - "确认启用": "确认启用", - "确认密码": "确认密码", - "确认导入配置": "确认导入配置", - "确认延长": "确认延长", - "确认延长容器时长": "确认延长容器时长", - "确认操作": "确认操作", - "确认新密码": "确认新密码", - "确认清理不活跃的磁盘缓存?": "确认清理不活跃的磁盘缓存?", - "确认清空全部渠道亲和性缓存": "确认清空全部渠道亲和性缓存", - "确认清空该规则缓存": "确认清空该规则缓存", - "确认清除历史日志": "确认清除历史日志", - "确认禁用": "确认禁用", - "确认补单": "确认补单", - "确认解绑": "确认解绑", - "确认解绑 Passkey": "确认解绑 Passkey", - "确认设置并完成初始化": "确认设置并完成初始化", - "确认重置 Passkey": "确认重置 Passkey", - "确认重置两步验证": "确认重置两步验证", - "确认重置密码": "确认重置密码", - "磁盘 阈值 (%)": "磁盘 阈值 (%)", - "磁盘使用率超过此值时拒绝请求": "磁盘使用率超过此值时拒绝请求", - "磁盘可用空间小于缓存最大总量设置": "磁盘可用空间小于缓存最大总量设置", - "磁盘命中": "磁盘命中", - "磁盘缓存最大总量 (MB)": "磁盘缓存最大总量 (MB)", - "磁盘缓存占用的最大空间": "磁盘缓存占用的最大空间", - "磁盘缓存已清理": "磁盘缓存已清理", - "磁盘缓存设置(磁盘换内存)": "磁盘缓存设置(磁盘换内存)", - "磁盘缓存阈值 (MB)": "磁盘缓存阈值 (MB)", - "示例": "示例", - "示例:{\"default\": [200, 100], \"vip\": [0, 1000]}。": "示例:{\"default\": [200, 100], \"vip\": [0, 1000]}。", - "视频": "视频", - "视频Remix": "视频Remix", - "视频无法在当前浏览器中播放,这可能是由于:": "视频无法在当前浏览器中播放,这可能是由于:", - "禁用": "禁用", - "禁用 store 透传": "禁用 store 透传", - "禁用2FA失败": "禁用2FA失败", - "禁用两步验证": "禁用两步验证", - "禁用全部": "禁用全部", - "禁用原因": "禁用原因", - "禁用后用户端不再展示,但历史订单不受影响。是否继续?": "禁用后用户端不再展示,但历史订单不受影响。是否继续?", - "禁用后的影响:": "禁用后的影响:", - "禁用密钥失败": "禁用密钥失败", - "禁用思考处理的模型列表": "禁用思考处理的模型列表", - "禁用所有密钥失败": "禁用所有密钥失败", - "禁用时间": "禁用时间", - "私有IP访问详细说明": "私有IP访问详细说明", - "私有部署地址": "私有部署地址", - "私有镜像仓库的密码": "私有镜像仓库的密码", - "私有镜像仓库的用户名": "私有镜像仓库的用户名", - "秒": "秒", - "移除 functionResponse.id 字段": "移除 functionResponse.id 字段", - "移除 One API 的版权标识必须首先获得授权,项目维护需要花费大量精力,如果本项目对你有意义,请主动支持本项目": "移除 One API 的版权标识必须首先获得授权,项目维护需要花费大量精力,如果本项目对你有意义,请主动支持本项目", - "窗口处理": "窗口处理", - "窗口等待": "窗口等待", - "立即签到": "立即签到", - "立即订阅": "立即订阅", - "站点额度展示类型及汇率": "站点额度展示类型及汇率", - "端口号必须在1-65535之间": "端口号必须在1-65535之间", - "端口配置详细说明": "端口配置详细说明", - "端点": "端点", - "端点 URL 必须是完整地址(以 http:// 或 https:// 开头)": "端点 URL 必须是完整地址(以 http:// 或 https:// 开头)", - "端点映射": "端点映射", - "端点类型": "端点类型", - "端点组": "端点组", - "第 {{line}} 条 prune_objects 缺少条件": "第 {{line}} 条 prune_objects 缺少条件", - "第 {{line}} 条 prune_objects 需要至少一个匹配条件": "第 {{line}} 条 prune_objects 需要至少一个匹配条件", - "第 {{line}} 条 return_error 需要 message 字段": "第 {{line}} 条 return_error 需要 message 字段", - "第 {{line}} 条操作缺少值": "第 {{line}} 条操作缺少值", - "第 {{line}} 条操作缺少来源字段": "第 {{line}} 条操作缺少来源字段", - "第 {{line}} 条操作缺少目标字段": "第 {{line}} 条操作缺少目标字段", - "第 {{line}} 条操作缺少目标路径": "第 {{line}} 条操作缺少目标路径", - "第 {{line}} 条请求头透传格式无效": "第 {{line}} 条请求头透传格式无效", - "第 {{line}} 条请求头透传缺少请求头名称": "第 {{line}} 条请求头透传缺少请求头名称", - "第三方支付配置": "第三方支付配置", - "等价金额:": "等价金额:", - "等待中": "等待中", - "等待获取邮箱信息...": "等待获取邮箱信息...", - "筛选": "筛选", - "签到最大额度": "签到最大额度", - "签到最小额度": "签到最小额度", - "签到功能允许用户每日签到获取随机额度奖励": "签到功能允许用户每日签到获取随机额度奖励", - "签到失败": "签到失败", - "签到奖励将直接添加到您的账户余额": "签到奖励将直接添加到您的账户余额", - "签到奖励的最大额度": "签到奖励的最大额度", - "签到奖励的最小额度": "签到奖励的最小额度", - "签到成功!获得": "签到成功!获得", - "签到设置": "签到设置", - "简洁": "简洁", - "简洁模式:按 type 全量清理对象,例如 redacted_thinking。": "简洁模式:按 type 全量清理对象,例如 redacted_thinking。", - "简洁模式仅返回 message;状态码和错误类型将使用系统默认值。": "简洁模式仅返回 message;状态码和错误类型将使用系统默认值。", - "管理": "管理", - "管理 Ollama 模型的拉取和删除": "管理 Ollama 模型的拉取和删除", - "管理你的 LinuxDO OAuth App": "管理你的 LinuxDO OAuth App", - "管理员": "管理员", - "管理员区域": "管理员区域", - "管理员暂时未设置任何关于内容": "管理员暂时未设置任何关于内容", - "管理员未开启 Creem 充值!": "管理员未开启 Creem 充值!", - "管理员未开启Stripe充值!": "管理员未开启Stripe充值!", - "管理员未开启在线充值!": "管理员未开启在线充值!", - "管理员未开启在线充值功能,请联系管理员开启或使用兑换码充值。": "管理员未开启在线充值功能,请联系管理员开启或使用兑换码充值。", - "管理员未开启在线支付功能,请联系管理员配置。": "管理员未开启在线支付功能,请联系管理员配置。", - "管理员未设置用户可选分组": "管理员未设置用户可选分组", - "管理员设置了外部链接,点击下方按钮访问": "管理员设置了外部链接,点击下方按钮访问", - "管理员账号": "管理员账号", - "管理员账号已经初始化过,请继续设置其他参数": "管理员账号已经初始化过,请继续设置其他参数", - "管理模型、标签、端点等预填组": "管理模型、标签、端点等预填组", - "管理用户已绑定的第三方账户,支持筛选与解绑": "管理用户已绑定的第三方账户,支持筛选与解绑", - "管理绑定": "管理绑定", - "类型": "类型", - "类型(常用)": "类型(常用)", - "粘贴图片失败": "粘贴图片失败", - "精确": "精确", - "系统": "系统", - "系统令牌已复制到剪切板": "系统令牌已复制到剪切板", - "系统任务记录": "系统任务记录", - "系统信息": "系统信息", - "系统公告": "系统公告", - "系统公告管理,可以发布系统通知和重要消息(最多100个,前端显示最新20条)": "系统公告管理,可以发布系统通知和重要消息(最多100个,前端显示最新20条)", - "系统内存": "系统内存", - "系统初始化": "系统初始化", - "系统初始化失败,请重试": "系统初始化失败,请重试", - "系统初始化成功,正在跳转...": "系统初始化成功,正在跳转...", - "系统参数配置": "系统参数配置", - "系统名称": "系统名称", - "系统名称已更新": "系统名称已更新", - "系统名称更新失败": "系统名称更新失败", - "系统已为该部署准备 Ollama 镜像与随机 API Key": "系统已为该部署准备 Ollama 镜像与随机 API Key", - "系统性能监控": "系统性能监控", - "系统提示覆盖": "系统提示覆盖", - "系统提示词": "系统提示词", - "系统提示词拼接": "系统提示词拼接", - "系统数据统计": "系统数据统计", - "系统文档和帮助信息": "系统文档和帮助信息", - "系统消息": "系统消息", - "系统管理功能": "系统管理功能", - "系统设置": "系统设置", - "系统访问令牌": "系统访问令牌", - "约": "约", - "索引": "索引", - "紧凑列表": "紧凑列表", - "累计签到": "累计签到", - "累计获得": "累计获得", - "线路描述": "线路描述", - "组列表": "组列表", - "组名": "组名", - "组织": "组织", - "组织,不填则为默认组织": "组织,不填则为默认组织", - "终止中": "终止中", - "终止请求中": "终止请求中", - "绑定": "绑定", - "绑定 Telegram": "绑定 Telegram", - "绑定信息": "绑定信息", - "绑定微信账户": "绑定微信账户", - "绑定成功!": "绑定成功!", - "绑定邮箱地址": "绑定邮箱地址", - "结束": "结束", - "结束时间": "结束时间", - "结果图片": "结果图片", - "结算差额": "结算差额", - "绘图": "绘图", - "绘图任务记录": "绘图任务记录", - "绘图日志": "绘图日志", - "绘图设置": "绘图设置", - "统一的": "统一的", - "统计Tokens": "统计Tokens", - "统计已重置": "统计已重置", - "统计次数": "统计次数", - "统计额度": "统计额度", - "继续": "继续", - "缓存 {{tokens}} tokens / 1M tokens * {{symbol}}{{price}} (倍率: {{ratio}})": "缓存 {{tokens}} tokens / 1M tokens * {{symbol}}{{price}} (倍率: {{ratio}})", - "缓存 Tokens": "缓存 Tokens", - "缓存: {{cacheRatio}}": "缓存: {{cacheRatio}}", - "缓存价格:{{symbol}}{{price}} * {{cacheRatio}} = {{symbol}}{{total}} / 1M tokens (缓存倍率: {{cacheRatio}})": "缓存价格:{{symbol}}{{price}} * {{cacheRatio}} = {{symbol}}{{total}} / 1M tokens (缓存倍率: {{cacheRatio}})", - "缓存价格:{{symbol}}{{price}} * {{ratio}} = {{symbol}}{{total}} / 1M tokens (缓存倍率: {{cacheRatio}})": "缓存价格:{{symbol}}{{price}} * {{ratio}} = {{symbol}}{{total}} / 1M tokens (缓存倍率: {{cacheRatio}})", - "缓存倍率": "缓存倍率", - "缓存倍率 {{cacheRatio}}": "缓存倍率 {{cacheRatio}}", - "缓存写": "缓存写", - "缓存创建 {{tokens}} tokens / 1M tokens * {{symbol}}{{price}} (倍率: {{ratio}})": "缓存创建 {{tokens}} tokens / 1M tokens * {{symbol}}{{price}} (倍率: {{ratio}})", - "缓存创建 Tokens": "缓存创建 Tokens", - "缓存创建: {{cacheCreationRatio}}": "缓存创建: {{cacheCreationRatio}}", - "缓存创建: 1h {{cacheCreationRatio1h}}": "缓存创建: 1h {{cacheCreationRatio1h}}", - "缓存创建: 5m {{cacheCreationRatio5m}}": "缓存创建: 5m {{cacheCreationRatio5m}}", - "缓存创建: 5m {{cacheCreationRatio5m}} / 1h {{cacheCreationRatio1h}}": "缓存创建: 5m {{cacheCreationRatio5m}} / 1h {{cacheCreationRatio1h}}", - "缓存创建价格:{{symbol}}{{price}} * {{ratio}} = {{symbol}}{{total}} / 1M tokens (缓存创建倍率: {{cacheCreationRatio}})": "缓存创建价格:{{symbol}}{{price}} * {{ratio}} = {{symbol}}{{total}} / 1M tokens (缓存创建倍率: {{cacheCreationRatio}})", - "缓存创建价格合计:5m {{symbol}}{{five}} + 1h {{symbol}}{{one}} = {{symbol}}{{total}} / 1M tokens": "缓存创建价格合计:5m {{symbol}}{{five}} + 1h {{symbol}}{{one}} = {{symbol}}{{total}} / 1M tokens", - "缓存创建倍率": "缓存创建倍率", - "缓存创建倍率 {{cacheCreationRatio}}": "缓存创建倍率 {{cacheCreationRatio}}", - "缓存创建倍率 1h {{cacheCreationRatio1h}}": "缓存创建倍率 1h {{cacheCreationRatio1h}}", - "缓存创建倍率 5m {{cacheCreationRatio5m}}": "缓存创建倍率 5m {{cacheCreationRatio5m}}", - "缓存创建倍率 5m {{cacheCreationRatio5m}} / 1h {{cacheCreationRatio1h}}": "缓存创建倍率 5m {{cacheCreationRatio5m}} / 1h {{cacheCreationRatio1h}}", - "缓存条目数": "缓存条目数", - "缓存目录": "缓存目录", - "缓存目录磁盘空间": "缓存目录磁盘空间", - "缓存读": "缓存读", - "编辑": "编辑", - "编辑 OAuth 提供商": "编辑 OAuth 提供商", - "编辑API": "编辑API", - "编辑产品": "编辑产品", - "编辑供应商": "编辑供应商", - "编辑公告": "编辑公告", - "编辑公告内容": "编辑公告内容", - "编辑分类": "编辑分类", - "编辑成功": "编辑成功", - "编辑方式": "编辑方式", - "编辑标签": "编辑标签", - "编辑模型": "编辑模型", - "编辑模式": "编辑模式", - "编辑用户": "编辑用户", - "编辑聊天配置": "编辑聊天配置", - "编辑规则": "编辑规则", - "编辑问答": "编辑问答", - "缩词": "缩词", - "缺省 MaxTokens": "缺省 MaxTokens", - "网站地址": "网站地址", - "网站域名标识": "网站域名标识", - "网络连接失败,请检查网络设置或稍后重试": "网络连接失败,请检查网络设置或稍后重试", - "网络配置": "网络配置", - "网络错误": "网络错误", - "置信度": "置信度", - "美元": "美元", - "聊天": "聊天", - "聊天会话管理": "聊天会话管理", - "聊天区域": "聊天区域", - "聊天应用名称": "聊天应用名称", - "聊天应用名称已存在,请使用其他名称": "聊天应用名称已存在,请使用其他名称", - "聊天设置": "聊天设置", - "聊天配置": "聊天配置", - "聊天链接配置错误,请联系管理员": "聊天链接配置错误,请联系管理员", - "联系我们": "联系我们", - "腾讯混元": "腾讯混元", - "自动分组auto,从第一个开始选择": "自动分组auto,从第一个开始选择", - "自动刷新": "自动刷新", - "自动刷新中": "自动刷新中", - "自动填充字段": "自动填充字段", - "自动检测": "自动检测", - "自动模式": "自动模式", - "自动测试所有通道间隔时间": "自动测试所有通道间隔时间", - "自动生成:": "自动生成:", - "自动禁用": "自动禁用", - "自动禁用关键词": "自动禁用关键词", - "自动禁用状态码": "自动禁用状态码", - "自动禁用状态码格式不正确": "自动禁用状态码格式不正确", - "自动重试状态码": "自动重试状态码", - "自动重试状态码格式不正确": "自动重试状态码格式不正确", - "自定义": "自定义", - "自定义 JSON": "自定义 JSON", - "自定义 OAuth 提供商": "自定义 OAuth 提供商", - "自定义充值数量选项": "自定义充值数量选项", - "自定义充值数量选项不是合法的 JSON 数组": "自定义充值数量选项不是合法的 JSON 数组", - "自定义变焦-提交": "自定义变焦-提交", - "自定义模型名称": "自定义模型名称", - "自定义模式下不可用": "自定义模式下不可用", - "自定义秒数": "自定义秒数", - "自定义请求体模式": "自定义请求体模式", - "自定义货币": "自定义货币", - "自定义货币符号": "自定义货币符号", - "自定义错误响应": "自定义错误响应", - "自定义镜像": "自定义镜像", - "自用模式": "自用模式", - "自适应列表": "自适应列表", - "至": "至", - "节省": "节省", - "花费": "花费", - "花费时间": "花费时间", - "若你的 OIDC Provider 支持 Discovery Endpoint,你可以仅填写 OIDC Well-Known URL,系统会自动获取 OIDC 配置": "若你的 OIDC Provider 支持 Discovery Endpoint,你可以仅填写 OIDC Well-Known URL,系统会自动获取 OIDC 配置", - "获取 Discovery 配置": "获取 Discovery 配置", - "获取 Discovery 配置失败:": "获取 Discovery 配置失败:", - "获取 io.net API Key": "获取 io.net API Key", - "获取 OIDC 配置失败,请检查网络状况和 Well-Known URL 是否正确": "获取 OIDC 配置失败,请检查网络状况和 Well-Known URL 是否正确", - "获取 OIDC 配置成功!": "获取 OIDC 配置成功!", - "获取 Ollama 版本失败": "获取 Ollama 版本失败", - "获取2FA状态失败": "获取2FA状态失败", - "获取初始化状态失败": "获取初始化状态失败", - "获取可用资源失败: ": "获取可用资源失败: ", - "获取启用模型失败": "获取启用模型失败", - "获取启用模型失败:": "获取启用模型失败:", - "获取容器信息失败": "获取容器信息失败", - "获取容器列表失败": "获取容器列表失败", - "获取容器详情失败": "获取容器详情失败", - "获取密钥": "获取密钥", - "获取密钥失败": "获取密钥失败", - "获取密钥状态失败": "获取密钥状态失败", - "获取日志失败": "获取日志失败", - "获取未配置模型失败": "获取未配置模型失败", - "获取模型列表": "获取模型列表", - "获取模型列表失败": "获取模型列表失败", - "获取渠道失败:": "获取渠道失败:", - "获取硬件类型失败: ": "获取硬件类型失败: ", - "获取签到状态失败": "获取签到状态失败", - "获取组列表失败": "获取组列表失败", - "获取绑定信息失败": "获取绑定信息失败", - "获取自定义 OAuth 提供商列表失败": "获取自定义 OAuth 提供商列表失败", - "获取详情失败": "获取详情失败", - "获取部署列表失败": "获取部署列表失败", - "获取金额失败": "获取金额失败", - "获取验证码": "获取验证码", - "获得": "获得", - "补全": "补全", - "补全 {{completion}} tokens / 1M tokens * {{symbol}}{{price}}": "补全 {{completion}} tokens / 1M tokens * {{symbol}}{{price}}", - "补全价格:{{symbol}}{{price}} * {{completionRatio}} = {{symbol}}{{total}} / 1M tokens (补全倍率: {{completionRatio}})": "补全价格:{{symbol}}{{price}} * {{completionRatio}} = {{symbol}}{{total}} / 1M tokens (补全倍率: {{completionRatio}})", - "补全价格:{{symbol}}{{price}} * {{ratio}} = {{symbol}}{{total}} / 1M tokens": "补全价格:{{symbol}}{{price}} * {{ratio}} = {{symbol}}{{total}} / 1M tokens", - "补全倍率": "补全倍率", - "补全倍率值": "补全倍率值", - "补单": "补单", - "补单失败": "补单失败", - "补单成功": "补单成功", - "表单引用错误,请刷新页面重试": "表单引用错误,请刷新页面重试", - "表格视图": "表格视图", - "覆盖模式:将完全替换现有的所有密钥": "覆盖模式:将完全替换现有的所有密钥", - "覆盖模板": "覆盖模板", - "覆盖现有密钥": "覆盖现有密钥", - "规则": "规则", - "规则 JSON": "规则 JSON", - "规则 JSON 格式不正确": "规则 JSON 格式不正确", - "规则 ttl_seconds 为 0 时使用。0 表示使用后端默认 TTL:3600 秒。": "规则 ttl_seconds 为 0 时使用。0 表示使用后端默认 TTL:3600 秒。", - "规则为 JSON 数组;可视化与 JSON 模式共用同一份数据。": "规则为 JSON 数组;可视化与 JSON 模式共用同一份数据。", - "规则名称(可读性更好,也会出现在管理侧日志中)。": "规则名称(可读性更好,也会出现在管理侧日志中)。", - "规则导航": "规则导航", - "规则未找到,请刷新后重试": "规则未找到,请刷新后重试", - "角色": "角色", - "解析响应数据时发生错误": "解析响应数据时发生错误", - "解析密钥文件失败: {{msg}}": "解析密钥文件失败: {{msg}}", - "解析错误": "解析错误", - "解绑": "解绑", - "解绑 Passkey": "解绑 Passkey", - "解绑后将无法使用 Passkey 登录,确定要继续吗?": "解绑后将无法使用 Passkey 登录,确定要继续吗?", - "解绑成功": "解绑成功", - "计价币种": "计价币种", - "计算中": "计算中", - "计算成本": "计算成本", - "计算费用中...": "计算费用中...", - "计费开始": "计费开始", - "计费模式": "计费模式", - "计费类型": "计费类型", - "计费过程": "计费过程", - "订单号": "订单号", - "订阅": "订阅", - "订阅剩余": "订阅剩余", - "订阅套餐": "订阅套餐", - "订阅套餐管理": "订阅套餐管理", - "订阅实例": "订阅实例", - "订阅抵扣": "订阅抵扣", - "订阅管理": "订阅管理", - "订阅结算": "订阅结算", - "订阅说明": "订阅说明", - "认证方式": "认证方式", - "讯飞星火": "讯飞星火", - "记录请求与错误日志IP": "记录请求与错误日志IP", - "设备": "设备", - "设备类型偏好": "设备类型偏好", - "设置 Logo": "设置 Logo", - "设置2FA失败": "设置2FA失败", - "设置不同充值金额对应的折扣,键为充值金额,值为折扣率,例如:{\"100\": 0.95, \"200\": 0.9, \"500\": 0.85}": "设置不同充值金额对应的折扣,键为充值金额,值为折扣率,例如:{\"100\": 0.95, \"200\": 0.9, \"500\": 0.85}", - "设置两步验证": "设置两步验证", - "设置令牌可用额度和数量": "设置令牌可用额度和数量", - "设置令牌的基本信息": "设置令牌的基本信息", - "设置令牌的访问限制": "设置令牌的访问限制", - "设置保存失败": "设置保存失败", - "设置保存成功": "设置保存成功", - "设置兑换码的基本信息": "设置兑换码的基本信息", - "设置兑换码的额度和数量": "设置兑换码的额度和数量", - "设置公告": "设置公告", - "设置关于": "设置关于", - "设置已保存": "设置已保存", - "设置模型的基本信息": "设置模型的基本信息", - "设置用于接收额度预警的邮箱地址,不填则使用账号绑定的邮箱": "设置用于接收额度预警的邮箱地址,不填则使用账号绑定的邮箱", - "设置用户协议": "设置用户协议", - "设置用户可选择的充值数量选项,例如:[10, 20, 50, 100, 200, 500]": "设置用户可选择的充值数量选项,例如:[10, 20, 50, 100, 200, 500]", - "设置管理员登录信息": "设置管理员登录信息", - "设置类型": "设置类型", - "设置系统名称": "设置系统名称", - "设置过短会影响数据库性能": "设置过短会影响数据库性能", - "设置隐私政策": "设置隐私政策", - "设置页脚": "设置页脚", - "设置预填组的基本信息": "设置预填组的基本信息", - "设置首页内容": "设置首页内容", - "设置默认地区和特定模型的专用地区": "设置默认地区和特定模型的专用地区", - "设计与开发由": "设计与开发由", - "访问 io.net 控制台的 API Keys 页面": "访问 io.net 控制台的 API Keys 页面", - "访问容器": "访问容器", - "访问模型部署功能需要先启用 io.net 部署服务": "访问模型部署功能需要先启用 io.net 部署服务", - "访问限制": "访问限制", - "该供应商提供多种AI模型,适用于不同的应用场景。": "该供应商提供多种AI模型,适用于不同的应用场景。", - "该域名已存在于白名单中": "该域名已存在于白名单中", - "该套餐未配置 Creem": "该套餐未配置 Creem", - "该套餐未配置 Stripe": "该套餐未配置 Stripe", - "该数据可能不可信,请谨慎使用": "该数据可能不可信,请谨慎使用", - "该服务器地址将影响支付回调地址以及默认首页展示的地址,请确保正确配置": "该服务器地址将影响支付回调地址以及默认首页展示的地址,请确保正确配置", - "该模型存在固定价格与倍率计费方式冲突,请确认选择": "该模型存在固定价格与倍率计费方式冲突,请确认选择", - "该渠道已开启请求透传:参数覆写、模型重定向、渠道适配等 NewAPI 内置功能将失效,非最佳实践;如因此产生问题,请勿提交 issue 反馈。": "该渠道已开启请求透传:参数覆写、模型重定向、渠道适配等 NewAPI 内置功能将失效,非最佳实践;如因此产生问题,请勿提交 issue 反馈。", - "该规则未启用“作用域:包含规则名称”,无法按规则清空缓存。": "该规则未启用“作用域:包含规则名称”,无法按规则清空缓存。", - "该规则未设置参数覆盖模板": "该规则未设置参数覆盖模板", - "该规则的缓存保留时长;0 表示使用默认 TTL:": "该规则的缓存保留时长;0 表示使用默认 TTL:", - "该记录不包含可用的 token 统计口径。": "该记录不包含可用的 token 统计口径。", - "详情": "详情", - "语言偏好": "语言偏好", - "语言偏好已保存": "语言偏好已保存", - "语音输入": "语音输入", - "语音输出": "语音输出", - "说明": "说明", - "说明:": "说明:", - "说明:本页测试为非流式请求;若渠道仅支持流式返回,可能出现测试失败,请以实际使用为准。": "说明:本页测试为非流式请求;若渠道仅支持流式返回,可能出现测试失败,请以实际使用为准。", - "说明:生成结果是可直接粘贴到渠道密钥里的 JSON(包含 access_token / refresh_token / account_id)。": "说明:生成结果是可直接粘贴到渠道密钥里的 JSON(包含 access_token / refresh_token / account_id)。", - "说明信息": "说明信息", - "请上传密钥文件": "请上传密钥文件", - "请上传密钥文件!": "请上传密钥文件!", - "请为渠道命名": "请为渠道命名", - "请使用 Project 为 io.cloud 的密钥": "请使用 Project 为 io.cloud 的密钥", - "请先在设置中启用图片功能": "请先在设置中启用图片功能", - "请先填写 API Key": "请先填写 API Key", - "请先填写 Discovery URL 或 Issuer URL": "请先填写 Discovery URL 或 Issuer URL", - "请先填写 Issuer URL,以自动生成完整的端点 URL": "请先填写 Issuer URL,以自动生成完整的端点 URL", - "请先填写 Ollama API 地址": "请先填写 Ollama API 地址", - "请先填写服务器地址": "请先填写服务器地址", - "请先粘贴回调 URL": "请先粘贴回调 URL", - "请先输入密钥": "请先输入密钥", - "请先选择一条规则": "请先选择一条规则", - "请先选择同步渠道": "请先选择同步渠道", - "请先选择模型!": "请先选择模型!", - "请先选择硬件类型": "请先选择硬件类型", - "请先选择要删除的令牌!": "请先选择要删除的令牌!", - "请先选择要删除的通道!": "请先选择要删除的通道!", - "请先选择要设置标签的渠道!": "请先选择要设置标签的渠道!", - "请先选择需要批量设置的模型": "请先选择需要批量设置的模型", - "请先阅读并同意用户协议和隐私政策": "请先阅读并同意用户协议和隐私政策", - "请再次输入新密码": "请再次输入新密码", - "请前往个人设置 → 安全设置进行配置。": "请前往个人设置 → 安全设置进行配置。", - "请勿过度信任此功能,IP可能被伪造,请配合nginx和cdn等网关使用": "请勿过度信任此功能,IP可能被伪造,请配合nginx和cdn等网关使用", - "请在系统设置页面编辑分组倍率以添加新的分组:": "请在系统设置页面编辑分组倍率以添加新的分组:", - "请填写完整的产品信息": "请填写完整的产品信息", - "请填写完整的管理员账号信息": "请填写完整的管理员账号信息", - "请填写密钥": "请填写密钥", - "请填写渠道名称和渠道密钥!": "请填写渠道名称和渠道密钥!", - "请填写部署地区": "请填写部署地区", - "请妥善保管密钥信息,不要泄露给他人。如有安全疑虑,请及时更换密钥。": "请妥善保管密钥信息,不要泄露给他人。如有安全疑虑,请及时更换密钥。", - "请尝试其他搜索关键词": "请尝试其他搜索关键词", - "请检查渠道配置或刷新重试": "请检查渠道配置或刷新重试", - "请检查表单填写是否正确": "请检查表单填写是否正确", - "请检查输入": "请检查输入", - "请求体 JSON": "请求体 JSON", - "请求体内存缓存": "请求体内存缓存", - "请求体磁盘缓存": "请求体磁盘缓存", - "请求体超过此大小时使用磁盘缓存": "请求体超过此大小时使用磁盘缓存", - "请求参数无效": "请求参数无效", - "请求发生错误": "请求发生错误", - "请求发生错误: ": "请求发生错误: ", - "请求后端接口失败:": "请求后端接口失败:", - "请求失败": "请求失败", - "请求头覆盖": "请求头覆盖", - "请求并计费模型": "请求并计费模型", - "请求时长: ${time}s": "请求时长: ${time}s", - "请求次数": "请求次数", - "请求结束后多退少补": "请求结束后多退少补", - "请求路径": "请求路径", - "请求转换": "请求转换", - "请求预扣费额度": "请求预扣费额度", - "请点击我": "请点击我", - "请确认以下设置信息,点击\"初始化系统\"开始配置": "请确认以下设置信息,点击\"初始化系统\"开始配置", - "请确认您已了解禁用两步验证的后果": "请确认您已了解禁用两步验证的后果", - "请确认管理员密码": "请确认管理员密码", - "请稍后几秒重试,Turnstile 正在检查用户环境!": "请稍后几秒重试,Turnstile 正在检查用户环境!", - "请粘贴完整回调 URL(包含 code 与 state)": "请粘贴完整回调 URL(包含 code 与 state)", - "请联系管理员在系统设置中配置API信息": "请联系管理员在系统设置中配置API信息", - "请联系管理员在系统设置中配置Uptime": "请联系管理员在系统设置中配置Uptime", - "请联系管理员在系统设置中配置公告信息": "请联系管理员在系统设置中配置公告信息", - "请联系管理员在系统设置中配置常见问答": "请联系管理员在系统设置中配置常见问答", - "请联系管理员配置聊天链接": "请联系管理员配置聊天链接", - "请至少选择一个令牌!": "请至少选择一个令牌!", - "请至少选择一个兑换码!": "请至少选择一个兑换码!", - "请至少选择一个模型": "请至少选择一个模型", - "请至少选择一个模型!": "请至少选择一个模型!", - "请至少选择一个渠道": "请至少选择一个渠道", - "请输入 API Key,一行一个,格式:APIKey|Region": "请输入 API Key,一行一个,格式:APIKey|Region", - "请输入 API Key,格式:APIKey|Region": "请输入 API Key,格式:APIKey|Region", - "请输入 Authorization Endpoint": "请输入 Authorization Endpoint", - "请输入 AZURE_OPENAI_ENDPOINT,例如:https://docs-test-001.openai.azure.com": "请输入 AZURE_OPENAI_ENDPOINT,例如:https://docs-test-001.openai.azure.com", - "请输入 Client ID": "请输入 Client ID", - "请输入 Client Secret": "请输入 Client Secret", - "请输入 io.net API Key(敏感信息不显示)": "请输入 io.net API Key(敏感信息不显示)", - "请输入 JSON 格式的 OAuth 凭据,例如:\n{\n \"access_token\": \"...\",\n \"account_id\": \"...\" \n}": "请输入 JSON 格式的 OAuth 凭据,例如:\n{\n \"access_token\": \"...\",\n \"account_id\": \"...\" \n}", - "请输入 JSON 格式的密钥内容,例如:\n{\n \"type\": \"service_account\",\n \"project_id\": \"your-project-id\",\n \"private_key_id\": \"...\",\n \"private_key\": \"...\",\n \"client_email\": \"...\",\n \"client_id\": \"...\",\n \"auth_uri\": \"...\",\n \"token_uri\": \"...\",\n \"auth_provider_x509_cert_url\": \"...\",\n \"client_x509_cert_url\": \"...\"\n}": "请输入 JSON 格式的密钥内容,例如:\n{\n \"type\": \"service_account\",\n \"project_id\": \"your-project-id\",\n \"private_key_id\": \"...\",\n \"private_key\": \"...\",\n \"client_email\": \"...\",\n \"client_id\": \"...\",\n \"auth_uri\": \"...\",\n \"token_uri\": \"...\",\n \"auth_provider_x509_cert_url\": \"...\",\n \"client_x509_cert_url\": \"...\"\n}", - "请输入 OIDC 的 Well-Known URL": "请输入 OIDC 的 Well-Known URL", - "请输入 Slug": "请输入 Slug", - "请输入 Token Endpoint": "请输入 Token Endpoint", - "请输入 User Info Endpoint": "请输入 User Info Endpoint", - "请输入6位验证码或8位备用码": "请输入6位验证码或8位备用码", - "请输入API地址": "请输入API地址", - "请输入API地址!": "请输入API地址!", - "请输入Bark推送URL": "请输入Bark推送URL", - "请输入Bark推送URL,例如: https://api.day.app/yourkey/{{title}}/{{content}}": "请输入Bark推送URL,例如: https://api.day.app/yourkey/{{title}}/{{content}}", - "请输入Gotify应用令牌": "请输入Gotify应用令牌", - "请输入Gotify服务器地址": "请输入Gotify服务器地址", - "请输入Gotify服务器地址,例如: https://gotify.example.com": "请输入Gotify服务器地址,例如: https://gotify.example.com", - "请输入Uptime Kuma地址": "请输入Uptime Kuma地址", - "请输入Uptime Kuma服务地址,如:https://status.example.com": "请输入Uptime Kuma服务地址,如:https://status.example.com", - "请输入URL链接": "请输入URL链接", - "请输入Webhook地址": "请输入Webhook地址", - "请输入Webhook地址,例如: https://example.com/webhook": "请输入Webhook地址,例如: https://example.com/webhook", - "请输入你的账户名以确认删除!": "请输入你的账户名以确认删除!", - "请输入供应商名称": "请输入供应商名称", - "请输入供应商名称,如:OpenAI": "请输入供应商名称,如:OpenAI", - "请输入供应商描述": "请输入供应商描述", - "请输入兑换码": "请输入兑换码", - "请输入兑换码!": "请输入兑换码!", - "请输入公告内容": "请输入公告内容", - "请输入公告内容(支持 Markdown/HTML)": "请输入公告内容(支持 Markdown/HTML)", - "请输入分类名称": "请输入分类名称", - "请输入分类名称,如:OpenAI、Claude等": "请输入分类名称,如:OpenAI、Claude等", - "请输入到 /suno 前的路径,通常就是域名,例如:https://api.example.com": "请输入到 /suno 前的路径,通常就是域名,例如:https://api.example.com", - "请输入副本数量": "请输入副本数量", - "请输入原密码": "请输入原密码", - "请输入原密码!": "请输入原密码!", - "请输入名称": "请输入名称", - "请输入回答内容": "请输入回答内容", - "请输入回答内容(支持 Markdown/HTML)": "请输入回答内容(支持 Markdown/HTML)", - "请输入图标名称": "请输入图标名称", - "请输入填充值": "请输入填充值", - "请输入备注(仅管理员可见)": "请输入备注(仅管理员可见)", - "请输入套餐标题": "请输入套餐标题", - "请输入完整的 JSON 格式密钥内容": "请输入完整的 JSON 格式密钥内容", - "请输入完整的URL,例如:https://api.openai.com/v1/chat/completions": "请输入完整的URL,例如:https://api.openai.com/v1/chat/completions", - "请输入完整的URL链接": "请输入完整的URL链接", - "请输入容器名称": "请输入容器名称", - "请输入密码": "请输入密码", - "请输入密钥": "请输入密钥", - "请输入密钥,一行一个": "请输入密钥,一行一个", - "请输入密钥,一行一个,格式:AccessKey|SecretAccessKey|Region": "请输入密钥,一行一个,格式:AccessKey|SecretAccessKey|Region", - "请输入密钥!": "请输入密钥!", - "请输入延长时长": "请输入延长时长", - "请输入总额度": "请输入总额度", - "请输入您的密码": "请输入您的密码", - "请输入您的用户名以确认删除": "请输入您的用户名以确认删除", - "请输入您的用户名或邮箱地址": "请输入您的用户名或邮箱地址", - "请输入您的邮箱地址": "请输入您的邮箱地址", - "请输入您的问题...": "请输入您的问题...", - "请输入数值": "请输入数值", - "请输入数字": "请输入数字", - "请输入新密码": "请输入新密码", - "请输入新密码!": "请输入新密码!", - "请输入新建数量": "请输入新建数量", - "请输入新标签,留空则解散标签": "请输入新标签,留空则解散标签", - "请输入新的剩余额度": "请输入新的剩余额度", - "请输入新的密码,最短 8 位": "请输入新的密码,最短 8 位", - "请输入新的显示名称": "请输入新的显示名称", - "请输入新的用户名": "请输入新的用户名", - "请输入新的部署名称": "请输入新的部署名称", - "请输入显示名称": "请输入显示名称", - "请输入有效的JSON格式的请求体。您可以参考预览面板中的默认请求体格式。": "请输入有效的JSON格式的请求体。您可以参考预览面板中的默认请求体格式。", - "请输入有效的数字": "请输入有效的数字", - "请输入有效的镜像地址": "请输入有效的镜像地址", - "请输入标签名称": "请输入标签名称", - "请输入模型倍率": "请输入模型倍率", - "请输入模型倍率和补全倍率": "请输入模型倍率和补全倍率", - "请输入模型名称": "请输入模型名称", - "请输入模型名称,例如: llama3.2, qwen2.5:7b": "请输入模型名称,例如: llama3.2, qwen2.5:7b", - "请输入模型名称,如:gpt-4": "请输入模型名称,如:gpt-4", - "请输入模型描述": "请输入模型描述", - "请输入消息内容...": "请输入消息内容...", - "请输入状态页面Slug": "请输入状态页面Slug", - "请输入状态页面的Slug,如:my-status": "请输入状态页面的Slug,如:my-status", - "请输入生成数量": "请输入生成数量", - "请输入用户名": "请输入用户名", - "请输入私有部署地址,格式为:https://fastgpt.run/api/openapi": "请输入私有部署地址,格式为:https://fastgpt.run/api/openapi", - "请输入秒数": "请输入秒数", - "请输入管理员密码": "请输入管理员密码", - "请输入管理员用户名": "请输入管理员用户名", - "请输入线路描述": "请输入线路描述", - "请输入组名": "请输入组名", - "请输入组描述": "请输入组描述", - "请输入组织org-xxx": "请输入组织org-xxx", - "请输入聊天应用名称": "请输入聊天应用名称", - "请输入补全倍率": "请输入补全倍率", - "请输入要延长的小时数": "请输入要延长的小时数", - "请输入要设置的标签名称": "请输入要设置的标签名称", - "请输入认证器验证码": "请输入认证器验证码", - "请输入认证器验证码或备用码": "请输入认证器验证码或备用码", - "请输入说明": "请输入说明", - "请输入运行时长": "请输入运行时长", - "请输入邮箱!": "请输入邮箱!", - "请输入邮箱地址": "请输入邮箱地址", - "请输入邮箱验证码!": "请输入邮箱验证码!", - "请输入部署名称": "请输入部署名称", - "请输入部署名称以完成二次确认": "请输入部署名称以完成二次确认", - "请输入部署地区,例如:us-central1\n支持使用模型映射格式\n{\n \"default\": \"us-central1\",\n \"claude-3-5-sonnet-20240620\": \"europe-west1\"\n}": "请输入部署地区,例如:us-central1\n支持使用模型映射格式\n{\n \"default\": \"us-central1\",\n \"claude-3-5-sonnet-20240620\": \"europe-west1\"\n}", - "请输入金额": "请输入金额", - "请输入镜像地址": "请输入镜像地址", - "请输入问题标题": "请输入问题标题", - "请输入预警阈值": "请输入预警阈值", - "请输入预警额度": "请输入预警额度", - "请输入额度": "请输入额度", - "请输入验证码": "请输入验证码", - "请输入验证码或备用码": "请输入验证码或备用码", - "请输入默认 API 版本,例如:2025-04-01-preview": "请输入默认 API 版本,例如:2025-04-01-preview", - "请选择API地址": "请选择API地址", - "请选择一条规则进行编辑。": "请选择一条规则进行编辑。", - "请选择主模型": "请选择主模型", - "请选择产品": "请选择产品", - "请选择你的复制方式": "请选择你的复制方式", - "请选择使用模式": "请选择使用模式", - "请选择分组": "请选择分组", - "请选择发布日期": "请选择发布日期", - "请选择可以使用该渠道的分组": "请选择可以使用该渠道的分组", - "请选择可以使用该渠道的分组,留空则不更改": "请选择可以使用该渠道的分组,留空则不更改", - "请选择同步语言": "请选择同步语言", - "请选择名称匹配类型": "请选择名称匹配类型", - "请选择多密钥使用策略": "请选择多密钥使用策略", - "请选择密钥更新模式": "请选择密钥更新模式", - "请选择密钥格式": "请选择密钥格式", - "请选择支付方式": "请选择支付方式", - "请选择日志记录时间": "请选择日志记录时间", - "请选择模型": "请选择模型", - "请选择模型。": "请选择模型。", - "请选择消息优先级": "请选择消息优先级", - "请选择渠道类型": "请选择渠道类型", - "请选择硬件类型": "请选择硬件类型", - "请选择组类型": "请选择组类型", - "请选择至少一个部署位置": "请选择至少一个部署位置", - "请选择订阅套餐": "请选择订阅套餐", - "请选择该令牌支持的模型,留空支持所有模型": "请选择该令牌支持的模型,留空支持所有模型", - "请选择该渠道所支持的模型": "请选择该渠道所支持的模型", - "请选择该渠道所支持的模型,留空则不更改": "请选择该渠道所支持的模型,留空则不更改", - "请选择过期时间": "请选择过期时间", - "请选择通知方式": "请选择通知方式", - "调用次数": "调用次数", - "调用次数分布": "调用次数分布", - "调用次数排行": "调用次数排行", - "调试信息": "调试信息", - "谨慎": "谨慎", - "警告": "警告", - "警告:启用保活后,如果已经写入保活数据后渠道出错,系统无法重试,如果必须开启,推荐设置尽可能大的Ping间隔": "警告:启用保活后,如果已经写入保活数据后渠道出错,系统无法重试,如果必须开启,推荐设置尽可能大的Ping间隔", - "警告:禁用两步验证将永久删除您的验证设置和所有备用码,此操作不可撤销!": "警告:禁用两步验证将永久删除您的验证设置和所有备用码,此操作不可撤销!", - "豆包": "豆包", - "账单": "账单", - "账户充值": "账户充值", - "账户已删除!": "账户已删除!", - "账户已锁定": "账户已锁定", - "账户数据": "账户数据", - "账户管理": "账户管理", - "账户绑定": "账户绑定", - "账户绑定、安全设置和身份验证": "账户绑定、安全设置和身份验证", - "账户绑定管理": "账户绑定管理", - "账户统计": "账户统计", - "货币": "货币", - "货币单位": "货币单位", - "购买上限": "购买上限", - "购买兑换码": "购买兑换码", - "购买套餐后即可享受模型权益": "购买套餐后即可享受模型权益", - "购买或手动新增订阅会升级到该分组;当套餐失效/过期或手动作废/删除后,将回退到升级前分组。回退不会立即生效,通常会有几分钟延迟。": "购买或手动新增订阅会升级到该分组;当套餐失效/过期或手动作废/删除后,将回退到升级前分组。回退不会立即生效,通常会有几分钟延迟。", - "购买订阅套餐": "购买订阅套餐", - "费用信息": "费用信息", - "费用预估": "费用预估", - "资源消耗": "资源消耗", - "起始时间": "起始时间", - "超级管理员": "超级管理员", - "超级管理员未设置充值链接!": "超级管理员未设置充值链接!", - "超过阈值时拒绝新请求": "超过阈值时拒绝新请求", - "跟随日志": "跟随日志", - "跟随系统主题设置": "跟随系统主题设置", - "跨分组": "跨分组", - "跨分组重试": "跨分组重试", - "路径正则": "路径正则", - "路径正则(每行一个)": "路径正则(每行一个)", - "跳转": "跳转", - "轮询": "轮询", - "轮询模式": "轮询模式", - "轮询模式必须搭配Redis和内存缓存功能使用,否则性能将大幅降低,并且无法实现轮询功能": "轮询模式必须搭配Redis和内存缓存功能使用,否则性能将大幅降低,并且无法实现轮询功能", - "输入": "输入", - "输入 OIDC 的 Authorization Endpoint": "输入 OIDC 的 Authorization Endpoint", - "输入 OIDC 的 Client ID": "输入 OIDC 的 Client ID", - "输入 OIDC 的 Token Endpoint": "输入 OIDC 的 Token Endpoint", - "输入 OIDC 的 Userinfo Endpoint": "输入 OIDC 的 Userinfo Endpoint", - "输入IP地址后回车,如:8.8.8.8": "输入IP地址后回车,如:8.8.8.8", - "输入JSON对象": "输入JSON对象", - "输入价格": "输入价格", - "输入价格:{{symbol}}{{price}} / 1M tokens{{audioPrice}}": "输入价格:{{symbol}}{{price}} / 1M tokens{{audioPrice}}", - "输入你注册的 LinuxDO OAuth APP 的 ID": "输入你注册的 LinuxDO OAuth APP 的 ID", - "输入你的账户名{{username}}以确认删除": "输入你的账户名{{username}}以确认删除", - "输入域名后回车": "输入域名后回车", - "输入域名后回车,如:example.com": "输入域名后回车,如:example.com", - "输入密码,最短 8 位,最长 20 位": "输入密码,最短 8 位,最长 20 位", - "输入数字": "输入数字", - "输入标签或使用\",\"分隔多个标签": "输入标签或使用\",\"分隔多个标签", - "输入模型倍率": "输入模型倍率", - "输入每次价格": "输入每次价格", - "输入端口后回车,如:80 或 8000-8999": "输入端口后回车,如:80 或 8000-8999", - "输入系统提示词,用户的系统提示词将优先于此设置": "输入系统提示词,用户的系统提示词将优先于此设置", - "输入自定义模型名称": "输入自定义模型名称", - "输入补全价格": "输入补全价格", - "输入补全倍率": "输入补全倍率", - "输入要添加的邮箱域名": "输入要添加的邮箱域名", - "输入认证器应用显示的6位数字验证码": "输入认证器应用显示的6位数字验证码", - "输入邮箱地址": "输入邮箱地址", - "输入金额": "输入金额", - "输入项目名称,按回车添加": "输入项目名称,按回车添加", - "输入额度": "输入额度", - "输入验证码": "输入验证码", - "输入验证码完成设置": "输入验证码完成设置", - "输出": "输出", - "输出 {{completion}} tokens / 1M tokens * {{symbol}}{{compPrice}}) * {{ratioType}} {{ratio}}": "输出 {{completion}} tokens / 1M tokens * {{symbol}}{{compPrice}}) * {{ratioType}} {{ratio}}", - "输出价格": "输出价格", - "输出价格:{{symbol}}{{price}} * {{completionRatio}} = {{symbol}}{{total}} / 1M tokens (补全倍率: {{completionRatio}})": "输出价格:{{symbol}}{{price}} * {{completionRatio}} = {{symbol}}{{total}} / 1M tokens (补全倍率: {{completionRatio}})", - "输出倍率 {{completionRatio}}": "输出倍率 {{completionRatio}}", - "边栏设置": "边栏设置", - "过期于": "过期于", - "过期时间": "过期时间", - "过期时间不能早于当前时间!": "过期时间不能早于当前时间!", - "过期时间快捷设置": "过期时间快捷设置", - "过期时间格式错误!": "过期时间格式错误!", - "运营设置": "运营设置", - "运行中": "运行中", - "运行命令 (Command)": "运行命令 (Command)", - "运行时长": "运行时长", - "运行时长(小时)": "运行时长(小时)", - "返回修改": "返回修改", - "返回登录": "返回登录", - "这将删除超过 10 分钟未使用的临时缓存文件": "这将删除超过 10 分钟未使用的临时缓存文件", - "这是基础金额,实际扣费 = 基础金额 x 系统分组倍率。": "这是基础金额,实际扣费 = 基础金额 x 系统分组倍率。", - "这是重复键中的最后一个,其值将被使用": "这是重复键中的最后一个,其值将被使用", - "这里直接编辑 JSON 对象。适合简单覆盖参数的场景。": "这里直接编辑 JSON 对象。适合简单覆盖参数的场景。", - "进度": "进度", - "进行中": "进行中", - "进行该操作时,可能导致渠道访问错误,请仅在数据库出现问题时使用": "进行该操作时,可能导致渠道访问错误,请仅在数据库出现问题时使用", - "违规扣费": "违规扣费", - "违规扣费金额": "违规扣费金额", - "连接保活设置": "连接保活设置", - "连接已断开": "连接已断开", - "连接测试中...": "连接测试中...", - "追加到现有密钥": "追加到现有密钥", - "追加模式:将新密钥添加到现有密钥列表末尾": "追加模式:将新密钥添加到现有密钥列表末尾", - "追加模式:新密钥将添加到现有密钥列表的末尾": "追加模式:新密钥将添加到现有密钥列表的末尾", - "追加模板": "追加模板", - "退出": "退出", - "退款": "退款", - "适用于个人使用的场景,不需要设置模型价格": "适用于个人使用的场景,不需要设置模型价格", - "适用于为多个用户提供服务的场景": "适用于为多个用户提供服务的场景", - "适用于展示系统功能的场景,提供基础功能演示": "适用于展示系统功能的场景,提供基础功能演示", - "适配 -thinking、-thinking-预算数字 和 -nothinking 后缀": "适配 -thinking、-thinking-预算数字 和 -nothinking 后缀", - "选择充值额度": "选择充值额度", - "选择分组": "选择分组", - "选择同步来源": "选择同步来源", - "选择同步渠道": "选择同步渠道", - "选择同步语言": "选择同步语言", - "选择容器": "选择容器", - "选择您的首选界面语言,设置将自动保存并同步到所有设备": "选择您的首选界面语言,设置将自动保存并同步到所有设备", - "选择成功": "选择成功", - "选择支付方式": "选择支付方式", - "选择支持的认证设备类型": "选择支持的认证设备类型", - "选择方式": "选择方式", - "选择时间": "选择时间", - "选择模型": "选择模型", - "选择模型供应商": "选择模型供应商", - "选择模型后可一键填充当前选中令牌(或本页第一个令牌)。": "选择模型后可一键填充当前选中令牌(或本页第一个令牌)。", - "选择模型开始对话": "选择模型开始对话", - "选择状态": "选择状态", - "选择硬件类型": "选择硬件类型", - "选择端点类型": "选择端点类型", - "选择系统运行模式": "选择系统运行模式", - "选择组类型": "选择组类型", - "选择要覆盖的冲突项": "选择要覆盖的冲突项", - "选择订阅套餐": "选择订阅套餐", - "选择语言": "选择语言", - "选择过期时间(可选,留空为永久)": "选择过期时间(可选,留空为永久)", - "选择部署位置(可多选)": "选择部署位置(可多选)", - "选择预设模板(可选)": "选择预设模板(可选)", - "透传请求体": "透传请求体", - "递归": "递归", - "递归策略": "递归策略", - "通义千问": "通义千问", - "通用设置": "通用设置", - "通知": "通知", - "通知、价格和隐私相关设置": "通知、价格和隐私相关设置", - "通知内容": "通知内容", - "通知内容,支持 {{value}} 变量占位符": "通知内容,支持 {{value}} 变量占位符", - "通知方式": "通知方式", - "通知标题": "通知标题", - "通知类型 (quota_exceed: 额度预警)": "通知类型 (quota_exceed: 额度预警)", - "通知邮箱": "通知邮箱", - "通知配置": "通知配置", - "通过划转功能将奖励额度转入到您的账户余额中": "通过划转功能将奖励额度转入到您的账户余额中", - "通过密码注册时需要进行邮箱验证": "通过密码注册时需要进行邮箱验证", - "通道 ${name} 余额更新成功!": "通道 ${name} 余额更新成功!", - "通道 ${name} 测试成功,模型 ${model} 耗时 ${time.toFixed(2)} 秒。": "通道 ${name} 测试成功,模型 ${model} 耗时 ${time.toFixed(2)} 秒。", - "通道 ${name} 测试成功,耗时 ${time.toFixed(2)} 秒。": "通道 ${name} 测试成功,耗时 ${time.toFixed(2)} 秒。", - "速率限制设置": "速率限制设置", - "逻辑": "逻辑", - "邀请": "邀请", - "邀请人": "邀请人", - "邀请人数": "邀请人数", - "邀请信息": "邀请信息", - "邀请奖励": "邀请奖励", - "邀请好友注册,好友充值后您可获得相应奖励": "邀请好友注册,好友充值后您可获得相应奖励", - "邀请好友获得额外奖励": "邀请好友获得额外奖励", - "邀请新用户奖励额度": "邀请新用户奖励额度", - "邀请的好友越多,获得的奖励越多": "邀请的好友越多,获得的奖励越多", - "邀请码": "邀请码", - "邀请获得额度": "邀请获得额度", - "邀请链接": "邀请链接", - "邀请链接已复制到剪切板": "邀请链接已复制到剪切板", - "邮件通知": "邮件通知", - "邮箱": "邮箱", - "邮箱地址": "邮箱地址", - "邮箱域名格式不正确,请输入有效的域名,如 gmail.com": "邮箱域名格式不正确,请输入有效的域名,如 gmail.com", - "邮箱域名白名单格式不正确": "邮箱域名白名单格式不正确", - "邮箱字段(可选)": "邮箱字段(可选)", - "邮箱账户绑定成功!": "邮箱账户绑定成功!", - "部分保存失败": "部分保存失败", - "部分保存失败,请重试": "部分保存失败,请重试", - "部分渠道测试失败:": "部分渠道测试失败:", - "部署 ID": "部署 ID", - "部署ID": "部署ID", - "部署中": "部署中", - "部署位置": "部署位置", - "部署位置加载中...": "部署位置加载中...", - "部署删除成功": "部署删除成功", - "部署名称": "部署名称", - "部署名称不匹配,请检查后重新输入": "部署名称不匹配,请检查后重新输入", - "部署名称只能包含字母、数字、横线、下划线和中文": "部署名称只能包含字母、数字、横线、下划线和中文", - "部署名称更新成功": "部署名称更新成功", - "部署启动成功": "部署启动成功", - "部署地区": "部署地区", - "部署请求中": "部署请求中", - "部署配置": "部署配置", - "部署重启成功": "部署重启成功", - "配置": "配置", - "配置 Discord OAuth": "配置 Discord OAuth", - "配置 GitHub OAuth App": "配置 GitHub OAuth App", - "配置 Linux DO OAuth": "配置 Linux DO OAuth", - "配置 OIDC": "配置 OIDC", - "配置 Passkey": "配置 Passkey", - "配置 SMTP": "配置 SMTP", - "配置 Telegram 登录": "配置 Telegram 登录", - "配置 Turnstile": "配置 Turnstile", - "配置 WeChat Server": "配置 WeChat Server", - "配置和消息已全部重置": "配置和消息已全部重置", - "配置套餐的有效时长": "配置套餐的有效时长", - "配置如何从用户信息 API 响应中提取用户数据,支持 JSONPath 语法": "配置如何从用户信息 API 响应中提取用户数据,支持 JSONPath 语法", - "配置完成后刷新页面即可使用模型部署功能": "配置完成后刷新页面即可使用模型部署功能", - "配置导入成功": "配置导入成功", - "配置已导出到下载文件夹": "配置已导出到下载文件夹", - "配置已重置,对话消息已保留": "配置已重置,对话消息已保留", - "配置文件同步": "配置文件同步", - "配置更新确认": "配置更新确认", - "配置有效的 io.net API Key": "配置有效的 io.net API Key", - "配置服务器端请求伪造(SSRF)防护,用于保护内网资源安全": "配置服务器端请求伪造(SSRF)防护,用于保护内网资源安全", - "配置模型部署服务提供商的API密钥和启用状态": "配置模型部署服务提供商的API密钥和启用状态", - "配置登录注册": "配置登录注册", - "配置自定义 OAuth 提供商,支持 GitHub Enterprise、GitLab、Gitea、Nextcloud、Keycloak、ORY 等兼容 OAuth 2.0 协议的身份提供商": "配置自定义 OAuth 提供商,支持 GitHub Enterprise、GitLab、Gitea、Nextcloud、Keycloak、ORY 等兼容 OAuth 2.0 协议的身份提供商", - "配置说明": "配置说明", - "配置邮箱域名白名单": "配置邮箱域名白名单", - "重启部署失败": "重启部署失败", - "重命名部署": "重命名部署", - "重复提交": "重复提交", - "重复的键名": "重复的键名", - "重复的键名,此值将被后面的同名键覆盖": "重复的键名,此值将被后面的同名键覆盖", - "重定向 URL 填": "重定向 URL 填", - "重新发送": "重新发送", - "重新生成": "重新生成", - "重新生成备用码": "重新生成备用码", - "重新生成备用码失败": "重新生成备用码失败", - "重新生成备用码将使现有的备用码失效,请确保您已保存了当前的备用码。": "重新生成备用码将使现有的备用码失效,请确保您已保存了当前的备用码。", - "重绘": "重绘", - "重置": "重置", - "重置 2FA": "重置 2FA", - "重置 Passkey": "重置 Passkey", - "重置为默认": "重置为默认", - "重置周期": "重置周期", - "重置失败": "重置失败", - "重置模型倍率": "重置模型倍率", - "重置统计": "重置统计", - "重置选项": "重置选项", - "重置邮件发送成功,请检查邮箱!": "重置邮件发送成功,请检查邮箱!", - "重置配置": "重置配置", - "重要提醒": "重要提醒", - "重试": "重试", - "重试建议": "重试建议", - "重试连接": "重试连接", - "金额": "金额", - "钱包管理": "钱包管理", - "链接中的{key}将自动替换为sk-xxxx,{address}将自动替换为系统设置的服务器地址,末尾不带/和/v1": "链接中的{key}将自动替换为sk-xxxx,{address}将自动替换为系统设置的服务器地址,末尾不带/和/v1", - "销毁容器": "销毁容器", - "错误": "错误", - "错误代码(可选)": "错误代码(可选)", - "错误消息(必填)": "错误消息(必填)", - "错误类型(可选)": "错误类型(可选)", - "键为分组名称,值为另一个 JSON 对象,键为分组名称,值为该分组的用户的特殊分组倍率,例如:{\"vip\": {\"default\": 0.5, \"test\": 1}},表示 vip 分组的用户在使用default分组的令牌时倍率为0.5,使用test分组时倍率为1": "键为分组名称,值为另一个 JSON 对象,键为分组名称,值为该分组的用户的特殊分组倍率,例如:{\"vip\": {\"default\": 0.5, \"test\": 1}},表示 vip 分组的用户在使用default分组的令牌时倍率为0.5,使用test分组时倍率为1", - "键为原状态码,值为要复写的状态码,仅影响本地判断": "键为原状态码,值为要复写的状态码,仅影响本地判断", - "键为用户分组名称,值为操作映射对象。内层键以\"+:\"开头表示添加指定分组(键值为分组名称,值为描述),以\"-:\"开头表示移除指定分组(键值为分组名称),不带前缀的键直接添加该分组。例如:{\"vip\": {\"+:premium\": \"高级分组\", \"special\": \"特殊分组\", \"-:default\": \"默认分组\"}},表示 vip 分组的用户可以使用 premium 和 special 分组,同时移除 default 分组的访问权限": "键为用户分组名称,值为操作映射对象。内层键以\"+:\"开头表示添加指定分组(键值为分组名称,值为描述),以\"-:\"开头表示移除指定分组(键值为分组名称),不带前缀的键直接添加该分组。例如:{\"vip\": {\"+:premium\": \"高级分组\", \"special\": \"特殊分组\", \"-:default\": \"默认分组\"}},表示 vip 分组的用户可以使用 premium 和 special 分组,同时移除 default 分组的访问权限", - "键为端点类型,值为路径和方法对象": "键为端点类型,值为路径和方法对象", - "键为请求中的模型名称,值为要替换的模型名称": "键为请求中的模型名称,值为要替换的模型名称", - "键名": "键名", - "镜像仓库密码": "镜像仓库密码", - "镜像仓库用户名": "镜像仓库用户名", - "镜像仓库配置": "镜像仓库配置", - "镜像地址": "镜像地址", - "镜像选择": "镜像选择", - "镜像配置": "镜像配置", - "问题标题": "问题标题", - "队列中": "队列中", - "附加条件": "附加条件", - "降低您账户的安全性": "降低您账户的安全性", - "降级": "降级", - "限制周期": "限制周期", - "限制周期统一使用上方配置的“限制周期”值。": "限制周期统一使用上方配置的“限制周期”值。", - "限流": "限流", - "限购": "限购", - "隐私政策": "隐私政策", - "隐私政策已更新": "隐私政策已更新", - "隐私政策更新失败": "隐私政策更新失败", - "隐私设置": "隐私设置", - "隐藏操作项": "隐藏操作项", - "隐藏调试": "隐藏调试", - "随机": "随机", - "随机模式": "随机模式", - "随机种子 (留空为随机)": "随机种子 (留空为随机)", - "零一万物": "零一万物", - "需要安全验证": "需要安全验证", - "需要登录访问": "需要登录访问", - "需要配置的项目": "需要配置的项目", - "需要重新完整设置才能再次启用": "需要重新完整设置才能再次启用", - "非必要,不建议启用模型限制": "非必要,不建议启用模型限制", - "非流": "非流", - "音乐预览": "音乐预览", - "音频倍率(仅部分模型支持该计费)": "音频倍率(仅部分模型支持该计费)", - "音频提示 {{input}} tokens / 1M tokens * {{symbol}}{{audioInputPrice}} + 音频补全 {{completion}} tokens / 1M tokens * {{symbol}}{{audioCompPrice}} = {{symbol}}{{total}}": "音频提示 {{input}} tokens / 1M tokens * {{symbol}}{{audioInputPrice}} + 音频补全 {{completion}} tokens / 1M tokens * {{symbol}}{{audioCompPrice}} = {{symbol}}{{total}}", - "音频提示价格:{{symbol}}{{price}} * {{audioRatio}} = {{symbol}}{{total}} / 1M tokens (音频倍率: {{audioRatio}})": "音频提示价格:{{symbol}}{{price}} * {{audioRatio}} = {{symbol}}{{total}} / 1M tokens (音频倍率: {{audioRatio}})", - "音频无法播放": "音频无法播放", - "音频补全价格:{{symbol}}{{price}} * {{audioRatio}} * {{audioCompRatio}} = {{symbol}}{{total}} / 1M tokens (音频补全倍率: {{audioCompRatio}})": "音频补全价格:{{symbol}}{{price}} * {{audioRatio}} * {{audioCompRatio}} = {{symbol}}{{total}} / 1M tokens (音频补全倍率: {{audioCompRatio}})", - "音频补全倍率(仅部分模型支持该计费)": "音频补全倍率(仅部分模型支持该计费)", - "音频输入相关的倍率设置,键为模型名称,值为倍率": "音频输入相关的倍率设置,键为模型名称,值为倍率", - "音频输出补全相关的倍率设置,键为模型名称,值为倍率": "音频输出补全相关的倍率设置,键为模型名称,值为倍率", - "页脚": "页脚", - "页面未找到,请检查您的浏览器地址是否正确": "页面未找到,请检查您的浏览器地址是否正确", - "顶栏管理": "顶栏管理", - "项": "项", - "项目": "项目", - "项目内容": "项目内容", - "项目操作按钮组": "项目操作按钮组", - "预估总费用": "预估总费用", - "预估费用仅供参考,实际费用可能略有差异": "预估费用仅供参考,实际费用可能略有差异", - "预填组管理": "预填组管理", - "预扣": "预扣", - "预览失败": "预览失败", - "预览更新": "预览更新", - "预览模板": "预览模板", - "预览请求体": "预览请求体", - "预计结束": "预计结束", - "预设模板": "预设模板", - "预警阈值必须为正数": "预警阈值必须为正数", - "频率惩罚,减少重复词汇的出现": "频率惩罚,减少重复词汇的出现", - "频率限制的周期(分钟)": "频率限制的周期(分钟)", - "颜色": "颜色", - "额度": "额度", - "额度充值": "额度充值", - "额度必须大于0": "额度必须大于0", - "额度提醒阈值": "额度提醒阈值", - "额度查询接口返回令牌额度而非用户额度": "额度查询接口返回令牌额度而非用户额度", - "额度设置": "额度设置", - "额度重置": "额度重置", - "额度预警阈值": "额度预警阈值", - "首尾生视频": "首尾生视频", - "首页": "首页", - "首页内容": "首页内容", - "验证": "验证", - "验证 Passkey": "验证 Passkey", - "验证失败,请重试": "验证失败,请重试", - "验证成功": "验证成功", - "验证数据库连接状态": "验证数据库连接状态", - "验证码": "验证码", - "验证码发送成功,请检查邮箱!": "验证码发送成功,请检查邮箱!", - "验证设置": "验证设置", - "验证身份": "验证身份", - "验证配置错误": "验证配置错误", - "高级": "高级", - "高级文本编辑": "高级文本编辑", - "高级设置": "高级设置", - "高级选项": "高级选项", - "高级配置": "高级配置", - "黑名单": "黑名单", - "默认": "默认", - "默认 API 版本": "默认 API 版本", - "默认 Responses API 版本,为空则使用上方版本": "默认 Responses API 版本,为空则使用上方版本", - "默认 TTL(秒)": "默认 TTL(秒)", - "默认为 5m 缓存创建倍率;1h 缓存创建倍率按固定乘法自动计算(当前为 1.6x)": "默认为 5m 缓存创建倍率;1h 缓存创建倍率按固定乘法自动计算(当前为 1.6x)", - "默认使用系统名称": "默认使用系统名称", - "默认助手消息": "默认助手消息", - "默认区域": "默认区域", - "默认区域,如: us-central1": "默认区域,如: us-central1", - "默认折叠侧边栏": "默认折叠侧边栏", - "默认测试模型": "默认测试模型", - "默认用户消息": "默认用户消息", - "默认补全倍率": "默认补全倍率" - } -} diff --git a/web/src/pages/Setting/Ratio/GroupRatioSettings.jsx b/web/src/pages/Setting/Ratio/GroupRatioSettings.jsx index fb9d0fb1f..bcfb1021f 100644 --- a/web/src/pages/Setting/Ratio/GroupRatioSettings.jsx +++ b/web/src/pages/Setting/Ratio/GroupRatioSettings.jsx @@ -265,7 +265,7 @@ export default function GroupRatioSettings(props) { - + ); } diff --git a/web/src/pages/Setting/Ratio/ModelRationNotSetEditor.jsx b/web/src/pages/Setting/Ratio/ModelRationNotSetEditor.jsx index 5d6dd154f..3a856bfce 100644 --- a/web/src/pages/Setting/Ratio/ModelRationNotSetEditor.jsx +++ b/web/src/pages/Setting/Ratio/ModelRationNotSetEditor.jsx @@ -18,47 +18,13 @@ For commercial licensing, please contact support@quantumnous.com */ import React, { useEffect, useState } from 'react'; -import { - Table, - Button, - Input, - Modal, - Form, - Space, - Typography, - Radio, - Notification, -} from '@douyinfe/semi-ui'; -import { - IconDelete, - IconPlus, - IconSearch, - IconSave, - IconBolt, -} from '@douyinfe/semi-icons'; -import { API, showError, showSuccess } from '../../../helpers'; +import { API, showError } from '../../../helpers'; import { useTranslation } from 'react-i18next'; +import ModelPricingEditor from './components/ModelPricingEditor'; export default function ModelRatioNotSetEditor(props) { const { t } = useTranslation(); - const [models, setModels] = useState([]); - const [visible, setVisible] = useState(false); - const [batchVisible, setBatchVisible] = useState(false); - const [currentModel, setCurrentModel] = useState(null); - const [searchText, setSearchText] = useState(''); - const [currentPage, setCurrentPage] = useState(1); - const [pageSize, setPageSize] = useState(10); - const [loading, setLoading] = useState(false); const [enabledModels, setEnabledModels] = useState([]); - const [selectedRowKeys, setSelectedRowKeys] = useState([]); - const [batchFillType, setBatchFillType] = useState('ratio'); - const [batchFillValue, setBatchFillValue] = useState(''); - const [batchRatioValue, setBatchRatioValue] = useState(''); - const [batchCompletionRatioValue, setBatchCompletionRatioValue] = - useState(''); - const { Text } = Typography; - // 定义可选的每页显示条数 - const pageSizeOptions = [10, 20, 50, 100]; const getAllEnabledModels = async () => { try { @@ -79,540 +45,20 @@ export default function ModelRatioNotSetEditor(props) { // 获取所有启用的模型 getAllEnabledModels(); }, []); - - useEffect(() => { - try { - const modelPrice = JSON.parse(props.options.ModelPrice || '{}'); - const modelRatio = JSON.parse(props.options.ModelRatio || '{}'); - const completionRatio = JSON.parse(props.options.CompletionRatio || '{}'); - - // 找出所有未设置价格和倍率的模型 - const unsetModels = enabledModels.filter((modelName) => { - const hasPrice = modelPrice[modelName] !== undefined; - const hasRatio = modelRatio[modelName] !== undefined; - - // 如果模型没有价格或者没有倍率设置,则显示 - return !hasPrice && !hasRatio; - }); - - // 创建模型数据 - const modelData = unsetModels.map((name) => ({ - name, - price: modelPrice[name] || '', - ratio: modelRatio[name] || '', - completionRatio: completionRatio[name] || '', - })); - - setModels(modelData); - // 清空选择 - setSelectedRowKeys([]); - } catch (error) { - console.error(t('JSON解析错误:'), error); - } - }, [props.options, enabledModels]); - - // 首先声明分页相关的工具函数 - const getPagedData = (data, currentPage, pageSize) => { - const start = (currentPage - 1) * pageSize; - const end = start + pageSize; - return data.slice(start, end); - }; - - // 处理页面大小变化 - const handlePageSizeChange = (size) => { - setPageSize(size); - // 重新计算当前页,避免数据丢失 - const totalPages = Math.ceil(filteredModels.length / size); - if (currentPage > totalPages) { - setCurrentPage(totalPages || 1); - } - }; - - // 在 return 语句之前,先处理过滤和分页逻辑 - const filteredModels = models.filter((model) => - searchText ? model.name.includes(searchText) : true, - ); - - // 然后基于过滤后的数据计算分页数据 - const pagedData = getPagedData(filteredModels, currentPage, pageSize); - - const SubmitData = async () => { - setLoading(true); - const output = { - ModelPrice: JSON.parse(props.options.ModelPrice || '{}'), - ModelRatio: JSON.parse(props.options.ModelRatio || '{}'), - CompletionRatio: JSON.parse(props.options.CompletionRatio || '{}'), - }; - - try { - // 数据转换 - 只处理已修改的模型 - models.forEach((model) => { - // 只有当用户设置了值时才更新 - if (model.price !== '') { - // 如果价格不为空,则转换为浮点数,忽略倍率参数 - output.ModelPrice[model.name] = parseFloat(model.price); - } else { - if (model.ratio !== '') - output.ModelRatio[model.name] = parseFloat(model.ratio); - if (model.completionRatio !== '') - output.CompletionRatio[model.name] = parseFloat( - model.completionRatio, - ); - } - }); - - // 准备API请求数组 - const finalOutput = { - ModelPrice: JSON.stringify(output.ModelPrice, null, 2), - ModelRatio: JSON.stringify(output.ModelRatio, null, 2), - CompletionRatio: JSON.stringify(output.CompletionRatio, null, 2), - }; - - const requestQueue = Object.entries(finalOutput).map(([key, value]) => { - return API.put('/api/option/', { - key, - value, - }); - }); - - // 批量处理请求 - const results = await Promise.all(requestQueue); - - // 验证结果 - if (requestQueue.length === 1) { - if (results.includes(undefined)) return; - } else if (requestQueue.length > 1) { - if (results.includes(undefined)) { - return showError(t('部分保存失败,请重试')); - } - } - - // 检查每个请求的结果 - for (const res of results) { - if (!res.data.success) { - return showError(res.data.message); - } - } - - showSuccess(t('保存成功')); - props.refresh(); - // 重新获取未设置的模型 - getAllEnabledModels(); - } catch (error) { - console.error(t('保存失败:'), error); - showError(t('保存失败,请重试')); - } finally { - setLoading(false); - } - }; - - const columns = [ - { - title: t('模型名称'), - dataIndex: 'name', - key: 'name', - }, - { - title: t('模型固定价格'), - dataIndex: 'price', - key: 'price', - render: (text, record) => ( - updateModel(record.name, 'price', value)} - /> - ), - }, - { - title: t('模型倍率'), - dataIndex: 'ratio', - key: 'ratio', - render: (text, record) => ( - updateModel(record.name, 'ratio', value)} - /> - ), - }, - { - title: t('补全倍率'), - dataIndex: 'completionRatio', - key: 'completionRatio', - render: (text, record) => ( - - updateModel(record.name, 'completionRatio', value) - } - /> - ), - }, - ]; - - const updateModel = (name, field, value) => { - if (value !== '' && isNaN(value)) { - showError(t('请输入数字')); - return; - } - setModels((prev) => - prev.map((model) => - model.name === name ? { ...model, [field]: value } : model, - ), - ); - }; - - const addModel = (values) => { - // 检查模型名称是否存在, 如果存在则拒绝添加 - if (models.some((model) => model.name === values.name)) { - showError(t('模型名称已存在')); - return; - } - setModels((prev) => [ - { - name: values.name, - price: values.price || '', - ratio: values.ratio || '', - completionRatio: values.completionRatio || '', - }, - ...prev, - ]); - setVisible(false); - showSuccess(t('添加成功')); - }; - - // 批量填充功能 - const handleBatchFill = () => { - if (selectedRowKeys.length === 0) { - showError(t('请先选择需要批量设置的模型')); - return; - } - - if (batchFillType === 'bothRatio') { - if (batchRatioValue === '' || batchCompletionRatioValue === '') { - showError(t('请输入模型倍率和补全倍率')); - return; - } - if (isNaN(batchRatioValue) || isNaN(batchCompletionRatioValue)) { - showError(t('请输入有效的数字')); - return; - } - } else { - if (batchFillValue === '') { - showError(t('请输入填充值')); - return; - } - if (isNaN(batchFillValue)) { - showError(t('请输入有效的数字')); - return; - } - } - - // 根据选择的类型批量更新模型 - setModels((prev) => - prev.map((model) => { - if (selectedRowKeys.includes(model.name)) { - if (batchFillType === 'price') { - return { - ...model, - price: batchFillValue, - ratio: '', - completionRatio: '', - }; - } else if (batchFillType === 'ratio') { - return { - ...model, - price: '', - ratio: batchFillValue, - }; - } else if (batchFillType === 'completionRatio') { - return { - ...model, - price: '', - completionRatio: batchFillValue, - }; - } else if (batchFillType === 'bothRatio') { - return { - ...model, - price: '', - ratio: batchRatioValue, - completionRatio: batchCompletionRatioValue, - }; - } - } - return model; - }), - ); - - setBatchVisible(false); - Notification.success({ - title: t('批量设置成功'), - content: t('已为 {{count}} 个模型设置{{type}}', { - count: selectedRowKeys.length, - type: - batchFillType === 'price' - ? t('固定价格') - : batchFillType === 'ratio' - ? t('模型倍率') - : batchFillType === 'completionRatio' - ? t('补全倍率') - : t('模型倍率和补全倍率'), - }), - duration: 3, - }); - }; - - const handleBatchTypeChange = (value) => { - console.log(t('Changing batch type to:'), value); - setBatchFillType(value); - - // 切换类型时清空对应的值 - if (value !== 'bothRatio') { - setBatchFillValue(''); - } else { - setBatchRatioValue(''); - setBatchCompletionRatioValue(''); - } - }; - - const rowSelection = { - selectedRowKeys, - onChange: (selectedKeys) => { - setSelectedRowKeys(selectedKeys); - }, - }; - return ( - <> - - - - - - } - placeholder={t('搜索模型名称')} - value={searchText} - onChange={(value) => { - setSearchText(value); - setCurrentPage(1); - }} - style={{ width: 200 }} - /> - - - - {t('此页面仅显示未设置价格或倍率的模型,设置后将自动从列表中移除')} - - - setCurrentPage(page), - onPageSizeChange: handlePageSizeChange, - pageSizeOptions: pageSizeOptions, - showTotal: true, - showSizeChanger: true, - }} - empty={ -
- {t('没有未设置的模型')} -
- } - /> - - - {/* 添加模型弹窗 */} - setVisible(false)} - onOk={() => { - currentModel && addModel(currentModel); - }} - > -
- - setCurrentModel((prev) => ({ ...prev, name: value })) - } - /> - - {t('定价模式')}: - {currentModel?.priceMode ? t('固定价格') : t('倍率模式')} - - } - onChange={(checked) => { - setCurrentModel((prev) => ({ - ...prev, - price: '', - ratio: '', - completionRatio: '', - priceMode: checked, - })); - }} - /> - {currentModel?.priceMode ? ( - - setCurrentModel((prev) => ({ ...prev, price: value })) - } - /> - ) : ( - <> - - setCurrentModel((prev) => ({ ...prev, ratio: value })) - } - /> - - setCurrentModel((prev) => ({ - ...prev, - completionRatio: value, - })) - } - /> - - )} - -
- - {/* 批量设置弹窗 */} - setBatchVisible(false)} - onOk={handleBatchFill} - width={500} - > -
- -
- - handleBatchTypeChange('price')} - > - {t('固定价格')} - - handleBatchTypeChange('ratio')} - > - {t('模型倍率')} - - handleBatchTypeChange('completionRatio')} - > - {t('补全倍率')} - - handleBatchTypeChange('bothRatio')} - > - {t('模型倍率和补全倍率同时设置')} - - -
-
- - {batchFillType === 'bothRatio' ? ( - <> - setBatchRatioValue(value)} - /> - setBatchCompletionRatioValue(value)} - /> - - ) : ( - setBatchFillValue(value)} - /> - )} - - - {t('将为选中的 ')} {selectedRowKeys.length}{' '} - {t(' 个模型设置相同的值')} - -
- - {t('当前设置类型: ')}{' '} - - {batchFillType === 'price' - ? t('固定价格') - : batchFillType === 'ratio' - ? t('模型倍率') - : batchFillType === 'completionRatio' - ? t('补全倍率') - : t('模型倍率和补全倍率')} - - -
- -
- + ); } diff --git a/web/src/pages/Setting/Ratio/ModelSettingsVisualEditor.jsx b/web/src/pages/Setting/Ratio/ModelSettingsVisualEditor.jsx index b5ad3e58d..2b7e44a46 100644 --- a/web/src/pages/Setting/Ratio/ModelSettingsVisualEditor.jsx +++ b/web/src/pages/Setting/Ratio/ModelSettingsVisualEditor.jsx @@ -17,741 +17,9 @@ along with this program. If not, see . For commercial licensing, please contact support@quantumnous.com */ -import React, { useEffect, useState, useRef } from 'react'; -import { - Table, - Button, - Input, - Modal, - Form, - Space, - RadioGroup, - Radio, - Checkbox, - Tag, -} from '@douyinfe/semi-ui'; -import { - IconDelete, - IconPlus, - IconSearch, - IconSave, - IconEdit, -} from '@douyinfe/semi-icons'; -import { API, showError, showSuccess, getQuotaPerUnit } from '../../../helpers'; -import { useTranslation } from 'react-i18next'; +import React from 'react'; +import ModelPricingEditor from './components/ModelPricingEditor'; export default function ModelSettingsVisualEditor(props) { - const { t } = useTranslation(); - const [models, setModels] = useState([]); - const [visible, setVisible] = useState(false); - const [isEditMode, setIsEditMode] = useState(false); - const [currentModel, setCurrentModel] = useState(null); - const [searchText, setSearchText] = useState(''); - const [currentPage, setCurrentPage] = useState(1); - const [loading, setLoading] = useState(false); - const [pricingMode, setPricingMode] = useState('per-token'); // 'per-token' or 'per-request' - const [pricingSubMode, setPricingSubMode] = useState('ratio'); // 'ratio' or 'token-price' - const [conflictOnly, setConflictOnly] = useState(false); - const formRef = useRef(null); - const pageSize = 10; - const quotaPerUnit = getQuotaPerUnit(); - - useEffect(() => { - try { - const modelPrice = JSON.parse(props.options.ModelPrice || '{}'); - const modelRatio = JSON.parse(props.options.ModelRatio || '{}'); - const completionRatio = JSON.parse(props.options.CompletionRatio || '{}'); - - // 合并所有模型名称 - const modelNames = new Set([ - ...Object.keys(modelPrice), - ...Object.keys(modelRatio), - ...Object.keys(completionRatio), - ]); - - const modelData = Array.from(modelNames).map((name) => { - const price = modelPrice[name] === undefined ? '' : modelPrice[name]; - const ratio = modelRatio[name] === undefined ? '' : modelRatio[name]; - const comp = - completionRatio[name] === undefined ? '' : completionRatio[name]; - - return { - name, - price, - ratio, - completionRatio: comp, - hasConflict: price !== '' && (ratio !== '' || comp !== ''), - }; - }); - - setModels(modelData); - } catch (error) { - console.error('JSON解析错误:', error); - } - }, [props.options]); - - // 首先声明分页相关的工具函数 - const getPagedData = (data, currentPage, pageSize) => { - const start = (currentPage - 1) * pageSize; - const end = start + pageSize; - return data.slice(start, end); - }; - - // 在 return 语句之前,先处理过滤和分页逻辑 - const filteredModels = models.filter((model) => { - const keywordMatch = searchText ? model.name.includes(searchText) : true; - const conflictMatch = conflictOnly ? model.hasConflict : true; - return keywordMatch && conflictMatch; - }); - - // 然后基于过滤后的数据计算分页数据 - const pagedData = getPagedData(filteredModels, currentPage, pageSize); - - const SubmitData = async () => { - setLoading(true); - const output = { - ModelPrice: {}, - ModelRatio: {}, - CompletionRatio: {}, - }; - let currentConvertModelName = ''; - - try { - // 数据转换 - models.forEach((model) => { - currentConvertModelName = model.name; - if (model.price !== '') { - // 如果价格不为空,则转换为浮点数,忽略倍率参数 - output.ModelPrice[model.name] = parseFloat(model.price); - } else { - if (model.ratio !== '') - output.ModelRatio[model.name] = parseFloat(model.ratio); - if (model.completionRatio !== '') - output.CompletionRatio[model.name] = parseFloat( - model.completionRatio, - ); - } - }); - - // 准备API请求数组 - const finalOutput = { - ModelPrice: JSON.stringify(output.ModelPrice, null, 2), - ModelRatio: JSON.stringify(output.ModelRatio, null, 2), - CompletionRatio: JSON.stringify(output.CompletionRatio, null, 2), - }; - - const requestQueue = Object.entries(finalOutput).map(([key, value]) => { - return API.put('/api/option/', { - key, - value, - }); - }); - - // 批量处理请求 - const results = await Promise.all(requestQueue); - - // 验证结果 - if (requestQueue.length === 1) { - if (results.includes(undefined)) return; - } else if (requestQueue.length > 1) { - if (results.includes(undefined)) { - return showError('部分保存失败,请重试'); - } - } - - // 检查每个请求的结果 - for (const res of results) { - if (!res.data.success) { - return showError(res.data.message); - } - } - - showSuccess('保存成功'); - props.refresh(); - } catch (error) { - console.error('保存失败:', error); - showError('保存失败,请重试'); - } finally { - setLoading(false); - } - }; - - const columns = [ - { - title: t('模型名称'), - dataIndex: 'name', - key: 'name', - render: (text, record) => ( - - {text} - {record.hasConflict && ( - - {t('矛盾')} - - )} - - ), - }, - { - title: t('模型固定价格'), - dataIndex: 'price', - key: 'price', - render: (text, record) => ( - updateModel(record.name, 'price', value)} - /> - ), - }, - { - title: t('模型倍率'), - dataIndex: 'ratio', - key: 'ratio', - render: (text, record) => ( - updateModel(record.name, 'ratio', value)} - /> - ), - }, - { - title: t('补全倍率'), - dataIndex: 'completionRatio', - key: 'completionRatio', - render: (text, record) => ( - - updateModel(record.name, 'completionRatio', value) - } - /> - ), - }, - { - title: t('操作'), - key: 'action', - render: (_, record) => ( - - - - - } - placeholder={t('搜索模型名称')} - value={searchText} - onChange={(value) => { - setSearchText(value); - setCurrentPage(1); - }} - style={{ width: 200 }} - showClear - /> - { - setConflictOnly(e.target.checked); - setCurrentPage(1); - }} - > - {t('仅显示矛盾倍率')} - - -
setCurrentPage(page), - showTotal: true, - showSizeChanger: false, - }} - /> - - - { - resetModalState(); - setVisible(false); - }} - onOk={() => { - if (currentModel) { - // If we're in token price mode, make sure ratio values are properly set - const valuesToSave = { ...currentModel }; - - if ( - pricingMode === 'per-token' && - pricingSubMode === 'token-price' && - currentModel.tokenPrice - ) { - // Calculate and set ratio from token price - const tokenPrice = parseFloat(currentModel.tokenPrice); - valuesToSave.ratio = (tokenPrice / 2).toString(); - - // Calculate and set completion ratio if both token prices are available - if ( - currentModel.completionTokenPrice && - currentModel.tokenPrice - ) { - const completionPrice = parseFloat( - currentModel.completionTokenPrice, - ); - const modelPrice = parseFloat(currentModel.tokenPrice); - if (modelPrice > 0) { - valuesToSave.completionRatio = ( - completionPrice / modelPrice - ).toString(); - } - } - } - - // Clear price if we're in per-token mode - if (pricingMode === 'per-token') { - valuesToSave.price = ''; - } else { - // Clear ratios if we're in per-request mode - valuesToSave.ratio = ''; - valuesToSave.completionRatio = ''; - } - - addOrUpdateModel(valuesToSave); - } - }} - > -
(formRef.current = api)}> - - setCurrentModel((prev) => ({ ...prev, name: value })) - } - /> - - -
- { - const newMode = e.target.value; - const oldMode = pricingMode; - setPricingMode(newMode); - - // Instead of resetting all values, convert between modes - if (currentModel) { - const updatedModel = { ...currentModel }; - - // Update formRef with converted values - if (formRef.current) { - const formValues = { - name: updatedModel.name, - }; - - if (newMode === 'per-request') { - formValues.priceInput = updatedModel.price || ''; - } else if (newMode === 'per-token') { - formValues.ratioInput = updatedModel.ratio || ''; - formValues.completionRatioInput = - updatedModel.completionRatio || ''; - formValues.modelTokenPrice = - updatedModel.tokenPrice || ''; - formValues.completionTokenPrice = - updatedModel.completionTokenPrice || ''; - } - - formRef.current.setValues(formValues); - } - - // Update the model state - setCurrentModel(updatedModel); - } - }} - > - {t('按量计费')} - {t('按次计费')} - -
-
- - {pricingMode === 'per-token' && ( - <> - -
- { - const newSubMode = e.target.value; - const oldSubMode = pricingSubMode; - setPricingSubMode(newSubMode); - - // Handle conversion between submodes - if (currentModel) { - const updatedModel = { ...currentModel }; - - // Convert between ratio and token price - if ( - oldSubMode === 'ratio' && - newSubMode === 'token-price' - ) { - if (updatedModel.ratio) { - updatedModel.tokenPrice = - calculateTokenPriceFromRatio( - parseFloat(updatedModel.ratio), - ).toString(); - - if (updatedModel.completionRatio) { - updatedModel.completionTokenPrice = ( - parseFloat(updatedModel.tokenPrice) * - parseFloat(updatedModel.completionRatio) - ).toString(); - } - } - } else if ( - oldSubMode === 'token-price' && - newSubMode === 'ratio' - ) { - // Ratio values should already be calculated by the handlers - } - - // Update the form values - if (formRef.current) { - const formValues = {}; - - if (newSubMode === 'ratio') { - formValues.ratioInput = updatedModel.ratio || ''; - formValues.completionRatioInput = - updatedModel.completionRatio || ''; - } else if (newSubMode === 'token-price') { - formValues.modelTokenPrice = - updatedModel.tokenPrice || ''; - formValues.completionTokenPrice = - updatedModel.completionTokenPrice || ''; - } - - formRef.current.setValues(formValues); - } - - setCurrentModel(updatedModel); - } - }} - > - {t('按倍率设置')} - {t('按价格设置')} - -
-
- - {pricingSubMode === 'ratio' && ( - <> - - setCurrentModel((prev) => ({ - ...(prev || {}), - ratio: value, - })) - } - initValue={currentModel?.ratio || ''} - /> - - setCurrentModel((prev) => ({ - ...(prev || {}), - completionRatio: value, - })) - } - initValue={currentModel?.completionRatio || ''} - /> - - )} - - {pricingSubMode === 'token-price' && ( - <> - { - handleTokenPriceChange(value); - }} - initValue={currentModel?.tokenPrice || ''} - suffix={t('$/1M tokens')} - /> - { - handleCompletionTokenPriceChange(value); - }} - initValue={currentModel?.completionTokenPrice || ''} - suffix={t('$/1M tokens')} - /> - - )} - - )} - - {pricingMode === 'per-request' && ( - - setCurrentModel((prev) => ({ - ...(prev || {}), - price: value, - })) - } - initValue={currentModel?.price || ''} - /> - )} - -
- - ); + return ; } diff --git a/web/src/pages/Setting/Ratio/components/ModelPricingEditor.jsx b/web/src/pages/Setting/Ratio/components/ModelPricingEditor.jsx new file mode 100644 index 000000000..d4c2ff662 --- /dev/null +++ b/web/src/pages/Setting/Ratio/components/ModelPricingEditor.jsx @@ -0,0 +1,739 @@ +/* +Copyright (C) 2025 QuantumNous + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 of the +License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . + +For commercial licensing, please contact support@quantumnous.com +*/ + +import React, { useMemo, useState } from 'react'; +import { + Banner, + Button, + Card, + Checkbox, + Empty, + Input, + Modal, + Radio, + RadioGroup, + Space, + Switch, + Table, + Tag, + Typography, +} from '@douyinfe/semi-ui'; +import { + IconDelete, + IconPlus, + IconSave, + IconSearch, +} from '@douyinfe/semi-icons'; +import { useTranslation } from 'react-i18next'; +import { + PAGE_SIZE, + PRICE_SUFFIX, + buildSummaryText, + hasValue, + useModelPricingEditorState, +} from '../hooks/useModelPricingEditorState'; +import { useIsMobile } from '../../../../hooks/common/useIsMobile'; + +const { Text } = Typography; +const EMPTY_CANDIDATE_MODEL_NAMES = []; + +const PriceInput = ({ + label, + value, + placeholder, + onChange, + suffix = PRICE_SUFFIX, + disabled = false, + extraText = '', + headerAction = null, + hidden = false, +}) => ( +
+
+ {label} + {headerAction} +
+ {!hidden ? ( + + ) : null} + {extraText ? ( +
{extraText}
+ ) : null} +
+); + +export default function ModelPricingEditor({ + options, + refresh, + candidateModelNames = EMPTY_CANDIDATE_MODEL_NAMES, + filterMode = 'all', + allowAddModel = true, + allowDeleteModel = true, + showConflictFilter = true, + listDescription = '', + emptyTitle = '', + emptyDescription = '', +}) { + const { t } = useTranslation(); + const isMobile = useIsMobile(); + const [addVisible, setAddVisible] = useState(false); + const [batchVisible, setBatchVisible] = useState(false); + const [newModelName, setNewModelName] = useState(''); + + const { + selectedModel, + selectedModelName, + selectedModelNames, + setSelectedModelName, + setSelectedModelNames, + searchText, + setSearchText, + currentPage, + setCurrentPage, + loading, + conflictOnly, + setConflictOnly, + filteredModels, + pagedData, + selectedWarnings, + previewRows, + isOptionalFieldEnabled, + handleOptionalFieldToggle, + handleNumericFieldChange, + handleBillingModeChange, + handleSubmit, + addModel, + deleteModel, + applySelectedModelPricing, + } = useModelPricingEditorState({ + options, + refresh, + t, + candidateModelNames, + filterMode, + }); + + const columns = useMemo( + () => [ + { + title: t('模型名称'), + dataIndex: 'name', + key: 'name', + render: (text, record) => ( + + + {selectedModelNames.includes(record.name) ? ( + + {t('已勾选')} + + ) : null} + {record.hasConflict ? ( + + {t('矛盾')} + + ) : null} + + ), + }, + { + title: t('计费方式'), + dataIndex: 'billingMode', + key: 'billingMode', + render: (_, record) => ( + + {record.billingMode === 'per-request' + ? t('按次计费') + : t('按量计费')} + + ), + }, + { + title: t('价格摘要'), + dataIndex: 'summary', + key: 'summary', + render: (_, record) => buildSummaryText(record, t), + }, + { + title: t('操作'), + key: 'action', + render: (_, record) => ( + + {allowDeleteModel ? ( + + ) : null} + + + } + placeholder={t('搜索模型名称')} + value={searchText} + onChange={(value) => setSearchText(value)} + style={{ width: isMobile ? '100%' : 220 }} + showClear + /> + {showConflictFilter ? ( + setConflictOnly(event.target.checked)} + > + {t('仅显示矛盾倍率')} + + ) : null} + + + {listDescription ? ( +
{listDescription}
+ ) : null} + {selectedModelNames.length > 0 ? ( +
+ {t('已勾选 {{count}} 个模型', { count: selectedModelNames.length })} +
+ ) : null} + +
+ +
+
setCurrentPage(page), + showTotal: true, + showSizeChanger: false, + }} + empty={ +
+ {emptyTitle || t('暂无模型')} +
+ } + onRow={(record) => ({ + style: { + background: selectedModelNames.includes(record.name) + ? 'var(--semi-color-success-light-default)' + : record.name === selectedModelName + ? 'var(--semi-color-primary-light-default)' + : undefined, + boxShadow: selectedModelNames.includes(record.name) + ? 'inset 4px 0 0 var(--semi-color-success)' + : record.name === selectedModelName + ? 'inset 4px 0 0 var(--semi-color-primary)' + : undefined, + transition: 'background 0.2s ease, box-shadow 0.2s ease', + }, + onClick: () => setSelectedModelName(record.name), + })} + scroll={isMobile ? { x: 720 } : undefined} + /> + + + + + {selectedModel.billingMode === 'per-request' + ? t('按次计费') + : t('按量计费')} + + ) : null + } + > + {!selectedModel ? ( + + ) : ( +
+
+
+ {t('计费方式')} +
+ handleBillingModeChange(event.target.value)} + > + {t('按量计费')} + {t('按次计费')} + +
+ {t( + '这个界面默认按价格填写,保存时会自动换算回后端需要的倍率 JSON。', + )} +
+
+ + {selectedWarnings.length > 0 ? ( + +
{t('当前提示')}
+ {selectedWarnings.map((warning) => ( +
+ {warning} +
+ ))} +
+ ) : null} + + {selectedModel.billingMode === 'per-request' ? ( + handleNumericFieldChange('fixedPrice', value)} + extraText={t('适合 MJ / 任务类等按次收费模型。')} + /> + ) : ( + <> + +
{t('基础价格')}
+ handleNumericFieldChange('inputPrice', value)} + /> + {selectedModel.completionRatioLocked ? ( + + ) : null} + + handleNumericFieldChange('completionPrice', value) + } + headerAction={ + + handleOptionalFieldToggle('completionPrice', checked) + } + /> + } + hidden={ + !isOptionalFieldEnabled(selectedModel, 'completionPrice') + } + disabled={ + !hasValue(selectedModel.inputPrice) || + selectedModel.completionRatioLocked + } + extraText={ + selectedModel.completionRatioLocked + ? t( + '后端固定倍率:{{ratio}}。该字段仅展示换算后的价格。', + { + ratio: selectedModel.lockedCompletionRatio || '-', + }, + ) + : !isOptionalFieldEnabled( + selectedModel, + 'completionPrice', + ) + ? t('当前未启用,需要时再打开即可。') + : '' + } + /> + handleNumericFieldChange('cachePrice', value)} + headerAction={ + + handleOptionalFieldToggle('cachePrice', checked) + } + /> + } + hidden={!isOptionalFieldEnabled(selectedModel, 'cachePrice')} + disabled={!hasValue(selectedModel.inputPrice)} + extraText={ + !isOptionalFieldEnabled(selectedModel, 'cachePrice') + ? t('当前未启用,需要时再打开即可。') + : '' + } + /> + + handleNumericFieldChange('createCachePrice', value) + } + headerAction={ + + handleOptionalFieldToggle('createCachePrice', checked) + } + /> + } + hidden={ + !isOptionalFieldEnabled(selectedModel, 'createCachePrice') + } + disabled={!hasValue(selectedModel.inputPrice)} + extraText={ + !isOptionalFieldEnabled( + selectedModel, + 'createCachePrice', + ) + ? t('当前未启用,需要时再打开即可。') + : '' + } + /> +
+ + +
+
{t('扩展价格')}
+
+ {t('这些价格都是可选项,不填也可以。')} +
+
+ handleNumericFieldChange('imagePrice', value)} + headerAction={ + + handleOptionalFieldToggle('imagePrice', checked) + } + /> + } + hidden={!isOptionalFieldEnabled(selectedModel, 'imagePrice')} + disabled={!hasValue(selectedModel.inputPrice)} + extraText={ + !isOptionalFieldEnabled(selectedModel, 'imagePrice') + ? t('当前未启用,需要时再打开即可。') + : '' + } + /> + + handleNumericFieldChange('audioInputPrice', value) + } + headerAction={ + + handleOptionalFieldToggle('audioInputPrice', checked) + } + /> + } + hidden={!isOptionalFieldEnabled(selectedModel, 'audioInputPrice')} + disabled={!hasValue(selectedModel.inputPrice)} + extraText={ + !isOptionalFieldEnabled( + selectedModel, + 'audioInputPrice', + ) + ? t('当前未启用,需要时再打开即可。') + : '' + } + /> + + handleNumericFieldChange('audioOutputPrice', value) + } + headerAction={ + + handleOptionalFieldToggle('audioOutputPrice', checked) + } + /> + } + hidden={ + !isOptionalFieldEnabled(selectedModel, 'audioOutputPrice') + } + disabled={!hasValue(selectedModel.audioInputPrice)} + extraText={ + !isOptionalFieldEnabled( + selectedModel, + 'audioInputPrice', + ) + ? t('请先开启并填写音频输入价格。') + : !isOptionalFieldEnabled( + selectedModel, + 'audioOutputPrice', + ) + ? t('当前未启用,需要时再打开即可。') + : '' + } + /> +
+ + )} + + +
{t('保存预览')}
+
+ {t( + '下面展示这个模型保存后会写入哪些后端字段,便于和原始 JSON 编辑框保持一致。', + )} +
+
+ {previewRows.map((row) => ( + + {row.label} + {row.value} + + ))} +
+
+
+ )} +
+ + + + {allowAddModel ? ( + { + setAddVisible(false); + setNewModelName(''); + }} + onOk={handleAddModel} + > + setNewModelName(value)} + /> + + ) : null} + + setBatchVisible(false)} + onOk={() => { + if (applySelectedModelPricing()) { + setBatchVisible(false); + } + }} + > +
+ {selectedModel + ? t( + '将把当前编辑中的模型 {{name}} 的价格配置,批量应用到已勾选的 {{count}} 个模型。', + { + name: selectedModel.name, + count: selectedModelNames.length, + }, + ) + : t('请先选择一个作为模板的模型')} +
+ {selectedModel ? ( +
+ {t( + '适合同系列模型一起定价,例如把 gpt-5.1 的价格批量同步到 gpt-5.1-high、gpt-5.1-low 等模型。', + )} +
+ ) : null} +
+ + ); +} diff --git a/web/src/pages/Setting/Ratio/hooks/useModelPricingEditorState.js b/web/src/pages/Setting/Ratio/hooks/useModelPricingEditorState.js new file mode 100644 index 000000000..c9897287e --- /dev/null +++ b/web/src/pages/Setting/Ratio/hooks/useModelPricingEditorState.js @@ -0,0 +1,937 @@ +import { useEffect, useMemo, useState } from 'react'; +import { API, showError, showSuccess } from '../../../../helpers'; + +export const PAGE_SIZE = 10; +export const PRICE_SUFFIX = '$/1M tokens'; +const EMPTY_CANDIDATE_MODEL_NAMES = []; + +const EMPTY_MODEL = { + name: '', + billingMode: 'per-token', + fixedPrice: '', + inputPrice: '', + completionPrice: '', + lockedCompletionRatio: '', + completionRatioLocked: false, + cachePrice: '', + createCachePrice: '', + imagePrice: '', + audioInputPrice: '', + audioOutputPrice: '', + rawRatios: { + modelRatio: '', + completionRatio: '', + cacheRatio: '', + createCacheRatio: '', + imageRatio: '', + audioRatio: '', + audioCompletionRatio: '', + }, + hasConflict: false, +}; + +const NUMERIC_INPUT_REGEX = /^(\d+(\.\d*)?|\.\d*)?$/; + +export const hasValue = (value) => + value !== '' && value !== null && value !== undefined && value !== false; + +const toNumericString = (value) => { + if (!hasValue(value) && value !== 0) { + return ''; + } + const num = Number(value); + return Number.isFinite(num) ? String(num) : ''; +}; + +const toNumberOrNull = (value) => { + if (!hasValue(value) && value !== 0) { + return null; + } + const num = Number(value); + return Number.isFinite(num) ? num : null; +}; + +const formatNumber = (value) => { + const num = toNumberOrNull(value); + if (num === null) { + return ''; + } + return parseFloat(num.toFixed(12)).toString(); +}; + +const parseOptionJSON = (rawValue) => { + if (!rawValue || rawValue.trim() === '') { + return {}; + } + try { + const parsed = JSON.parse(rawValue); + return parsed && typeof parsed === 'object' ? parsed : {}; + } catch (error) { + console.error('JSON解析错误:', error); + return {}; + } +}; + +const ratioToBasePrice = (ratio) => { + const num = toNumberOrNull(ratio); + if (num === null) return ''; + return formatNumber(num * 2); +}; + +const normalizeCompletionRatioMeta = (rawMeta) => { + if (!rawMeta || typeof rawMeta !== 'object' || Array.isArray(rawMeta)) { + return { + locked: false, + ratio: '', + }; + } + + return { + locked: Boolean(rawMeta.locked), + ratio: toNumericString(rawMeta.ratio), + }; +}; + +const buildModelState = (name, sourceMaps) => { + const modelRatio = toNumericString(sourceMaps.ModelRatio[name]); + const completionRatio = toNumericString(sourceMaps.CompletionRatio[name]); + const completionRatioMeta = normalizeCompletionRatioMeta( + sourceMaps.CompletionRatioMeta?.[name], + ); + const cacheRatio = toNumericString(sourceMaps.CacheRatio[name]); + const createCacheRatio = toNumericString(sourceMaps.CreateCacheRatio[name]); + const imageRatio = toNumericString(sourceMaps.ImageRatio[name]); + const audioRatio = toNumericString(sourceMaps.AudioRatio[name]); + const audioCompletionRatio = toNumericString( + sourceMaps.AudioCompletionRatio[name], + ); + const fixedPrice = toNumericString(sourceMaps.ModelPrice[name]); + const inputPrice = ratioToBasePrice(modelRatio); + const inputPriceNumber = toNumberOrNull(inputPrice); + const audioInputPrice = + inputPriceNumber !== null && hasValue(audioRatio) + ? formatNumber(inputPriceNumber * Number(audioRatio)) + : ''; + + return { + ...EMPTY_MODEL, + name, + billingMode: hasValue(fixedPrice) ? 'per-request' : 'per-token', + fixedPrice, + inputPrice, + completionRatioLocked: completionRatioMeta.locked, + lockedCompletionRatio: completionRatioMeta.ratio, + completionPrice: + inputPriceNumber !== null && + hasValue(completionRatioMeta.locked ? completionRatioMeta.ratio : completionRatio) + ? formatNumber( + inputPriceNumber * + Number( + completionRatioMeta.locked + ? completionRatioMeta.ratio + : completionRatio, + ), + ) + : '', + cachePrice: + inputPriceNumber !== null && hasValue(cacheRatio) + ? formatNumber(inputPriceNumber * Number(cacheRatio)) + : '', + createCachePrice: + inputPriceNumber !== null && hasValue(createCacheRatio) + ? formatNumber(inputPriceNumber * Number(createCacheRatio)) + : '', + imagePrice: + inputPriceNumber !== null && hasValue(imageRatio) + ? formatNumber(inputPriceNumber * Number(imageRatio)) + : '', + audioInputPrice, + audioOutputPrice: + toNumberOrNull(audioInputPrice) !== null && hasValue(audioCompletionRatio) + ? formatNumber(Number(audioInputPrice) * Number(audioCompletionRatio)) + : '', + rawRatios: { + modelRatio, + completionRatio, + cacheRatio, + createCacheRatio, + imageRatio, + audioRatio, + audioCompletionRatio, + }, + hasConflict: + hasValue(fixedPrice) && + [ + modelRatio, + completionRatio, + cacheRatio, + createCacheRatio, + imageRatio, + audioRatio, + audioCompletionRatio, + ].some(hasValue), + }; +}; + +export const isBasePricingUnset = (model) => + !hasValue(model.fixedPrice) && !hasValue(model.inputPrice); + +export const getModelWarnings = (model, t) => { + if (!model) { + return []; + } + const warnings = []; + const hasDerivedPricing = [ + model.inputPrice, + model.completionPrice, + model.cachePrice, + model.createCachePrice, + model.imagePrice, + model.audioInputPrice, + model.audioOutputPrice, + ].some(hasValue); + + if (model.hasConflict) { + warnings.push(t('当前模型同时存在按次价格和倍率配置,保存时会按当前计费方式覆盖。')); + } + + if ( + !hasValue(model.inputPrice) && + [ + model.rawRatios.completionRatio, + model.rawRatios.cacheRatio, + model.rawRatios.createCacheRatio, + model.rawRatios.imageRatio, + model.rawRatios.audioRatio, + model.rawRatios.audioCompletionRatio, + ].some(hasValue) + ) { + warnings.push( + t('当前模型存在未显式设置输入倍率的扩展倍率;填写输入价格后会自动换算为价格字段。'), + ); + } + + if (model.billingMode === 'per-token' && hasDerivedPricing && !hasValue(model.inputPrice)) { + warnings.push(t('按量计费下需要先填写输入价格,才能保存其它价格项。')); + } + + if ( + model.billingMode === 'per-token' && + hasValue(model.audioOutputPrice) && + !hasValue(model.audioInputPrice) + ) { + warnings.push(t('填写音频补全价格前,需要先填写音频输入价格。')); + } + + return warnings; +}; + +export const buildSummaryText = (model, t) => { + if (model.billingMode === 'per-request' && hasValue(model.fixedPrice)) { + return `${t('按次')} $${model.fixedPrice} / ${t('次')}`; + } + + if (hasValue(model.inputPrice)) { + const extraCount = [ + model.completionPrice, + model.cachePrice, + model.createCachePrice, + model.imagePrice, + model.audioInputPrice, + model.audioOutputPrice, + ].filter(hasValue).length; + const extraLabel = + extraCount > 0 ? `,${t('额外价格项')} ${extraCount}` : ''; + return `${t('输入')} $${model.inputPrice}${extraLabel}`; + } + + return t('未设置价格'); +}; + +export const buildOptionalFieldToggles = (model) => ({ + completionPrice: model.completionRatioLocked || hasValue(model.completionPrice), + cachePrice: hasValue(model.cachePrice), + createCachePrice: hasValue(model.createCachePrice), + imagePrice: hasValue(model.imagePrice), + audioInputPrice: hasValue(model.audioInputPrice), + audioOutputPrice: hasValue(model.audioOutputPrice), +}); + +const serializeModel = (model, t) => { + const result = { + ModelPrice: null, + ModelRatio: null, + CompletionRatio: null, + CacheRatio: null, + CreateCacheRatio: null, + ImageRatio: null, + AudioRatio: null, + AudioCompletionRatio: null, + }; + + if (model.billingMode === 'per-request') { + if (hasValue(model.fixedPrice)) { + result.ModelPrice = Number(model.fixedPrice); + } + return result; + } + + const inputPrice = toNumberOrNull(model.inputPrice); + const completionPrice = toNumberOrNull(model.completionPrice); + const cachePrice = toNumberOrNull(model.cachePrice); + const createCachePrice = toNumberOrNull(model.createCachePrice); + const imagePrice = toNumberOrNull(model.imagePrice); + const audioInputPrice = toNumberOrNull(model.audioInputPrice); + const audioOutputPrice = toNumberOrNull(model.audioOutputPrice); + + const hasDependentPrice = [ + completionPrice, + cachePrice, + createCachePrice, + imagePrice, + audioInputPrice, + audioOutputPrice, + ].some((value) => value !== null); + + if (inputPrice === null) { + if (hasDependentPrice) { + throw new Error( + t('模型 {{name}} 缺少输入价格,无法计算补全/缓存/图片/音频价格对应的倍率', { + name: model.name, + }), + ); + } + + if (hasValue(model.rawRatios.modelRatio)) { + result.ModelRatio = Number(model.rawRatios.modelRatio); + } + if (hasValue(model.rawRatios.completionRatio)) { + result.CompletionRatio = Number(model.rawRatios.completionRatio); + } + if (hasValue(model.rawRatios.cacheRatio)) { + result.CacheRatio = Number(model.rawRatios.cacheRatio); + } + if (hasValue(model.rawRatios.createCacheRatio)) { + result.CreateCacheRatio = Number(model.rawRatios.createCacheRatio); + } + if (hasValue(model.rawRatios.imageRatio)) { + result.ImageRatio = Number(model.rawRatios.imageRatio); + } + if (hasValue(model.rawRatios.audioRatio)) { + result.AudioRatio = Number(model.rawRatios.audioRatio); + } + if (hasValue(model.rawRatios.audioCompletionRatio)) { + result.AudioCompletionRatio = Number(model.rawRatios.audioCompletionRatio); + } + return result; + } + + result.ModelRatio = inputPrice / 2; + + if (!model.completionRatioLocked && completionPrice !== null) { + result.CompletionRatio = completionPrice / inputPrice; + } else if ( + model.completionRatioLocked && + hasValue(model.rawRatios.completionRatio) + ) { + result.CompletionRatio = Number(model.rawRatios.completionRatio); + } + if (cachePrice !== null) { + result.CacheRatio = cachePrice / inputPrice; + } + if (createCachePrice !== null) { + result.CreateCacheRatio = createCachePrice / inputPrice; + } + if (imagePrice !== null) { + result.ImageRatio = imagePrice / inputPrice; + } + if (audioInputPrice !== null) { + result.AudioRatio = audioInputPrice / inputPrice; + } + if (audioOutputPrice !== null) { + if (audioInputPrice === null || audioInputPrice === 0) { + throw new Error( + t('模型 {{name}} 缺少音频输入价格,无法计算音频补全倍率', { + name: model.name, + }), + ); + } + result.AudioCompletionRatio = audioOutputPrice / audioInputPrice; + } + + return result; +}; + +export const buildPreviewRows = (model, t) => { + if (!model) return []; + + if (model.billingMode === 'per-request') { + return [ + { + key: 'ModelPrice', + label: 'ModelPrice', + value: hasValue(model.fixedPrice) ? model.fixedPrice : t('空'), + }, + ]; + } + + const inputPrice = toNumberOrNull(model.inputPrice); + if (inputPrice === null) { + return [ + { + key: 'ModelRatio', + label: 'ModelRatio', + value: hasValue(model.rawRatios.modelRatio) + ? model.rawRatios.modelRatio + : t('空'), + }, + { + key: 'CompletionRatio', + label: 'CompletionRatio', + value: hasValue(model.rawRatios.completionRatio) + ? model.rawRatios.completionRatio + : t('空'), + }, + { + key: 'CacheRatio', + label: 'CacheRatio', + value: hasValue(model.rawRatios.cacheRatio) + ? model.rawRatios.cacheRatio + : t('空'), + }, + { + key: 'CreateCacheRatio', + label: 'CreateCacheRatio', + value: hasValue(model.rawRatios.createCacheRatio) + ? model.rawRatios.createCacheRatio + : t('空'), + }, + { + key: 'ImageRatio', + label: 'ImageRatio', + value: hasValue(model.rawRatios.imageRatio) + ? model.rawRatios.imageRatio + : t('空'), + }, + { + key: 'AudioRatio', + label: 'AudioRatio', + value: hasValue(model.rawRatios.audioRatio) + ? model.rawRatios.audioRatio + : t('空'), + }, + { + key: 'AudioCompletionRatio', + label: 'AudioCompletionRatio', + value: hasValue(model.rawRatios.audioCompletionRatio) + ? model.rawRatios.audioCompletionRatio + : t('空'), + }, + ]; + } + + const completionPrice = toNumberOrNull(model.completionPrice); + const cachePrice = toNumberOrNull(model.cachePrice); + const createCachePrice = toNumberOrNull(model.createCachePrice); + const imagePrice = toNumberOrNull(model.imagePrice); + const audioInputPrice = toNumberOrNull(model.audioInputPrice); + const audioOutputPrice = toNumberOrNull(model.audioOutputPrice); + + return [ + { + key: 'ModelRatio', + label: 'ModelRatio', + value: formatNumber(inputPrice / 2), + }, + { + key: 'CompletionRatio', + label: 'CompletionRatio', + value: model.completionRatioLocked + ? `${model.lockedCompletionRatio || t('空')} (${t('后端固定')})` + : completionPrice !== null + ? formatNumber(completionPrice / inputPrice) + : t('空'), + }, + { + key: 'CacheRatio', + label: 'CacheRatio', + value: cachePrice !== null ? formatNumber(cachePrice / inputPrice) : t('空'), + }, + { + key: 'CreateCacheRatio', + label: 'CreateCacheRatio', + value: + createCachePrice !== null + ? formatNumber(createCachePrice / inputPrice) + : t('空'), + }, + { + key: 'ImageRatio', + label: 'ImageRatio', + value: imagePrice !== null ? formatNumber(imagePrice / inputPrice) : t('空'), + }, + { + key: 'AudioRatio', + label: 'AudioRatio', + value: + audioInputPrice !== null + ? formatNumber(audioInputPrice / inputPrice) + : t('空'), + }, + { + key: 'AudioCompletionRatio', + label: 'AudioCompletionRatio', + value: + audioOutputPrice !== null && audioInputPrice !== null && audioInputPrice !== 0 + ? formatNumber(audioOutputPrice / audioInputPrice) + : t('空'), + }, + ]; +}; + +export function useModelPricingEditorState({ + options, + refresh, + t, + candidateModelNames = EMPTY_CANDIDATE_MODEL_NAMES, + filterMode = 'all', +}) { + const [models, setModels] = useState([]); + const [initialVisibleModelNames, setInitialVisibleModelNames] = useState([]); + const [selectedModelName, setSelectedModelName] = useState(''); + const [selectedModelNames, setSelectedModelNames] = useState([]); + const [searchText, setSearchText] = useState(''); + const [currentPage, setCurrentPage] = useState(1); + const [loading, setLoading] = useState(false); + const [conflictOnly, setConflictOnly] = useState(false); + const [optionalFieldToggles, setOptionalFieldToggles] = useState({}); + + useEffect(() => { + const sourceMaps = { + ModelPrice: parseOptionJSON(options.ModelPrice), + ModelRatio: parseOptionJSON(options.ModelRatio), + CompletionRatio: parseOptionJSON(options.CompletionRatio), + CompletionRatioMeta: parseOptionJSON(options.CompletionRatioMeta), + CacheRatio: parseOptionJSON(options.CacheRatio), + CreateCacheRatio: parseOptionJSON(options.CreateCacheRatio), + ImageRatio: parseOptionJSON(options.ImageRatio), + AudioRatio: parseOptionJSON(options.AudioRatio), + AudioCompletionRatio: parseOptionJSON(options.AudioCompletionRatio), + }; + + const names = new Set([ + ...candidateModelNames, + ...Object.keys(sourceMaps.ModelPrice), + ...Object.keys(sourceMaps.ModelRatio), + ...Object.keys(sourceMaps.CompletionRatio), + ...Object.keys(sourceMaps.CompletionRatioMeta), + ...Object.keys(sourceMaps.CacheRatio), + ...Object.keys(sourceMaps.CreateCacheRatio), + ...Object.keys(sourceMaps.ImageRatio), + ...Object.keys(sourceMaps.AudioRatio), + ...Object.keys(sourceMaps.AudioCompletionRatio), + ]); + + const nextModels = Array.from(names) + .map((name) => buildModelState(name, sourceMaps)) + .sort((a, b) => a.name.localeCompare(b.name)); + + setModels(nextModels); + setInitialVisibleModelNames( + filterMode === 'unset' + ? nextModels + .filter((model) => isBasePricingUnset(model)) + .map((model) => model.name) + : nextModels.map((model) => model.name), + ); + setOptionalFieldToggles( + nextModels.reduce((acc, model) => { + acc[model.name] = buildOptionalFieldToggles(model); + return acc; + }, {}), + ); + setSelectedModelName((previous) => { + if (previous && nextModels.some((model) => model.name === previous)) { + return previous; + } + const nextVisibleModels = + filterMode === 'unset' + ? nextModels.filter((model) => isBasePricingUnset(model)) + : nextModels; + return nextVisibleModels[0]?.name || ''; + }); + }, [candidateModelNames, filterMode, options]); + + const visibleModels = useMemo(() => { + return filterMode === 'unset' + ? models.filter((model) => initialVisibleModelNames.includes(model.name)) + : models; + }, [filterMode, initialVisibleModelNames, models]); + + const filteredModels = useMemo(() => { + return visibleModels.filter((model) => { + const keyword = searchText.trim().toLowerCase(); + const keywordMatch = keyword + ? model.name.toLowerCase().includes(keyword) + : true; + const conflictMatch = conflictOnly ? model.hasConflict : true; + return keywordMatch && conflictMatch; + }); + }, [conflictOnly, searchText, visibleModels]); + + const pagedData = useMemo(() => { + const start = (currentPage - 1) * PAGE_SIZE; + return filteredModels.slice(start, start + PAGE_SIZE); + }, [currentPage, filteredModels]); + + const selectedModel = useMemo( + () => visibleModels.find((model) => model.name === selectedModelName) || null, + [selectedModelName, visibleModels], + ); + + const selectedWarnings = useMemo( + () => getModelWarnings(selectedModel, t), + [selectedModel, t], + ); + + const previewRows = useMemo( + () => buildPreviewRows(selectedModel, t), + [selectedModel, t], + ); + + useEffect(() => { + setCurrentPage(1); + }, [searchText, conflictOnly, filterMode, candidateModelNames]); + + useEffect(() => { + setSelectedModelNames((previous) => + previous.filter((name) => visibleModels.some((model) => model.name === name)), + ); + }, [visibleModels]); + + useEffect(() => { + if (visibleModels.length === 0) { + setSelectedModelName(''); + return; + } + if (!visibleModels.some((model) => model.name === selectedModelName)) { + setSelectedModelName(visibleModels[0].name); + } + }, [selectedModelName, visibleModels]); + + const upsertModel = (name, updater) => { + setModels((previous) => + previous.map((model) => { + if (model.name !== name) return model; + return typeof updater === 'function' ? updater(model) : updater; + }), + ); + }; + + const isOptionalFieldEnabled = (model, field) => { + if (!model) return false; + const modelToggles = optionalFieldToggles[model.name]; + if (modelToggles && typeof modelToggles[field] === 'boolean') { + return modelToggles[field]; + } + return buildOptionalFieldToggles(model)[field]; + }; + + const updateOptionalFieldToggle = (modelName, field, checked) => { + setOptionalFieldToggles((prev) => ({ + ...prev, + [modelName]: { + ...(prev[modelName] || {}), + [field]: checked, + }, + })); + }; + + const handleOptionalFieldToggle = (field, checked) => { + if (!selectedModel) return; + + updateOptionalFieldToggle(selectedModel.name, field, checked); + + if (checked) { + return; + } + + upsertModel(selectedModel.name, (model) => { + const nextModel = { ...model, [field]: '' }; + + if (field === 'audioInputPrice') { + nextModel.audioOutputPrice = ''; + setOptionalFieldToggles((prev) => ({ + ...prev, + [selectedModel.name]: { + ...(prev[selectedModel.name] || {}), + audioInputPrice: false, + audioOutputPrice: false, + }, + })); + } + + return nextModel; + }); + }; + + const fillDerivedPricesFromBase = (model, nextInputPrice) => { + const baseNumber = toNumberOrNull(nextInputPrice); + if (baseNumber === null) { + return model; + } + + return { + ...model, + completionPrice: + model.completionRatioLocked && hasValue(model.lockedCompletionRatio) + ? formatNumber(baseNumber * Number(model.lockedCompletionRatio)) + : !hasValue(model.completionPrice) && + hasValue(model.rawRatios.completionRatio) + ? formatNumber(baseNumber * Number(model.rawRatios.completionRatio)) + : model.completionPrice, + cachePrice: + !hasValue(model.cachePrice) && hasValue(model.rawRatios.cacheRatio) + ? formatNumber(baseNumber * Number(model.rawRatios.cacheRatio)) + : model.cachePrice, + createCachePrice: + !hasValue(model.createCachePrice) && + hasValue(model.rawRatios.createCacheRatio) + ? formatNumber(baseNumber * Number(model.rawRatios.createCacheRatio)) + : model.createCachePrice, + imagePrice: + !hasValue(model.imagePrice) && hasValue(model.rawRatios.imageRatio) + ? formatNumber(baseNumber * Number(model.rawRatios.imageRatio)) + : model.imagePrice, + audioInputPrice: + !hasValue(model.audioInputPrice) && hasValue(model.rawRatios.audioRatio) + ? formatNumber(baseNumber * Number(model.rawRatios.audioRatio)) + : model.audioInputPrice, + audioOutputPrice: + !hasValue(model.audioOutputPrice) && + hasValue(model.rawRatios.audioRatio) && + hasValue(model.rawRatios.audioCompletionRatio) + ? formatNumber( + baseNumber * + Number(model.rawRatios.audioRatio) * + Number(model.rawRatios.audioCompletionRatio), + ) + : model.audioOutputPrice, + }; + }; + + const handleNumericFieldChange = (field, value) => { + if (!selectedModel || !NUMERIC_INPUT_REGEX.test(value)) { + return; + } + + upsertModel(selectedModel.name, (model) => { + const updatedModel = { ...model, [field]: value }; + + if (field === 'inputPrice') { + return fillDerivedPricesFromBase(updatedModel, value); + } + + return updatedModel; + }); + }; + + const handleBillingModeChange = (value) => { + if (!selectedModel) return; + upsertModel(selectedModel.name, (model) => ({ + ...model, + billingMode: value, + })); + }; + + const addModel = (modelName) => { + const trimmedName = modelName.trim(); + if (!trimmedName) { + showError(t('请输入模型名称')); + return false; + } + if (models.some((model) => model.name === trimmedName)) { + showError(t('模型名称已存在')); + return false; + } + + const nextModel = { + ...EMPTY_MODEL, + name: trimmedName, + rawRatios: { ...EMPTY_MODEL.rawRatios }, + }; + + setModels((previous) => [nextModel, ...previous]); + setOptionalFieldToggles((prev) => ({ + ...prev, + [trimmedName]: buildOptionalFieldToggles(nextModel), + })); + setSelectedModelName(trimmedName); + setCurrentPage(1); + return true; + }; + + const deleteModel = (name) => { + const nextModels = models.filter((model) => model.name !== name); + setModels(nextModels); + setOptionalFieldToggles((prev) => { + const next = { ...prev }; + delete next[name]; + return next; + }); + setSelectedModelNames((previous) => previous.filter((item) => item !== name)); + if (selectedModelName === name) { + setSelectedModelName(nextModels[0]?.name || ''); + } + }; + + const applySelectedModelPricing = () => { + if (!selectedModel) { + showError(t('请先选择一个作为模板的模型')); + return false; + } + if (selectedModelNames.length === 0) { + showError(t('请先勾选需要批量设置的模型')); + return false; + } + + const sourceToggles = optionalFieldToggles[selectedModel.name] || {}; + + setModels((previous) => + previous.map((model) => { + if (!selectedModelNames.includes(model.name)) { + return model; + } + + const nextModel = { + ...model, + billingMode: selectedModel.billingMode, + fixedPrice: selectedModel.fixedPrice, + inputPrice: selectedModel.inputPrice, + completionPrice: selectedModel.completionPrice, + cachePrice: selectedModel.cachePrice, + createCachePrice: selectedModel.createCachePrice, + imagePrice: selectedModel.imagePrice, + audioInputPrice: selectedModel.audioInputPrice, + audioOutputPrice: selectedModel.audioOutputPrice, + }; + + if ( + nextModel.billingMode === 'per-token' && + nextModel.completionRatioLocked && + hasValue(nextModel.inputPrice) && + hasValue(nextModel.lockedCompletionRatio) + ) { + nextModel.completionPrice = formatNumber( + Number(nextModel.inputPrice) * Number(nextModel.lockedCompletionRatio), + ); + } + + return nextModel; + }), + ); + + setOptionalFieldToggles((previous) => { + const next = { ...previous }; + selectedModelNames.forEach((modelName) => { + const targetModel = models.find((item) => item.name === modelName); + next[modelName] = { + completionPrice: targetModel?.completionRatioLocked + ? true + : Boolean(sourceToggles.completionPrice), + cachePrice: Boolean(sourceToggles.cachePrice), + createCachePrice: Boolean(sourceToggles.createCachePrice), + imagePrice: Boolean(sourceToggles.imagePrice), + audioInputPrice: Boolean(sourceToggles.audioInputPrice), + audioOutputPrice: + Boolean(sourceToggles.audioInputPrice) && + Boolean(sourceToggles.audioOutputPrice), + }; + }); + return next; + }); + + showSuccess( + t('已将模型 {{name}} 的价格配置批量应用到 {{count}} 个模型', { + name: selectedModel.name, + count: selectedModelNames.length, + }), + ); + return true; + }; + + const handleSubmit = async () => { + setLoading(true); + try { + const output = { + ModelPrice: {}, + ModelRatio: {}, + CompletionRatio: {}, + CacheRatio: {}, + CreateCacheRatio: {}, + ImageRatio: {}, + AudioRatio: {}, + AudioCompletionRatio: {}, + }; + + for (const model of models) { + const serialized = serializeModel(model, t); + Object.entries(serialized).forEach(([key, value]) => { + if (value !== null) { + output[key][model.name] = value; + } + }); + } + + const requestQueue = Object.entries(output).map(([key, value]) => + API.put('/api/option/', { + key, + value: JSON.stringify(value, null, 2), + }), + ); + + const results = await Promise.all(requestQueue); + for (const res of results) { + if (!res?.data?.success) { + throw new Error(res?.data?.message || t('保存失败,请重试')); + } + } + + showSuccess(t('保存成功')); + await refresh(); + } catch (error) { + console.error('保存失败:', error); + showError(error.message || t('保存失败,请重试')); + } finally { + setLoading(false); + } + }; + + return { + models, + selectedModel, + selectedModelName, + selectedModelNames, + setSelectedModelName, + setSelectedModelNames, + searchText, + setSearchText, + currentPage, + setCurrentPage, + loading, + conflictOnly, + setConflictOnly, + filteredModels, + pagedData, + selectedWarnings, + previewRows, + isOptionalFieldEnabled, + handleOptionalFieldToggle, + handleNumericFieldChange, + handleBillingModeChange, + handleSubmit, + addModel, + deleteModel, + applySelectedModelPricing, + }; +}