Commit 8d7f35e3 by ccran

feat: 添加模型映射

parent a77bdb2c
......@@ -124,6 +124,7 @@
"tw-animate-css": "^1.4.0",
"use-stick-to-bottom": "^1.1.6",
"vaul": "^1.1.2",
"xlsx": "^0.18.5",
"zod": "^4.4.3",
"zustand": "^5.0.14",
},
......@@ -1253,6 +1254,8 @@
"acorn-jsx": ["acorn-jsx@5.3.2", "https://registry.npmmirror.com/acorn-jsx/-/acorn-jsx-5.3.2.tgz", { "peerDependencies": { "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" } }, "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ=="],
"adler-32": ["adler-32@1.3.1", "https://registry.npmmirror.com/adler-32/-/adler-32-1.3.1.tgz", {}, "sha512-ynZ4w/nUUv5rrsR8UUGoe1VC9hZj6V5hU9Qw1HlMDJGEJw5S7TfTErWTjMys6M7vr0YWcPqs3qAr4ss0nDfP+A=="],
"agent-base": ["agent-base@6.0.2", "https://registry.npmmirror.com/agent-base/-/agent-base-6.0.2.tgz", { "dependencies": { "debug": "4" } }, "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ=="],
"ahooks": ["ahooks@3.9.7", "https://registry.npmmirror.com/ahooks/-/ahooks-3.9.7.tgz", { "dependencies": { "@babel/runtime": "^7.21.0", "@types/js-cookie": "^3.0.6", "dayjs": "^1.9.1", "intersection-observer": "^0.12.0", "js-cookie": "^3.0.5", "lodash": "^4.17.21", "react-fast-compare": "^3.2.2", "resize-observer-polyfill": "^1.5.1", "screenfull": "^5.0.0", "tslib": "^2.4.1" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-S0lvzhbdlhK36RFBkGv+RbOM/dbbweym+BIHM/bwwuWVSVN5TuVErHPMWo4w0t1NDYg5KPp2iEf7Y7E5LASYiw=="],
......@@ -1349,6 +1352,8 @@
"ccount": ["ccount@2.0.1", "https://registry.npmmirror.com/ccount/-/ccount-2.0.1.tgz", {}, "sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg=="],
"cfb": ["cfb@1.2.2", "https://registry.npmmirror.com/cfb/-/cfb-1.2.2.tgz", { "dependencies": { "adler-32": "~1.3.0", "crc-32": "~1.2.0" } }, "sha512-KfdUZsSOw19/ObEWasvBP/Ac4reZvAGauZhs6S/gqNhXhI7cKwvlH7ulj+dOEYnca4bm4SGo8C1bTAQvnTjgQA=="],
"chalk": ["chalk@4.1.2", "https://registry.npmmirror.com/chalk/-/chalk-4.1.2.tgz", { "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" } }, "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA=="],
"character-entities": ["character-entities@2.0.2", "https://registry.npmmirror.com/character-entities/-/character-entities-2.0.2.tgz", {}, "sha512-shx7oQ0Awen/BRIdkjkvz54PnEEI/EjwXDSIZp86/KKdbafHh1Df/RYGBhn4hbe2+uKC9FnT5UCEdyPz3ai9hQ=="],
......@@ -1383,6 +1388,8 @@
"code-block-writer": ["code-block-writer@13.0.3", "https://registry.npmmirror.com/code-block-writer/-/code-block-writer-13.0.3.tgz", {}, "sha512-Oofo0pq3IKnsFtuHqSF7TqBfr71aeyZDVJ0HpmqB7FBM2qEigL0iPONSCZSO9pE9dZTAxANe5XHG9Uy0YMv8cg=="],
"codepage": ["codepage@1.15.0", "https://registry.npmmirror.com/codepage/-/codepage-1.15.0.tgz", {}, "sha512-3g6NUTPd/YtuuGrhMnOMRjFc+LJw/bnMp3+0r/Wcz3IXUuCosKRJvMphm5+Q+bvTVGcJJuRvVLuYba+WojaFaA=="],
"collapse-white-space": ["collapse-white-space@2.1.0", "https://registry.npmmirror.com/collapse-white-space/-/collapse-white-space-2.1.0.tgz", {}, "sha512-loKTxY1zCOuG4j9f6EPnuyyYkf58RnhhWTvRoZEokgB+WbdXehfjFviyOVYkqzEWz1Q5kRiZdBYS5SwxbQYwzw=="],
"color-convert": ["color-convert@2.0.1", "https://registry.npmmirror.com/color-convert/-/color-convert-2.0.1.tgz", { "dependencies": { "color-name": "~1.1.4" } }, "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ=="],
......@@ -1429,6 +1436,8 @@
"cosmiconfig": ["cosmiconfig@9.0.2", "https://registry.npmmirror.com/cosmiconfig/-/cosmiconfig-9.0.2.tgz", { "dependencies": { "env-paths": "^2.2.1", "import-fresh": "^3.3.0", "js-yaml": "^4.1.0", "parse-json": "^5.2.0" }, "peerDependencies": { "typescript": ">=4.9.5" }, "optionalPeers": ["typescript"] }, "sha512-gtTZxTDau1wL7Y7zifc2dd8jHSK/k6BTx/2Xp/BpdlAdnlYWFVt7qhJqgwi7637yRwRQ3qL4ZidbB4I8tA5VOg=="],
"crc-32": ["crc-32@1.2.2", "https://registry.npmmirror.com/crc-32/-/crc-32-1.2.2.tgz", { "bin": { "crc32": "bin/crc32.njs" } }, "sha512-ROmzCKrTnOwybPcJApAA6WBWij23HVfGVNKqqrZpuyZOHqK2CwHSvpGuyt/UNNvaIjEd8X5IFGp4Mh+Ie1IHJQ=="],
"crelt": ["crelt@1.0.7", "https://registry.npmmirror.com/crelt/-/crelt-1.0.7.tgz", {}, "sha512-aK6BbWfhf4U/wCcLHKPJl/xa6VkVstRaPywWtMKGwuOLc/wZTyQYuoxgvZnNsBvv7Kg3YTBQYYBCggcviQczuA=="],
"cross-spawn": ["cross-spawn@7.0.6", "https://registry.npmmirror.com/cross-spawn/-/cross-spawn-7.0.6.tgz", { "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", "which": "^2.0.1" } }, "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA=="],
......@@ -1727,6 +1736,8 @@
"forwarded": ["forwarded@0.2.0", "https://registry.npmmirror.com/forwarded/-/forwarded-0.2.0.tgz", {}, "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow=="],
"frac": ["frac@1.1.2", "https://registry.npmmirror.com/frac/-/frac-1.1.2.tgz", {}, "sha512-w/XBfkibaTl3YDqASwfDUqkna4Z2p9cFSr1aHDt0WoMTECnRfBOv2WArlZILlqgWlmdIlALXGpM2AOhEk5W3IA=="],
"fraction.js": ["fraction.js@5.3.4", "https://registry.npmmirror.com/fraction.js/-/fraction.js-5.3.4.tgz", {}, "sha512-1X1NTtiJphryn/uLQz3whtY6jK3fTqoE3ohKs0tT+Ujr1W59oopxmoEh7Lu5p6vBaPbgoM0bzveAW4Qi5RyWDQ=="],
"framer-motion": ["framer-motion@12.42.2", "https://registry.npmmirror.com/framer-motion/-/framer-motion-12.42.2.tgz", { "dependencies": { "motion-dom": "^12.42.2", "motion-utils": "^12.39.0", "tslib": "^2.4.0" }, "peerDependencies": { "@emotion/is-prop-valid": "*", "react": "^18.0.0 || ^19.0.0", "react-dom": "^18.0.0 || ^19.0.0" }, "optionalPeers": ["@emotion/is-prop-valid", "react", "react-dom"] }, "sha512-5XY9luDiu0oHfHBjpDthFMh0ES+122w6p/papSJBweMkO8Sn+PW2QaEgRblQBpWFnuvZS5qvarpt/hO2pjGmnw=="],
......@@ -2709,6 +2720,8 @@
"sse.js": ["sse.js@2.8.0", "https://registry.npmmirror.com/sse.js/-/sse.js-2.8.0.tgz", {}, "sha512-35RyyFYpzzHZgMw9D5GxwADbL6gnntSwW/rKXcuIy1KkYCPjW6oia0moNdNRhs34oVHU1Sjgovj3l7uIEZjrKA=="],
"ssf": ["ssf@0.11.2", "https://registry.npmmirror.com/ssf/-/ssf-0.11.2.tgz", { "dependencies": { "frac": "~1.1.2" } }, "sha512-+idbmIXoYET47hH+d7dfm2epdOMUDjqcB4648sTZ+t2JwoyBFL/insLfB/racrDmsKB3diwsDA696pZMieAC5g=="],
"statuses": ["statuses@2.0.2", "https://registry.npmmirror.com/statuses/-/statuses-2.0.2.tgz", {}, "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw=="],
"stdin-discarder": ["stdin-discarder@0.2.2", "https://registry.npmmirror.com/stdin-discarder/-/stdin-discarder-0.2.2.tgz", {}, "sha512-UhDfHmA92YAlNnCfhmq0VeNL5bDbiZGg7sZ2IvPsXubGkiNa9EC+tUTsjBRsYUAz87btI6/1wf4XoVvQ3uRnmQ=="],
......@@ -2909,12 +2922,18 @@
"which": ["which@4.0.0", "https://registry.npmmirror.com/which/-/which-4.0.0.tgz", { "dependencies": { "isexe": "^3.1.1" }, "bin": { "node-which": "bin/which.js" } }, "sha512-GlaYyEb07DPxYCKhKzplCWBJtvxZcZMrL+4UkrTSJHHPyZU4mYYTv3qaOe77H7EODLSSopAUFAc6W8U4yqvscg=="],
"wmf": ["wmf@1.0.2", "https://registry.npmmirror.com/wmf/-/wmf-1.0.2.tgz", {}, "sha512-/p9K7bEh0Dj6WbXg4JG0xvLQmIadrner1bi45VMJTfnbVHsc7yIajZyoSoK60/dtVBs12Fm6WkUI5/3WAVsNMw=="],
"word": ["word@0.3.0", "https://registry.npmmirror.com/word/-/word-0.3.0.tgz", {}, "sha512-OELeY0Q61OXpdUfTp+oweA/vtLVg5VDOXh+3he3PNzLGG/y0oylSOC1xRVj0+l4vQ3tj/bB1HVHv1ocXkQceFA=="],
"word-wrap": ["word-wrap@1.2.5", "https://registry.npmmirror.com/word-wrap/-/word-wrap-1.2.5.tgz", {}, "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA=="],
"wrappy": ["wrappy@1.0.2", "https://registry.npmmirror.com/wrappy/-/wrappy-1.0.2.tgz", {}, "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ=="],
"wsl-utils": ["wsl-utils@0.3.1", "https://registry.npmmirror.com/wsl-utils/-/wsl-utils-0.3.1.tgz", { "dependencies": { "is-wsl": "^3.1.0", "powershell-utils": "^0.1.0" } }, "sha512-g/eziiSUNBSsdDJtCLB8bdYEUMj4jR7AGeUo96p/3dTafgjHhpF4RiCFPiRILwjQoDXx5MqkBr4fwWtR3Ky4Wg=="],
"xlsx": ["xlsx@0.18.5", "https://registry.npmmirror.com/xlsx/-/xlsx-0.18.5.tgz", { "dependencies": { "adler-32": "~1.3.0", "cfb": "~1.2.1", "codepage": "~1.15.0", "crc-32": "~1.2.1", "ssf": "~0.11.2", "wmf": "~1.0.1", "word": "~0.3.0" }, "bin": { "xlsx": "bin/xlsx.njs" } }, "sha512-dmg3LCjBPHZnQp5/F/+nnTa+miPJxUXB6vtk42YjBBKayDNagxGEeIdWApkYPOf3Z3pm3k62Knjzp7lMeTEtFQ=="],
"yallist": ["yallist@3.1.1", "https://registry.npmmirror.com/yallist/-/yallist-3.1.1.tgz", {}, "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g=="],
"yaml": ["yaml@2.9.0", "https://registry.npmmirror.com/yaml/-/yaml-2.9.0.tgz", { "bin": { "yaml": "bin.mjs" } }, "sha512-2AvhNX3mb8zd6Zy7INTtSpl1F15HW6Wnqj0srWlkKLcpYl/gMIMJiyuGq2KeI2YFxUPjdlB+3Lc10seMLtL4cA=="],
......
......@@ -19,7 +19,7 @@ For commercial licensing, please contact support@quantumnous.com
import { useState, useEffect, useMemo } from 'react';
import { useTranslation } from 'react-i18next';
import { API, showError, showSuccess } from '../../helpers';
import { API, showError, showSuccess, showWarning } from '../../helpers';
import { ITEMS_PER_PAGE } from '../../constants';
import { useTableCompactMode } from '../common/useTableCompactMode';
......@@ -282,6 +282,14 @@ export const useModelsData = () => {
},
),
);
const failedModels = (data?.rows || [])
.filter((row) => row.status === 'failed' && row.model_name)
.map((row) => row.model_name);
if (failedModels.length > 0) {
showWarning(
t('导入失败的模型:{{models}}', { models: failedModels.join('、') }),
);
}
await loadVendors();
await refresh();
return true;
......
......@@ -29,7 +29,7 @@ import {
import { useTranslation } from 'react-i18next';
import ModelPricingEditor from './components/ModelPricingEditor';
import ModelRatioSettings from './ModelRatioSettings';
import { API, showError, showSuccess } from '../../../helpers';
import { API, showError, showSuccess, showWarning } from '../../../helpers';
export default function ModelPricingCombined({ options, refresh }) {
const { t } = useTranslation();
......@@ -78,6 +78,14 @@ export default function ModelPricingCombined({ options, refresh }) {
},
),
);
const failedModels = (data?.rows || [])
.filter((row) => row.status === 'failed' && row.model_name)
.map((row) => row.model_name);
if (failedModels.length > 0) {
showWarning(
t('导入失败的模型:{{models}}', { models: failedModels.join('、') }),
);
}
await refresh();
setImportVisible(false);
resetImportState();
......
......@@ -74,6 +74,7 @@
"tw-animate-css": "^1.4.0",
"use-stick-to-bottom": "^1.1.6",
"vaul": "^1.1.2",
"xlsx": "^0.18.5",
"zod": "^4.4.3",
"zustand": "^5.0.14"
},
......
......@@ -97,10 +97,7 @@ function hardenIsolatedHtml(html: string): string {
template.content.querySelectorAll('a[target="_blank"]').forEach((link) => {
const rel = new Set(
link
.getAttribute('rel')
?.split(/\s+/)
.filter(Boolean) ?? []
link.getAttribute('rel')?.split(/\s+/).filter(Boolean) ?? []
)
rel.add('noopener')
......@@ -182,10 +179,7 @@ function IsolatedHtmlContent(props: {
}, [props.html])
return (
<div
ref={containerRef}
className={cn('block w-full', props.className)}
/>
<div ref={containerRef} className={cn('block w-full', props.className)} />
)
}
......
......@@ -16,10 +16,7 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
For commercial licensing, please contact support@quantumnous.com
*/
import {
HtmlContent,
type HtmlContentVariant,
} from '@/components/html-content'
import { HtmlContent, type HtmlContentVariant } from '@/components/html-content'
import { Markdown } from '@/components/ui/markdown'
type RichContentMode = 'markdown' | 'html'
......
......@@ -46,12 +46,12 @@ import {
TooltipProvider,
TooltipTrigger,
} from '@/components/ui/tooltip'
import { toIntlLocale } from '@/i18n/languages'
import {
formatCurrencyFromUSD,
formatQuotaWithCurrency,
getCurrencyLabel,
} from '@/lib/currency'
import { toIntlLocale } from '@/i18n/languages'
import { formatTimestampToDate } from '@/lib/format'
import { truncateText } from '@/lib/utils'
......
......@@ -16,9 +16,17 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
For commercial licensing, please contact support@quantumnous.com
*/
import { Code, Plus, Table, Trash2 } from 'lucide-react'
import { useEffect, useId, useMemo, useRef, useState } from 'react'
import { Code, FileUp, Loader2, Plus, Table, Trash2 } from 'lucide-react'
import {
type ChangeEvent,
useEffect,
useId,
useMemo,
useRef,
useState,
} from 'react'
import { useTranslation } from 'react-i18next'
import { toast } from 'sonner'
import { Alert, AlertDescription } from '@/components/ui/alert'
import { Button } from '@/components/ui/button'
......@@ -57,7 +65,7 @@ function getDuplicateSources(rows: MappingRow[]): string[] {
}
}
return Array.from(duplicates)
return [...duplicates]
}
export function ModelMappingEditor(props: ModelMappingEditorProps) {
......@@ -68,6 +76,8 @@ export function ModelMappingEditor(props: ModelMappingEditorProps) {
const [rows, setRows] = useState<MappingRow[]>([])
const [jsonValue, setJsonValue] = useState(props.value)
const [jsonError, setJsonError] = useState<string | null>(null)
const [isImporting, setIsImporting] = useState(false)
const excelInputRef = useRef<HTMLInputElement>(null)
const nextRowIdRef = useRef(0)
const duplicateSources = useMemo(() => getDuplicateSources(rows), [rows])
......@@ -121,7 +131,7 @@ export function ModelMappingEditor(props: ModelMappingEditorProps) {
})
setJsonError(null)
return true
} catch (_error) {
} catch {
setJsonError(t('Model mapping must be valid JSON format'))
return false
}
......@@ -132,6 +142,8 @@ export function ModelMappingEditor(props: ModelMappingEditorProps) {
// eslint-disable-next-line react-hooks/set-state-in-effect
setJsonValue(props.value)
parseJsonToRows(props.value)
// The parser synchronizes local editor state with the controlled value.
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [props.value])
const convertRowsToJson = (updatedRows: MappingRow[]): string => {
......@@ -204,6 +216,108 @@ export function ModelMappingEditor(props: ModelMappingEditorProps) {
parseJsonToRows(template)
}
const handleMappingsImported = (mappings: Record<string, string>) => {
const importedMappings = new Map(Object.entries(mappings))
const updatedRows = rows.map((row) => {
const mappedModel = importedMappings.get(row.from.trim())
return mappedModel === undefined ? row : { ...row, to: mappedModel }
})
const existingSources = new Set(
rows.map((row) => row.from.trim()).filter(Boolean)
)
for (const [from, to] of importedMappings) {
if (!existingSources.has(from)) {
updatedRows.push({ id: createRowId(), from, to })
}
}
syncRows(updatedRows)
}
const handleExcelFileChange = async (
event: ChangeEvent<HTMLInputElement>
) => {
const file = event.currentTarget.files?.[0]
event.currentTarget.value = ''
if (!file) return
setIsImporting(true)
try {
if (file.size > 10 * 1024 * 1024) {
throw new Error(t('Excel file cannot exceed 10 MB.'))
}
const XLSX = await import('xlsx')
const workbook = XLSX.read(await file.arrayBuffer(), { type: 'array' })
const firstSheetName = workbook.SheetNames[0]
if (!firstSheetName) {
throw new Error(t('The Excel workbook must contain a worksheet.'))
}
const worksheet = workbook.Sheets[firstSheetName]
const excelRows = XLSX.utils.sheet_to_json<unknown[]>(worksheet, {
header: 1,
defval: '',
raw: false,
})
const headers = excelRows[0] ?? []
const modelNameColumn = headers.findIndex(
(header) => String(header).trim() === '模型名称'
)
const mappingModelColumn = headers.findIndex(
(header) => String(header).trim() === '映射模型'
)
if (modelNameColumn < 0 || mappingModelColumn < 0) {
throw new Error(
t(
'The Excel worksheet must contain the headers “模型名称” and “映射模型”.'
)
)
}
const mappings: Record<string, string> = {}
for (let index = 1; index < excelRows.length; index += 1) {
const row = excelRows[index] ?? []
const modelName = String(row[modelNameColumn] ?? '').trim()
const mappedModel = String(row[mappingModelColumn] ?? '').trim()
if (!modelName && !mappedModel) continue
if (!modelName || !mappedModel) {
throw new Error(
t('Invalid model mapping in row {{row}}.', { row: index + 1 })
)
}
if (Object.hasOwn(mappings, modelName)) {
throw new Error(
t('Duplicate model name in row {{row}}: {{model}}.', {
row: index + 1,
model: modelName,
})
)
}
mappings[modelName] = mappedModel
}
const importedCount = Object.keys(mappings).length
if (importedCount === 0) {
throw new Error(t('No model mappings found in the Excel worksheet.'))
}
handleMappingsImported(mappings)
toast.success(
t('Model mapping import completed: {{count}} mappings imported.', {
count: importedCount,
})
)
} catch (error: unknown) {
toast.error(
error instanceof Error
? error.message
: t('Model mapping import failed')
)
} finally {
setIsImporting(false)
}
}
const handleModeChange = (nextMode: string) => {
if (nextMode !== 'visual' && nextMode !== 'json') return
if (nextMode === 'json') {
......@@ -234,16 +348,48 @@ export function ModelMappingEditor(props: ModelMappingEditorProps) {
{t('JSON')}
</TabsTrigger>
</TabsList>
<Button
type='button'
variant='link'
size='sm'
className='h-auto p-0'
onClick={handleFillTemplate}
disabled={props.disabled}
>
{t('Fill Template')}
</Button>
<div className='flex items-center gap-3'>
<input
ref={excelInputRef}
type='file'
accept='.xlsx,application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
className='hidden'
disabled={props.disabled || isImporting}
onChange={handleExcelFileChange}
/>
<Button
type='button'
variant='link'
size='sm'
className='h-auto p-0'
onClick={handleFillTemplate}
disabled={props.disabled}
>
{t('Fill Template')}
</Button>
<Button
type='button'
variant='link'
size='sm'
className='h-auto p-0'
onClick={(event) => {
event.preventDefault()
event.stopPropagation()
excelInputRef.current?.click()
}}
disabled={props.disabled || isImporting}
>
{isImporting ? (
<Loader2
className='mr-1 h-4 w-4 animate-spin'
aria-hidden='true'
/>
) : (
<FileUp className='mr-1 h-4 w-4' aria-hidden='true' />
)}
{t('Import from Excel')}
</Button>
</div>
</div>
{jsonError && (
......@@ -268,7 +414,7 @@ export function ModelMappingEditor(props: ModelMappingEditorProps) {
<div className='grid grid-cols-[1fr_1fr_auto] gap-2 text-sm font-medium'>
<div>{t('Original Model')}</div>
<div>{t('Replacement Model')}</div>
<div className='w-10'></div>
<div className='w-10' />
</div>
{rows.map((row) => (
<div
......
......@@ -125,11 +125,7 @@ export function LegalDocument({
return (
<PublicLayout showMainContainer={!contentIsHtml}>
{contentIsHtml ? (
<RichContent
mode='html'
htmlVariant='isolated'
content={rawContent}
/>
<RichContent mode='html' htmlVariant='isolated' content={rawContent} />
) : (
<div className='mx-auto max-w-4xl space-y-6 py-12'>
<div className='space-y-2'>
......
......@@ -63,6 +63,14 @@ export function ImportModelsDialog(props: ImportModelsDialogProps) {
}
)
)
const failedModels = result?.rows
.filter((row) => row.status === 'failed' && row.model_name)
.map((row) => row.model_name)
if (failedModels?.length) {
toast.error(
t('Failed models: {{models}}', { models: failedModels.join(', ') })
)
}
await Promise.all([
queryClient.invalidateQueries({ queryKey: modelsQueryKeys.lists() }),
queryClient.invalidateQueries({ queryKey: vendorsQueryKeys.lists() }),
......
......@@ -22,7 +22,10 @@ import { useTranslation } from 'react-i18next'
import { toast } from 'sonner'
import { ConfirmDialog } from '@/components/confirm-dialog'
import { DataTableRowActionMenu, StaticDataTable } from '@/components/data-table'
import {
DataTableRowActionMenu,
StaticDataTable,
} from '@/components/data-table'
import {
sideDrawerContentClassName,
sideDrawerFormClassName,
......
......@@ -404,7 +404,9 @@ function GroupPricingGuide({ open, onOpenChange }: GroupPricingGuideProps) {
<div className={sideDrawerFormClassName('gap-5')}>
<section className='space-y-2'>
<h3 className='text-sm font-semibold'>{t('The two roles of a group')}</h3>
<h3 className='text-sm font-semibold'>
{t('The two roles of a group')}
</h3>
<div className='text-muted-foreground space-y-2 text-sm leading-6'>
<p>
{t(
......@@ -416,7 +418,9 @@ function GroupPricingGuide({ open, onOpenChange }: GroupPricingGuideProps) {
{t('Token group')}
</span>
{': '}
{t('decides which channels are used and which base ratio applies.')}
{t(
'decides which channels are used and which base ratio applies.'
)}
</p>
<p>
<span className='text-foreground font-medium'>
......@@ -431,7 +435,9 @@ function GroupPricingGuide({ open, onOpenChange }: GroupPricingGuideProps) {
</section>
<section className='space-y-2'>
<h3 className='text-sm font-semibold'>{t('How a call is priced')}</h3>
<h3 className='text-sm font-semibold'>
{t('How a call is priced')}
</h3>
<ol className='text-muted-foreground list-decimal space-y-2 pl-5 text-sm leading-6'>
<li>
<span className='text-foreground font-medium'>
......@@ -453,7 +459,9 @@ function GroupPricingGuide({ open, onOpenChange }: GroupPricingGuideProps) {
<span className='text-foreground font-medium'>
{t('Charge.')}
</span>{' '}
{t('Cost = model price × that one ratio. Nothing else from the group settings enters the formula.')}
{t(
'Cost = model price × that one ratio. Nothing else from the group settings enters the formula.'
)}
</li>
</ol>
<p className='text-muted-foreground text-sm leading-6'>
......@@ -466,7 +474,9 @@ function GroupPricingGuide({ open, onOpenChange }: GroupPricingGuideProps) {
<section className='space-y-3'>
<h3 className='text-sm font-semibold'>{t('Worked example')}</h3>
<p className='text-muted-foreground text-sm leading-6'>
{t('The admin configured three groups and one special ratio rule:')}
{t(
'The admin configured three groups and one special ratio rule:'
)}
</p>
<div className='overflow-hidden rounded-lg border'>
......@@ -529,7 +539,9 @@ function GroupPricingGuide({ open, onOpenChange }: GroupPricingGuideProps) {
</div>
<div className='space-y-2 p-3'>
<GuideStepRow chip='1'>
{t('Billing group = premium (the token has a group, so use it)')}
{t(
'Billing group = premium (the token has a group, so use it)'
)}
</GuideStepRow>
<GuideStepRow chip='2'>
{t(
......@@ -550,7 +562,9 @@ function GroupPricingGuide({ open, onOpenChange }: GroupPricingGuideProps) {
</div>
<div className='space-y-2 p-3'>
<GuideStepRow chip='1'>
{t('Billing group = default (the token has a group, so use it)')}
{t(
'Billing group = default (the token has a group, so use it)'
)}
</GuideStepRow>
<GuideStepRow chip='2'>
{t(
......
......@@ -29,6 +29,7 @@ import { useTranslation } from 'react-i18next'
import { StaticDataTable } from '@/components/data-table/static/static-data-table'
import { StaticRowActions } from '@/components/data-table/static/static-row-actions'
import { Dialog } from '@/components/dialog'
import {
sideDrawerContentClassName,
sideDrawerFormClassName,
......@@ -49,7 +50,6 @@ import {
CollapsibleContent,
CollapsibleTrigger,
} from '@/components/ui/collapsible'
import { Dialog } from '@/components/dialog'
import { Input } from '@/components/ui/input'
import { Label } from '@/components/ui/label'
import {
......@@ -1100,10 +1100,13 @@ function GroupOverrideDialog({
<p className='text-muted-foreground text-xs'>
{baseRatio !== undefined
? t('(instead of {{ratio}})', { ratio: baseRatio })
: t('Multiplier applied when {{userGroup}} uses {{targetGroup}}', {
userGroup: userGroup || t('this user group'),
targetGroup: targetGroup || t('this token group'),
})}
: t(
'Multiplier applied when {{userGroup}} uses {{targetGroup}}',
{
userGroup: userGroup || t('this user group'),
targetGroup: targetGroup || t('this token group'),
}
)}
</p>
</div>
</div>
......
......@@ -61,6 +61,14 @@ export function ModelPricingImportDialog(props: ModelPricingImportDialogProps) {
}
)
)
const failedModels = result?.rows
.filter((row) => row.status === 'failed' && row.model_name)
.map((row) => row.model_name)
if (failedModels?.length) {
toast.error(
t('Failed models: {{models}}', { models: failedModels.join(', ') })
)
}
await queryClient.invalidateQueries({ queryKey: ['system-options'] })
return true
} catch {
......
......@@ -50,9 +50,7 @@ export function TransferDialog({
transferring,
}: TransferDialogProps) {
const { t } = useTranslation()
const currencyConfig = useSystemConfigStore(
(state) => state.config.currency
)
const currencyConfig = useSystemConfigStore((state) => state.config.currency)
const minimumQuota = Math.ceil(
currencyConfig.quotaPerUnit > 0
? currencyConfig.quotaPerUnit
......
......@@ -26,8 +26,8 @@ import fr from './locales/fr.json'
import ja from './locales/ja.json'
import ru from './locales/ru.json'
import vi from './locales/vi.json'
import zhCN from './locales/zh.json'
import zhTW from './locales/zh-TW.json'
import zhCN from './locales/zh.json'
export const resources = {
en,
......@@ -36,7 +36,7 @@ export const resources = {
ru,
ja,
vi,
zhTW
zhTW,
} as const
i18n
......
......@@ -24,7 +24,7 @@ export const INTERFACE_LANGUAGE_OPTIONS = [
{ code: 'ru', label: 'Русский' },
{ code: 'ja', label: '日本語' },
{ code: 'vi', label: 'Tiếng Việt' },
{ code: 'zhTW', label: '繁體中文' }
{ code: 'zhTW', label: '繁體中文' },
] as const
export type InterfaceLanguageCode =
......@@ -34,10 +34,15 @@ export function normalizeInterfaceLanguage(value?: string | null): string {
if (!value) return 'en'
let normalized = value.trim().replaceAll('_', '-').toLowerCase()
if (value === 'zh-TW' || value === 'zh-HK' || value === 'zh-MO' || value === 'zhTW') {
if (
value === 'zh-TW' ||
value === 'zh-HK' ||
value === 'zh-MO' ||
value === 'zhTW'
) {
normalized = 'zhTW'
}
if (value === 'zh-CN' || value === 'zh-Hans' || value === "zhCN") {
if (value === 'zh-CN' || value === 'zh-Hans' || value === 'zhCN') {
normalized = 'zhCN'
}
......
......@@ -1445,6 +1445,7 @@
"Drawing task records": "Drawing task records",
"Duplicate": "Duplicate",
"Duplicate group names: {{names}}": "Duplicate group names: {{names}}",
"Duplicate model name in row {{row}}: {{model}}.": "Duplicate model name in row {{row}}: {{model}}.",
"Duplicate source model mappings are not allowed": "Duplicate source model mappings are not allowed",
"Duplicate source model(s): {{models}}": "Duplicate source model(s): {{models}}",
"Duration": "Duration",
......@@ -1721,6 +1722,7 @@
"example.com&#10;blocked-site.com": "example.com&#10;blocked-site.com",
"example.com&#10;company.com": "example.com&#10;company.com",
"Excel file": "Excel file",
"Excel file cannot exceed 10 MB.": "Excel file cannot exceed 10 MB.",
"Excel import": "Excel import",
"Excellent": "Excellent",
"Exchange rate is required": "Exchange rate is required",
......@@ -1771,6 +1773,7 @@
"Fail Reason Details": "Fail Reason Details",
"failed": "failed",
"Failed": "Failed",
"Failed models: {{models}}": "Failed models: {{models}}",
"Failed to {{action}} user": "Failed to {{action}} user",
"Failed to adjust quota": "Failed to adjust quota",
"Failed to apply overwrite.": "Failed to apply overwrite.",
......@@ -2261,12 +2264,16 @@
"Image Tokens": "Image Tokens",
"Imagine the pricing table has three groups: default (ratio 1.0), premium (ratio 0.5), and vip (ratio 0.8). Users whose account is in the vip group get user-level perks, and premium is a cheaper channel pool that users can pick for their tokens.": "Imagine the pricing table has three groups: default (ratio 1.0), premium (ratio 0.5), and vip (ratio 0.8). Users whose account is in the vip group get user-level perks, and premium is a cheaper channel pool that users can pick for their tokens.",
"Import": "Import",
"Import from Excel": "Import from Excel",
"Import mappings from an Excel workbook.": "Import mappings from an Excel workbook.",
"Import model mappings": "Import model mappings",
"Import model metadata from an Excel workbook.": "Import model metadata from an Excel workbook.",
"Import model prices from an Excel workbook.": "Import model prices from an Excel workbook.",
"Import model pricing": "Import model pricing",
"Import models": "Import models",
"Import to CC Switch": "Import to CC Switch",
"Important": "Important",
"Imported mappings replace existing mappings with the same model name.": "Imported mappings replace existing mappings with the same model name.",
"Importing replaces the billing mode for matching models: per-token billing clears fixed prices, while per-request billing clears ratio settings.": "Importing replaces the billing mode for matching models: per-token billing clears fixed prices, while per-request billing clears ratio settings.",
"Importing...": "Importing...",
"In JSON, the user group is the outer key and the billing group is the inner key. The example below means: vip users pay 0.8 when billed as standard, and 0.3 when billed as premium.": "In JSON, the user group is the outer key and the billing group is the inner key. The example below means: vip users pay 0.8 when billed as standard, and 0.3 when billed as premium.",
......@@ -2325,6 +2332,7 @@
"Invalid JSON in parameter override template": "Invalid JSON in parameter override template",
"Invalid JSON string.": "Invalid JSON string.",
"Invalid model mapping format": "Invalid model mapping format",
"Invalid model mapping in row {{row}}.": "Invalid model mapping in row {{row}}.",
"Invalid Passkey registration response": "Invalid Passkey registration response",
"Invalid Passkey response": "Invalid Passkey response",
"Invalid payment redirect URL": "Invalid payment redirect URL",
......@@ -2642,6 +2650,9 @@
"Model Limits": "Model Limits",
"Model Mapping": "Model Mapping",
"Model Mapping (JSON)": "Model Mapping (JSON)",
"Model mapping import completed: {{count}} mappings imported.": "Model mapping import completed: {{count}} mappings imported.",
"Model mapping import failed": "Model mapping import failed",
"Model mapping import requirements": "Model mapping import requirements",
"Model Mapping must be a JSON object like": "Model Mapping must be a JSON object like",
"Model mapping must be a JSON object with string values": "Model mapping must be a JSON object with string values",
"Model mapping must be a valid JSON object": "Model mapping must be a valid JSON object",
......@@ -2883,6 +2894,7 @@
"No missing models found.": "No missing models found.",
"No model found.": "No model found.",
"No model mappings configured. Click \"Add Mapping\" to get started.": "No model mappings configured. Click \"Add Mapping\" to get started.",
"No model mappings found in the Excel worksheet.": "No model mappings found in the Excel worksheet.",
"No model price changes to save": "No model price changes to save",
"No models available": "No models available",
"No models available in this category": "No models available in this category",
......@@ -4424,8 +4436,11 @@
"The entered text does not match the required text.": "The entered text does not match the required text.",
"The environment (test vs production) is decided by the key you paste here — use the Test key while integrating, then swap to the Production key when going live.": "The environment (test vs production) is decided by the key you paste here — use the Test key while integrating, then swap to the Production key when going live.",
"The exact model identifier as used in API requests.": "The exact model identifier as used in API requests.",
"The Excel workbook must contain a worksheet.": "The Excel workbook must contain a worksheet.",
"The Excel worksheet must contain the headers “模型名称” and “映射模型”.": "The Excel worksheet must contain the headers “模型名称” and “映射模型”.",
"The first row must contain headers, and model_name is required. For per-request billing, provide fixed_price. Otherwise input_price is required; optional columns are completion_price, cache_price, create_cache_price, image_price, audio_input_price, and audio_output_price.": "The first row must contain headers, and model_name is required. For per-request billing, provide fixed_price. Otherwise input_price is required; optional columns are completion_price, cache_price, create_cache_price, image_price, audio_input_price, and audio_output_price.",
"The first row must contain headers, and model_name is required. Optional columns: description, icon, tags, vendor_name, endpoints, status, sync_official, name_rule.": "The first row must contain headers, and model_name is required. Optional columns: description, icon, tags, vendor_name, endpoints, status, sync_official, name_rule.",
"The first row must contain the headers “模型名称” and “映射模型”. Both columns are required.": "The first row must contain the headers “模型名称” and “映射模型”. Both columns are required.",
"The following models have billing type conflicts (fixed price vs ratio billing). Confirm to proceed with the changes.": "The following models have billing type conflicts (fixed price vs ratio billing). Confirm to proceed with the changes.",
"The following models in the model redirect have not been added to the \"Models\" list and may fail during invocation due to missing available models:": "The following models in the model redirect have not been added to the \"Models\" list and may fail during invocation due to missing available models:",
"The mapped upstream model(s)": "The mapped upstream model(s)",
......
......@@ -1445,6 +1445,7 @@
"Drawing task records": "Historique des tâches de dessin",
"Duplicate": "Dupliquer",
"Duplicate group names: {{names}}": "Noms de groupe en double : {{names}}",
"Duplicate model name in row {{row}}: {{model}}.": "Nom de modèle dupliqué à la ligne {{row}} : {{model}}.",
"Duplicate source model mappings are not allowed": "Les mappages de modèles source en double ne sont pas autorisés",
"Duplicate source model(s): {{models}}": "Modèle(s) source en double : {{models}}",
"Duration": "Durée",
......@@ -1721,6 +1722,7 @@
"example.com&#10;blocked-site.com": "example.com&#10;blocked-site.com",
"example.com&#10;company.com": "example.com&#10;company.com",
"Excel file": "Fichier Excel",
"Excel file cannot exceed 10 MB.": "Le fichier Excel ne peut pas dépasser 10 Mo.",
"Excel import": "Importation Excel",
"Excellent": "Excellent",
"Exchange rate is required": "Le taux de change est requis",
......@@ -1771,6 +1773,7 @@
"Fail Reason Details": "Détails de la raison de l'échec",
"failed": "échoué",
"Failed": "Échec",
"Failed models: {{models}}": "Modèles en échec : {{models}}",
"Failed to {{action}} user": "Échec de l'action {{action}} sur l'utilisateur",
"Failed to adjust quota": "Échec de l'ajustement du quota",
"Failed to apply overwrite.": "Échec de l'application de l'écrasement.",
......@@ -2261,12 +2264,16 @@
"Image Tokens": "Tokens image",
"Imagine the pricing table has three groups: default (ratio 1.0), premium (ratio 0.5), and vip (ratio 0.8). Users whose account is in the vip group get user-level perks, and premium is a cheaper channel pool that users can pick for their tokens.": "Imaginez que le tableau tarifaire contient trois groupes : default (taux 1,0), premium (taux 0,5) et vip (taux 0,8). Les utilisateurs du groupe vip bénéficient d’avantages au niveau du compte, et premium est un pool de canaux moins cher que les utilisateurs peuvent choisir pour leurs jetons.",
"Import": "Importer",
"Import from Excel": "Importer depuis Excel",
"Import mappings from an Excel workbook.": "Importer des mappages depuis un classeur Excel.",
"Import model mappings": "Importer les mappages de modèles",
"Import model metadata from an Excel workbook.": "Importer les métadonnées des modèles depuis un classeur Excel.",
"Import model prices from an Excel workbook.": "Importer les tarifs des modèles depuis un classeur Excel.",
"Import model pricing": "Importer les tarifs des modèles",
"Import models": "Importer des modèles",
"Import to CC Switch": "Importer vers CC Switch",
"Important": "Important",
"Imported mappings replace existing mappings with the same model name.": "Les mappages importés remplacent les mappages existants du même nom de modèle.",
"Importing replaces the billing mode for matching models: per-token billing clears fixed prices, while per-request billing clears ratio settings.": "L’importation remplace le mode de facturation des modèles correspondants : la facturation au token efface les prix fixes, tandis que la facturation par requête efface les ratios.",
"Importing...": "Importation...",
"In JSON, the user group is the outer key and the billing group is the inner key. The example below means: vip users pay 0.8 when billed as standard, and 0.3 when billed as premium.": "En JSON, le groupe d’utilisateurs est la clé externe et le groupe de facturation la clé interne. L’exemple ci-dessous signifie : les utilisateurs vip paient 0,8 sous standard et 0,3 sous premium.",
......@@ -2325,6 +2332,7 @@
"Invalid JSON in parameter override template": "JSON invalide dans le modèle de remplacement de paramètres",
"Invalid JSON string.": "Chaîne JSON invalide.",
"Invalid model mapping format": "Format de mappage de modèle invalide",
"Invalid model mapping in row {{row}}.": "Mappage de modèle invalide à la ligne {{row}}.",
"Invalid Passkey registration response": "Réponse d'enregistrement de passe-clé invalide",
"Invalid Passkey response": "Réponse Passkey invalide",
"Invalid payment redirect URL": "URL de redirection de paiement non valide",
......@@ -2642,6 +2650,9 @@
"Model Limits": "Limites du modèle",
"Model Mapping": "Mappage de modèle",
"Model Mapping (JSON)": "Mappage de modèle (JSON)",
"Model mapping import completed: {{count}} mappings imported.": "Importation des mappages terminée : {{count}} mappages importés.",
"Model mapping import failed": "Échec de l’importation des mappages de modèles",
"Model mapping import requirements": "Exigences d’importation des mappages de modèles",
"Model Mapping must be a JSON object like": "Le mappage de modèle doit être un objet JSON tel que",
"Model mapping must be a JSON object with string values": "Le mappage de modèles doit être un objet JSON avec des valeurs de chaîne",
"Model mapping must be a valid JSON object": "Le mapping des modèles doit être un objet JSON valide",
......@@ -2883,6 +2894,7 @@
"No missing models found.": "Aucun modèle manquant trouvé.",
"No model found.": "Aucun modèle trouvé.",
"No model mappings configured. Click \"Add Mapping\" to get started.": "Aucun mappage de modèle configuré. Cliquez sur « Ajouter un mappage » pour commencer.",
"No model mappings found in the Excel worksheet.": "Aucun mappage de modèle trouvé dans la feuille Excel.",
"No model price changes to save": "Aucun changement de prix de modèle à sauvegarder",
"No models available": "Aucun modèle disponible",
"No models available in this category": "Aucun modèle disponible dans cette catégorie",
......@@ -4424,8 +4436,11 @@
"The entered text does not match the required text.": "Le texte saisi ne correspond pas au texte requis.",
"The environment (test vs production) is decided by the key you paste here — use the Test key while integrating, then swap to the Production key when going live.": "L’environnement (test ou production) est déterminé par la clé collée ici : utilisez la clé de test pendant l’intégration, puis remplacez-la par la clé de production lors de la mise en ligne.",
"The exact model identifier as used in API requests.": "L'identifiant exact du modèle tel qu'utilisé dans les requêtes API.",
"The Excel workbook must contain a worksheet.": "Le classeur Excel doit contenir une feuille de calcul.",
"The Excel worksheet must contain the headers “模型名称” and “映射模型”.": "La feuille Excel doit contenir les en-têtes « 模型名称 » et « 映射模型 ».",
"The first row must contain headers, and model_name is required. For per-request billing, provide fixed_price. Otherwise input_price is required; optional columns are completion_price, cache_price, create_cache_price, image_price, audio_input_price, and audio_output_price.": "La première ligne doit contenir les en-têtes et model_name est obligatoire. Pour la facturation par requête, renseignez fixed_price. Sinon, input_price est obligatoire ; colonnes facultatives : completion_price, cache_price, create_cache_price, image_price, audio_input_price et audio_output_price.",
"The first row must contain headers, and model_name is required. Optional columns: description, icon, tags, vendor_name, endpoints, status, sync_official, name_rule.": "La première ligne doit contenir les en-têtes et model_name est obligatoire. Colonnes facultatives : description, icon, tags, vendor_name, endpoints, status, sync_official, name_rule.",
"The first row must contain the headers “模型名称” and “映射模型”. Both columns are required.": "La première ligne doit contenir les en-têtes « 模型名称 » et « 映射模型 ». Les deux colonnes sont obligatoires.",
"The following models have billing type conflicts (fixed price vs ratio billing). Confirm to proceed with the changes.": "Les modèles suivants présentent des conflits de type de facturation (prix fixe vs facturation au ratio). Confirmez pour procéder aux changements.",
"The following models in the model redirect have not been added to the \"Models\" list and may fail during invocation due to missing available models:": "Les modèles suivants dans la redirection du modèle n'ont pas été ajoutés à la liste \"Modèles\" et peuvent échouer lors de l'invocation en raison de modèles disponibles manquants :",
"The mapped upstream model(s)": "Le(s) modèle(s) amont mappé(s)",
......
......@@ -1445,6 +1445,7 @@
"Drawing task records": "描画タスク記録",
"Duplicate": "複製",
"Duplicate group names: {{names}}": "重複するグループ名: {{names}}",
"Duplicate model name in row {{row}}: {{model}}.": "{{row}} 行目に重複したモデル名があります:{{model}}。",
"Duplicate source model mappings are not allowed": "重複したソースモデルのマッピングは許可されていません",
"Duplicate source model(s): {{models}}": "重複したソースモデル: {{models}}",
"Duration": "所要時間",
......@@ -1721,6 +1722,7 @@
"example.com&#10;blocked-site.com": "example.com &#10;blocked-site.com",
"example.com&#10;company.com": "example.com &#10;company.com",
"Excel file": "Excel ファイル",
"Excel file cannot exceed 10 MB.": "Excel ファイルは 10 MB を超えることはできません。",
"Excel import": "Excel インポート",
"Excellent": "優秀",
"Exchange rate is required": "為替レートは必須です",
......@@ -1771,6 +1773,7 @@
"Fail Reason Details": "失敗理由の詳細",
"failed": "失敗",
"Failed": "失敗",
"Failed models: {{models}}": "インポートに失敗したモデル:{{models}}",
"Failed to {{action}} user": "ユーザーの{{action}}に失敗しました",
"Failed to adjust quota": "クォータの調整に失敗しました",
"Failed to apply overwrite.": "オーバーライトの適用に失敗しました。",
......@@ -2261,12 +2264,16 @@
"Image Tokens": "画像トークン",
"Imagine the pricing table has three groups: default (ratio 1.0), premium (ratio 0.5), and vip (ratio 0.8). Users whose account is in the vip group get user-level perks, and premium is a cheaper channel pool that users can pick for their tokens.": "料金表に3つのグループがあるとします:default(倍率 1.0)、premium(倍率 0.5)、vip(倍率 0.8)。アカウントが vip グループのユーザーはユーザーレベルの特典を受けられ、premium はユーザーがトークン用に選べる安いチャネルプールです。",
"Import": "インポート",
"Import from Excel": "Excel からインポート",
"Import mappings from an Excel workbook.": "Excel ブックからモデルマッピングをインポートします。",
"Import model mappings": "モデルマッピングをインポート",
"Import model metadata from an Excel workbook.": "Excel ワークブックからモデルのメタデータをインポートします。",
"Import model prices from an Excel workbook.": "Excel ワークブックからモデル料金をインポートします。",
"Import model pricing": "モデル料金をインポート",
"Import models": "モデルをインポート",
"Import to CC Switch": "CC Switch にインポート",
"Important": "重要",
"Imported mappings replace existing mappings with the same model name.": "インポートしたマッピングは、同名モデルの既存マッピングを置き換えます。",
"Importing replaces the billing mode for matching models: per-token billing clears fixed prices, while per-request billing clears ratio settings.": "インポートすると該当モデルの課金方式が上書きされます。トークン単位課金では固定価格が削除され、リクエスト単位課金では倍率設定が削除されます。",
"Importing...": "インポート中...",
"In JSON, the user group is the outer key and the billing group is the inner key. The example below means: vip users pay 0.8 when billed as standard, and 0.3 when billed as premium.": "JSONでは外側のキーがユーザーグループ、内側のキーが課金グループです。以下の例は、vip ユーザーが standard として課金されると 0.8、premium として課金されると 0.3 を意味します。",
......@@ -2325,6 +2332,7 @@
"Invalid JSON in parameter override template": "パラメータオーバーライドテンプレートのJSONが無効です",
"Invalid JSON string.": "無効な JSON 文字列です。",
"Invalid model mapping format": "無効なモデルマッピング形式",
"Invalid model mapping in row {{row}}.": "{{row}} 行目のモデルマッピングが無効です。",
"Invalid Passkey registration response": "無効なパスキー登録応答",
"Invalid Passkey response": "無効なパスキー応答",
"Invalid payment redirect URL": "無効な支払いリダイレクトURL",
......@@ -2642,6 +2650,9 @@
"Model Limits": "モデル制限",
"Model Mapping": "モデルマッピング",
"Model Mapping (JSON)": "モデルマッピング (JSON)",
"Model mapping import completed: {{count}} mappings imported.": "モデルマッピングのインポートが完了しました:{{count}} 件をインポートしました。",
"Model mapping import failed": "モデルマッピングのインポートに失敗しました",
"Model mapping import requirements": "モデルマッピングのインポート要件",
"Model Mapping must be a JSON object like": "モデルマッピングは次のようなJSONオブジェクトである必要があります",
"Model mapping must be a JSON object with string values": "モデルマッピングは文字列値を持つ JSON オブジェクトである必要があります",
"Model mapping must be a valid JSON object": "モデルマッピングは有効なJSONオブジェクトである必要があります",
......@@ -2883,6 +2894,7 @@
"No missing models found.": "不足しているモデルは見つかりません。",
"No model found.": "モデルが見つかりません。",
"No model mappings configured. Click \"Add Mapping\" to get started.": "モデルマッピングは設定されていません。「マッピングを追加」をクリックして開始してください。",
"No model mappings found in the Excel worksheet.": "Excel シートにモデルマッピングがありません。",
"No model price changes to save": "保存するモデル価格の変更はありません",
"No models available": "利用可能なモデルがありません",
"No models available in this category": "このカテゴリにはモデルがありません",
......@@ -4424,8 +4436,11 @@
"The entered text does not match the required text.": "入力したテキストが必要なテキストと一致しません。",
"The environment (test vs production) is decided by the key you paste here — use the Test key while integrating, then swap to the Production key when going live.": "環境(テスト/本番)はここに貼り付けるキーで決まります。統合中はテストキーを使用し、本番公開時に本番キーへ切り替えてください。",
"The exact model identifier as used in API requests.": "APIリクエストで使用される正確なモデル識別子。",
"The Excel workbook must contain a worksheet.": "Excel ブックにはワークシートが必要です。",
"The Excel worksheet must contain the headers “模型名称” and “映射模型”.": "Excel シートには「模型名称」と「映射模型」のヘッダーが必要です。",
"The first row must contain headers, and model_name is required. For per-request billing, provide fixed_price. Otherwise input_price is required; optional columns are completion_price, cache_price, create_cache_price, image_price, audio_input_price, and audio_output_price.": "1 行目はヘッダーで、model_name は必須です。リクエスト単位課金では fixed_price を入力してください。それ以外では input_price が必須です。任意列:completion_price、cache_price、create_cache_price、image_price、audio_input_price、audio_output_price。",
"The first row must contain headers, and model_name is required. Optional columns: description, icon, tags, vendor_name, endpoints, status, sync_official, name_rule.": "1 行目はヘッダーで、model_name は必須です。任意列:description、icon、tags、vendor_name、endpoints、status、sync_official、name_rule。",
"The first row must contain the headers “模型名称” and “映射模型”. Both columns are required.": "最初の行には「模型名称」と「映射模型」のヘッダーが必要です。両方の列が必須です。",
"The following models have billing type conflicts (fixed price vs ratio billing). Confirm to proceed with the changes.": "以下のモデルには請求タイプ(固定価格 vs 比率請求)の競合があります。変更を続行するには確認してください。",
"The following models in the model redirect have not been added to the \"Models\" list and may fail during invocation due to missing available models:": "モデルリダイレクト内の以下のモデルは\"モデル\"リストに追加されていないため、利用可能なモデルが不足して呼び出しが失敗する可能性があります:",
"The mapped upstream model(s)": "マッピングされたアップストリームモデル",
......
......@@ -1445,6 +1445,7 @@
"Drawing task records": "Lịch sử tác vụ vẽ",
"Duplicate": "Nhân bản",
"Duplicate group names: {{names}}": "Tên nhóm bị trùng: {{names}}",
"Duplicate model name in row {{row}}: {{model}}.": "Tên mô hình bị trùng ở hàng {{row}}: {{model}}.",
"Duplicate source model mappings are not allowed": "Không cho phép ánh xạ mô hình nguồn trùng lặp",
"Duplicate source model(s): {{models}}": "Mô hình nguồn trùng lặp: {{models}}",
"Duration": "Thời lượng",
......@@ -1721,6 +1722,7 @@
"example.com&#10;blocked-site.com": "example.com\nblocked-site.com",
"example.com&#10;company.com": "example.com\ncompany.com",
"Excel file": "Tệp Excel",
"Excel file cannot exceed 10 MB.": "Tệp Excel không được vượt quá 10 MB.",
"Excel import": "Nhập từ Excel",
"Excellent": "Tuyệt vời",
"Exchange rate is required": "Cần có tỷ giá",
......@@ -1771,6 +1773,7 @@
"Fail Reason Details": "Chi tiết lý do thất bại",
"failed": "thất bại",
"Failed": "Thất bại",
"Failed models: {{models}}": "Các mô hình nhập thất bại: {{models}}",
"Failed to {{action}} user": "Không thể {{action}} người dùng",
"Failed to adjust quota": "Không thể điều chỉnh hạn mức",
"Failed to apply overwrite.": "Không thể áp dụng ghi đè.",
......@@ -2261,12 +2264,16 @@
"Image Tokens": "Token hình ảnh",
"Imagine the pricing table has three groups: default (ratio 1.0), premium (ratio 0.5), and vip (ratio 0.8). Users whose account is in the vip group get user-level perks, and premium is a cheaper channel pool that users can pick for their tokens.": "Giả sử bảng định giá có ba nhóm: default (hệ số 1.0), premium (hệ số 0.5) và vip (hệ số 0.8). Người dùng có tài khoản thuộc nhóm vip nhận ưu đãi cấp người dùng, còn premium là một nhóm kênh rẻ hơn mà người dùng có thể chọn cho token của mình.",
"Import": "Nhập",
"Import from Excel": "Nhập từ Excel",
"Import mappings from an Excel workbook.": "Nhập ánh xạ từ sổ làm việc Excel.",
"Import model mappings": "Nhập ánh xạ mô hình",
"Import model metadata from an Excel workbook.": "Nhập metadata mô hình từ sổ làm việc Excel.",
"Import model prices from an Excel workbook.": "Nhập giá mô hình từ sổ làm việc Excel.",
"Import model pricing": "Nhập giá mô hình",
"Import models": "Nhập mô hình",
"Import to CC Switch": "Nhập vào CC Switch",
"Important": "Quan trọng",
"Imported mappings replace existing mappings with the same model name.": "Ánh xạ đã nhập sẽ thay thế ánh xạ hiện có có cùng tên mô hình.",
"Importing replaces the billing mode for matching models: per-token billing clears fixed prices, while per-request billing clears ratio settings.": "Việc nhập sẽ thay thế chế độ tính phí của các mô hình trùng khớp: tính phí theo token sẽ xóa giá cố định, còn tính phí theo lượt sẽ xóa cấu hình hệ số.",
"Importing...": "Đang nhập...",
"In JSON, the user group is the outer key and the billing group is the inner key. The example below means: vip users pay 0.8 when billed as standard, and 0.3 when billed as premium.": "Trong JSON, khóa ngoài là nhóm người dùng, khóa trong là nhóm tính phí. Ví dụ dưới đây nghĩa là: người dùng vip trả 0.8 khi tính phí theo standard và 0.3 khi theo premium.",
......@@ -2325,6 +2332,7 @@
"Invalid JSON in parameter override template": "JSON không hợp lệ trong mẫu ghi đè tham số",
"Invalid JSON string.": "Chuỗi JSON không hợp lệ.",
"Invalid model mapping format": "Định dạng ánh xạ mô hình không hợp lệ",
"Invalid model mapping in row {{row}}.": "Ánh xạ mô hình ở hàng {{row}} không hợp lệ.",
"Invalid Passkey registration response": "Phản hồi đăng ký Passkey không hợp lệ",
"Invalid Passkey response": "Phản hồi Passkey không hợp lệ",
"Invalid payment redirect URL": "URL chuyển hướng thanh toán không hợp lệ",
......@@ -2642,6 +2650,9 @@
"Model Limits": "Giới hạn Mô hình",
"Model Mapping": "Ánh xạ mô hình",
"Model Mapping (JSON)": "Ánh xạ mô hình (JSON)",
"Model mapping import completed: {{count}} mappings imported.": "Đã nhập xong ánh xạ mô hình: {{count}} ánh xạ.",
"Model mapping import failed": "Không thể nhập ánh xạ mô hình",
"Model mapping import requirements": "Yêu cầu nhập ánh xạ mô hình",
"Model Mapping must be a JSON object like": "Ánh xạ Mô hình phải là một đối tượng JSON như",
"Model mapping must be a JSON object with string values": "Ánh xạ mô hình phải là đối tượng JSON với giá trị chuỗi",
"Model mapping must be a valid JSON object": "Ánh xạ mô hình phải là một đối tượng JSON hợp lệ",
......@@ -2883,6 +2894,7 @@
"No missing models found.": "Không tìm thấy mô hình nào bị thiếu.",
"No model found.": "Không tìm thấy mô hình.",
"No model mappings configured. Click \"Add Mapping\" to get started.": "Chưa có ánh xạ mô hình nào được cấu hình. Nhấp vào \"Thêm ánh xạ\" để bắt đầu.",
"No model mappings found in the Excel worksheet.": "Không tìm thấy ánh xạ mô hình trong trang tính Excel.",
"No model price changes to save": "Không có thay đổi giá mô hình nào cần lưu",
"No models available": "Không có mô hình nào khả dụng",
"No models available in this category": "Không có mô hình nào trong danh mục này",
......@@ -4424,8 +4436,11 @@
"The entered text does not match the required text.": "Văn bản đã nhập không khớp với văn bản yêu cầu.",
"The environment (test vs production) is decided by the key you paste here — use the Test key while integrating, then swap to the Production key when going live.": "Môi trường (test hay production) được quyết định bởi khóa bạn dán tại đây — dùng khóa Test khi tích hợp, sau đó đổi sang khóa Production khi chạy chính thức.",
"The exact model identifier as used in API requests.": "Mã định danh mô hình chính xác như được sử dụng trong các yêu cầu API.",
"The Excel workbook must contain a worksheet.": "Sổ làm việc Excel phải có một trang tính.",
"The Excel worksheet must contain the headers “模型名称” and “映射模型”.": "Trang tính Excel phải có tiêu đề “模型名称” và “映射模型”.",
"The first row must contain headers, and model_name is required. For per-request billing, provide fixed_price. Otherwise input_price is required; optional columns are completion_price, cache_price, create_cache_price, image_price, audio_input_price, and audio_output_price.": "Hàng đầu tiên phải chứa tiêu đề và bắt buộc có model_name. Với tính phí theo lượt, hãy nhập fixed_price. Nếu không, bắt buộc có input_price; các cột tùy chọn gồm completion_price, cache_price, create_cache_price, image_price, audio_input_price và audio_output_price.",
"The first row must contain headers, and model_name is required. Optional columns: description, icon, tags, vendor_name, endpoints, status, sync_official, name_rule.": "Hàng đầu tiên phải chứa tiêu đề và bắt buộc có model_name. Các cột tùy chọn: description, icon, tags, vendor_name, endpoints, status, sync_official, name_rule.",
"The first row must contain the headers “模型名称” and “映射模型”. Both columns are required.": "Hàng đầu tiên phải có tiêu đề “模型名称” và “映射模型”. Cả hai cột đều bắt buộc.",
"The following models have billing type conflicts (fixed price vs ratio billing). Confirm to proceed with the changes.": "Các mô hình sau có xung đột loại thanh toán (giá cố định so với thanh toán theo tỷ lệ). Xác nhận để tiếp tục với các thay đổi.",
"The following models in the model redirect have not been added to the \"Models\" list and may fail during invocation due to missing available models:": "Các mô hình sau trong chuyển hướng mô hình chưa được thêm vào danh sách \"Mô hình\" và có thể gọi thất bại do thiếu các mô hình có sẵn:",
"The mapped upstream model(s)": "Mô hình(s) thượng nguồn được ánh xạ",
......
......@@ -1445,6 +1445,7 @@
"Drawing task records": "繪圖任務記錄",
"Duplicate": "重複",
"Duplicate group names: {{names}}": "存在重複的分組名稱:{{names}}",
"Duplicate model name in row {{row}}: {{model}}.": "第 {{row}} 行的模型名稱重複:{{model}}。",
"Duplicate source model mappings are not allowed": "不允許重複的源模型映射",
"Duplicate source model(s): {{models}}": "重複的源模型:{{models}}",
"Duration": "耗時",
......@@ -1721,6 +1722,7 @@
"example.com&#10;blocked-site.com": "example.com&#10;blocked-site.com",
"example.com&#10;company.com": "example.com&#10;company.com",
"Excel file": "Excel 檔案",
"Excel file cannot exceed 10 MB.": "Excel 檔案不能超過 10 MB。",
"Excel import": "Excel 匯入",
"Excellent": "優秀",
"Exchange rate is required": "匯率為必填項",
......@@ -1771,6 +1773,7 @@
"Fail Reason Details": "失敗原因詳情",
"failed": "已失敗",
"Failed": "失敗",
"Failed models: {{models}}": "Failed models: {{models}}",
"Failed to {{action}} user": "{{action}}用戶失敗",
"Failed to adjust quota": "調整額度失敗",
"Failed to apply overwrite.": "套用覆蓋失敗。",
......@@ -2261,12 +2264,16 @@
"Image Tokens": "圖像 Token",
"Imagine the pricing table has three groups: default (ratio 1.0), premium (ratio 0.5), and vip (ratio 0.8). Users whose account is in the vip group get user-level perks, and premium is a cheaper channel pool that users can pick for their tokens.": "假設定價分組表裡有三個分組:default(倍率 1.0)、premium(倍率 0.5)、vip(倍率 0.8)。賬號在 vip 分組的用戶享受用戶級待遇,premium 則是一個更便宜的渠道池,用戶建令牌時可以選它。",
"Import": "匯入",
"Import from Excel": "Import from Excel",
"Import mappings from an Excel workbook.": "Import mappings from an Excel workbook.",
"Import model mappings": "Import model mappings",
"Import model metadata from an Excel workbook.": "從 Excel 活頁簿匯入模型中繼資料。",
"Import model prices from an Excel workbook.": "從 Excel 活頁簿匯入模型定價。",
"Import model pricing": "匯入模型定價",
"Import models": "匯入模型",
"Import to CC Switch": "填入 CC Switch",
"Important": "重要",
"Imported mappings replace existing mappings with the same model name.": "Imported mappings replace existing mappings with the same model name.",
"Importing replaces the billing mode for matching models: per-token billing clears fixed prices, while per-request billing clears ratio settings.": "匯入會覆寫相符模型的計費方式:按量計費會清除固定價格,按次計費會清除倍率設定。",
"Importing...": "正在匯入...",
"In JSON, the user group is the outer key and the billing group is the inner key. The example below means: vip users pay 0.8 when billed as standard, and 0.3 when billed as premium.": "在 JSON 中,外層鍵是用戶分組,內層鍵是收費分組。下面的示例表示:vip 用戶按 standard 收費時用 0.8,按 premium 收費時用 0.3。",
......@@ -2325,6 +2332,7 @@
"Invalid JSON in parameter override template": "參數覆蓋模板中的 JSON 格式無效",
"Invalid JSON string.": "無效的 JSON 字串。",
"Invalid model mapping format": "無效的模型映射格式",
"Invalid model mapping in row {{row}}.": "第 {{row}} 行的模型映射無效。",
"Invalid Passkey registration response": "無效的 Passkey 註冊回應",
"Invalid Passkey response": "無效的 Passkey 回應",
"Invalid payment redirect URL": "無效的支付跳轉連結",
......@@ -2642,6 +2650,9 @@
"Model Limits": "模型限制",
"Model Mapping": "模型映射",
"Model Mapping (JSON)": "模型映射 (JSON)",
"Model mapping import completed: {{count}} mappings imported.": "Model mapping import completed: {{count}} mappings imported.",
"Model mapping import failed": "Model mapping import failed",
"Model mapping import requirements": "Model mapping import requirements",
"Model Mapping must be a JSON object like": "模型映射必須是如下所示的 JSON 物件",
"Model mapping must be a JSON object with string values": "模型映射必須是值為字串的 JSON 物件",
"Model mapping must be a valid JSON object": "模型映射必須是有效的 JSON 物件",
......@@ -2883,6 +2894,7 @@
"No missing models found.": "未找到缺失的模型。",
"No model found.": "未找到模型。",
"No model mappings configured. Click \"Add Mapping\" to get started.": "未設定模型映射。點擊「新增映射」即可開始使用。",
"No model mappings found in the Excel worksheet.": "Excel 工作表中找不到模型映射。",
"No model price changes to save": "沒有模型價格變更需要儲存",
"No models available": "沒有可用的模型",
"No models available in this category": "該分類下沒有可用模型",
......@@ -4424,8 +4436,11 @@
"The entered text does not match the required text.": "輸入文字與要求文字不匹配。",
"The environment (test vs production) is decided by the key you paste here — use the Test key while integrating, then swap to the Production key when going live.": "環境(測試或生產)由你在此貼上的金鑰決定。整合期間使用測試金鑰,上線時再切換為生產金鑰。",
"The exact model identifier as used in API requests.": "API 請求中使用的確切模型標識符。",
"The Excel workbook must contain a worksheet.": "Excel 活頁簿必須包含一個工作表。",
"The Excel worksheet must contain the headers “模型名称” and “映射模型”.": "Excel 工作表必須包含「模型名称」和「映射模型」欄位名稱。",
"The first row must contain headers, and model_name is required. For per-request billing, provide fixed_price. Otherwise input_price is required; optional columns are completion_price, cache_price, create_cache_price, image_price, audio_input_price, and audio_output_price.": "第一列必須是表頭,且 model_name 為必填欄位。按次計費請填寫 fixed_price;否則必須填寫 input_price。可選欄位:completion_price、cache_price、create_cache_price、image_price、audio_input_price、audio_output_price。",
"The first row must contain headers, and model_name is required. Optional columns: description, icon, tags, vendor_name, endpoints, status, sync_official, name_rule.": "第一列必須是表頭,且 model_name 為必填欄位。可選欄位:description、icon、tags、vendor_name、endpoints、status、sync_official、name_rule。",
"The first row must contain the headers “模型名称” and “映射模型”. Both columns are required.": "The first row must contain the headers “模型名称” and “映射模型”. Both columns are required.",
"The following models have billing type conflicts (fixed price vs ratio billing). Confirm to proceed with the changes.": "以下模型存在收費類型衝突(固定價格 vs 比例收費)。確認以繼續更改。",
"The following models in the model redirect have not been added to the \"Models\" list and may fail during invocation due to missing available models:": "模型重新導向裡的下列模型尚未新增到「模型」列表,呼叫時會因為缺少可用模型而失敗:",
"The mapped upstream model(s)": "映射的上游模型",
......
......@@ -1445,6 +1445,7 @@
"Drawing task records": "绘图任务记录",
"Duplicate": "重复",
"Duplicate group names: {{names}}": "存在重复的分组名称:{{names}}",
"Duplicate model name in row {{row}}: {{model}}.": "第 {{row}} 行的模型名称重复:{{model}}。",
"Duplicate source model mappings are not allowed": "不允许重复的源模型映射",
"Duplicate source model(s): {{models}}": "重复的源模型:{{models}}",
"Duration": "耗时",
......@@ -1721,6 +1722,7 @@
"example.com&#10;blocked-site.com": "example.com&#10;blocked-site.com",
"example.com&#10;company.com": "example.com&#10;company.com",
"Excel file": "Excel 文件",
"Excel file cannot exceed 10 MB.": "Excel 文件不能超过 10 MB。",
"Excel import": "Excel 导入",
"Excellent": "优秀",
"Exchange rate is required": "汇率为必填项",
......@@ -1771,6 +1773,7 @@
"Fail Reason Details": "失败原因详情",
"failed": "已失败",
"Failed": "失败",
"Failed models: {{models}}": "导入失败的模型:{{models}}",
"Failed to {{action}} user": "{{action}}用户失败",
"Failed to adjust quota": "调整额度失败",
"Failed to apply overwrite.": "应用覆盖失败。",
......@@ -2261,12 +2264,16 @@
"Image Tokens": "图像 Token",
"Imagine the pricing table has three groups: default (ratio 1.0), premium (ratio 0.5), and vip (ratio 0.8). Users whose account is in the vip group get user-level perks, and premium is a cheaper channel pool that users can pick for their tokens.": "假设定价分组表里有三个分组:default(倍率 1.0)、premium(倍率 0.5)、vip(倍率 0.8)。账号在 vip 分组的用户享受用户级待遇,premium 则是一个更便宜的渠道池,用户建令牌时可以选它。",
"Import": "导入",
"Import from Excel": "从 Excel 导入",
"Import mappings from an Excel workbook.": "从 Excel 工作簿导入模型映射。",
"Import model mappings": "导入模型映射",
"Import model metadata from an Excel workbook.": "从 Excel 工作簿导入模型元数据。",
"Import model prices from an Excel workbook.": "从 Excel 工作簿导入模型定价。",
"Import model pricing": "导入模型定价",
"Import models": "导入模型",
"Import to CC Switch": "填入 CC Switch",
"Important": "重要",
"Imported mappings replace existing mappings with the same model name.": "导入的映射会替换同名模型的现有映射。",
"Importing replaces the billing mode for matching models: per-token billing clears fixed prices, while per-request billing clears ratio settings.": "导入会覆盖匹配模型的计费方式:按量计费会清除固定价格,按次计费会清除倍率设置。",
"Importing...": "正在导入...",
"In JSON, the user group is the outer key and the billing group is the inner key. The example below means: vip users pay 0.8 when billed as standard, and 0.3 when billed as premium.": "在 JSON 中,外层键是用户分组,内层键是计费分组。下面的示例表示:vip 用户按 standard 计费时用 0.8,按 premium 计费时用 0.3。",
......@@ -2325,6 +2332,7 @@
"Invalid JSON in parameter override template": "参数覆盖模板中的 JSON 格式无效",
"Invalid JSON string.": "无效的 JSON 字符串。",
"Invalid model mapping format": "无效的模型映射格式",
"Invalid model mapping in row {{row}}.": "第 {{row}} 行的模型映射无效。",
"Invalid Passkey registration response": "无效的 Passkey 注册响应",
"Invalid Passkey response": "无效的 Passkey 响应",
"Invalid payment redirect URL": "无效的支付跳转链接",
......@@ -2642,6 +2650,9 @@
"Model Limits": "模型限制",
"Model Mapping": "模型映射",
"Model Mapping (JSON)": "模型映射 (JSON)",
"Model mapping import completed: {{count}} mappings imported.": "模型映射导入完成:已导入 {{count}} 条映射。",
"Model mapping import failed": "模型映射导入失败",
"Model mapping import requirements": "模型映射导入要求",
"Model Mapping must be a JSON object like": "模型映射必须是如下所示的 JSON 对象",
"Model mapping must be a JSON object with string values": "模型映射必须是值为字符串的 JSON 对象",
"Model mapping must be a valid JSON object": "模型映射必须是有效的 JSON 对象",
......@@ -2883,6 +2894,7 @@
"No missing models found.": "未找到缺失的模型。",
"No model found.": "未找到模型。",
"No model mappings configured. Click \"Add Mapping\" to get started.": "未配置模型映射。点击“添加映射”即可开始使用。",
"No model mappings found in the Excel worksheet.": "Excel 工作表中未找到模型映射。",
"No model price changes to save": "没有模型价格变更需要保存",
"No models available": "没有可用的模型",
"No models available in this category": "该分类下没有可用模型",
......@@ -4424,8 +4436,11 @@
"The entered text does not match the required text.": "输入文本与要求文本不匹配。",
"The environment (test vs production) is decided by the key you paste here — use the Test key while integrating, then swap to the Production key when going live.": "环境(测试或生产)由你在此粘贴的密钥决定。集成期间使用测试密钥,上线时再切换为生产密钥。",
"The exact model identifier as used in API requests.": "API 请求中使用的确切模型标识符。",
"The Excel workbook must contain a worksheet.": "Excel 工作簿必须包含一个工作表。",
"The Excel worksheet must contain the headers “模型名称” and “映射模型”.": "Excel 工作表必须包含“模型名称”和“映射模型”表头。",
"The first row must contain headers, and model_name is required. For per-request billing, provide fixed_price. Otherwise input_price is required; optional columns are completion_price, cache_price, create_cache_price, image_price, audio_input_price, and audio_output_price.": "第一行必须是表头,且 model_name 为必填列。按次计费请填写 fixed_price;否则必须填写 input_price。可选列:completion_price、cache_price、create_cache_price、image_price、audio_input_price、audio_output_price。",
"The first row must contain headers, and model_name is required. Optional columns: description, icon, tags, vendor_name, endpoints, status, sync_official, name_rule.": "第一行必须是表头,且 model_name 为必填列。可选列:description、icon、tags、vendor_name、endpoints、status、sync_official、name_rule。",
"The first row must contain the headers “模型名称” and “映射模型”. Both columns are required.": "第一行必须包含“模型名称”和“映射模型”表头,两列均为必填。",
"The following models have billing type conflicts (fixed price vs ratio billing). Confirm to proceed with the changes.": "以下模型存在计费类型冲突(固定价格 vs 比例计费)。确认以继续更改。",
"The following models in the model redirect have not been added to the \"Models\" list and may fail during invocation due to missing available models:": "模型重定向里的下列模型尚未添加到\"模型\"列表,调用时会因为缺少可用模型而失败:",
"The mapped upstream model(s)": "映射的上游模型",
......
......@@ -419,8 +419,8 @@ For commercial licensing, please contact support@quantumnous.com
* cards/borders rather than tinting them with the clay accent).
* - `simple-large`: keeps intentionally neutral, high-contrast surfaces. */
[data-theme-preset]:not([data-theme-preset='default']):not(
[data-theme-preset='anthropic']
):not([data-theme-preset='simple-large']) {
[data-theme-preset='anthropic']
):not([data-theme-preset='simple-large']) {
--card: color-mix(in oklch, var(--primary) 3%, var(--background));
--popover: color-mix(in oklch, var(--primary) 5%, var(--background));
--muted: color-mix(in oklch, var(--primary) 7%, var(--background));
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment