chore: update project files

This commit is contained in:
CJACK
2026-04-27 02:09:11 +08:00
parent 40d5e3ebb5
commit 90ce595325
27 changed files with 511 additions and 150 deletions

View File

@@ -1,3 +1,4 @@
import { useEffect, useMemo, useState } from 'react'
import clsx from 'clsx'
import { useI18n } from '../../i18n'
@@ -6,8 +7,75 @@ import { useChatStreamClient } from './useChatStreamClient'
import ConfigPanel from './ConfigPanel'
import ChatPanel from './ChatPanel'
function describeModel(t, modelID) {
const noThinking = modelID.endsWith('-nothinking')
let description = t('apiTester.models.generic')
if (modelID.includes('vision-search')) {
description = t('apiTester.models.visionSearch')
} else if (modelID.includes('vision')) {
description = t('apiTester.models.vision')
} else if (modelID.includes('pro-search')) {
description = t('apiTester.models.proSearch')
} else if (modelID.includes('pro')) {
description = t('apiTester.models.pro')
} else if (modelID.includes('flash-search')) {
description = t('apiTester.models.flashSearch')
} else if (modelID.includes('flash')) {
description = t('apiTester.models.flash')
}
if (noThinking) {
return `${description} · ${t('apiTester.models.noThinking')}`
}
return description
}
function decorateModel(t, modelID) {
const isVision = modelID.includes('vision')
const isSearch = modelID.includes('search')
const isPro = modelID.includes('pro')
if (isVision && isSearch) {
return {
id: modelID,
name: modelID,
icon: 'ImageIcon',
desc: describeModel(t, modelID),
color: 'text-fuchsia-600',
}
}
if (isVision) {
return {
id: modelID,
name: modelID,
icon: 'ImageIcon',
desc: describeModel(t, modelID),
color: 'text-violet-500',
}
}
if (isSearch) {
return {
id: modelID,
name: modelID,
icon: 'SearchIcon',
desc: describeModel(t, modelID),
color: isPro ? 'text-cyan-600' : 'text-cyan-500',
}
}
return {
id: modelID,
name: modelID,
icon: isPro ? 'Cpu' : 'MessageSquare',
desc: describeModel(t, modelID),
color: isPro ? 'text-amber-600' : 'text-amber-500',
}
}
export default function ApiTesterContainer({ config, onMessage, authFetch }) {
const { t } = useI18n()
const [availableModelIDs, setAvailableModelIDs] = useState([])
const [modelsLoaded, setModelsLoaded] = useState(false)
const {
model,
@@ -49,14 +117,58 @@ export default function ApiTesterContainer({ config, onMessage, authFetch }) {
const customKeyActive = trimmedApiKey !== ''
const customKeyManaged = customKeyActive && configuredKeys.includes(trimmedApiKey)
const models = [
{ id: 'deepseek-v4-flash', name: 'deepseek-v4-flash', icon: 'MessageSquare', desc: t('apiTester.models.flash'), color: 'text-amber-500' },
{ id: 'deepseek-v4-pro', name: 'deepseek-v4-pro', icon: 'Cpu', desc: t('apiTester.models.pro'), color: 'text-amber-600' },
{ id: 'deepseek-v4-flash-search', name: 'deepseek-v4-flash-search', icon: 'SearchIcon', desc: t('apiTester.models.flashSearch'), color: 'text-cyan-500' },
{ id: 'deepseek-v4-pro-search', name: 'deepseek-v4-pro-search', icon: 'SearchIcon', desc: t('apiTester.models.proSearch'), color: 'text-cyan-600' },
{ id: 'deepseek-v4-vision', name: 'deepseek-v4-vision', icon: 'ImageIcon', desc: t('apiTester.models.vision'), color: 'text-violet-500' },
{ id: 'deepseek-v4-vision-search', name: 'deepseek-v4-vision-search', icon: 'SearchIcon', desc: t('apiTester.models.visionSearch'), color: 'text-fuchsia-600' },
]
useEffect(() => {
let disposed = false
async function loadModels() {
try {
const res = await authFetch('/v1/models')
if (!res.ok) {
throw new Error(`failed to fetch models: ${res.status}`)
}
const data = await res.json()
const modelIDs = Array.isArray(data?.data)
? data.data
.map((item) => String(item?.id || '').trim())
.filter(Boolean)
: []
if (!disposed) {
setAvailableModelIDs(modelIDs)
}
} catch (_err) {
if (!disposed) {
setAvailableModelIDs([])
}
} finally {
if (!disposed) {
setModelsLoaded(true)
}
}
}
setModelsLoaded(false)
loadModels()
return () => {
disposed = true
}
}, [authFetch])
const models = useMemo(
() => availableModelIDs.map((modelID) => decorateModel(t, modelID)),
[availableModelIDs, t]
)
useEffect(() => {
if (!models.length) {
if (model) {
setModel('')
}
return
}
if (!model || !models.some((item) => item.id === model)) {
setModel(models[0].id)
}
}, [model, models, setModel])
const { runTest, stopGeneration } = useChatStreamClient({
t,
@@ -84,6 +196,7 @@ export default function ApiTesterContainer({ config, onMessage, authFetch }) {
models={models}
model={model}
setModel={setModel}
modelsLoaded={modelsLoaded}
streamingMode={streamingMode}
setStreamingMode={setStreamingMode}
selectedAccount={selectedAccount}
@@ -114,6 +227,7 @@ export default function ApiTesterContainer({ config, onMessage, authFetch }) {
streamingContent={streamingContent}
onRunTest={runTest}
onStopGeneration={stopGeneration}
hasAvailableModel={models.length > 0}
/>
</div>
)