/* 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 from 'react'; import { Modal, Button, Input, Table, Tag, Typography, Select, } from '@douyinfe/semi-ui'; import { IconSearch } from '@douyinfe/semi-icons'; import { copy, showError, showInfo, showSuccess } from '../../../../helpers'; import { MODEL_TABLE_PAGE_SIZE } from '../../../../constants'; const ModelTestModal = ({ showModelTestModal, currentTestChannel, handleCloseModal, isBatchTesting, batchTestModels, modelSearchKeyword, setModelSearchKeyword, selectedModelKeys, setSelectedModelKeys, modelTestResults, testingModels, testChannel, modelTablePage, setModelTablePage, selectedEndpointType, setSelectedEndpointType, allSelectingRef, isMobile, t, }) => { const hasChannel = Boolean(currentTestChannel); const filteredModels = hasChannel ? currentTestChannel.models .split(',') .filter((model) => model.toLowerCase().includes(modelSearchKeyword.toLowerCase()), ) : []; const endpointTypeOptions = [ { value: '', label: t('自动检测') }, { value: 'openai', label: 'OpenAI (/v1/chat/completions)' }, { value: 'openai-response', label: 'OpenAI Response (/v1/responses)' }, { value: 'anthropic', label: 'Anthropic (/v1/messages)' }, { value: 'gemini', label: 'Gemini (/v1beta/models/{model}:generateContent)', }, { value: 'jina-rerank', label: 'Jina Rerank (/rerank)' }, { value: 'image-generation', label: t('图像生成') + ' (/v1/images/generations)', }, { value: 'embeddings', label: 'Embeddings (/v1/embeddings)' }, ]; const handleCopySelected = () => { if (selectedModelKeys.length === 0) { showError(t('请先选择模型!')); return; } copy(selectedModelKeys.join(',')).then((ok) => { if (ok) { showSuccess( t('已复制 ${count} 个模型').replace( '${count}', selectedModelKeys.length, ), ); } else { showError(t('复制失败,请手动复制')); } }); }; const handleSelectSuccess = () => { if (!currentTestChannel) return; const successKeys = currentTestChannel.models .split(',') .filter((m) => m.toLowerCase().includes(modelSearchKeyword.toLowerCase())) .filter((m) => { const result = modelTestResults[`${currentTestChannel.id}-${m}`]; return result && result.success; }); if (successKeys.length === 0) { showInfo(t('暂无成功模型')); } setSelectedModelKeys(successKeys); }; const columns = [ { title: t('模型名称'), dataIndex: 'model', render: (text) => (
{text}
), }, { title: t('状态'), dataIndex: 'status', render: (text, record) => { const testResult = modelTestResults[`${currentTestChannel.id}-${record.model}`]; const isTesting = testingModels.has(record.model); if (isTesting) { return ( {t('测试中')} ); } if (!testResult) { return ( {t('未开始')} ); } return (
{testResult.success ? t('成功') : t('失败')} {testResult.success && ( {t('请求时长: ${time}s').replace( '${time}', testResult.time.toFixed(2), )} )}
); }, }, { title: '', dataIndex: 'operate', render: (text, record) => { const isTesting = testingModels.has(record.model); return ( ); }, }, ]; const dataSource = (() => { if (!hasChannel) return []; const start = (modelTablePage - 1) * MODEL_TABLE_PAGE_SIZE; const end = start + MODEL_TABLE_PAGE_SIZE; return filteredModels.slice(start, end).map((model) => ({ model, key: model, })); })(); return (
{currentTestChannel.name} {t('渠道的模型测试')} {t('共')} {currentTestChannel.models.split(',').length}{' '} {t('个模型')}
) : null } visible={showModelTestModal} onCancel={handleCloseModal} footer={ hasChannel ? (
{isBatchTesting ? ( ) : ( )}
) : null } maskClosable={!isBatchTesting} className='!rounded-lg' size={isMobile ? 'full-width' : 'large'} > {hasChannel && (
{/* 端点类型选择器 */}
{t('端点类型')}: { setModelSearchKeyword(v); setModelTablePage(1); }} className='!w-full' prefix={} showClear />
{ if (allSelectingRef.current) { allSelectingRef.current = false; return; } setSelectedModelKeys(keys); }, onSelectAll: (checked) => { allSelectingRef.current = true; setSelectedModelKeys(checked ? filteredModels : []); }, }} pagination={{ currentPage: modelTablePage, pageSize: MODEL_TABLE_PAGE_SIZE, total: filteredModels.length, showSizeChanger: false, onPageChange: (page) => setModelTablePage(page), }} /> )} ); }; export default ModelTestModal;